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