From d9b2a2bbbb4d0bc89129504eb1503bb8506158ed Mon Sep 17 00:00:00 2001 From: Lauri Kasanen Date: Sat, 23 Jan 2021 09:53:27 +0200 Subject: block: Add n64 cart driver This adds support for the Nintendo 64 console's carts. Carts are a read-only media ranging from 8mb to 64mb. Only one cart can be connected at once, and switching it requires a reboot. No module support to save RAM, as the target has 8mb RAM. Signed-off-by: Lauri Kasanen Reviewed-by: Chaitanya Kulkarni Reviewed-by: Christoph Hellwig Acked-by: Jens Axboe Signed-off-by: Thomas Bogendoerfer --- drivers/block/Kconfig | 6 ++ drivers/block/Makefile | 1 + drivers/block/n64cart.c | 189 ++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 196 insertions(+) create mode 100644 drivers/block/n64cart.c (limited to 'drivers/block') diff --git a/drivers/block/Kconfig b/drivers/block/Kconfig index 262326973ee0..32ef21563e2a 100644 --- a/drivers/block/Kconfig +++ b/drivers/block/Kconfig @@ -66,6 +66,12 @@ config AMIGA_Z2RAM To compile this driver as a module, choose M here: the module will be called z2ram. +config N64CART + bool "N64 cart support" + depends on MACH_NINTENDO64 + help + Support for the N64 cart. + config CDROM tristate select BLK_SCSI_REQUEST diff --git a/drivers/block/Makefile b/drivers/block/Makefile index a3170859e01d..2aeee3f5f3ce 100644 --- a/drivers/block/Makefile +++ b/drivers/block/Makefile @@ -17,6 +17,7 @@ obj-$(CONFIG_PS3_DISK) += ps3disk.o obj-$(CONFIG_PS3_VRAM) += ps3vram.o obj-$(CONFIG_ATARI_FLOPPY) += ataflop.o obj-$(CONFIG_AMIGA_Z2RAM) += z2ram.o +obj-$(CONFIG_N64CART) += n64cart.o obj-$(CONFIG_BLK_DEV_RAM) += brd.o obj-$(CONFIG_BLK_DEV_LOOP) += loop.o obj-$(CONFIG_XILINX_SYSACE) += xsysace.o diff --git a/drivers/block/n64cart.c b/drivers/block/n64cart.c new file mode 100644 index 000000000000..e76722acba46 --- /dev/null +++ b/drivers/block/n64cart.c @@ -0,0 +1,189 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Support for the N64 cart. + * + * Copyright (c) 2021 Lauri Kasanen + */ + +#include +#include +#include +#include +#include +#include + +MODULE_AUTHOR("Lauri Kasanen "); +MODULE_DESCRIPTION("Driver for the N64 cart"); +MODULE_LICENSE("GPL"); + +static unsigned int start, size; +static u32 __iomem *reg_base; +static struct device *dev; + +#define PI_DRAM_REG 0 +#define PI_CART_REG 1 +#define PI_READ_REG 2 +#define PI_WRITE_REG 3 +#define PI_STATUS_REG 4 + +#define PI_STATUS_DMA_BUSY (1 << 0) +#define PI_STATUS_IO_BUSY (1 << 1) + +#define CART_DOMAIN 0x10000000 +#define CART_MAX 0x1FFFFFFF + +#define MIN_ALIGNMENT 8 + +static void n64cart_write_reg(const u8 reg, const u32 value) +{ + writel(value, reg_base + reg); +} + +static u32 n64cart_read_reg(const u8 reg) +{ + return readl(reg_base + reg); +} + +static void n64cart_wait_dma(void) +{ + while (n64cart_read_reg(PI_STATUS_REG) & + (PI_STATUS_DMA_BUSY | PI_STATUS_IO_BUSY)) + cpu_relax(); +} + +/* + * Process a single bvec of a bio. + */ +static bool n64cart_do_bvec(struct device *dev, struct bio_vec *bv, u32 pos) +{ + dma_addr_t dma_addr; + const u32 bstart = pos + start; + + /* Alignment check */ + WARN_ON_ONCE((bv->bv_offset & (MIN_ALIGNMENT - 1)) || + (bv->bv_len & (MIN_ALIGNMENT - 1))); + + dma_addr = dma_map_bvec(dev, bv, DMA_FROM_DEVICE, 0); + if (dma_mapping_error(dev, dma_addr)) + return false; + + n64cart_wait_dma(); + + n64cart_write_reg(PI_DRAM_REG, dma_addr + bv->bv_offset); + n64cart_write_reg(PI_CART_REG, (bstart | CART_DOMAIN) & CART_MAX); + n64cart_write_reg(PI_WRITE_REG, bv->bv_len - 1); + + n64cart_wait_dma(); + + dma_unmap_page(dev, dma_addr, bv->bv_len, DMA_FROM_DEVICE); + return true; +} + +static blk_qc_t n64cart_submit_bio(struct bio *bio) +{ + struct bio_vec bvec; + u32 pos; + struct bvec_iter iter; + + pos = bio->bi_iter.bi_sector << SECTOR_SHIFT; + + bio_for_each_segment(bvec, bio, iter) { + if (!n64cart_do_bvec(dev, &bvec, pos)) + goto io_error; + pos += bvec.bv_len; + } + + bio_endio(bio); + return BLK_QC_T_NONE; +io_error: + bio_io_error(bio); + return BLK_QC_T_NONE; +} + +static const struct block_device_operations n64cart_fops = { + .owner = THIS_MODULE, + .submit_bio = n64cart_submit_bio, +}; + +/* + * The target device is embedded and RAM-constrained. We save RAM + * by initializing in __init code that gets dropped late in boot. + * For the same reason there is no module or unloading support. + */ +static int __init n64cart_probe(struct platform_device *pdev) +{ + int err; + struct request_queue *queue; + struct gendisk *disk; + + if (!start || !size) { + pr_err("n64cart: start and size not specified\n"); + return -ENODEV; + } + + if (size & 4095) { + pr_err("n64cart: size must be a multiple of 4K\n"); + return -ENODEV; + } + + queue = blk_alloc_queue(NUMA_NO_NODE); + if (!queue) { + return -ENOMEM; + } + + reg_base = devm_platform_ioremap_resource(pdev, 0); + if (!reg_base) { + err = -EINVAL; + goto fail_queue; + } + + disk = alloc_disk(0); + if (!disk) { + err = -ENOMEM; + goto fail_queue; + } + + dev = &pdev->dev; + + disk->first_minor = 0; + disk->queue = queue; + disk->flags = GENHD_FL_NO_PART_SCAN | GENHD_FL_EXT_DEVT; + disk->fops = &n64cart_fops; + strcpy(disk->disk_name, "n64cart"); + + set_capacity(disk, size / 512); + set_disk_ro(disk, 1); + + blk_queue_flag_set(QUEUE_FLAG_NONROT, queue); + blk_queue_physical_block_size(queue, 4096); + blk_queue_logical_block_size(queue, 4096); + + add_disk(disk); + + pr_info("n64cart: %u kb disk\n", size / 1024); + + return 0; +fail_queue: + blk_cleanup_queue(queue); + + return err; +} + +static struct platform_driver n64cart_driver = { + .driver = { + .name = "n64cart", + }, +}; + +static int __init n64cart_init(void) +{ + return platform_driver_probe(&n64cart_driver, n64cart_probe); +} + +module_param(start, uint, 0); +MODULE_PARM_DESC(start, "Start address of the cart block data"); + +module_param(size, uint, 0); +MODULE_PARM_DESC(size, "Size of the cart block data, in bytes"); + +module_init(n64cart_init); -- cgit v1.2.3 From f1e19224f5948ae61ff9972d35d3cd7176815cd9 Mon Sep 17 00:00:00 2001 From: Chaitanya Kulkarni Date: Mon, 25 Jan 2021 15:32:35 -0800 Subject: n64: use pr_fmt to avoid duplicate string Instead of repeating the n64cart string all over the module use pr_fmt macro and remove the duplicate string. Also, replace and with or in the one of the error message. Signed-off-by: Chaitanya Kulkarni Reviewed-by: Lauri Kasanen Signed-off-by: Thomas Bogendoerfer --- drivers/block/n64cart.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'drivers/block') diff --git a/drivers/block/n64cart.c b/drivers/block/n64cart.c index e76722acba46..8c7c9249071b 100644 --- a/drivers/block/n64cart.c +++ b/drivers/block/n64cart.c @@ -5,6 +5,7 @@ * Copyright (c) 2021 Lauri Kasanen */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include #include #include @@ -117,12 +118,12 @@ static int __init n64cart_probe(struct platform_device *pdev) struct gendisk *disk; if (!start || !size) { - pr_err("n64cart: start and size not specified\n"); + pr_err("start or size not specified\n"); return -ENODEV; } if (size & 4095) { - pr_err("n64cart: size must be a multiple of 4K\n"); + pr_err("size must be a multiple of 4K\n"); return -ENODEV; } -- cgit v1.2.3 From 9ee8c9a1c752f6181c1403fa5b4da620b410d9af Mon Sep 17 00:00:00 2001 From: Chaitanya Kulkarni Date: Mon, 25 Jan 2021 15:32:36 -0800 Subject: n64: move module info at the end Move the module auth, description, and license at the end of the file just like what we have for the other modules. Signed-off-by: Chaitanya Kulkarni Reviewed-by: Lauri Kasanen Signed-off-by: Thomas Bogendoerfer --- drivers/block/n64cart.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'drivers/block') diff --git a/drivers/block/n64cart.c b/drivers/block/n64cart.c index 8c7c9249071b..63090030ed2b 100644 --- a/drivers/block/n64cart.c +++ b/drivers/block/n64cart.c @@ -13,10 +13,6 @@ #include #include -MODULE_AUTHOR("Lauri Kasanen "); -MODULE_DESCRIPTION("Driver for the N64 cart"); -MODULE_LICENSE("GPL"); - static unsigned int start, size; static u32 __iomem *reg_base; static struct device *dev; @@ -188,3 +184,7 @@ module_param(size, uint, 0); MODULE_PARM_DESC(size, "Size of the cart block data, in bytes"); module_init(n64cart_init); + +MODULE_AUTHOR("Lauri Kasanen "); +MODULE_DESCRIPTION("Driver for the N64 cart"); +MODULE_LICENSE("GPL"); -- cgit v1.2.3 From e39e31326305d9bb35f8ab78c4310b9a38bbb3aa Mon Sep 17 00:00:00 2001 From: Chaitanya Kulkarni Date: Mon, 25 Jan 2021 15:32:37 -0800 Subject: n64: move module param at the top Move module parameters at the top of the file after macro definition & global variables below macro definitions just like we have for other modules. Signed-off-by: Chaitanya Kulkarni Reviewed-by: Lauri Kasanen Signed-off-by: Thomas Bogendoerfer --- drivers/block/n64cart.c | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) (limited to 'drivers/block') diff --git a/drivers/block/n64cart.c b/drivers/block/n64cart.c index 63090030ed2b..b18f034ee1ad 100644 --- a/drivers/block/n64cart.c +++ b/drivers/block/n64cart.c @@ -13,10 +13,6 @@ #include #include -static unsigned int start, size; -static u32 __iomem *reg_base; -static struct device *dev; - #define PI_DRAM_REG 0 #define PI_CART_REG 1 #define PI_READ_REG 2 @@ -31,6 +27,17 @@ static struct device *dev; #define MIN_ALIGNMENT 8 +static u32 __iomem *reg_base; +static struct device *dev; + +static unsigned int start; +module_param(start, uint, 0); +MODULE_PARM_DESC(start, "Start address of the cart block data"); + +static unsigned int size; +module_param(size, uint, 0); +MODULE_PARM_DESC(size, "Size of the cart block data, in bytes"); + static void n64cart_write_reg(const u8 reg, const u32 value) { writel(value, reg_base + reg); @@ -177,12 +184,6 @@ static int __init n64cart_init(void) return platform_driver_probe(&n64cart_driver, n64cart_probe); } -module_param(start, uint, 0); -MODULE_PARM_DESC(start, "Start address of the cart block data"); - -module_param(size, uint, 0); -MODULE_PARM_DESC(size, "Size of the cart block data, in bytes"); - module_init(n64cart_init); MODULE_AUTHOR("Lauri Kasanen "); -- cgit v1.2.3 From 2ce503b35dcea29767c6d03b44e3c535809fdfcc Mon Sep 17 00:00:00 2001 From: Chaitanya Kulkarni Date: Mon, 25 Jan 2021 15:32:38 -0800 Subject: n64: use enums for reg Macros tend to be not type-safe. Use enum for register definitions. Signed-off-by: Chaitanya Kulkarni Reviewed-by: Lauri Kasanen Signed-off-by: Thomas Bogendoerfer --- drivers/block/n64cart.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) (limited to 'drivers/block') diff --git a/drivers/block/n64cart.c b/drivers/block/n64cart.c index b18f034ee1ad..620f9e080d5d 100644 --- a/drivers/block/n64cart.c +++ b/drivers/block/n64cart.c @@ -13,11 +13,13 @@ #include #include -#define PI_DRAM_REG 0 -#define PI_CART_REG 1 -#define PI_READ_REG 2 -#define PI_WRITE_REG 3 -#define PI_STATUS_REG 4 +enum { + PI_DRAM_REG = 0, + PI_CART_REG, + PI_READ_REG, + PI_WRITE_REG, + PI_STATUS_REG, +}; #define PI_STATUS_DMA_BUSY (1 << 0) #define PI_STATUS_IO_BUSY (1 << 1) -- cgit v1.2.3 From 857f6fde1c6e800b685c2da864dabd7ff9091dca Mon Sep 17 00:00:00 2001 From: Chaitanya Kulkarni Date: Mon, 25 Jan 2021 15:32:39 -0800 Subject: n64: use sector SECTOR_SHIFT instead 512 Instead of using magic numbers use SECTOR_SHIFT to get the number of sectors from the size. Signed-off-by: Chaitanya Kulkarni Reviewed-by: Lauri Kasanen Signed-off-by: Thomas Bogendoerfer --- drivers/block/n64cart.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/block') diff --git a/drivers/block/n64cart.c b/drivers/block/n64cart.c index 620f9e080d5d..c83a6af5a718 100644 --- a/drivers/block/n64cart.c +++ b/drivers/block/n64cart.c @@ -157,7 +157,7 @@ static int __init n64cart_probe(struct platform_device *pdev) disk->fops = &n64cart_fops; strcpy(disk->disk_name, "n64cart"); - set_capacity(disk, size / 512); + set_capacity(disk, size >> SECTOR_SHIFT); set_disk_ro(disk, 1); blk_queue_flag_set(QUEUE_FLAG_NONROT, queue); -- cgit v1.2.3 From 82a0c13a08d8265fe6412f8683a6011ce881df49 Mon Sep 17 00:00:00 2001 From: Chaitanya Kulkarni Date: Mon, 25 Jan 2021 15:32:40 -0800 Subject: n64: remove curly brackets Remove extra braces for the if which has only single statement. Signed-off-by: Chaitanya Kulkarni Reviewed-by: Lauri Kasanen Signed-off-by: Thomas Bogendoerfer --- drivers/block/n64cart.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'drivers/block') diff --git a/drivers/block/n64cart.c b/drivers/block/n64cart.c index c83a6af5a718..7906b5b2f12e 100644 --- a/drivers/block/n64cart.c +++ b/drivers/block/n64cart.c @@ -133,9 +133,8 @@ static int __init n64cart_probe(struct platform_device *pdev) } queue = blk_alloc_queue(NUMA_NO_NODE); - if (!queue) { + if (!queue) return -ENOMEM; - } reg_base = devm_platform_ioremap_resource(pdev, 0); if (!reg_base) { -- cgit v1.2.3 From 37772f9136f442a1098d0ae1238def72f1216057 Mon Sep 17 00:00:00 2001 From: Chaitanya Kulkarni Date: Mon, 25 Jan 2021 15:32:41 -0800 Subject: n64: cosmetics changes Make the variable declaration ascending order and initialize the variables at the time of declaration when possible. Signed-off-by: Chaitanya Kulkarni Reviewed-by: Lauri Kasanen Signed-off-by: Thomas Bogendoerfer --- drivers/block/n64cart.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) (limited to 'drivers/block') diff --git a/drivers/block/n64cart.c b/drivers/block/n64cart.c index 7906b5b2f12e..3bfb010402e3 100644 --- a/drivers/block/n64cart.c +++ b/drivers/block/n64cart.c @@ -88,10 +88,8 @@ static bool n64cart_do_bvec(struct device *dev, struct bio_vec *bv, u32 pos) static blk_qc_t n64cart_submit_bio(struct bio *bio) { struct bio_vec bvec; - u32 pos; struct bvec_iter iter; - - pos = bio->bi_iter.bi_sector << SECTOR_SHIFT; + u32 pos = bio->bi_iter.bi_sector << SECTOR_SHIFT; bio_for_each_segment(bvec, bio, iter) { if (!n64cart_do_bvec(dev, &bvec, pos)) @@ -119,8 +117,8 @@ static const struct block_device_operations n64cart_fops = { static int __init n64cart_probe(struct platform_device *pdev) { int err; - struct request_queue *queue; struct gendisk *disk; + struct request_queue *queue; if (!start || !size) { pr_err("start or size not specified\n"); -- cgit v1.2.3 From 0d424780852eb60467a6f053d92495bb845ac186 Mon Sep 17 00:00:00 2001 From: Chaitanya Kulkarni Date: Mon, 25 Jan 2021 15:32:42 -0800 Subject: n64: cleanup n64cart_probe() The goto label fail_queue is needed to cleanup the queue allocation when devm_platform_ioremap_resource() or alloc_disk() fails, either of these two functions are not dependent on the queue variable which is allocated prior to these calls. Allocate the queue variable after successful alloc_disk(). Return error directly when devm_platform_ioremap_resource() or alloc_disk() fail. Remove fail_queue label and a call to the blk_cleanup_queue(). Direct return from these two functions allows us to remove the local variable err and allocating queue after alloc_disk() allows us to remove the local variable queue so we use disk->queue directly. Signed-off-by: Chaitanya Kulkarni Reviewed-by: Lauri Kasanen Signed-off-by: Thomas Bogendoerfer --- drivers/block/n64cart.c | 33 +++++++++++---------------------- 1 file changed, 11 insertions(+), 22 deletions(-) (limited to 'drivers/block') diff --git a/drivers/block/n64cart.c b/drivers/block/n64cart.c index 3bfb010402e3..43482d158640 100644 --- a/drivers/block/n64cart.c +++ b/drivers/block/n64cart.c @@ -116,9 +116,7 @@ static const struct block_device_operations n64cart_fops = { */ static int __init n64cart_probe(struct platform_device *pdev) { - int err; struct gendisk *disk; - struct request_queue *queue; if (!start || !size) { pr_err("start or size not specified\n"); @@ -130,26 +128,21 @@ static int __init n64cart_probe(struct platform_device *pdev) return -ENODEV; } - queue = blk_alloc_queue(NUMA_NO_NODE); - if (!queue) - return -ENOMEM; - reg_base = devm_platform_ioremap_resource(pdev, 0); - if (!reg_base) { - err = -EINVAL; - goto fail_queue; - } + if (!reg_base) + return -EINVAL; disk = alloc_disk(0); - if (!disk) { - err = -ENOMEM; - goto fail_queue; - } + if (!disk) + return -ENOMEM; + + disk->queue = blk_alloc_queue(NUMA_NO_NODE); + if (!disk->queue) + return -ENOMEM; dev = &pdev->dev; disk->first_minor = 0; - disk->queue = queue; disk->flags = GENHD_FL_NO_PART_SCAN | GENHD_FL_EXT_DEVT; disk->fops = &n64cart_fops; strcpy(disk->disk_name, "n64cart"); @@ -157,19 +150,15 @@ static int __init n64cart_probe(struct platform_device *pdev) set_capacity(disk, size >> SECTOR_SHIFT); set_disk_ro(disk, 1); - blk_queue_flag_set(QUEUE_FLAG_NONROT, queue); - blk_queue_physical_block_size(queue, 4096); - blk_queue_logical_block_size(queue, 4096); + blk_queue_flag_set(QUEUE_FLAG_NONROT, disk->queue); + blk_queue_physical_block_size(disk->queue, 4096); + blk_queue_logical_block_size(disk->queue, 4096); add_disk(disk); pr_info("n64cart: %u kb disk\n", size / 1024); return 0; -fail_queue: - blk_cleanup_queue(queue); - - return err; } static struct platform_driver n64cart_driver = { -- cgit v1.2.3 From 13d41b537df7d2538f901aa98f82672482b50d12 Mon Sep 17 00:00:00 2001 From: Chaitanya Kulkarni Date: Mon, 25 Jan 2021 15:32:43 -0800 Subject: n64: store dev instance into disk private data The device instance is declared globally. Remove global variable & use the disk->private_data to store the device instance in the n64cart_probe() and get the same instance from bio->bi_disk->private data in n64cart_submit_bio. Signed-off-by: Chaitanya Kulkarni Reviewed-by: Lauri Kasanen Signed-off-by: Thomas Bogendoerfer --- drivers/block/n64cart.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'drivers/block') diff --git a/drivers/block/n64cart.c b/drivers/block/n64cart.c index 43482d158640..47bdf324e962 100644 --- a/drivers/block/n64cart.c +++ b/drivers/block/n64cart.c @@ -30,7 +30,6 @@ enum { #define MIN_ALIGNMENT 8 static u32 __iomem *reg_base; -static struct device *dev; static unsigned int start; module_param(start, uint, 0); @@ -89,6 +88,7 @@ static blk_qc_t n64cart_submit_bio(struct bio *bio) { struct bio_vec bvec; struct bvec_iter iter; + struct device *dev = bio->bi_disk->private_data; u32 pos = bio->bi_iter.bi_sector << SECTOR_SHIFT; bio_for_each_segment(bvec, bio, iter) { @@ -140,11 +140,10 @@ static int __init n64cart_probe(struct platform_device *pdev) if (!disk->queue) return -ENOMEM; - dev = &pdev->dev; - disk->first_minor = 0; disk->flags = GENHD_FL_NO_PART_SCAN | GENHD_FL_EXT_DEVT; disk->fops = &n64cart_fops; + disk->private_data = &pdev->dev; strcpy(disk->disk_name, "n64cart"); set_capacity(disk, size >> SECTOR_SHIFT); -- cgit v1.2.3