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