Commit | Line | Data |
---|---|---|
1da177e4 LT |
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> | |
8535fd6f | 34 | #include <linux/blk-mq.h> |
1da177e4 | 35 | #include <linux/bitops.h> |
2a48fc0a | 36 | #include <linux/mutex.h> |
5a0e3ad6 | 37 | #include <linux/slab.h> |
65fddcfc | 38 | #include <linux/pgtable.h> |
1da177e4 LT |
39 | |
40 | #include <asm/setup.h> | |
41 | #include <asm/amigahw.h> | |
1da177e4 LT |
42 | |
43 | #include <linux/zorro.h> | |
44 | ||
1da177e4 LT |
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) | |
6c3a05e1 | 52 | #define Z2MINOR_COUNT (8) /* Move this down when adding a new minor */ |
1da177e4 LT |
53 | |
54 | #define Z2RAM_CHUNK1024 ( Z2RAM_CHUNKSIZE >> 10 ) | |
55 | ||
2a48fc0a | 56 | static DEFINE_MUTEX(z2ram_mutex); |
6c3a05e1 CH |
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; | |
1da177e4 LT |
63 | |
64 | static DEFINE_SPINLOCK(z2ram_lock); | |
65 | ||
76487f02 | 66 | static struct gendisk *z2ram_gendisk[Z2MINOR_COUNT]; |
1da177e4 | 67 | |
8535fd6f JA |
68 | static blk_status_t z2_queue_rq(struct blk_mq_hw_ctx *hctx, |
69 | const struct blk_mq_queue_data *bd) | |
1da177e4 | 70 | { |
8535fd6f JA |
71 | struct request *req = bd->rq; |
72 | unsigned long start = blk_rq_pos(req) << 9; | |
6c3a05e1 | 73 | unsigned long len = blk_rq_cur_bytes(req); |
8535fd6f JA |
74 | |
75 | blk_mq_start_request(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(&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(req->bio); | |
91 | ||
92 | if (len < size) | |
93 | size = len; | |
6c3a05e1 | 94 | addr += z2ram_map[start >> Z2RAM_CHUNKSHIFT]; |
8535fd6f JA |
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; | |
1da177e4 | 101 | } |
8535fd6f JA |
102 | |
103 | spin_unlock_irq(&z2ram_lock); | |
104 | blk_mq_end_request(req, BLK_STS_OK); | |
105 | return BLK_STS_OK; | |
1da177e4 LT |
106 | } |
107 | ||
6c3a05e1 | 108 | static void get_z2ram(void) |
1da177e4 | 109 | { |
6c3a05e1 CH |
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(i, zorro_unused_z2ram); | |
119 | } | |
1da177e4 | 120 | } |
1da177e4 | 121 | |
6c3a05e1 | 122 | return; |
1da177e4 LT |
123 | } |
124 | ||
6c3a05e1 | 125 | static void get_chipram(void) |
1da177e4 LT |
126 | { |
127 | ||
6c3a05e1 CH |
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"); | |
1da177e4 | 132 | |
6c3a05e1 CH |
133 | if (z2ram_map[z2ram_size] == 0) { |
134 | break; | |
135 | } | |
136 | ||
137 | z2ram_size++; | |
1da177e4 LT |
138 | } |
139 | ||
6c3a05e1 | 140 | return; |
1da177e4 LT |
141 | } |
142 | ||
ab746cb9 | 143 | static int z2_open(struct block_device *bdev, fmode_t mode) |
1da177e4 | 144 | { |
6c3a05e1 CH |
145 | int device; |
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; | |
1da177e4 | 150 | |
6c3a05e1 | 151 | device = MINOR(bdev->bd_dev); |
1da177e4 | 152 | |
6c3a05e1 CH |
153 | mutex_lock(&z2ram_mutex); |
154 | if (current_device != -1 && current_device != device) { | |
155 | rc = -EBUSY; | |
156 | goto err_out; | |
157 | } | |
1da177e4 | 158 | |
6c3a05e1 CH |
159 | if (current_device == -1) { |
160 | z2_count = 0; | |
161 | chip_count = 0; | |
162 | list_count = 0; | |
163 | z2ram_size = 0; | |
1da177e4 | 164 | |
6c3a05e1 CH |
165 | /* Use a specific list entry. */ |
166 | if (device >= Z2MINOR_MEMLIST1 && device <= Z2MINOR_MEMLIST4) { | |
167 | int index = device - Z2MINOR_MEMLIST1 + 1; | |
168 | unsigned long size, paddr, vaddr; | |
1da177e4 | 169 | |
6c3a05e1 CH |
170 | if (index >= m68k_realnum_memory) { |
171 | printk(KERN_ERR DEVICE_NAME | |
172 | ": no such entry in z2ram_map\n"); | |
173 | goto err_out; | |
174 | } | |
1da177e4 | 175 | |
6c3a05e1 CH |
176 | paddr = m68k_memory[index].addr; |
177 | size = m68k_memory[index].size & ~(Z2RAM_CHUNKSIZE - 1); | |
1da177e4 | 178 | |
6c3a05e1 CH |
179 | #ifdef __powerpc__ |
180 | /* FIXME: ioremap doesn't build correct memory tables. */ | |
181 | { | |
182 | vfree(vmalloc(size)); | |
183 | } | |
1da177e4 | 184 | |
6c3a05e1 | 185 | vaddr = (unsigned long)ioremap_wt(paddr, size); |
1da177e4 | 186 | |
6c3a05e1 CH |
187 | #else |
188 | vaddr = | |
189 | (unsigned long)z_remap_nocache_nonser(paddr, size); | |
190 | #endif | |
191 | z2ram_map = | |
192 | kmalloc_array(size / Z2RAM_CHUNKSIZE, | |
193 | sizeof(z2ram_map[0]), GFP_KERNEL); | |
194 | if (z2ram_map == NULL) { | |
195 | printk(KERN_ERR DEVICE_NAME | |
196 | ": cannot get mem for z2ram_map\n"); | |
197 | goto err_out; | |
198 | } | |
199 | ||
200 | while (size) { | |
201 | z2ram_map[z2ram_size++] = vaddr; | |
202 | size -= Z2RAM_CHUNKSIZE; | |
203 | vaddr += Z2RAM_CHUNKSIZE; | |
204 | list_count++; | |
205 | } | |
206 | ||
207 | if (z2ram_size != 0) | |
208 | printk(KERN_INFO DEVICE_NAME | |
209 | ": using %iK List Entry %d Memory\n", | |
210 | list_count * Z2RAM_CHUNK1024, index); | |
211 | } else | |
212 | switch (device) { | |
213 | case Z2MINOR_COMBINED: | |
214 | ||
215 | z2ram_map = | |
216 | kmalloc(max_z2_map + max_chip_map, | |
217 | GFP_KERNEL); | |
218 | if (z2ram_map == NULL) { | |
219 | printk(KERN_ERR DEVICE_NAME | |
220 | ": cannot get mem for z2ram_map\n"); | |
221 | goto err_out; | |
222 | } | |
223 | ||
224 | get_z2ram(); | |
225 | get_chipram(); | |
226 | ||
227 | if (z2ram_size != 0) | |
228 | printk(KERN_INFO DEVICE_NAME | |
229 | ": using %iK Zorro II RAM and %iK Chip RAM (Total %dK)\n", | |
230 | z2_count * Z2RAM_CHUNK1024, | |
231 | chip_count * Z2RAM_CHUNK1024, | |
232 | (z2_count + | |
233 | chip_count) * Z2RAM_CHUNK1024); | |
234 | ||
235 | break; | |
236 | ||
237 | case Z2MINOR_Z2ONLY: | |
238 | z2ram_map = kmalloc(max_z2_map, GFP_KERNEL); | |
ec1e7e88 | 239 | if (!z2ram_map) |
6c3a05e1 | 240 | goto err_out; |
6c3a05e1 CH |
241 | |
242 | get_z2ram(); | |
243 | ||
244 | if (z2ram_size != 0) | |
245 | printk(KERN_INFO DEVICE_NAME | |
246 | ": using %iK of Zorro II RAM\n", | |
247 | z2_count * Z2RAM_CHUNK1024); | |
248 | ||
249 | break; | |
250 | ||
251 | case Z2MINOR_CHIPONLY: | |
252 | z2ram_map = kmalloc(max_chip_map, GFP_KERNEL); | |
ec1e7e88 | 253 | if (!z2ram_map) |
6c3a05e1 | 254 | goto err_out; |
6c3a05e1 CH |
255 | |
256 | get_chipram(); | |
257 | ||
258 | if (z2ram_size != 0) | |
259 | printk(KERN_INFO DEVICE_NAME | |
260 | ": using %iK Chip RAM\n", | |
261 | chip_count * Z2RAM_CHUNK1024); | |
262 | ||
263 | break; | |
264 | ||
265 | default: | |
266 | rc = -ENODEV; | |
267 | goto err_out; | |
268 | ||
269 | break; | |
270 | } | |
271 | ||
272 | if (z2ram_size == 0) { | |
273 | printk(KERN_NOTICE DEVICE_NAME | |
274 | ": no unused ZII/Chip RAM found\n"); | |
275 | goto err_out_kfree; | |
1da177e4 LT |
276 | } |
277 | ||
6c3a05e1 CH |
278 | current_device = device; |
279 | z2ram_size <<= Z2RAM_CHUNKSHIFT; | |
76487f02 | 280 | set_capacity(z2ram_gendisk[device], z2ram_size >> 9); |
1da177e4 LT |
281 | } |
282 | ||
6c3a05e1 CH |
283 | mutex_unlock(&z2ram_mutex); |
284 | return 0; | |
1da177e4 LT |
285 | |
286 | err_out_kfree: | |
6c3a05e1 | 287 | kfree(z2ram_map); |
1da177e4 | 288 | err_out: |
6c3a05e1 CH |
289 | mutex_unlock(&z2ram_mutex); |
290 | return rc; | |
1da177e4 LT |
291 | } |
292 | ||
6c3a05e1 | 293 | static void z2_release(struct gendisk *disk, fmode_t mode) |
1da177e4 | 294 | { |
6c3a05e1 CH |
295 | mutex_lock(&z2ram_mutex); |
296 | if (current_device == -1) { | |
297 | mutex_unlock(&z2ram_mutex); | |
298 | return; | |
299 | } | |
300 | mutex_unlock(&z2ram_mutex); | |
301 | /* | |
302 | * FIXME: unmap memory | |
303 | */ | |
1da177e4 LT |
304 | } |
305 | ||
6c3a05e1 CH |
306 | static const struct block_device_operations z2_fops = { |
307 | .owner = THIS_MODULE, | |
308 | .open = z2_open, | |
309 | .release = z2_release, | |
1da177e4 LT |
310 | }; |
311 | ||
8535fd6f JA |
312 | static struct blk_mq_tag_set tag_set; |
313 | ||
314 | static const struct blk_mq_ops z2_mq_ops = { | |
6c3a05e1 | 315 | .queue_rq = z2_queue_rq, |
8535fd6f | 316 | }; |
1da177e4 | 317 | |
76487f02 CH |
318 | static int z2ram_register_disk(int minor) |
319 | { | |
76487f02 | 320 | struct gendisk *disk; |
15733754 | 321 | int err; |
76487f02 | 322 | |
ec06c989 CH |
323 | disk = blk_mq_alloc_disk(&tag_set, NULL); |
324 | if (IS_ERR(disk)) | |
325 | return PTR_ERR(disk); | |
76487f02 CH |
326 | |
327 | disk->major = Z2RAM_MAJOR; | |
328 | disk->first_minor = minor; | |
ec06c989 | 329 | disk->minors = 1; |
1ebe2e5f | 330 | disk->flags |= GENHD_FL_NO_PART; |
76487f02 CH |
331 | disk->fops = &z2_fops; |
332 | if (minor) | |
333 | sprintf(disk->disk_name, "z2ram%d", minor); | |
334 | else | |
335 | sprintf(disk->disk_name, "z2ram"); | |
76487f02 CH |
336 | |
337 | z2ram_gendisk[minor] = disk; | |
15733754 LC |
338 | err = add_disk(disk); |
339 | if (err) | |
8b9ab626 | 340 | put_disk(disk); |
15733754 | 341 | return err; |
76487f02 CH |
342 | } |
343 | ||
6c3a05e1 | 344 | static int __init z2_init(void) |
1da177e4 | 345 | { |
76487f02 | 346 | int ret, i; |
1da177e4 | 347 | |
6c3a05e1 CH |
348 | if (!MACH_IS_AMIGA) |
349 | return -ENODEV; | |
1da177e4 | 350 | |
6c3a05e1 | 351 | if (register_blkdev(Z2RAM_MAJOR, DEVICE_NAME)) |
76487f02 CH |
352 | return -EBUSY; |
353 | ||
354 | tag_set.ops = &z2_mq_ops; | |
355 | tag_set.nr_hw_queues = 1; | |
356 | tag_set.nr_maps = 1; | |
357 | tag_set.queue_depth = 16; | |
358 | tag_set.numa_node = NUMA_NO_NODE; | |
359 | tag_set.flags = BLK_MQ_F_SHOULD_MERGE; | |
360 | ret = blk_mq_alloc_tag_set(&tag_set); | |
361 | if (ret) | |
362 | goto out_unregister_blkdev; | |
363 | ||
364 | for (i = 0; i < Z2MINOR_COUNT; i++) { | |
365 | ret = z2ram_register_disk(i); | |
366 | if (ret && i == 0) | |
367 | goto out_free_tagset; | |
6c3a05e1 | 368 | } |
1da177e4 | 369 | |
6c3a05e1 | 370 | return 0; |
1da177e4 | 371 | |
76487f02 CH |
372 | out_free_tagset: |
373 | blk_mq_free_tag_set(&tag_set); | |
374 | out_unregister_blkdev: | |
6c3a05e1 | 375 | unregister_blkdev(Z2RAM_MAJOR, DEVICE_NAME); |
6c3a05e1 | 376 | return ret; |
1da177e4 LT |
377 | } |
378 | ||
f39d88ad | 379 | static void __exit z2_exit(void) |
1da177e4 | 380 | { |
6c3a05e1 | 381 | int i, j; |
76487f02 | 382 | |
6c3a05e1 | 383 | unregister_blkdev(Z2RAM_MAJOR, DEVICE_NAME); |
76487f02 CH |
384 | |
385 | for (i = 0; i < Z2MINOR_COUNT; i++) { | |
386 | del_gendisk(z2ram_gendisk[i]); | |
76487f02 CH |
387 | put_disk(z2ram_gendisk[i]); |
388 | } | |
6c3a05e1 CH |
389 | blk_mq_free_tag_set(&tag_set); |
390 | ||
391 | if (current_device != -1) { | |
392 | i = 0; | |
393 | ||
394 | for (j = 0; j < z2_count; j++) { | |
395 | set_bit(i++, zorro_unused_z2ram); | |
396 | } | |
1da177e4 | 397 | |
6c3a05e1 CH |
398 | for (j = 0; j < chip_count; j++) { |
399 | if (z2ram_map[i]) { | |
400 | amiga_chip_free((void *)z2ram_map[i++]); | |
401 | } | |
402 | } | |
1da177e4 | 403 | |
6c3a05e1 CH |
404 | if (z2ram_map != NULL) { |
405 | kfree(z2ram_map); | |
406 | } | |
1da177e4 | 407 | } |
1da177e4 | 408 | |
6c3a05e1 CH |
409 | return; |
410 | } | |
f39d88ad AV |
411 | |
412 | module_init(z2_init); | |
413 | module_exit(z2_exit); | |
414 | MODULE_LICENSE("GPL"); |