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> |
1da177e4 LT |
38 | |
39 | #include <asm/setup.h> | |
40 | #include <asm/amigahw.h> | |
41 | #include <asm/pgtable.h> | |
42 | ||
43 | #include <linux/zorro.h> | |
44 | ||
45 | ||
1da177e4 LT |
46 | #define Z2MINOR_COMBINED (0) |
47 | #define Z2MINOR_Z2ONLY (1) | |
48 | #define Z2MINOR_CHIPONLY (2) | |
49 | #define Z2MINOR_MEMLIST1 (4) | |
50 | #define Z2MINOR_MEMLIST2 (5) | |
51 | #define Z2MINOR_MEMLIST3 (6) | |
52 | #define Z2MINOR_MEMLIST4 (7) | |
53 | #define Z2MINOR_COUNT (8) /* Move this down when adding a new minor */ | |
54 | ||
55 | #define Z2RAM_CHUNK1024 ( Z2RAM_CHUNKSIZE >> 10 ) | |
56 | ||
2a48fc0a | 57 | static DEFINE_MUTEX(z2ram_mutex); |
1da177e4 LT |
58 | static u_long *z2ram_map = NULL; |
59 | static u_long z2ram_size = 0; | |
60 | static int z2_count = 0; | |
61 | static int chip_count = 0; | |
62 | static int list_count = 0; | |
63 | static int current_device = -1; | |
64 | ||
65 | static DEFINE_SPINLOCK(z2ram_lock); | |
66 | ||
1da177e4 LT |
67 | static struct gendisk *z2ram_gendisk; |
68 | ||
8535fd6f JA |
69 | static blk_status_t z2_queue_rq(struct blk_mq_hw_ctx *hctx, |
70 | const struct blk_mq_queue_data *bd) | |
1da177e4 | 71 | { |
8535fd6f JA |
72 | struct request *req = bd->rq; |
73 | unsigned long start = blk_rq_pos(req) << 9; | |
74 | unsigned long len = blk_rq_cur_bytes(req); | |
75 | ||
76 | blk_mq_start_request(req); | |
77 | ||
78 | if (start + len > z2ram_size) { | |
79 | pr_err(DEVICE_NAME ": bad access: block=%llu, " | |
80 | "count=%u\n", | |
81 | (unsigned long long)blk_rq_pos(req), | |
82 | blk_rq_cur_sectors(req)); | |
83 | return BLK_STS_IOERR; | |
84 | } | |
85 | ||
86 | spin_lock_irq(&z2ram_lock); | |
87 | ||
88 | while (len) { | |
89 | unsigned long addr = start & Z2RAM_CHUNKMASK; | |
90 | unsigned long size = Z2RAM_CHUNKSIZE - addr; | |
91 | void *buffer = bio_data(req->bio); | |
92 | ||
93 | if (len < size) | |
94 | size = len; | |
95 | addr += z2ram_map[ start >> Z2RAM_CHUNKSHIFT ]; | |
96 | if (rq_data_dir(req) == READ) | |
97 | memcpy(buffer, (char *)addr, size); | |
98 | else | |
99 | memcpy((char *)addr, buffer, size); | |
100 | start += size; | |
101 | len -= size; | |
1da177e4 | 102 | } |
8535fd6f JA |
103 | |
104 | spin_unlock_irq(&z2ram_lock); | |
105 | blk_mq_end_request(req, BLK_STS_OK); | |
106 | return BLK_STS_OK; | |
1da177e4 LT |
107 | } |
108 | ||
109 | static void | |
110 | get_z2ram( void ) | |
111 | { | |
112 | int i; | |
113 | ||
114 | for ( i = 0; i < Z2RAM_SIZE / Z2RAM_CHUNKSIZE; i++ ) | |
115 | { | |
116 | if ( test_bit( i, zorro_unused_z2ram ) ) | |
117 | { | |
118 | z2_count++; | |
6112ea08 GU |
119 | z2ram_map[z2ram_size++] = (unsigned long)ZTWO_VADDR(Z2RAM_START) + |
120 | (i << Z2RAM_CHUNKSHIFT); | |
1da177e4 LT |
121 | clear_bit( i, zorro_unused_z2ram ); |
122 | } | |
123 | } | |
124 | ||
125 | return; | |
126 | } | |
127 | ||
128 | static void | |
129 | get_chipram( void ) | |
130 | { | |
131 | ||
132 | while ( amiga_chip_avail() > ( Z2RAM_CHUNKSIZE * 4 ) ) | |
133 | { | |
134 | chip_count++; | |
135 | z2ram_map[ z2ram_size ] = | |
136 | (u_long)amiga_chip_alloc( Z2RAM_CHUNKSIZE, "z2ram" ); | |
137 | ||
138 | if ( z2ram_map[ z2ram_size ] == 0 ) | |
139 | { | |
140 | break; | |
141 | } | |
142 | ||
143 | z2ram_size++; | |
144 | } | |
145 | ||
146 | return; | |
147 | } | |
148 | ||
ab746cb9 | 149 | static int z2_open(struct block_device *bdev, fmode_t mode) |
1da177e4 LT |
150 | { |
151 | int device; | |
152 | int max_z2_map = ( Z2RAM_SIZE / Z2RAM_CHUNKSIZE ) * | |
153 | sizeof( z2ram_map[0] ); | |
154 | int max_chip_map = ( amiga_chip_size / Z2RAM_CHUNKSIZE ) * | |
155 | sizeof( z2ram_map[0] ); | |
156 | int rc = -ENOMEM; | |
157 | ||
ab746cb9 | 158 | device = MINOR(bdev->bd_dev); |
1da177e4 | 159 | |
2a48fc0a | 160 | mutex_lock(&z2ram_mutex); |
1da177e4 LT |
161 | if ( current_device != -1 && current_device != device ) |
162 | { | |
163 | rc = -EBUSY; | |
164 | goto err_out; | |
165 | } | |
166 | ||
167 | if ( current_device == -1 ) | |
168 | { | |
169 | z2_count = 0; | |
170 | chip_count = 0; | |
171 | list_count = 0; | |
172 | z2ram_size = 0; | |
173 | ||
174 | /* Use a specific list entry. */ | |
175 | if (device >= Z2MINOR_MEMLIST1 && device <= Z2MINOR_MEMLIST4) { | |
176 | int index = device - Z2MINOR_MEMLIST1 + 1; | |
177 | unsigned long size, paddr, vaddr; | |
178 | ||
179 | if (index >= m68k_realnum_memory) { | |
180 | printk( KERN_ERR DEVICE_NAME | |
181 | ": no such entry in z2ram_map\n" ); | |
182 | goto err_out; | |
183 | } | |
184 | ||
185 | paddr = m68k_memory[index].addr; | |
186 | size = m68k_memory[index].size & ~(Z2RAM_CHUNKSIZE-1); | |
187 | ||
188 | #ifdef __powerpc__ | |
189 | /* FIXME: ioremap doesn't build correct memory tables. */ | |
190 | { | |
191 | vfree(vmalloc (size)); | |
192 | } | |
193 | ||
ed18e423 | 194 | vaddr = (unsigned long)ioremap_wt(paddr, size); |
1da177e4 LT |
195 | |
196 | #else | |
197 | vaddr = (unsigned long)z_remap_nocache_nonser(paddr, size); | |
198 | #endif | |
199 | z2ram_map = | |
6da2ec56 KC |
200 | kmalloc_array(size / Z2RAM_CHUNKSIZE, |
201 | sizeof(z2ram_map[0]), | |
202 | GFP_KERNEL); | |
1da177e4 LT |
203 | if ( z2ram_map == NULL ) |
204 | { | |
205 | printk( KERN_ERR DEVICE_NAME | |
206 | ": cannot get mem for z2ram_map\n" ); | |
207 | goto err_out; | |
208 | } | |
209 | ||
210 | while (size) { | |
211 | z2ram_map[ z2ram_size++ ] = vaddr; | |
212 | size -= Z2RAM_CHUNKSIZE; | |
213 | vaddr += Z2RAM_CHUNKSIZE; | |
214 | list_count++; | |
215 | } | |
216 | ||
217 | if ( z2ram_size != 0 ) | |
218 | printk( KERN_INFO DEVICE_NAME | |
219 | ": using %iK List Entry %d Memory\n", | |
220 | list_count * Z2RAM_CHUNK1024, index ); | |
221 | } else | |
222 | ||
223 | switch ( device ) | |
224 | { | |
225 | case Z2MINOR_COMBINED: | |
226 | ||
227 | z2ram_map = kmalloc( max_z2_map + max_chip_map, GFP_KERNEL ); | |
228 | if ( z2ram_map == NULL ) | |
229 | { | |
230 | printk( KERN_ERR DEVICE_NAME | |
231 | ": cannot get mem for z2ram_map\n" ); | |
232 | goto err_out; | |
233 | } | |
234 | ||
235 | get_z2ram(); | |
236 | get_chipram(); | |
237 | ||
238 | if ( z2ram_size != 0 ) | |
239 | printk( KERN_INFO DEVICE_NAME | |
240 | ": using %iK Zorro II RAM and %iK Chip RAM (Total %dK)\n", | |
241 | z2_count * Z2RAM_CHUNK1024, | |
242 | chip_count * Z2RAM_CHUNK1024, | |
243 | ( z2_count + chip_count ) * Z2RAM_CHUNK1024 ); | |
244 | ||
245 | break; | |
246 | ||
247 | case Z2MINOR_Z2ONLY: | |
248 | z2ram_map = kmalloc( max_z2_map, GFP_KERNEL ); | |
249 | if ( z2ram_map == NULL ) | |
250 | { | |
251 | printk( KERN_ERR DEVICE_NAME | |
252 | ": cannot get mem for z2ram_map\n" ); | |
253 | goto err_out; | |
254 | } | |
255 | ||
256 | get_z2ram(); | |
257 | ||
258 | if ( z2ram_size != 0 ) | |
259 | printk( KERN_INFO DEVICE_NAME | |
260 | ": using %iK of Zorro II RAM\n", | |
261 | z2_count * Z2RAM_CHUNK1024 ); | |
262 | ||
263 | break; | |
264 | ||
265 | case Z2MINOR_CHIPONLY: | |
266 | z2ram_map = kmalloc( max_chip_map, GFP_KERNEL ); | |
267 | if ( z2ram_map == NULL ) | |
268 | { | |
269 | printk( KERN_ERR DEVICE_NAME | |
270 | ": cannot get mem for z2ram_map\n" ); | |
271 | goto err_out; | |
272 | } | |
273 | ||
274 | get_chipram(); | |
275 | ||
276 | if ( z2ram_size != 0 ) | |
277 | printk( KERN_INFO DEVICE_NAME | |
278 | ": using %iK Chip RAM\n", | |
279 | chip_count * Z2RAM_CHUNK1024 ); | |
280 | ||
281 | break; | |
282 | ||
283 | default: | |
284 | rc = -ENODEV; | |
285 | goto err_out; | |
286 | ||
287 | break; | |
288 | } | |
289 | ||
290 | if ( z2ram_size == 0 ) | |
291 | { | |
292 | printk( KERN_NOTICE DEVICE_NAME | |
293 | ": no unused ZII/Chip RAM found\n" ); | |
294 | goto err_out_kfree; | |
295 | } | |
296 | ||
297 | current_device = device; | |
298 | z2ram_size <<= Z2RAM_CHUNKSHIFT; | |
299 | set_capacity(z2ram_gendisk, z2ram_size >> 9); | |
300 | } | |
301 | ||
2a48fc0a | 302 | mutex_unlock(&z2ram_mutex); |
1da177e4 LT |
303 | return 0; |
304 | ||
305 | err_out_kfree: | |
f9101210 | 306 | kfree(z2ram_map); |
1da177e4 | 307 | err_out: |
2a48fc0a | 308 | mutex_unlock(&z2ram_mutex); |
1da177e4 LT |
309 | return rc; |
310 | } | |
311 | ||
db2a144b | 312 | static void |
ab746cb9 | 313 | z2_release(struct gendisk *disk, fmode_t mode) |
1da177e4 | 314 | { |
2a48fc0a | 315 | mutex_lock(&z2ram_mutex); |
6e9624b8 | 316 | if ( current_device == -1 ) { |
2a48fc0a | 317 | mutex_unlock(&z2ram_mutex); |
db2a144b | 318 | return; |
6e9624b8 | 319 | } |
2a48fc0a | 320 | mutex_unlock(&z2ram_mutex); |
1da177e4 LT |
321 | /* |
322 | * FIXME: unmap memory | |
323 | */ | |
1da177e4 LT |
324 | } |
325 | ||
83d5cde4 | 326 | static const struct block_device_operations z2_fops = |
1da177e4 LT |
327 | { |
328 | .owner = THIS_MODULE, | |
ab746cb9 AV |
329 | .open = z2_open, |
330 | .release = z2_release, | |
1da177e4 LT |
331 | }; |
332 | ||
333 | static struct kobject *z2_find(dev_t dev, int *part, void *data) | |
334 | { | |
335 | *part = 0; | |
3079c22e | 336 | return get_disk_and_module(z2ram_gendisk); |
1da177e4 LT |
337 | } |
338 | ||
339 | static struct request_queue *z2_queue; | |
8535fd6f JA |
340 | static struct blk_mq_tag_set tag_set; |
341 | ||
342 | static const struct blk_mq_ops z2_mq_ops = { | |
343 | .queue_rq = z2_queue_rq, | |
344 | }; | |
1da177e4 | 345 | |
f39d88ad | 346 | static int __init |
1da177e4 LT |
347 | z2_init(void) |
348 | { | |
349 | int ret; | |
350 | ||
351 | if (!MACH_IS_AMIGA) | |
fd5b462f | 352 | return -ENODEV; |
1da177e4 LT |
353 | |
354 | ret = -EBUSY; | |
355 | if (register_blkdev(Z2RAM_MAJOR, DEVICE_NAME)) | |
356 | goto err; | |
357 | ||
358 | ret = -ENOMEM; | |
359 | z2ram_gendisk = alloc_disk(1); | |
360 | if (!z2ram_gendisk) | |
361 | goto out_disk; | |
362 | ||
8535fd6f JA |
363 | z2_queue = blk_mq_init_sq_queue(&tag_set, &z2_mq_ops, 16, |
364 | BLK_MQ_F_SHOULD_MERGE); | |
365 | if (IS_ERR(z2_queue)) { | |
366 | ret = PTR_ERR(z2_queue); | |
367 | z2_queue = NULL; | |
1da177e4 | 368 | goto out_queue; |
8535fd6f | 369 | } |
1da177e4 LT |
370 | |
371 | z2ram_gendisk->major = Z2RAM_MAJOR; | |
372 | z2ram_gendisk->first_minor = 0; | |
373 | z2ram_gendisk->fops = &z2_fops; | |
374 | sprintf(z2ram_gendisk->disk_name, "z2ram"); | |
1da177e4 LT |
375 | |
376 | z2ram_gendisk->queue = z2_queue; | |
377 | add_disk(z2ram_gendisk); | |
378 | blk_register_region(MKDEV(Z2RAM_MAJOR, 0), Z2MINOR_COUNT, THIS_MODULE, | |
379 | z2_find, NULL, NULL); | |
380 | ||
381 | return 0; | |
382 | ||
383 | out_queue: | |
384 | put_disk(z2ram_gendisk); | |
385 | out_disk: | |
386 | unregister_blkdev(Z2RAM_MAJOR, DEVICE_NAME); | |
387 | err: | |
388 | return ret; | |
389 | } | |
390 | ||
f39d88ad | 391 | static void __exit z2_exit(void) |
1da177e4 LT |
392 | { |
393 | int i, j; | |
c9d4bc28 | 394 | blk_unregister_region(MKDEV(Z2RAM_MAJOR, 0), Z2MINOR_COUNT); |
00d59405 | 395 | unregister_blkdev(Z2RAM_MAJOR, DEVICE_NAME); |
1da177e4 LT |
396 | del_gendisk(z2ram_gendisk); |
397 | put_disk(z2ram_gendisk); | |
398 | blk_cleanup_queue(z2_queue); | |
8535fd6f | 399 | blk_mq_free_tag_set(&tag_set); |
1da177e4 LT |
400 | |
401 | if ( current_device != -1 ) | |
402 | { | |
403 | i = 0; | |
404 | ||
405 | for ( j = 0 ; j < z2_count; j++ ) | |
406 | { | |
407 | set_bit( i++, zorro_unused_z2ram ); | |
408 | } | |
409 | ||
410 | for ( j = 0 ; j < chip_count; j++ ) | |
411 | { | |
412 | if ( z2ram_map[ i ] ) | |
413 | { | |
414 | amiga_chip_free( (void *) z2ram_map[ i++ ] ); | |
415 | } | |
416 | } | |
417 | ||
418 | if ( z2ram_map != NULL ) | |
419 | { | |
420 | kfree( z2ram_map ); | |
421 | } | |
422 | } | |
423 | ||
424 | return; | |
425 | } | |
f39d88ad AV |
426 | |
427 | module_init(z2_init); | |
428 | module_exit(z2_exit); | |
429 | MODULE_LICENSE("GPL"); |