1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * Support for the N64 cart. 4 * 5 * Copyright (c) 2021 Lauri Kasanen 6 */ 7 8 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 9 #include <linux/bitops.h> 10 #include <linux/blkdev.h> 11 #include <linux/dma-mapping.h> 12 #include <linux/init.h> 13 #include <linux/module.h> 14 #include <linux/platform_device.h> 15 16 MODULE_AUTHOR("Lauri Kasanen <cand@gmx.com>"); 17 MODULE_DESCRIPTION("Driver for the N64 cart"); 18 MODULE_LICENSE("GPL"); 19 20 static unsigned int start, size; 21 static u32 __iomem *reg_base; 22 static struct device *dev; 23 24 #define PI_DRAM_REG 0 25 #define PI_CART_REG 1 26 #define PI_READ_REG 2 27 #define PI_WRITE_REG 3 28 #define PI_STATUS_REG 4 29 30 #define PI_STATUS_DMA_BUSY (1 << 0) 31 #define PI_STATUS_IO_BUSY (1 << 1) 32 33 #define CART_DOMAIN 0x10000000 34 #define CART_MAX 0x1FFFFFFF 35 36 #define MIN_ALIGNMENT 8 37 38 static void n64cart_write_reg(const u8 reg, const u32 value) 39 { 40 writel(value, reg_base + reg); 41 } 42 43 static u32 n64cart_read_reg(const u8 reg) 44 { 45 return readl(reg_base + reg); 46 } 47 48 static void n64cart_wait_dma(void) 49 { 50 while (n64cart_read_reg(PI_STATUS_REG) & 51 (PI_STATUS_DMA_BUSY | PI_STATUS_IO_BUSY)) 52 cpu_relax(); 53 } 54 55 /* 56 * Process a single bvec of a bio. 57 */ 58 static bool n64cart_do_bvec(struct device *dev, struct bio_vec *bv, u32 pos) 59 { 60 dma_addr_t dma_addr; 61 const u32 bstart = pos + start; 62 63 /* Alignment check */ 64 WARN_ON_ONCE((bv->bv_offset & (MIN_ALIGNMENT - 1)) || 65 (bv->bv_len & (MIN_ALIGNMENT - 1))); 66 67 dma_addr = dma_map_bvec(dev, bv, DMA_FROM_DEVICE, 0); 68 if (dma_mapping_error(dev, dma_addr)) 69 return false; 70 71 n64cart_wait_dma(); 72 73 n64cart_write_reg(PI_DRAM_REG, dma_addr + bv->bv_offset); 74 n64cart_write_reg(PI_CART_REG, (bstart | CART_DOMAIN) & CART_MAX); 75 n64cart_write_reg(PI_WRITE_REG, bv->bv_len - 1); 76 77 n64cart_wait_dma(); 78 79 dma_unmap_page(dev, dma_addr, bv->bv_len, DMA_FROM_DEVICE); 80 return true; 81 } 82 83 static blk_qc_t n64cart_submit_bio(struct bio *bio) 84 { 85 struct bio_vec bvec; 86 u32 pos; 87 struct bvec_iter iter; 88 89 pos = bio->bi_iter.bi_sector << SECTOR_SHIFT; 90 91 bio_for_each_segment(bvec, bio, iter) { 92 if (!n64cart_do_bvec(dev, &bvec, pos)) 93 goto io_error; 94 pos += bvec.bv_len; 95 } 96 97 bio_endio(bio); 98 return BLK_QC_T_NONE; 99 io_error: 100 bio_io_error(bio); 101 return BLK_QC_T_NONE; 102 } 103 104 static const struct block_device_operations n64cart_fops = { 105 .owner = THIS_MODULE, 106 .submit_bio = n64cart_submit_bio, 107 }; 108 109 /* 110 * The target device is embedded and RAM-constrained. We save RAM 111 * by initializing in __init code that gets dropped late in boot. 112 * For the same reason there is no module or unloading support. 113 */ 114 static int __init n64cart_probe(struct platform_device *pdev) 115 { 116 int err; 117 struct request_queue *queue; 118 struct gendisk *disk; 119 120 if (!start || !size) { 121 pr_err("start or size not specified\n"); 122 return -ENODEV; 123 } 124 125 if (size & 4095) { 126 pr_err("size must be a multiple of 4K\n"); 127 return -ENODEV; 128 } 129 130 queue = blk_alloc_queue(NUMA_NO_NODE); 131 if (!queue) { 132 return -ENOMEM; 133 } 134 135 reg_base = devm_platform_ioremap_resource(pdev, 0); 136 if (!reg_base) { 137 err = -EINVAL; 138 goto fail_queue; 139 } 140 141 disk = alloc_disk(0); 142 if (!disk) { 143 err = -ENOMEM; 144 goto fail_queue; 145 } 146 147 dev = &pdev->dev; 148 149 disk->first_minor = 0; 150 disk->queue = queue; 151 disk->flags = GENHD_FL_NO_PART_SCAN | GENHD_FL_EXT_DEVT; 152 disk->fops = &n64cart_fops; 153 strcpy(disk->disk_name, "n64cart"); 154 155 set_capacity(disk, size / 512); 156 set_disk_ro(disk, 1); 157 158 blk_queue_flag_set(QUEUE_FLAG_NONROT, queue); 159 blk_queue_physical_block_size(queue, 4096); 160 blk_queue_logical_block_size(queue, 4096); 161 162 add_disk(disk); 163 164 pr_info("n64cart: %u kb disk\n", size / 1024); 165 166 return 0; 167 fail_queue: 168 blk_cleanup_queue(queue); 169 170 return err; 171 } 172 173 static struct platform_driver n64cart_driver = { 174 .driver = { 175 .name = "n64cart", 176 }, 177 }; 178 179 static int __init n64cart_init(void) 180 { 181 return platform_driver_probe(&n64cart_driver, n64cart_probe); 182 } 183 184 module_param(start, uint, 0); 185 MODULE_PARM_DESC(start, "Start address of the cart block data"); 186 187 module_param(size, uint, 0); 188 MODULE_PARM_DESC(size, "Size of the cart block data, in bytes"); 189 190 module_init(n64cart_init); 191