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