blob: 8c7c9249071b42a4d991a7fb7c2af556e719fb61 [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
Chaitanya Kulkarnif1e19222021-01-25 15:32:35 -08008#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
Lauri Kasanend9b2a2b2021-01-23 09:53:27 +02009#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
16MODULE_AUTHOR("Lauri Kasanen <cand@gmx.com>");
17MODULE_DESCRIPTION("Driver for the N64 cart");
18MODULE_LICENSE("GPL");
19
20static unsigned int start, size;
21static u32 __iomem *reg_base;
22static 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
38static void n64cart_write_reg(const u8 reg, const u32 value)
39{
40 writel(value, reg_base + reg);
41}
42
43static u32 n64cart_read_reg(const u8 reg)
44{
45 return readl(reg_base + reg);
46}
47
48static 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 */
58static 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
83static 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;
99io_error:
100 bio_io_error(bio);
101 return BLK_QC_T_NONE;
102}
103
104static 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 */
114static 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) {
Chaitanya Kulkarnif1e19222021-01-25 15:32:35 -0800121 pr_err("start or size not specified\n");
Lauri Kasanend9b2a2b2021-01-23 09:53:27 +0200122 return -ENODEV;
123 }
124
125 if (size & 4095) {
Chaitanya Kulkarnif1e19222021-01-25 15:32:35 -0800126 pr_err("size must be a multiple of 4K\n");
Lauri Kasanend9b2a2b2021-01-23 09:53:27 +0200127 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;
167fail_queue:
168 blk_cleanup_queue(queue);
169
170 return err;
171}
172
173static struct platform_driver n64cart_driver = {
174 .driver = {
175 .name = "n64cart",
176 },
177};
178
179static int __init n64cart_init(void)
180{
181 return platform_driver_probe(&n64cart_driver, n64cart_probe);
182}
183
184module_param(start, uint, 0);
185MODULE_PARM_DESC(start, "Start address of the cart block data");
186
187module_param(size, uint, 0);
188MODULE_PARM_DESC(size, "Size of the cart block data, in bytes");
189
190module_init(n64cart_init);