1 | /* |
2 | ** z2ram - Amiga pseudo-driver to access 16bit-RAM in ZorroII space |
3 | ** as a block device, to be used as a RAM disk or swap space |
4 | ** |
5 | ** Copyright (C) 1994 by Ingo Wilken (Ingo.Wilken@informatik.uni-oldenburg.de) |
6 | ** |
7 | ** ++Geert: support for zorro_unused_z2ram, better range checking |
8 | ** ++roman: translate accesses via an array |
9 | ** ++Milan: support for ChipRAM usage |
10 | ** ++yambo: converted to 2.0 kernel |
11 | ** ++yambo: modularized and support added for 3 minor devices including: |
12 | ** MAJOR MINOR DESCRIPTION |
13 | ** ----- ----- ---------------------------------------------- |
14 | ** 37 0 Use Zorro II and Chip ram |
15 | ** 37 1 Use only Zorro II ram |
16 | ** 37 2 Use only Chip ram |
17 | ** 37 4-7 Use memory list entry 1-4 (first is 0) |
18 | ** ++jskov: support for 1-4th memory list entry. |
19 | ** |
20 | ** Permission to use, copy, modify, and distribute this software and its |
21 | ** documentation for any purpose and without fee is hereby granted, provided |
22 | ** that the above copyright notice appear in all copies and that both that |
23 | ** copyright notice and this permission notice appear in supporting |
24 | ** documentation. This software is provided "as is" without express or |
25 | ** implied warranty. |
26 | */ |
27 | |
28 | #define DEVICE_NAME "Z2RAM" |
29 | |
30 | #include <linux/major.h> |
31 | #include <linux/vmalloc.h> |
32 | #include <linux/init.h> |
33 | #include <linux/module.h> |
34 | #include <linux/blk-mq.h> |
35 | #include <linux/bitops.h> |
36 | #include <linux/mutex.h> |
37 | #include <linux/slab.h> |
38 | #include <linux/pgtable.h> |
39 | |
40 | #include <asm/setup.h> |
41 | #include <asm/amigahw.h> |
42 | |
43 | #include <linux/zorro.h> |
44 | |
45 | #define Z2MINOR_COMBINED (0) |
46 | #define Z2MINOR_Z2ONLY (1) |
47 | #define Z2MINOR_CHIPONLY (2) |
48 | #define Z2MINOR_MEMLIST1 (4) |
49 | #define Z2MINOR_MEMLIST2 (5) |
50 | #define Z2MINOR_MEMLIST3 (6) |
51 | #define Z2MINOR_MEMLIST4 (7) |
52 | #define Z2MINOR_COUNT (8) /* Move this down when adding a new minor */ |
53 | |
54 | #define Z2RAM_CHUNK1024 ( Z2RAM_CHUNKSIZE >> 10 ) |
55 | |
56 | static DEFINE_MUTEX(z2ram_mutex); |
57 | static u_long *z2ram_map = NULL; |
58 | static u_long z2ram_size = 0; |
59 | static int z2_count = 0; |
60 | static int chip_count = 0; |
61 | static int list_count = 0; |
62 | static int current_device = -1; |
63 | |
64 | static DEFINE_SPINLOCK(z2ram_lock); |
65 | |
66 | static struct gendisk *z2ram_gendisk[Z2MINOR_COUNT]; |
67 | |
68 | static blk_status_t z2_queue_rq(struct blk_mq_hw_ctx *hctx, |
69 | const struct blk_mq_queue_data *bd) |
70 | { |
71 | struct request *req = bd->rq; |
72 | unsigned long start = blk_rq_pos(rq: req) << 9; |
73 | unsigned long len = blk_rq_cur_bytes(rq: req); |
74 | |
75 | blk_mq_start_request(rq: req); |
76 | |
77 | if (start + len > z2ram_size) { |
78 | pr_err(DEVICE_NAME ": bad access: block=%llu, " |
79 | "count=%u\n" , |
80 | (unsigned long long)blk_rq_pos(req), |
81 | blk_rq_cur_sectors(req)); |
82 | return BLK_STS_IOERR; |
83 | } |
84 | |
85 | spin_lock_irq(lock: &z2ram_lock); |
86 | |
87 | while (len) { |
88 | unsigned long addr = start & Z2RAM_CHUNKMASK; |
89 | unsigned long size = Z2RAM_CHUNKSIZE - addr; |
90 | void *buffer = bio_data(bio: req->bio); |
91 | |
92 | if (len < size) |
93 | size = len; |
94 | addr += z2ram_map[start >> Z2RAM_CHUNKSHIFT]; |
95 | if (rq_data_dir(req) == READ) |
96 | memcpy(buffer, (char *)addr, size); |
97 | else |
98 | memcpy((char *)addr, buffer, size); |
99 | start += size; |
100 | len -= size; |
101 | } |
102 | |
103 | spin_unlock_irq(lock: &z2ram_lock); |
104 | blk_mq_end_request(rq: req, BLK_STS_OK); |
105 | return BLK_STS_OK; |
106 | } |
107 | |
108 | static void get_z2ram(void) |
109 | { |
110 | int i; |
111 | |
112 | for (i = 0; i < Z2RAM_SIZE / Z2RAM_CHUNKSIZE; i++) { |
113 | if (test_bit(i, zorro_unused_z2ram)) { |
114 | z2_count++; |
115 | z2ram_map[z2ram_size++] = |
116 | (unsigned long)ZTWO_VADDR(Z2RAM_START) + |
117 | (i << Z2RAM_CHUNKSHIFT); |
118 | clear_bit(nr: i, addr: zorro_unused_z2ram); |
119 | } |
120 | } |
121 | |
122 | return; |
123 | } |
124 | |
125 | static void get_chipram(void) |
126 | { |
127 | |
128 | while (amiga_chip_avail() > (Z2RAM_CHUNKSIZE * 4)) { |
129 | chip_count++; |
130 | z2ram_map[z2ram_size] = |
131 | (u_long) amiga_chip_alloc(Z2RAM_CHUNKSIZE, "z2ram" ); |
132 | |
133 | if (z2ram_map[z2ram_size] == 0) { |
134 | break; |
135 | } |
136 | |
137 | z2ram_size++; |
138 | } |
139 | |
140 | return; |
141 | } |
142 | |
143 | static int z2_open(struct gendisk *disk, blk_mode_t mode) |
144 | { |
145 | int device = disk->first_minor; |
146 | int max_z2_map = (Z2RAM_SIZE / Z2RAM_CHUNKSIZE) * sizeof(z2ram_map[0]); |
147 | int max_chip_map = (amiga_chip_size / Z2RAM_CHUNKSIZE) * |
148 | sizeof(z2ram_map[0]); |
149 | int rc = -ENOMEM; |
150 | |
151 | mutex_lock(&z2ram_mutex); |
152 | if (current_device != -1 && current_device != device) { |
153 | rc = -EBUSY; |
154 | goto err_out; |
155 | } |
156 | |
157 | if (current_device == -1) { |
158 | z2_count = 0; |
159 | chip_count = 0; |
160 | list_count = 0; |
161 | z2ram_size = 0; |
162 | |
163 | /* Use a specific list entry. */ |
164 | if (device >= Z2MINOR_MEMLIST1 && device <= Z2MINOR_MEMLIST4) { |
165 | int index = device - Z2MINOR_MEMLIST1 + 1; |
166 | unsigned long size, paddr, vaddr; |
167 | |
168 | if (index >= m68k_realnum_memory) { |
169 | printk(KERN_ERR DEVICE_NAME |
170 | ": no such entry in z2ram_map\n" ); |
171 | goto err_out; |
172 | } |
173 | |
174 | paddr = m68k_memory[index].addr; |
175 | size = m68k_memory[index].size & ~(Z2RAM_CHUNKSIZE - 1); |
176 | |
177 | #ifdef __powerpc__ |
178 | /* FIXME: ioremap doesn't build correct memory tables. */ |
179 | { |
180 | vfree(vmalloc(size)); |
181 | } |
182 | |
183 | vaddr = (unsigned long)ioremap_wt(paddr, size); |
184 | |
185 | #else |
186 | vaddr = |
187 | (unsigned long)z_remap_nocache_nonser(paddr, size); |
188 | #endif |
189 | z2ram_map = |
190 | kmalloc_array(n: size / Z2RAM_CHUNKSIZE, |
191 | size: sizeof(z2ram_map[0]), GFP_KERNEL); |
192 | if (z2ram_map == NULL) { |
193 | printk(KERN_ERR DEVICE_NAME |
194 | ": cannot get mem for z2ram_map\n" ); |
195 | goto err_out; |
196 | } |
197 | |
198 | while (size) { |
199 | z2ram_map[z2ram_size++] = vaddr; |
200 | size -= Z2RAM_CHUNKSIZE; |
201 | vaddr += Z2RAM_CHUNKSIZE; |
202 | list_count++; |
203 | } |
204 | |
205 | if (z2ram_size != 0) |
206 | printk(KERN_INFO DEVICE_NAME |
207 | ": using %iK List Entry %d Memory\n" , |
208 | list_count * Z2RAM_CHUNK1024, index); |
209 | } else |
210 | switch (device) { |
211 | case Z2MINOR_COMBINED: |
212 | |
213 | z2ram_map = |
214 | kmalloc(size: max_z2_map + max_chip_map, |
215 | GFP_KERNEL); |
216 | if (z2ram_map == NULL) { |
217 | printk(KERN_ERR DEVICE_NAME |
218 | ": cannot get mem for z2ram_map\n" ); |
219 | goto err_out; |
220 | } |
221 | |
222 | get_z2ram(); |
223 | get_chipram(); |
224 | |
225 | if (z2ram_size != 0) |
226 | printk(KERN_INFO DEVICE_NAME |
227 | ": using %iK Zorro II RAM and %iK Chip RAM (Total %dK)\n" , |
228 | z2_count * Z2RAM_CHUNK1024, |
229 | chip_count * Z2RAM_CHUNK1024, |
230 | (z2_count + |
231 | chip_count) * Z2RAM_CHUNK1024); |
232 | |
233 | break; |
234 | |
235 | case Z2MINOR_Z2ONLY: |
236 | z2ram_map = kmalloc(size: max_z2_map, GFP_KERNEL); |
237 | if (!z2ram_map) |
238 | goto err_out; |
239 | |
240 | get_z2ram(); |
241 | |
242 | if (z2ram_size != 0) |
243 | printk(KERN_INFO DEVICE_NAME |
244 | ": using %iK of Zorro II RAM\n" , |
245 | z2_count * Z2RAM_CHUNK1024); |
246 | |
247 | break; |
248 | |
249 | case Z2MINOR_CHIPONLY: |
250 | z2ram_map = kmalloc(size: max_chip_map, GFP_KERNEL); |
251 | if (!z2ram_map) |
252 | goto err_out; |
253 | |
254 | get_chipram(); |
255 | |
256 | if (z2ram_size != 0) |
257 | printk(KERN_INFO DEVICE_NAME |
258 | ": using %iK Chip RAM\n" , |
259 | chip_count * Z2RAM_CHUNK1024); |
260 | |
261 | break; |
262 | |
263 | default: |
264 | rc = -ENODEV; |
265 | goto err_out; |
266 | |
267 | break; |
268 | } |
269 | |
270 | if (z2ram_size == 0) { |
271 | printk(KERN_NOTICE DEVICE_NAME |
272 | ": no unused ZII/Chip RAM found\n" ); |
273 | goto err_out_kfree; |
274 | } |
275 | |
276 | current_device = device; |
277 | z2ram_size <<= Z2RAM_CHUNKSHIFT; |
278 | set_capacity(disk: z2ram_gendisk[device], size: z2ram_size >> 9); |
279 | } |
280 | |
281 | mutex_unlock(lock: &z2ram_mutex); |
282 | return 0; |
283 | |
284 | err_out_kfree: |
285 | kfree(objp: z2ram_map); |
286 | err_out: |
287 | mutex_unlock(lock: &z2ram_mutex); |
288 | return rc; |
289 | } |
290 | |
291 | static void z2_release(struct gendisk *disk) |
292 | { |
293 | mutex_lock(&z2ram_mutex); |
294 | if (current_device == -1) { |
295 | mutex_unlock(lock: &z2ram_mutex); |
296 | return; |
297 | } |
298 | mutex_unlock(lock: &z2ram_mutex); |
299 | /* |
300 | * FIXME: unmap memory |
301 | */ |
302 | } |
303 | |
304 | static const struct block_device_operations z2_fops = { |
305 | .owner = THIS_MODULE, |
306 | .open = z2_open, |
307 | .release = z2_release, |
308 | }; |
309 | |
310 | static struct blk_mq_tag_set tag_set; |
311 | |
312 | static const struct blk_mq_ops z2_mq_ops = { |
313 | .queue_rq = z2_queue_rq, |
314 | }; |
315 | |
316 | static int z2ram_register_disk(int minor) |
317 | { |
318 | struct gendisk *disk; |
319 | int err; |
320 | |
321 | disk = blk_mq_alloc_disk(&tag_set, NULL); |
322 | if (IS_ERR(ptr: disk)) |
323 | return PTR_ERR(ptr: disk); |
324 | |
325 | disk->major = Z2RAM_MAJOR; |
326 | disk->first_minor = minor; |
327 | disk->minors = 1; |
328 | disk->flags |= GENHD_FL_NO_PART; |
329 | disk->fops = &z2_fops; |
330 | if (minor) |
331 | sprintf(buf: disk->disk_name, fmt: "z2ram%d" , minor); |
332 | else |
333 | sprintf(buf: disk->disk_name, fmt: "z2ram" ); |
334 | |
335 | z2ram_gendisk[minor] = disk; |
336 | err = add_disk(disk); |
337 | if (err) |
338 | put_disk(disk); |
339 | return err; |
340 | } |
341 | |
342 | static int __init z2_init(void) |
343 | { |
344 | int ret, i; |
345 | |
346 | if (!MACH_IS_AMIGA) |
347 | return -ENODEV; |
348 | |
349 | if (register_blkdev(Z2RAM_MAJOR, DEVICE_NAME)) |
350 | return -EBUSY; |
351 | |
352 | tag_set.ops = &z2_mq_ops; |
353 | tag_set.nr_hw_queues = 1; |
354 | tag_set.nr_maps = 1; |
355 | tag_set.queue_depth = 16; |
356 | tag_set.numa_node = NUMA_NO_NODE; |
357 | tag_set.flags = BLK_MQ_F_SHOULD_MERGE; |
358 | ret = blk_mq_alloc_tag_set(set: &tag_set); |
359 | if (ret) |
360 | goto out_unregister_blkdev; |
361 | |
362 | for (i = 0; i < Z2MINOR_COUNT; i++) { |
363 | ret = z2ram_register_disk(minor: i); |
364 | if (ret && i == 0) |
365 | goto out_free_tagset; |
366 | } |
367 | |
368 | return 0; |
369 | |
370 | out_free_tagset: |
371 | blk_mq_free_tag_set(set: &tag_set); |
372 | out_unregister_blkdev: |
373 | unregister_blkdev(Z2RAM_MAJOR, DEVICE_NAME); |
374 | return ret; |
375 | } |
376 | |
377 | static void __exit z2_exit(void) |
378 | { |
379 | int i, j; |
380 | |
381 | unregister_blkdev(Z2RAM_MAJOR, DEVICE_NAME); |
382 | |
383 | for (i = 0; i < Z2MINOR_COUNT; i++) { |
384 | del_gendisk(gp: z2ram_gendisk[i]); |
385 | put_disk(disk: z2ram_gendisk[i]); |
386 | } |
387 | blk_mq_free_tag_set(set: &tag_set); |
388 | |
389 | if (current_device != -1) { |
390 | i = 0; |
391 | |
392 | for (j = 0; j < z2_count; j++) { |
393 | set_bit(nr: i++, addr: zorro_unused_z2ram); |
394 | } |
395 | |
396 | for (j = 0; j < chip_count; j++) { |
397 | if (z2ram_map[i]) { |
398 | amiga_chip_free((void *)z2ram_map[i++]); |
399 | } |
400 | } |
401 | |
402 | if (z2ram_map != NULL) { |
403 | kfree(objp: z2ram_map); |
404 | } |
405 | } |
406 | |
407 | return; |
408 | } |
409 | |
410 | module_init(z2_init); |
411 | module_exit(z2_exit); |
412 | MODULE_LICENSE("GPL" ); |
413 | |