Message ID | 20210122110043.f36cd22df1233754753518c1@gmx.com (mailing list archive) |
---|---|
State | Superseded |
Headers | show |
Series | [v9] block: Add n64 cart driver | expand |
On Fri, 22 Jan 2021 11:17:27 +0000 Christoph Hellwig <hch@infradead.org> wrote: > Sorry for not noticing this earlier, but why do you need the bounce > buffer vs just mapping the data buffer? I think I've answered this once already a few iterations before. DMA alignment restrictions, not sure what guarantees the bio buffer has. - Lauri
On Fri, Jan 22, 2021 at 03:58:01PM +0200, Lauri Kasanen wrote: > On Fri, 22 Jan 2021 11:17:27 +0000 > Christoph Hellwig <hch@infradead.org> wrote: > > > Sorry for not noticing this earlier, but why do you need the bounce > > buffer vs just mapping the data buffer? > > I think I've answered this once already a few iterations before. DMA > alignment restrictions, not sure what guarantees the bio buffer has. Your driver can communicate the required alignment using the blk_queue_update_dma_alignment API.
On Fri, 22 Jan 2021 16:18:04 +0000 Christoph Hellwig <hch@infradead.org> wrote: > On Fri, Jan 22, 2021 at 03:58:01PM +0200, Lauri Kasanen wrote: > > On Fri, 22 Jan 2021 11:17:27 +0000 > > Christoph Hellwig <hch@infradead.org> wrote: > > > > > Sorry for not noticing this earlier, but why do you need the bounce > > > buffer vs just mapping the data buffer? > > > > I think I've answered this once already a few iterations before. DMA > > alignment restrictions, not sure what guarantees the bio buffer has. > > Your driver can communicate the required alignment using the > blk_queue_update_dma_alignment API. Is it guaranteed to be physically contiguous? - Lauri
On Fri, Jan 22, 2021 at 06:23:49PM +0200, Lauri Kasanen wrote: > > Your driver can communicate the required alignment using the > > blk_queue_update_dma_alignment API. > > Is it guaranteed to be physically contiguous? The alignment requirement applies to each bio_vec. What alignment does the hardware require?
On Fri, 22 Jan 2021 16:39:13 +0000 Christoph Hellwig <hch@infradead.org> wrote: > On Fri, Jan 22, 2021 at 06:23:49PM +0200, Lauri Kasanen wrote: > > > Your driver can communicate the required alignment using the > > > blk_queue_update_dma_alignment API. > > > > Is it guaranteed to be physically contiguous? > > The alignment requirement applies to each bio_vec. > > What alignment does the hardware require? It requires 8-byte alignment, but the buffer must also be physically contiguous. I grepped around, but apparently no driver under drivers/block does direct DMA to the bio buffer. They all use their own buffer and memcpy, like this patch. ps3vram, ps3disk, amiflop, etc etc. If all existing drivers use their own buffer, why is a different approach required for new drivers? - Lauri
On Fri, Jan 22, 2021 at 06:56:19PM +0200, Lauri Kasanen wrote: > > What alignment does the hardware require? > > It requires 8-byte alignment, but the buffer must also be physically > contiguous. If you want a single contiguous buffer you must use the blk_queue_max_segments() API to ask for a single contiguous segment. > I grepped around, but apparently no driver under drivers/block does > direct DMA to the bio buffer. They all use their own buffer and memcpy, > like this patch. ps3vram, ps3disk, amiflop, etc etc. > > If all existing drivers use their own buffer, why is a different > approach required for new drivers? That selections is a bunch of weirdo legacy drivers. If you want to look at something reasonably modern but still simple take a look at drivers/block/virtio_blk.c.
On Fri, Jan 22, 2021 at 06:56:19PM +0200, Lauri Kasanen wrote: > On Fri, 22 Jan 2021 16:39:13 +0000 > Christoph Hellwig <hch@infradead.org> wrote: > > > On Fri, Jan 22, 2021 at 06:23:49PM +0200, Lauri Kasanen wrote: > > > > Your driver can communicate the required alignment using the > > > > blk_queue_update_dma_alignment API. > > > > > > Is it guaranteed to be physically contiguous? > > > > The alignment requirement applies to each bio_vec. > > > > What alignment does the hardware require? > > It requires 8-byte alignment, but the buffer must also be physically > contiguous. Each bio_vec segment is physically contiguous. > I grepped around, but apparently no driver under drivers/block does > direct DMA to the bio buffer. They all use their own buffer and memcpy, > like this patch. ps3vram, ps3disk, amiflop, etc etc. block/brd.c > If all existing drivers use their own buffer, why is a different > approach required for new drivers? It's certainly more efficient to send the data directly to the destination without an unnecessary middle-man. And 64k seems like quite a lot of memory to sequestor considering how little you have to start with.
On Fri, 22 Jan 2021 09:11:20 -0800 Keith Busch <kbusch@kernel.org> wrote: > Each bio_vec segment is physically contiguous. What's the official way to get that buffer's physical address? I couldn't find any driver that does DMA to the bio buffer, to use as an example. Maybe used wrong keywords. > > I grepped around, but apparently no driver under drivers/block does > > direct DMA to the bio buffer. They all use their own buffer and memcpy, > > like this patch. ps3vram, ps3disk, amiflop, etc etc. > > block/brd.c That uses memcpy, not DMA? It uses the virtual address. I was hoping for an example that does direct DMA to the bio_data buffer. - Lauri
On Fri, Jan 22, 2021 at 08:01:17PM +0200, Lauri Kasanen wrote: > On Fri, 22 Jan 2021 09:11:20 -0800 > Keith Busch <kbusch@kernel.org> wrote: > > Each bio_vec segment is physically contiguous. > > What's the official way to get that buffer's physical address? I > couldn't find any driver that does DMA to the bio buffer, to use as an > example. Maybe used wrong keywords. In Linux we never DMA to the physical address, but the DMA address. For a bio with just a single bvec (that is a single contigous segment) we have the dma_map_bvec helper that is only used by the nvme-pci driver for special optimizations in the case of a single contigous segment.
diff --git a/drivers/block/Kconfig b/drivers/block/Kconfig index ecceaaa..924d768 100644 --- a/drivers/block/Kconfig +++ b/drivers/block/Kconfig @@ -72,6 +72,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 e1f6311..b9642cf 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 0000000..01dd008 --- /dev/null +++ b/drivers/block/n64cart.c @@ -0,0 +1,211 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Support for the N64 cart. + * + * Copyright (c) 2021 Lauri Kasanen + */ + +#include <linux/bitops.h> +#include <linux/blk-mq.h> +#include <linux/dma-mapping.h> +#include <linux/init.h> +#include <linux/module.h> +#include <linux/platform_device.h> + +MODULE_AUTHOR("Lauri Kasanen <cand@gmx.com>"); +MODULE_DESCRIPTION("Driver for the N64 cart"); +MODULE_LICENSE("GPL"); + +#define BUFSIZE SZ_64K + +static unsigned int start, size; +static struct request_queue *queue; +static struct blk_mq_tag_set tag_set; +static struct gendisk *disk; + +static void *buf; +static dma_addr_t dma_addr; + +static u32 __iomem *reg_base; + +#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 + +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(); +} + +static blk_status_t n64cart_get_seg(struct request *req) +{ + u32 bstart = blk_rq_pos(req) * 512; + u32 len = blk_rq_cur_bytes(req); + void *dst = bio_data(req->bio); + + if (bstart + len > size) + return BLK_STS_IOERR; + + bstart += start; + + while (len) { + const u32 curlen = min_t(u32, len, BUFSIZE); + + dma_sync_single_for_device(disk_to_dev(disk), dma_addr, + BUFSIZE, DMA_FROM_DEVICE); + + n64cart_wait_dma(); + + n64cart_write_reg(PI_DRAM_REG, dma_addr); + n64cart_write_reg(PI_CART_REG, + (bstart | CART_DOMAIN) & CART_MAX); + n64cart_write_reg(PI_WRITE_REG, curlen - 1); + + n64cart_wait_dma(); + + dma_sync_single_for_cpu(disk_to_dev(disk), dma_addr, + BUFSIZE, DMA_FROM_DEVICE); + + memcpy(dst, buf, curlen); + + len -= curlen; + dst += curlen; + bstart += curlen; + } + + return BLK_STS_OK; +} + +static blk_status_t n64cart_queue_rq(struct blk_mq_hw_ctx *hctx, + const struct blk_mq_queue_data *bd) +{ + struct request *req = bd->rq; + blk_status_t err; + + blk_mq_start_request(req); + + do { + err = n64cart_get_seg(req); + } while (blk_update_request(req, err, blk_rq_cur_bytes(req))); + + __blk_mq_end_request(req, BLK_STS_OK); + return BLK_STS_OK; +} + +static const struct blk_mq_ops n64cart_mq_ops = { + .queue_rq = n64cart_queue_rq, +}; + +static const struct block_device_operations n64cart_fops = { + .owner = THIS_MODULE, +}; + +/* + * 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; + + 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_mq_init_sq_queue(&tag_set, &n64cart_mq_ops, 1, + BLK_MQ_F_SHOULD_MERGE | BLK_MQ_F_BLOCKING); + if (IS_ERR(queue)) { + return PTR_ERR(queue); + } + + buf = dma_alloc_noncoherent(&pdev->dev, BUFSIZE, &dma_addr, + DMA_FROM_DEVICE, GFP_KERNEL); + if (!buf) { + err = -ENOMEM; + goto fail_queue; + } + + reg_base = devm_platform_ioremap_resource(pdev, 0); + if (!reg_base) { + err = -EINVAL; + goto fail_dma; + } + + disk = alloc_disk(0); + if (!disk) { + err = -ENOMEM; + goto fail_dma; + } + + 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_dma: + dma_free_noncoherent(&pdev->dev, BUFSIZE, buf, dma_addr, + DMA_FROM_DEVICE); +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);
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 <cand@gmx.com> --- drivers/block/Kconfig | 6 ++ drivers/block/Makefile | 1 + drivers/block/n64cart.c | 211 ++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 218 insertions(+) create mode 100644 drivers/block/n64cart.c v9: cosmetics min_t v8: SZ_64K remove barriers add defines for block domain constants __blk_mq_end_request remove register_blkdev via GENHD_FL_EXT_DEVT dma_alloc_noncoherent, sync -- 2.6.2