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 | ||
05bdb996 | 143 | static int z2_open(struct gendisk *disk, blk_mode_t mode) |
1da177e4 | 144 | { |
d32e2bf8 | 145 | int device = disk->first_minor; |
6c3a05e1 CH |
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 CH |
151 | mutex_lock(&z2ram_mutex); |
152 | if (current_device != -1 && current_device != device) { | |
153 | rc = -EBUSY; | |
154 | goto err_out; | |
155 | } | |
1da177e4 | 156 | |
6c3a05e1 CH |
157 | if (current_device == -1) { |
158 | z2_count = 0; | |
159 | chip_count = 0; | |
160 | list_count = 0; | |
161 | z2ram_size = 0; | |
1da177e4 | 162 | |
6c3a05e1 CH |
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; | |
1da177e4 | 167 | |
6c3a05e1 CH |
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 | } | |
1da177e4 | 173 | |
6c3a05e1 CH |
174 | paddr = m68k_memory[index].addr; |
175 | size = m68k_memory[index].size & ~(Z2RAM_CHUNKSIZE - 1); | |
1da177e4 | 176 | |
6c3a05e1 CH |
177 | #ifdef __powerpc__ |
178 | /* FIXME: ioremap doesn't build correct memory tables. */ | |
179 | { | |
180 | vfree(vmalloc(size)); | |
181 | } | |
1da177e4 | 182 | |
6c3a05e1 | 183 | vaddr = (unsigned long)ioremap_wt(paddr, size); |
1da177e4 | 184 | |
6c3a05e1 CH |
185 | #else |
186 | vaddr = | |
187 | (unsigned long)z_remap_nocache_nonser(paddr, size); | |
188 | #endif | |
189 | z2ram_map = | |
190 | kmalloc_array(size / Z2RAM_CHUNKSIZE, | |
191 | 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(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(max_z2_map, GFP_KERNEL); | |
ec1e7e88 | 237 | if (!z2ram_map) |
6c3a05e1 | 238 | goto err_out; |
6c3a05e1 CH |
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(max_chip_map, GFP_KERNEL); | |
ec1e7e88 | 251 | if (!z2ram_map) |
6c3a05e1 | 252 | goto err_out; |
6c3a05e1 CH |
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; | |
1da177e4 LT |
274 | } |
275 | ||
6c3a05e1 CH |
276 | current_device = device; |
277 | z2ram_size <<= Z2RAM_CHUNKSHIFT; | |
76487f02 | 278 | set_capacity(z2ram_gendisk[device], z2ram_size >> 9); |
1da177e4 LT |
279 | } |
280 | ||
6c3a05e1 CH |
281 | mutex_unlock(&z2ram_mutex); |
282 | return 0; | |
1da177e4 LT |
283 | |
284 | err_out_kfree: | |
6c3a05e1 | 285 | kfree(z2ram_map); |
1da177e4 | 286 | err_out: |
6c3a05e1 CH |
287 | mutex_unlock(&z2ram_mutex); |
288 | return rc; | |
1da177e4 LT |
289 | } |
290 | ||
ae220766 | 291 | static void z2_release(struct gendisk *disk) |
1da177e4 | 292 | { |
6c3a05e1 CH |
293 | mutex_lock(&z2ram_mutex); |
294 | if (current_device == -1) { | |
295 | mutex_unlock(&z2ram_mutex); | |
296 | return; | |
297 | } | |
298 | mutex_unlock(&z2ram_mutex); | |
299 | /* | |
300 | * FIXME: unmap memory | |
301 | */ | |
1da177e4 LT |
302 | } |
303 | ||
6c3a05e1 CH |
304 | static const struct block_device_operations z2_fops = { |
305 | .owner = THIS_MODULE, | |
306 | .open = z2_open, | |
307 | .release = z2_release, | |
1da177e4 LT |
308 | }; |
309 | ||
8535fd6f JA |
310 | static struct blk_mq_tag_set tag_set; |
311 | ||
312 | static const struct blk_mq_ops z2_mq_ops = { | |
6c3a05e1 | 313 | .queue_rq = z2_queue_rq, |
8535fd6f | 314 | }; |
1da177e4 | 315 | |
76487f02 CH |
316 | static int z2ram_register_disk(int minor) |
317 | { | |
76487f02 | 318 | struct gendisk *disk; |
15733754 | 319 | int err; |
76487f02 | 320 | |
ec06c989 CH |
321 | disk = blk_mq_alloc_disk(&tag_set, NULL); |
322 | if (IS_ERR(disk)) | |
323 | return PTR_ERR(disk); | |
76487f02 CH |
324 | |
325 | disk->major = Z2RAM_MAJOR; | |
326 | disk->first_minor = minor; | |
ec06c989 | 327 | disk->minors = 1; |
1ebe2e5f | 328 | disk->flags |= GENHD_FL_NO_PART; |
76487f02 CH |
329 | disk->fops = &z2_fops; |
330 | if (minor) | |
331 | sprintf(disk->disk_name, "z2ram%d", minor); | |
332 | else | |
333 | sprintf(disk->disk_name, "z2ram"); | |
76487f02 CH |
334 | |
335 | z2ram_gendisk[minor] = disk; | |
15733754 LC |
336 | err = add_disk(disk); |
337 | if (err) | |
8b9ab626 | 338 | put_disk(disk); |
15733754 | 339 | return err; |
76487f02 CH |
340 | } |
341 | ||
6c3a05e1 | 342 | static int __init z2_init(void) |
1da177e4 | 343 | { |
76487f02 | 344 | int ret, i; |
1da177e4 | 345 | |
6c3a05e1 CH |
346 | if (!MACH_IS_AMIGA) |
347 | return -ENODEV; | |
1da177e4 | 348 | |
6c3a05e1 | 349 | if (register_blkdev(Z2RAM_MAJOR, DEVICE_NAME)) |
76487f02 CH |
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(&tag_set); | |
359 | if (ret) | |
360 | goto out_unregister_blkdev; | |
361 | ||
362 | for (i = 0; i < Z2MINOR_COUNT; i++) { | |
363 | ret = z2ram_register_disk(i); | |
364 | if (ret && i == 0) | |
365 | goto out_free_tagset; | |
6c3a05e1 | 366 | } |
1da177e4 | 367 | |
6c3a05e1 | 368 | return 0; |
1da177e4 | 369 | |
76487f02 CH |
370 | out_free_tagset: |
371 | blk_mq_free_tag_set(&tag_set); | |
372 | out_unregister_blkdev: | |
6c3a05e1 | 373 | unregister_blkdev(Z2RAM_MAJOR, DEVICE_NAME); |
6c3a05e1 | 374 | return ret; |
1da177e4 LT |
375 | } |
376 | ||
f39d88ad | 377 | static void __exit z2_exit(void) |
1da177e4 | 378 | { |
6c3a05e1 | 379 | int i, j; |
76487f02 | 380 | |
6c3a05e1 | 381 | unregister_blkdev(Z2RAM_MAJOR, DEVICE_NAME); |
76487f02 CH |
382 | |
383 | for (i = 0; i < Z2MINOR_COUNT; i++) { | |
384 | del_gendisk(z2ram_gendisk[i]); | |
76487f02 CH |
385 | put_disk(z2ram_gendisk[i]); |
386 | } | |
6c3a05e1 CH |
387 | blk_mq_free_tag_set(&tag_set); |
388 | ||
389 | if (current_device != -1) { | |
390 | i = 0; | |
391 | ||
392 | for (j = 0; j < z2_count; j++) { | |
393 | set_bit(i++, zorro_unused_z2ram); | |
394 | } | |
1da177e4 | 395 | |
6c3a05e1 CH |
396 | for (j = 0; j < chip_count; j++) { |
397 | if (z2ram_map[i]) { | |
398 | amiga_chip_free((void *)z2ram_map[i++]); | |
399 | } | |
400 | } | |
1da177e4 | 401 | |
6c3a05e1 CH |
402 | if (z2ram_map != NULL) { |
403 | kfree(z2ram_map); | |
404 | } | |
1da177e4 | 405 | } |
1da177e4 | 406 | |
6c3a05e1 CH |
407 | return; |
408 | } | |
f39d88ad AV |
409 | |
410 | module_init(z2_init); | |
411 | module_exit(z2_exit); | |
412 | MODULE_LICENSE("GPL"); |