]>
Commit | Line | Data |
---|---|---|
81eb669b DC |
1 | /* cyanblkdev_block.c - West Bridge Linux Block Driver source file |
2 | ## =========================== | |
3 | ## Copyright (C) 2010 Cypress Semiconductor | |
4 | ## | |
5 | ## This program is free software; you can redistribute it and/or | |
6 | ## modify it under the terms of the GNU General Public License | |
7 | ## as published by the Free Software Foundation; either version 2 | |
8 | ## of the License, or (at your option) any later version. | |
9 | ## | |
10 | ## This program is distributed in the hope that it will be useful, | |
11 | ## but WITHOUT ANY WARRANTY; without even the implied warranty of | |
12 | ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
13 | ## GNU General Public License for more details. | |
14 | ## | |
15 | ## You should have received a copy of the GNU General Public License | |
16 | ## along with this program; if not, write to the Free Software | |
17 | ## Foundation, Inc., 51 Franklin Street, Fifth Floor | |
18 | ## Boston, MA 02110-1301, USA. | |
19 | ## =========================== | |
20 | */ | |
21 | ||
22 | /* | |
23 | * Linux block driver implementation for Cypress West Bridge. | |
24 | * Based on the mmc block driver implementation by Andrew Christian | |
25 | * for the linux 2.6.26 kernel. | |
26 | * mmc_block.c, 5/28/2002 | |
27 | */ | |
28 | ||
29 | /* | |
30 | * Block driver for media (i.e., flash cards) | |
31 | * | |
32 | * Copyright 2002 Hewlett-Packard Company | |
33 | * | |
34 | * Use consistent with the GNU GPL is permitted, | |
35 | * provided that this copyright notice is | |
36 | * preserved in its entirety in all copies and derived works. | |
37 | * | |
38 | * HEWLETT-PACKARD COMPANY MAKES NO WARRANTIES, EXPRESSED OR IMPLIED, | |
39 | * AS TO THE USEFULNESS OR CORRECTNESS OF THIS CODE OR ITS | |
40 | * FITNESS FOR ANY PARTICULAR PURPOSE. | |
41 | * | |
42 | * Many thanks to Alessandro Rubini and Jonathan Corbet! | |
43 | * | |
44 | * Author: Andrew Christian | |
45 | * 28 May 2002 | |
46 | */ | |
47 | ||
48 | #include <linux/moduleparam.h> | |
49 | #include <linux/module.h> | |
50 | #include <linux/init.h> | |
51 | #include <linux/slab.h> | |
52 | #include <linux/sched.h> | |
53 | #include <linux/kernel.h> | |
54 | #include <linux/fs.h> | |
55 | #include <linux/errno.h> | |
56 | #include <linux/hdreg.h> | |
57 | #include <linux/kdev_t.h> | |
58 | #include <linux/blkdev.h> | |
59 | ||
60 | #include <asm/system.h> | |
61 | #include <linux/uaccess.h> | |
62 | ||
63 | #include <linux/scatterlist.h> | |
64 | #include <linux/time.h> | |
65 | #include <linux/signal.h> | |
66 | #include <linux/delay.h> | |
67 | ||
68 | #include "cyasblkdev_queue.h" | |
69 | ||
70 | #define CYASBLKDEV_SHIFT 0 /* Only a single partition. */ | |
71 | #define CYASBLKDEV_MAX_REQ_LEN (256) | |
72 | #define CYASBLKDEV_NUM_MINORS (256 >> CYASBLKDEV_SHIFT) | |
73 | #define CY_AS_TEST_NUM_BLOCKS (64) | |
74 | #define CYASBLKDEV_MINOR_0 1 | |
75 | #define CYASBLKDEV_MINOR_1 2 | |
76 | #define CYASBLKDEV_MINOR_2 3 | |
77 | ||
78 | static int major; | |
79 | module_param(major, int, 0444); | |
80 | MODULE_PARM_DESC(major, | |
81 | "specify the major device number for cyasblkdev block driver"); | |
82 | ||
83 | /* parameters passed from the user space */ | |
84 | static int vfat_search; | |
85 | module_param(vfat_search, bool, S_IRUGO | S_IWUSR); | |
86 | MODULE_PARM_DESC(vfat_search, | |
87 | "dynamically find the location of the first sector"); | |
88 | ||
89 | static int private_partition_bus = -1; | |
90 | module_param(private_partition_bus, int, S_IRUGO | S_IWUSR); | |
91 | MODULE_PARM_DESC(private_partition_bus, | |
92 | "bus number for private partition"); | |
93 | ||
94 | static int private_partition_size = -1; | |
95 | module_param(private_partition_size, int, S_IRUGO | S_IWUSR); | |
96 | MODULE_PARM_DESC(private_partition_size, | |
97 | "size of the private partition"); | |
98 | ||
99 | /* | |
100 | * There is one cyasblkdev_blk_data per slot. | |
101 | */ | |
102 | struct cyasblkdev_blk_data { | |
103 | spinlock_t lock; | |
104 | int media_count[2]; | |
105 | const struct block_device_operations *blkops; | |
106 | unsigned int usage; | |
107 | unsigned int suspended; | |
108 | ||
109 | /* handle to the west bridge device this handle, typdefed as *void */ | |
110 | cy_as_device_handle dev_handle; | |
111 | ||
112 | /* our custom structure, in addition to request queue, | |
113 | * adds lock & semaphore items*/ | |
114 | struct cyasblkdev_queue queue; | |
115 | ||
116 | /* 16 entries is enough given max request size | |
117 | * 16 * 4K (64 K per request)*/ | |
118 | struct scatterlist sg[16]; | |
119 | ||
120 | /* non-zero enables printk of executed reqests */ | |
121 | unsigned int dbgprn_flags; | |
122 | ||
123 | /*gen_disk for private, system disk */ | |
124 | struct gendisk *system_disk; | |
125 | cy_as_media_type system_disk_type; | |
126 | cy_bool system_disk_read_only; | |
127 | cy_bool system_disk_bus_num; | |
128 | ||
129 | /* sector size for the medium */ | |
130 | unsigned int system_disk_blk_size; | |
131 | unsigned int system_disk_first_sector; | |
132 | unsigned int system_disk_unit_no; | |
133 | ||
134 | /*gen_disk for bus 0 */ | |
135 | struct gendisk *user_disk_0; | |
136 | cy_as_media_type user_disk_0_type; | |
137 | cy_bool user_disk_0_read_only; | |
138 | cy_bool user_disk_0_bus_num; | |
139 | ||
140 | /* sector size for the medium */ | |
141 | unsigned int user_disk_0_blk_size; | |
142 | unsigned int user_disk_0_first_sector; | |
143 | unsigned int user_disk_0_unit_no; | |
144 | ||
145 | /*gen_disk for bus 1 */ | |
146 | struct gendisk *user_disk_1; | |
147 | cy_as_media_type user_disk_1_type; | |
148 | cy_bool user_disk_1_read_only; | |
149 | cy_bool user_disk_1_bus_num; | |
150 | ||
151 | /* sector size for the medium */ | |
152 | unsigned int user_disk_1_blk_size; | |
153 | unsigned int user_disk_1_first_sector; | |
154 | unsigned int user_disk_1_unit_no; | |
155 | }; | |
156 | ||
157 | /* pointer to west bridge block data device superstructure */ | |
0769c38d | 158 | static struct cyasblkdev_blk_data *gl_bd; |
81eb669b | 159 | |
45f4d024 | 160 | static DEFINE_SEMAPHORE(open_lock); |
81eb669b DC |
161 | |
162 | /* local forwardd declarationss */ | |
163 | static cy_as_device_handle *cyas_dev_handle; | |
164 | static void cyasblkdev_blk_deinit(struct cyasblkdev_blk_data *bd); | |
165 | ||
166 | /*change debug print options */ | |
167 | #define DBGPRN_RD_RQ (1 < 0) | |
168 | #define DBGPRN_WR_RQ (1 < 1) | |
169 | #define DBGPRN_RQ_END (1 < 2) | |
170 | ||
171 | int blkdev_ctl_dbgprn( | |
172 | int prn_flags | |
173 | ) | |
174 | { | |
175 | int cur_options = gl_bd->dbgprn_flags; | |
176 | ||
177 | DBGPRN_FUNC_NAME; | |
178 | ||
179 | /* set new debug print options */ | |
180 | gl_bd->dbgprn_flags = prn_flags; | |
181 | ||
182 | /* return previous */ | |
183 | return cur_options; | |
184 | } | |
185 | EXPORT_SYMBOL(blkdev_ctl_dbgprn); | |
186 | ||
187 | static struct cyasblkdev_blk_data *cyasblkdev_blk_get( | |
188 | struct gendisk *disk | |
189 | ) | |
190 | { | |
191 | struct cyasblkdev_blk_data *bd; | |
192 | ||
193 | DBGPRN_FUNC_NAME; | |
194 | ||
195 | down(&open_lock); | |
196 | ||
197 | bd = disk->private_data; | |
198 | ||
199 | if (bd && (bd->usage == 0)) | |
200 | bd = NULL; | |
201 | ||
202 | if (bd) { | |
203 | bd->usage++; | |
204 | #ifndef NBDEBUG | |
205 | cy_as_hal_print_message( | |
0769c38d | 206 | "cyasblkdev_blk_get: usage = %d\n", bd->usage); |
81eb669b DC |
207 | #endif |
208 | } | |
209 | up(&open_lock); | |
210 | ||
211 | return bd; | |
212 | } | |
213 | ||
214 | static void cyasblkdev_blk_put( | |
215 | struct cyasblkdev_blk_data *bd | |
216 | ) | |
217 | { | |
218 | DBGPRN_FUNC_NAME; | |
219 | ||
220 | down(&open_lock); | |
221 | ||
222 | if (bd) { | |
223 | bd->usage--; | |
224 | #ifndef WESTBRIDGE_NDEBUG | |
225 | cy_as_hal_print_message( | |
226 | " cyasblkdev_blk_put , bd->usage= %d\n", bd->usage); | |
227 | #endif | |
228 | } else { | |
229 | #ifndef WESTBRIDGE_NDEBUG | |
230 | cy_as_hal_print_message( | |
231 | "cyasblkdev: blk_put(bd) on bd = NULL!: usage = %d\n", | |
232 | bd->usage); | |
233 | #endif | |
234 | up(&open_lock); | |
0769c38d | 235 | return; |
81eb669b DC |
236 | } |
237 | ||
238 | if (bd->usage == 0) { | |
239 | put_disk(bd->user_disk_0); | |
240 | put_disk(bd->user_disk_1); | |
241 | put_disk(bd->system_disk); | |
242 | cyasblkdev_cleanup_queue(&bd->queue); | |
243 | ||
244 | if (CY_AS_ERROR_SUCCESS != | |
245 | cy_as_storage_release(bd->dev_handle, 0, 0, 0, 0)) { | |
246 | #ifndef WESTBRIDGE_NDEBUG | |
247 | cy_as_hal_print_message( | |
0769c38d | 248 | "cyasblkdev: cannot release bus 0\n"); |
81eb669b DC |
249 | #endif |
250 | } | |
251 | ||
252 | if (CY_AS_ERROR_SUCCESS != | |
253 | cy_as_storage_release(bd->dev_handle, 1, 0, 0, 0)) { | |
254 | #ifndef WESTBRIDGE_NDEBUG | |
255 | cy_as_hal_print_message( | |
0769c38d | 256 | "cyasblkdev: cannot release bus 1\n"); |
81eb669b DC |
257 | #endif |
258 | } | |
259 | ||
260 | if (CY_AS_ERROR_SUCCESS != | |
261 | cy_as_storage_stop(bd->dev_handle, 0, 0)) { | |
262 | #ifndef WESTBRIDGE_NDEBUG | |
263 | cy_as_hal_print_message( | |
0769c38d | 264 | "cyasblkdev: cannot stop storage stack\n"); |
81eb669b DC |
265 | #endif |
266 | } | |
267 | ||
268 | #ifdef __CY_ASTORIA_SCM_KERNEL_HAL__ | |
269 | /* If the SCM Kernel HAL is being used, disable the use | |
270 | * of scatter/gather lists at the end of block driver usage. | |
271 | */ | |
0769c38d | 272 | cy_as_hal_disable_scatter_list(cyasdevice_gethaltag()); |
81eb669b DC |
273 | #endif |
274 | ||
275 | /*ptr to global struct cyasblkdev_blk_data */ | |
0769c38d | 276 | gl_bd = NULL; |
81eb669b DC |
277 | kfree(bd); |
278 | } | |
279 | ||
280 | #ifndef WESTBRIDGE_NDEBUG | |
281 | cy_as_hal_print_message( | |
282 | "cyasblkdev (blk_put): usage = %d\n", | |
0769c38d | 283 | bd->usage); |
81eb669b DC |
284 | #endif |
285 | up(&open_lock); | |
286 | } | |
287 | ||
288 | static int cyasblkdev_blk_open( | |
289 | struct block_device *bdev, | |
290 | fmode_t mode | |
291 | ) | |
292 | { | |
293 | struct cyasblkdev_blk_data *bd = cyasblkdev_blk_get(bdev->bd_disk); | |
294 | int ret = -ENXIO; | |
295 | ||
296 | DBGPRN_FUNC_NAME; | |
297 | ||
298 | if (bd) { | |
299 | if (bd->usage == 2) | |
300 | check_disk_change(bdev); | |
301 | ||
302 | ret = 0; | |
303 | ||
304 | if (bdev->bd_disk == bd->user_disk_0) { | |
305 | if ((mode & FMODE_WRITE) && bd->user_disk_0_read_only) { | |
306 | #ifndef WESTBRIDGE_NDEBUG | |
307 | cy_as_hal_print_message( | |
308 | "device marked as readonly " | |
309 | "and write requested\n"); | |
310 | #endif | |
311 | ||
312 | cyasblkdev_blk_put(bd); | |
313 | ret = -EROFS; | |
314 | } | |
315 | } else if (bdev->bd_disk == bd->user_disk_1) { | |
316 | if ((mode & FMODE_WRITE) && bd->user_disk_1_read_only) { | |
317 | #ifndef WESTBRIDGE_NDEBUG | |
318 | cy_as_hal_print_message( | |
319 | "device marked as readonly " | |
320 | "and write requested\n"); | |
321 | #endif | |
322 | ||
323 | cyasblkdev_blk_put(bd); | |
324 | ret = -EROFS; | |
325 | } | |
326 | } else if (bdev->bd_disk == bd->system_disk) { | |
327 | if ((mode & FMODE_WRITE) && bd->system_disk_read_only) { | |
328 | #ifndef WESTBRIDGE_NDEBUG | |
329 | cy_as_hal_print_message( | |
330 | "device marked as readonly " | |
331 | "and write requested\n"); | |
332 | #endif | |
333 | ||
334 | cyasblkdev_blk_put(bd); | |
335 | ret = -EROFS; | |
336 | } | |
337 | } | |
338 | } | |
339 | ||
340 | return ret; | |
341 | } | |
342 | ||
343 | static int cyasblkdev_blk_release( | |
344 | struct gendisk *disk, | |
345 | fmode_t mode | |
346 | ) | |
347 | { | |
348 | struct cyasblkdev_blk_data *bd = disk->private_data; | |
349 | ||
350 | DBGPRN_FUNC_NAME; | |
351 | ||
352 | cyasblkdev_blk_put(bd); | |
353 | return 0; | |
354 | } | |
355 | ||
356 | static int cyasblkdev_blk_ioctl( | |
357 | struct block_device *bdev, | |
358 | fmode_t mode, | |
359 | unsigned int cmd, | |
360 | unsigned long arg | |
361 | ) | |
362 | { | |
363 | DBGPRN_FUNC_NAME; | |
364 | ||
365 | if (cmd == HDIO_GETGEO) { | |
366 | /*for now we only process geometry IOCTL*/ | |
367 | struct hd_geometry geo; | |
368 | ||
369 | memset(&geo, 0, sizeof(struct hd_geometry)); | |
370 | ||
371 | geo.cylinders = get_capacity(bdev->bd_disk) / (4 * 16); | |
372 | geo.heads = 4; | |
373 | geo.sectors = 16; | |
374 | geo.start = get_start_sect(bdev); | |
375 | ||
376 | /* copy to user space */ | |
377 | return copy_to_user((void __user *)arg, &geo, sizeof(geo)) | |
378 | ? -EFAULT : 0; | |
379 | } | |
380 | ||
381 | return -ENOTTY; | |
382 | } | |
383 | ||
384 | /* Media_changed block_device opp | |
385 | * this one is called by kernel to confirm if the media really changed | |
386 | * as we indicated by issuing check_disk_change() call */ | |
387 | int cyasblkdev_media_changed(struct gendisk *gd) | |
388 | { | |
389 | struct cyasblkdev_blk_data *bd; | |
390 | ||
391 | #ifndef WESTBRIDGE_NDEBUG | |
392 | cy_as_hal_print_message("cyasblkdev_media_changed() is called\n"); | |
393 | #endif | |
394 | ||
395 | if (gd) | |
396 | bd = gd->private_data; | |
397 | else { | |
398 | #ifndef WESTBRIDGE_NDEBUG | |
399 | cy_as_hal_print_message( | |
400 | "cyasblkdev_media_changed() is called, " | |
401 | "but gd is null\n"); | |
402 | #endif | |
403 | } | |
404 | ||
405 | /* return media change state "1" yes, 0 no */ | |
9bed00f2 | 406 | return 0; |
81eb669b DC |
407 | } |
408 | ||
409 | /* this one called by kernel to give us a chence | |
410 | * to prep the new media before it starts to rescaning | |
411 | * of the newlly inserted SD media */ | |
412 | int cyasblkdev_revalidate_disk(struct gendisk *gd) | |
413 | { | |
414 | /*int (*revalidate_disk) (struct gendisk *); */ | |
415 | ||
416 | #ifndef WESTBRIDGE_NDEBUG | |
417 | if (gd) | |
418 | cy_as_hal_print_message( | |
419 | "cyasblkdev_revalidate_disk() is called, " | |
420 | "(gl_bd->usage:%d)\n", gl_bd->usage); | |
421 | #endif | |
422 | ||
423 | /* 0 means ok, kern can go ahead with partition rescan */ | |
424 | return 0; | |
425 | } | |
426 | ||
427 | ||
428 | /*standard block device driver interface */ | |
429 | static struct block_device_operations cyasblkdev_bdops = { | |
430 | .open = cyasblkdev_blk_open, | |
431 | .release = cyasblkdev_blk_release, | |
432 | .ioctl = cyasblkdev_blk_ioctl, | |
433 | /* .getgeo = cyasblkdev_blk_getgeo, */ | |
434 | /* added to support media removal( real and simulated) media */ | |
435 | .media_changed = cyasblkdev_media_changed, | |
436 | /* added to support media removal( real and simulated) media */ | |
437 | .revalidate_disk = cyasblkdev_revalidate_disk, | |
438 | .owner = THIS_MODULE, | |
439 | }; | |
440 | ||
441 | /* west bridge block device prep request function */ | |
442 | static int cyasblkdev_blk_prep_rq( | |
443 | struct cyasblkdev_queue *bq, | |
444 | struct request *req | |
445 | ) | |
446 | { | |
447 | struct cyasblkdev_blk_data *bd = bq->data; | |
448 | int stat = BLKPREP_OK; | |
449 | ||
450 | DBGPRN_FUNC_NAME; | |
451 | ||
452 | /* If we have no device, we haven't finished initialising. */ | |
453 | if (!bd || !bd->dev_handle) { | |
454 | #ifndef WESTBRIDGE_NDEBUG | |
455 | cy_as_hal_print_message(KERN_ERR | |
456 | "cyasblkdev %s: killing request - no device/host\n", | |
457 | req->rq_disk->disk_name); | |
458 | #endif | |
459 | stat = BLKPREP_KILL; | |
460 | } | |
461 | ||
462 | if (bd->suspended) { | |
463 | blk_plug_device(bd->queue.queue); | |
464 | stat = BLKPREP_DEFER; | |
465 | } | |
466 | ||
467 | /* Check for excessive requests.*/ | |
468 | if (blk_rq_pos(req) + blk_rq_sectors(req) > get_capacity(req->rq_disk)) { | |
469 | cy_as_hal_print_message("cyasblkdev: bad request address\n"); | |
470 | stat = BLKPREP_KILL; | |
471 | } | |
472 | ||
473 | return stat; | |
474 | } | |
475 | ||
476 | /*west bridge storage async api on_completed callback */ | |
477 | static void cyasblkdev_issuecallback( | |
478 | /* Handle to the device completing the storage operation */ | |
479 | cy_as_device_handle handle, | |
480 | /* The media type completing the operation */ | |
481 | cy_as_media_type type, | |
482 | /* The device completing the operation */ | |
483 | uint32_t device, | |
484 | /* The unit completing the operation */ | |
485 | uint32_t unit, | |
486 | /* The block number of the completed operation */ | |
487 | uint32_t block_number, | |
488 | /* The type of operation */ | |
489 | cy_as_oper_type op, | |
490 | /* The error status */ | |
491 | cy_as_return_status_t status | |
492 | ) | |
493 | { | |
494 | int retry_cnt = 0; | |
495 | DBGPRN_FUNC_NAME; | |
496 | ||
497 | if (status != CY_AS_ERROR_SUCCESS) { | |
498 | #ifndef WESTBRIDGE_NDEBUG | |
499 | cy_as_hal_print_message( | |
500 | "%s: async r/w: op:%d failed with error %d at address %d\n", | |
0769c38d | 501 | __func__, op, status, block_number); |
81eb669b DC |
502 | #endif |
503 | } | |
504 | ||
505 | #ifndef WESTBRIDGE_NDEBUG | |
506 | cy_as_hal_print_message( | |
507 | "%s calling blk_end_request from issue_callback " | |
508 | "req=0x%x, status=0x%x, nr_sectors=0x%x\n", | |
509 | __func__, (unsigned int) gl_bd->queue.req, status, | |
0769c38d | 510 | (unsigned int) blk_rq_sectors(gl_bd->queue.req)); |
81eb669b DC |
511 | #endif |
512 | ||
513 | /* note: blk_end_request w/o __ prefix should | |
514 | * not require spinlocks on the queue*/ | |
515 | while (blk_end_request(gl_bd->queue.req, | |
516 | status, blk_rq_sectors(gl_bd->queue.req)*512)) { | |
517 | retry_cnt++; | |
518 | }; | |
519 | ||
520 | #ifndef WESTBRIDGE_NDEBUG | |
521 | cy_as_hal_print_message( | |
522 | "%s blkdev_callback: ended rq on %d sectors, " | |
523 | "with err:%d, n:%d times\n", __func__, | |
524 | (int)blk_rq_sectors(gl_bd->queue.req), status, | |
525 | retry_cnt | |
526 | ); | |
527 | #endif | |
528 | ||
529 | spin_lock_irq(&gl_bd->lock); | |
530 | ||
531 | /*elevate next request, if there is one*/ | |
532 | if (!blk_queue_plugged(gl_bd->queue.queue)) { | |
533 | /* queue is not plugged */ | |
534 | gl_bd->queue.req = blk_fetch_request(gl_bd->queue.queue); | |
535 | #ifndef WESTBRIDGE_NDEBUG | |
536 | cy_as_hal_print_message("%s blkdev_callback: " | |
537 | "blk_fetch_request():%p\n", | |
538 | __func__, gl_bd->queue.req); | |
539 | #endif | |
540 | } | |
541 | ||
542 | if (gl_bd->queue.req) { | |
543 | spin_unlock_irq(&gl_bd->lock); | |
544 | ||
545 | #ifndef WESTBRIDGE_NDEBUG | |
546 | cy_as_hal_print_message("%s blkdev_callback: about to " | |
547 | "call issue_fn:%p\n", __func__, gl_bd->queue.req); | |
548 | #endif | |
549 | ||
550 | gl_bd->queue.issue_fn(&gl_bd->queue, gl_bd->queue.req); | |
551 | } else { | |
552 | spin_unlock_irq(&gl_bd->lock); | |
553 | } | |
554 | } | |
555 | ||
556 | /* issue astoria blkdev request (issue_fn) */ | |
557 | static int cyasblkdev_blk_issue_rq( | |
558 | struct cyasblkdev_queue *bq, | |
559 | struct request *req | |
560 | ) | |
561 | { | |
562 | struct cyasblkdev_blk_data *bd = bq->data; | |
0769c38d | 563 | int index = 0; |
81eb669b DC |
564 | int ret = CY_AS_ERROR_SUCCESS; |
565 | uint32_t req_sector = 0; | |
566 | uint32_t req_nr_sectors = 0; | |
567 | int bus_num = 0; | |
568 | int lcl_unit_no = 0; | |
569 | ||
570 | DBGPRN_FUNC_NAME; | |
571 | ||
572 | /* | |
573 | * will construct a scatterlist for the given request; | |
574 | * the return value is the number of actually used | |
575 | * entries in the resulting list. Then, this scatterlist | |
576 | * can be used for the actual DMA prep operation. | |
577 | */ | |
578 | spin_lock_irq(&bd->lock); | |
579 | index = blk_rq_map_sg(bq->queue, req, bd->sg); | |
580 | ||
581 | if (req->rq_disk == bd->user_disk_0) { | |
582 | bus_num = bd->user_disk_0_bus_num; | |
583 | req_sector = blk_rq_pos(req) + gl_bd->user_disk_0_first_sector; | |
584 | req_nr_sectors = blk_rq_sectors(req); | |
585 | lcl_unit_no = gl_bd->user_disk_0_unit_no; | |
586 | ||
587 | #ifndef WESTBRIDGE_NDEBUG | |
588 | cy_as_hal_print_message("%s: request made to disk 0 " | |
589 | "for sector=%d, num_sectors=%d, unit_no=%d\n", | |
590 | __func__, req_sector, (int) blk_rq_sectors(req), | |
591 | lcl_unit_no); | |
592 | #endif | |
593 | } else if (req->rq_disk == bd->user_disk_1) { | |
594 | bus_num = bd->user_disk_1_bus_num; | |
595 | req_sector = blk_rq_pos(req) + gl_bd->user_disk_1_first_sector; | |
596 | /*SECT_NUM_TRANSLATE(blk_rq_sectors(req));*/ | |
597 | req_nr_sectors = blk_rq_sectors(req); | |
598 | lcl_unit_no = gl_bd->user_disk_1_unit_no; | |
599 | ||
600 | #ifndef WESTBRIDGE_NDEBUG | |
601 | cy_as_hal_print_message("%s: request made to disk 1 for " | |
602 | "sector=%d, num_sectors=%d, unit_no=%d\n", __func__, | |
603 | req_sector, (int) blk_rq_sectors(req), lcl_unit_no); | |
604 | #endif | |
605 | } else if (req->rq_disk == bd->system_disk) { | |
606 | bus_num = bd->system_disk_bus_num; | |
607 | req_sector = blk_rq_pos(req) + gl_bd->system_disk_first_sector; | |
608 | req_nr_sectors = blk_rq_sectors(req); | |
609 | lcl_unit_no = gl_bd->system_disk_unit_no; | |
610 | ||
611 | #ifndef WESTBRIDGE_NDEBUG | |
612 | cy_as_hal_print_message("%s: request made to system disk " | |
613 | "for sector=%d, num_sectors=%d, unit_no=%d\n", __func__, | |
614 | req_sector, (int) blk_rq_sectors(req), lcl_unit_no); | |
615 | #endif | |
616 | } | |
617 | #ifndef WESTBRIDGE_NDEBUG | |
618 | else { | |
619 | cy_as_hal_print_message( | |
620 | "%s: invalid disk used for request\n", __func__); | |
621 | } | |
622 | #endif | |
623 | ||
624 | spin_unlock_irq(&bd->lock); | |
625 | ||
626 | if (rq_data_dir(req) == READ) { | |
627 | #ifndef WESTBRIDGE_NDEBUG | |
628 | cy_as_hal_print_message("%s: calling readasync() " | |
629 | "req_sector=0x%x, req_nr_sectors=0x%x, bd->sg:%x\n\n", | |
630 | __func__, req_sector, req_nr_sectors, (uint32_t)bd->sg); | |
631 | #endif | |
632 | ||
633 | ret = cy_as_storage_read_async(bd->dev_handle, bus_num, 0, | |
634 | lcl_unit_no, req_sector, bd->sg, req_nr_sectors, | |
635 | (cy_as_storage_callback)cyasblkdev_issuecallback); | |
636 | ||
637 | if (ret != CY_AS_ERROR_SUCCESS) { | |
638 | #ifndef WESTBRIDGE_NDEBUG | |
639 | cy_as_hal_print_message("%s:readasync() error %d at " | |
640 | "address %ld, unit no %d\n", __func__, ret, | |
641 | blk_rq_pos(req), lcl_unit_no); | |
642 | cy_as_hal_print_message("%s:ending i/o request " | |
643 | "on reg:%x\n", __func__, (uint32_t)req); | |
644 | #endif | |
645 | ||
646 | while (blk_end_request(req, | |
647 | (ret == CY_AS_ERROR_SUCCESS), | |
648 | req_nr_sectors*512)) | |
649 | ; | |
650 | ||
0769c38d | 651 | bq->req = NULL; |
81eb669b DC |
652 | } |
653 | } else { | |
654 | ret = cy_as_storage_write_async(bd->dev_handle, bus_num, 0, | |
655 | lcl_unit_no, req_sector, bd->sg, req_nr_sectors, | |
656 | (cy_as_storage_callback)cyasblkdev_issuecallback); | |
657 | ||
658 | if (ret != CY_AS_ERROR_SUCCESS) { | |
659 | #ifndef WESTBRIDGE_NDEBUG | |
660 | cy_as_hal_print_message("%s: write failed with " | |
661 | "error %d at address %ld, unit no %d\n", | |
662 | __func__, ret, blk_rq_pos(req), lcl_unit_no); | |
663 | #endif | |
664 | ||
665 | /*end IO op on this request(does both | |
666 | * end_that_request_... _first & _last) */ | |
667 | while (blk_end_request(req, | |
668 | (ret == CY_AS_ERROR_SUCCESS), | |
669 | req_nr_sectors*512)) | |
670 | ; | |
671 | ||
0769c38d | 672 | bq->req = NULL; |
81eb669b DC |
673 | } |
674 | } | |
675 | ||
676 | return ret; | |
677 | } | |
678 | ||
679 | static unsigned long | |
680 | dev_use[CYASBLKDEV_NUM_MINORS / (8 * sizeof(unsigned long))]; | |
681 | ||
682 | ||
683 | /* storage event callback (note: called in astoria isr context) */ | |
684 | static void cyasblkdev_storage_callback( | |
685 | cy_as_device_handle dev_h, | |
686 | cy_as_bus_number_t bus, | |
687 | uint32_t device, | |
688 | cy_as_storage_event evtype, | |
689 | void *evdata | |
690 | ) | |
691 | { | |
692 | #ifndef WESTBRIDGE_NDEBUG | |
693 | cy_as_hal_print_message("%s: bus:%d, device:%d, evtype:%d, " | |
694 | "evdata:%p\n ", __func__, bus, device, evtype, evdata); | |
695 | #endif | |
696 | ||
697 | switch (evtype) { | |
698 | case cy_as_storage_processor: | |
699 | break; | |
700 | ||
701 | case cy_as_storage_removed: | |
702 | break; | |
703 | ||
704 | case cy_as_storage_inserted: | |
705 | break; | |
706 | ||
707 | default: | |
708 | break; | |
709 | } | |
710 | } | |
711 | ||
712 | #define SECTORS_TO_SCAN 4096 | |
713 | ||
714 | uint32_t cyasblkdev_get_vfat_offset(int bus_num, int unit_no) | |
715 | { | |
716 | /* | |
717 | * for sd media, vfat partition boot record is not always | |
718 | * located at sector it greatly depends on the system and | |
719 | * software that was used to format the sd however, linux | |
720 | * fs layer always expects it at sector 0, this function | |
721 | * finds the offset and then uses it in all media r/w | |
722 | * operations | |
723 | */ | |
724 | int sect_no, stat; | |
725 | uint8_t *sect_buf; | |
726 | bool br_found = false; | |
727 | ||
728 | DBGPRN_FUNC_NAME; | |
729 | ||
730 | sect_buf = kmalloc(1024, GFP_KERNEL); | |
731 | ||
732 | /* since HAL layer always uses sg lists instead of the | |
733 | * buffer (for hw dmas) we need to initialize the sg list | |
734 | * for local buffer*/ | |
735 | sg_init_one(gl_bd->sg, sect_buf, 512); | |
736 | ||
737 | /* | |
738 | * Check MPR partition table 1st, then try to scan through | |
739 | * 1st 384 sectors until BR signature(intel JMP istruction | |
740 | * code and ,0x55AA) is found | |
741 | */ | |
742 | #ifndef WESTBRIDGE_NDEBUG | |
743 | cy_as_hal_print_message( | |
0769c38d | 744 | "%s scanning media for vfat partition...\n", __func__); |
81eb669b DC |
745 | #endif |
746 | ||
747 | for (sect_no = 0; sect_no < SECTORS_TO_SCAN; sect_no++) { | |
748 | #ifndef WESTBRIDGE_NDEBUG | |
749 | cy_as_hal_print_message("%s before cyasstorageread " | |
750 | "gl_bd->sg addr=0x%x\n", __func__, | |
751 | (unsigned int) gl_bd->sg); | |
752 | #endif | |
753 | ||
754 | stat = cy_as_storage_read( | |
755 | /* Handle to the device of interest */ | |
756 | gl_bd->dev_handle, | |
757 | /* The bus to access */ | |
758 | bus_num, | |
759 | /* The device to access */ | |
760 | 0, | |
761 | /* The unit to access */ | |
762 | unit_no, | |
763 | /* absolute sector number */ | |
764 | sect_no, | |
765 | /* sg structure */ | |
766 | gl_bd->sg, | |
767 | /* The number of blocks to be read */ | |
768 | 1 | |
769 | ); | |
770 | ||
771 | /* try only sectors with boot signature */ | |
772 | if ((sect_buf[510] == 0x55) && (sect_buf[511] == 0xaa)) { | |
773 | /* vfat boot record may also be located at | |
774 | * sector 0, check it first */ | |
775 | if (sect_buf[0] == 0xEB) { | |
776 | #ifndef WESTBRIDGE_NDEBUG | |
777 | cy_as_hal_print_message( | |
778 | "%s vfat partition found " | |
779 | "at sector:%d\n", | |
780 | __func__, sect_no); | |
781 | #endif | |
782 | ||
783 | br_found = true; | |
784 | break; | |
785 | } | |
786 | } | |
787 | ||
788 | if (stat != 0) { | |
789 | #ifndef WESTBRIDGE_NDEBUG | |
790 | cy_as_hal_print_message("%s sector scan error\n", | |
791 | __func__); | |
792 | #endif | |
793 | break; | |
794 | } | |
795 | } | |
796 | ||
797 | kfree(sect_buf); | |
798 | ||
799 | if (br_found) { | |
800 | return sect_no; | |
801 | } else { | |
802 | #ifndef WESTBRIDGE_NDEBUG | |
803 | cy_as_hal_print_message( | |
804 | "%s vfat partition is not found, using 0 offset\n", | |
805 | __func__); | |
806 | #endif | |
807 | return 0; | |
808 | } | |
809 | } | |
810 | ||
0769c38d | 811 | cy_as_storage_query_device_data dev_data = {0}; |
81eb669b DC |
812 | |
813 | static int cyasblkdev_add_disks(int bus_num, | |
814 | struct cyasblkdev_blk_data *bd, | |
815 | int total_media_count, | |
816 | int devidx) | |
817 | { | |
818 | int ret = 0; | |
819 | uint64_t disk_cap; | |
820 | int lcl_unit_no; | |
0769c38d | 821 | cy_as_storage_query_unit_data unit_data = {0}; |
81eb669b DC |
822 | |
823 | #ifndef WESTBRIDGE_NDEBUG | |
824 | cy_as_hal_print_message("%s:query device: " | |
825 | "type:%d, removable:%d, writable:%d, " | |
826 | "blksize %d, units:%d, locked:%d, " | |
827 | "erase_sz:%d\n", | |
828 | __func__, | |
829 | dev_data.desc_p.type, | |
830 | dev_data.desc_p.removable, | |
831 | dev_data.desc_p.writeable, | |
832 | dev_data.desc_p.block_size, | |
833 | dev_data.desc_p.number_units, | |
834 | dev_data.desc_p.locked, | |
835 | dev_data.desc_p.erase_unit_size | |
836 | ); | |
837 | #endif | |
838 | ||
839 | /* make sure that device is not locked */ | |
840 | if (dev_data.desc_p.locked) { | |
841 | #ifndef WESTBRIDGE_NDEBUG | |
842 | cy_as_hal_print_message( | |
843 | "%s: device is locked\n", __func__); | |
844 | #endif | |
845 | ret = cy_as_storage_release( | |
0769c38d | 846 | bd->dev_handle, bus_num, 0, 0, 0); |
81eb669b DC |
847 | if (ret != CY_AS_ERROR_SUCCESS) { |
848 | #ifndef WESTBRIDGE_NDEBUG | |
849 | cy_as_hal_print_message("%s cannot release" | |
0769c38d | 850 | " storage\n", __func__); |
81eb669b DC |
851 | #endif |
852 | goto out; | |
853 | } | |
854 | goto out; | |
855 | } | |
856 | ||
0769c38d DC |
857 | unit_data.device = 0; |
858 | unit_data.unit = 0; | |
81eb669b DC |
859 | unit_data.bus = bus_num; |
860 | ret = cy_as_storage_query_unit(bd->dev_handle, | |
0769c38d | 861 | &unit_data, 0, 0); |
81eb669b DC |
862 | if (ret != CY_AS_ERROR_SUCCESS) { |
863 | #ifndef WESTBRIDGE_NDEBUG | |
864 | cy_as_hal_print_message("%s: cannot query " | |
865 | "%d device unit - reason code %d\n", | |
0769c38d | 866 | __func__, bus_num, ret); |
81eb669b | 867 | #endif |
0769c38d | 868 | goto out; |
81eb669b DC |
869 | } |
870 | ||
871 | if (private_partition_bus == bus_num) { | |
872 | if (private_partition_size > 0) { | |
873 | ret = cy_as_storage_create_p_partition( | |
874 | bd->dev_handle, bus_num, 0, | |
875 | private_partition_size, 0, 0); | |
876 | if ((ret != CY_AS_ERROR_SUCCESS) && | |
877 | (ret != CY_AS_ERROR_ALREADY_PARTITIONED)) { | |
878 | #ifndef WESTBRIDGE_NDEBUG | |
879 | cy_as_hal_print_message("%s: cy_as_storage_" | |
880 | "create_p_partition after size > 0 check " | |
881 | "failed with error code %d\n", | |
882 | __func__, ret); | |
883 | #endif | |
884 | ||
885 | disk_cap = (uint64_t) | |
886 | (unit_data.desc_p.unit_size); | |
887 | lcl_unit_no = 0; | |
888 | ||
889 | } else if (ret == CY_AS_ERROR_ALREADY_PARTITIONED) { | |
890 | #ifndef WESTBRIDGE_NDEBUG | |
891 | cy_as_hal_print_message( | |
892 | "%s: cy_as_storage_create_p_partition " | |
893 | "indicates memory already partitioned\n", | |
894 | __func__); | |
895 | #endif | |
896 | ||
897 | /*check to see that partition | |
898 | * matches size */ | |
899 | if (unit_data.desc_p.unit_size != | |
900 | private_partition_size) { | |
901 | ret = cy_as_storage_remove_p_partition( | |
902 | bd->dev_handle, | |
903 | bus_num, 0, 0, 0); | |
904 | if (ret == CY_AS_ERROR_SUCCESS) { | |
905 | ret = cy_as_storage_create_p_partition( | |
906 | bd->dev_handle, bus_num, 0, | |
907 | private_partition_size, 0, 0); | |
908 | if (ret == CY_AS_ERROR_SUCCESS) { | |
0769c38d DC |
909 | unit_data.bus = bus_num; |
910 | unit_data.device = 0; | |
911 | unit_data.unit = 1; | |
81eb669b DC |
912 | } else { |
913 | #ifndef WESTBRIDGE_NDEBUG | |
914 | cy_as_hal_print_message( | |
915 | "%s: cy_as_storage_create_p_partition " | |
916 | "after removal unexpectedly failed " | |
0769c38d | 917 | "with error %d\n", __func__, ret); |
81eb669b DC |
918 | #endif |
919 | ||
920 | /* need to requery bus | |
921 | * seeing as delete | |
922 | * successful and create | |
923 | * failed we have changed | |
924 | * the disk properties */ | |
0769c38d DC |
925 | unit_data.bus = bus_num; |
926 | unit_data.device = 0; | |
927 | unit_data.unit = 0; | |
81eb669b DC |
928 | } |
929 | ||
930 | ret = cy_as_storage_query_unit( | |
931 | bd->dev_handle, | |
0769c38d | 932 | &unit_data, 0, 0); |
81eb669b DC |
933 | if (ret != CY_AS_ERROR_SUCCESS) { |
934 | #ifndef WESTBRIDGE_NDEBUG | |
935 | cy_as_hal_print_message( | |
936 | "%s: cannot query %d " | |
937 | "device unit - reason code %d\n", | |
0769c38d | 938 | __func__, bus_num, ret); |
81eb669b | 939 | #endif |
0769c38d | 940 | goto out; |
81eb669b DC |
941 | } else { |
942 | disk_cap = (uint64_t) | |
943 | (unit_data.desc_p.unit_size); | |
944 | lcl_unit_no = | |
945 | unit_data.unit; | |
946 | } | |
947 | } else { | |
948 | #ifndef WESTBRIDGE_NDEBUG | |
949 | cy_as_hal_print_message( | |
950 | "%s: cy_as_storage_remove_p_partition " | |
951 | "failed with error %d\n", | |
952 | __func__, ret); | |
953 | #endif | |
954 | ||
0769c38d DC |
955 | unit_data.bus = bus_num; |
956 | unit_data.device = 0; | |
957 | unit_data.unit = 1; | |
81eb669b DC |
958 | |
959 | ret = cy_as_storage_query_unit( | |
0769c38d | 960 | bd->dev_handle, &unit_data, 0, 0); |
81eb669b DC |
961 | if (ret != CY_AS_ERROR_SUCCESS) { |
962 | #ifndef WESTBRIDGE_NDEBUG | |
963 | cy_as_hal_print_message( | |
964 | "%s: cannot query %d " | |
965 | "device unit - reason " | |
966 | "code %d\n", __func__, | |
0769c38d | 967 | bus_num, ret); |
81eb669b | 968 | #endif |
0769c38d | 969 | goto out; |
81eb669b DC |
970 | } |
971 | ||
972 | disk_cap = (uint64_t) | |
973 | (unit_data.desc_p.unit_size); | |
974 | lcl_unit_no = | |
0769c38d | 975 | unit_data.unit; |
81eb669b DC |
976 | } |
977 | } else { | |
978 | #ifndef WESTBRIDGE_NDEBUG | |
979 | cy_as_hal_print_message("%s: partition " | |
980 | "exists and sizes equal\n", | |
981 | __func__); | |
982 | #endif | |
983 | ||
984 | /*partition already existed, | |
985 | * need to query second unit*/ | |
0769c38d DC |
986 | unit_data.bus = bus_num; |
987 | unit_data.device = 0; | |
988 | unit_data.unit = 1; | |
81eb669b DC |
989 | |
990 | ret = cy_as_storage_query_unit( | |
0769c38d | 991 | bd->dev_handle, &unit_data, 0, 0); |
81eb669b DC |
992 | if (ret != CY_AS_ERROR_SUCCESS) { |
993 | #ifndef WESTBRIDGE_NDEBUG | |
994 | cy_as_hal_print_message( | |
995 | "%s: cannot query %d " | |
996 | "device unit " | |
997 | "- reason code %d\n", | |
0769c38d | 998 | __func__, bus_num, ret); |
81eb669b | 999 | #endif |
0769c38d | 1000 | goto out; |
81eb669b DC |
1001 | } else { |
1002 | disk_cap = (uint64_t) | |
1003 | (unit_data.desc_p.unit_size); | |
0769c38d | 1004 | lcl_unit_no = unit_data.unit; |
81eb669b DC |
1005 | } |
1006 | } | |
1007 | } else { | |
1008 | #ifndef WESTBRIDGE_NDEBUG | |
1009 | cy_as_hal_print_message( | |
1010 | "%s: cy_as_storage_create_p_partition " | |
1011 | "created successfully\n", __func__); | |
1012 | #endif | |
1013 | ||
1014 | disk_cap = (uint64_t) | |
1015 | (unit_data.desc_p.unit_size - | |
1016 | private_partition_size); | |
1017 | ||
1018 | lcl_unit_no = 1; | |
1019 | } | |
1020 | } | |
1021 | #ifndef WESTBRIDGE_NDEBUG | |
1022 | else { | |
1023 | cy_as_hal_print_message( | |
1024 | "%s: invalid partition_size%d\n", __func__, | |
1025 | private_partition_size); | |
1026 | ||
1027 | disk_cap = (uint64_t) | |
1028 | (unit_data.desc_p.unit_size); | |
1029 | lcl_unit_no = 0; | |
1030 | } | |
1031 | #endif | |
1032 | } else { | |
1033 | disk_cap = (uint64_t) | |
1034 | (unit_data.desc_p.unit_size); | |
1035 | lcl_unit_no = 0; | |
1036 | } | |
1037 | ||
1038 | if ((bus_num == 0) || | |
1039 | (total_media_count == 1)) { | |
1040 | sprintf(bd->user_disk_0->disk_name, | |
1041 | "cyasblkdevblk%d", devidx); | |
1042 | ||
1043 | #ifndef WESTBRIDGE_NDEBUG | |
1044 | cy_as_hal_print_message( | |
1045 | "%s: disk unit_sz:%lu blk_sz:%d, " | |
1046 | "start_blk:%lu, capacity:%llu\n", | |
1047 | __func__, (unsigned long) | |
1048 | unit_data.desc_p.unit_size, | |
1049 | unit_data.desc_p.block_size, | |
1050 | (unsigned long) | |
1051 | unit_data.desc_p.start_block, | |
1052 | (uint64_t)disk_cap | |
1053 | ); | |
1054 | #endif | |
1055 | ||
1056 | #ifndef WESTBRIDGE_NDEBUG | |
1057 | cy_as_hal_print_message("%s: setting gendisk disk " | |
1058 | "capacity to %d\n", __func__, (int) disk_cap); | |
1059 | #endif | |
1060 | ||
1061 | /* initializing bd->queue */ | |
1062 | #ifndef WESTBRIDGE_NDEBUG | |
1063 | cy_as_hal_print_message("%s: init bd->queue\n", | |
1064 | __func__); | |
1065 | #endif | |
1066 | ||
1067 | /* this will create a | |
1068 | * queue kernel thread */ | |
1069 | cyasblkdev_init_queue( | |
0769c38d | 1070 | &bd->queue, &bd->lock); |
81eb669b DC |
1071 | |
1072 | bd->queue.prep_fn = cyasblkdev_blk_prep_rq; | |
1073 | bd->queue.issue_fn = cyasblkdev_blk_issue_rq; | |
1074 | bd->queue.data = bd; | |
1075 | ||
1076 | /*blk_size should always | |
1077 | * be a multiple of 512, | |
1078 | * set to the max to ensure | |
1079 | * that all accesses aligned | |
1080 | * to the greatest multiple, | |
1081 | * can adjust request to | |
1082 | * smaller block sizes | |
1083 | * dynamically*/ | |
1084 | ||
1085 | bd->user_disk_0_read_only = !dev_data.desc_p.writeable; | |
1086 | bd->user_disk_0_blk_size = dev_data.desc_p.block_size; | |
1087 | bd->user_disk_0_type = dev_data.desc_p.type; | |
1088 | bd->user_disk_0_bus_num = bus_num; | |
1089 | bd->user_disk_0->major = major; | |
1090 | bd->user_disk_0->first_minor = devidx << CYASBLKDEV_SHIFT; | |
1091 | bd->user_disk_0->minors = 8; | |
1092 | bd->user_disk_0->fops = &cyasblkdev_bdops; | |
1093 | bd->user_disk_0->private_data = bd; | |
1094 | bd->user_disk_0->queue = bd->queue.queue; | |
1095 | bd->dbgprn_flags = DBGPRN_RD_RQ; | |
1096 | bd->user_disk_0_unit_no = lcl_unit_no; | |
1097 | ||
1098 | blk_queue_logical_block_size(bd->queue.queue, | |
1099 | bd->user_disk_0_blk_size); | |
1100 | ||
1101 | set_capacity(bd->user_disk_0, | |
1102 | disk_cap); | |
1103 | ||
1104 | #ifndef WESTBRIDGE_NDEBUG | |
1105 | cy_as_hal_print_message( | |
1106 | "%s: returned from set_capacity %d\n", | |
1107 | __func__, (int) disk_cap); | |
1108 | #endif | |
1109 | ||
1110 | /* need to start search from | |
1111 | * public partition beginning */ | |
1112 | if (vfat_search) { | |
1113 | bd->user_disk_0_first_sector = | |
9bed00f2 DC |
1114 | cyasblkdev_get_vfat_offset( |
1115 | bd->user_disk_0_bus_num, | |
81eb669b DC |
1116 | bd->user_disk_0_unit_no); |
1117 | } else { | |
1118 | bd->user_disk_0_first_sector = 0; | |
1119 | } | |
1120 | ||
1121 | #ifndef WESTBRIDGE_NDEBUG | |
1122 | cy_as_hal_print_message( | |
1123 | "%s: set user_disk_0_first " | |
1124 | "sector to %d\n", __func__, | |
1125 | bd->user_disk_0_first_sector); | |
1126 | cy_as_hal_print_message( | |
1127 | "%s: add_disk: disk->major=0x%x\n", | |
1128 | __func__, | |
1129 | bd->user_disk_0->major); | |
1130 | cy_as_hal_print_message( | |
1131 | "%s: add_disk: " | |
1132 | "disk->first_minor=0x%x\n", __func__, | |
1133 | bd->user_disk_0->first_minor); | |
1134 | cy_as_hal_print_message( | |
1135 | "%s: add_disk: " | |
1136 | "disk->minors=0x%x\n", __func__, | |
1137 | bd->user_disk_0->minors); | |
1138 | cy_as_hal_print_message( | |
1139 | "%s: add_disk: " | |
1140 | "disk->disk_name=%s\n", | |
1141 | __func__, | |
1142 | bd->user_disk_0->disk_name); | |
1143 | cy_as_hal_print_message( | |
1144 | "%s: add_disk: " | |
1145 | "disk->part_tbl=0x%x\n", __func__, | |
1146 | (unsigned int) | |
1147 | bd->user_disk_0->part_tbl); | |
1148 | cy_as_hal_print_message( | |
1149 | "%s: add_disk: " | |
1150 | "disk->queue=0x%x\n", __func__, | |
1151 | (unsigned int) | |
1152 | bd->user_disk_0->queue); | |
1153 | cy_as_hal_print_message( | |
1154 | "%s: add_disk: " | |
1155 | "disk->flags=0x%x\n", | |
1156 | __func__, (unsigned int) | |
1157 | bd->user_disk_0->flags); | |
1158 | cy_as_hal_print_message( | |
1159 | "%s: add_disk: " | |
1160 | "disk->driverfs_dev=0x%x\n", | |
1161 | __func__, (unsigned int) | |
1162 | bd->user_disk_0->driverfs_dev); | |
1163 | cy_as_hal_print_message( | |
1164 | "%s: add_disk: " | |
1165 | "disk->slave_dir=0x%x\n", | |
1166 | __func__, (unsigned int) | |
1167 | bd->user_disk_0->slave_dir); | |
1168 | cy_as_hal_print_message( | |
1169 | "%s: add_disk: " | |
1170 | "disk->random=0x%x\n", | |
1171 | __func__, (unsigned int) | |
1172 | bd->user_disk_0->random); | |
1173 | cy_as_hal_print_message( | |
1174 | "%s: add_disk: " | |
1175 | "disk->node_id=0x%x\n", | |
1176 | __func__, (unsigned int) | |
1177 | bd->user_disk_0->node_id); | |
1178 | ||
1179 | #endif | |
1180 | ||
1181 | add_disk(bd->user_disk_0); | |
1182 | ||
1183 | } else if ((bus_num == 1) && | |
1184 | (total_media_count == 2)) { | |
1185 | bd->user_disk_1_read_only = !dev_data.desc_p.writeable; | |
1186 | bd->user_disk_1_blk_size = dev_data.desc_p.block_size; | |
1187 | bd->user_disk_1_type = dev_data.desc_p.type; | |
1188 | bd->user_disk_1_bus_num = bus_num; | |
1189 | bd->user_disk_1->major = major; | |
1190 | bd->user_disk_1->first_minor = (devidx + 1) << CYASBLKDEV_SHIFT; | |
1191 | bd->user_disk_1->minors = 8; | |
1192 | bd->user_disk_1->fops = &cyasblkdev_bdops; | |
1193 | bd->user_disk_1->private_data = bd; | |
1194 | bd->user_disk_1->queue = bd->queue.queue; | |
1195 | bd->dbgprn_flags = DBGPRN_RD_RQ; | |
1196 | bd->user_disk_1_unit_no = lcl_unit_no; | |
1197 | ||
1198 | sprintf(bd->user_disk_1->disk_name, | |
1199 | "cyasblkdevblk%d", (devidx + 1)); | |
1200 | ||
1201 | #ifndef WESTBRIDGE_NDEBUG | |
1202 | cy_as_hal_print_message( | |
1203 | "%s: disk unit_sz:%lu " | |
1204 | "blk_sz:%d, " | |
1205 | "start_blk:%lu, " | |
1206 | "capacity:%llu\n", | |
1207 | __func__, | |
1208 | (unsigned long) | |
1209 | unit_data.desc_p.unit_size, | |
1210 | unit_data.desc_p.block_size, | |
1211 | (unsigned long) | |
1212 | unit_data.desc_p.start_block, | |
1213 | (uint64_t)disk_cap | |
1214 | ); | |
1215 | #endif | |
1216 | ||
1217 | /*blk_size should always be a | |
1218 | * multiple of 512, set to the max | |
1219 | * to ensure that all accesses | |
1220 | * aligned to the greatest multiple, | |
1221 | * can adjust request to smaller | |
1222 | * block sizes dynamically*/ | |
1223 | if (bd->user_disk_0_blk_size > | |
1224 | bd->user_disk_1_blk_size) { | |
1225 | blk_queue_logical_block_size(bd->queue.queue, | |
1226 | bd->user_disk_0_blk_size); | |
1227 | #ifndef WESTBRIDGE_NDEBUG | |
1228 | cy_as_hal_print_message( | |
1229 | "%s: set hard sect_sz:%d\n", | |
1230 | __func__, | |
1231 | bd->user_disk_0_blk_size); | |
1232 | #endif | |
1233 | } else { | |
1234 | blk_queue_logical_block_size(bd->queue.queue, | |
1235 | bd->user_disk_1_blk_size); | |
1236 | #ifndef WESTBRIDGE_NDEBUG | |
1237 | cy_as_hal_print_message( | |
1238 | "%s: set hard sect_sz:%d\n", | |
1239 | __func__, | |
1240 | bd->user_disk_1_blk_size); | |
1241 | #endif | |
1242 | } | |
1243 | ||
1244 | set_capacity(bd->user_disk_1, disk_cap); | |
1245 | if (vfat_search) { | |
1246 | bd->user_disk_1_first_sector = | |
1247 | cyasblkdev_get_vfat_offset( | |
9bed00f2 DC |
1248 | bd->user_disk_1_bus_num, |
1249 | bd->user_disk_1_unit_no); | |
81eb669b DC |
1250 | } else { |
1251 | bd->user_disk_1_first_sector | |
1252 | = 0; | |
1253 | } | |
1254 | ||
1255 | add_disk(bd->user_disk_1); | |
1256 | } | |
1257 | ||
1258 | if (lcl_unit_no > 0) { | |
1259 | if (bd->system_disk == NULL) { | |
1260 | bd->system_disk = | |
9bed00f2 DC |
1261 | alloc_disk(8); |
1262 | ||
81eb669b DC |
1263 | if (bd->system_disk == NULL) { |
1264 | kfree(bd); | |
1265 | bd = ERR_PTR(-ENOMEM); | |
0769c38d | 1266 | return bd; |
81eb669b DC |
1267 | } |
1268 | disk_cap = (uint64_t) | |
1269 | (private_partition_size); | |
1270 | ||
1271 | /* set properties of | |
1272 | * system disk */ | |
1273 | bd->system_disk_read_only = !dev_data.desc_p.writeable; | |
1274 | bd->system_disk_blk_size = dev_data.desc_p.block_size; | |
1275 | bd->system_disk_bus_num = bus_num; | |
1276 | bd->system_disk->major = major; | |
1277 | bd->system_disk->first_minor = | |
1278 | (devidx + 2) << CYASBLKDEV_SHIFT; | |
1279 | bd->system_disk->minors = 8; | |
1280 | bd->system_disk->fops = &cyasblkdev_bdops; | |
1281 | bd->system_disk->private_data = bd; | |
1282 | bd->system_disk->queue = bd->queue.queue; | |
1283 | /* don't search for vfat | |
1284 | * with system disk */ | |
1285 | bd->system_disk_first_sector = 0; | |
1286 | sprintf( | |
1287 | bd->system_disk->disk_name, | |
1288 | "cyasblkdevblk%d", (devidx + 2)); | |
1289 | ||
1290 | set_capacity(bd->system_disk, | |
1291 | disk_cap); | |
1292 | ||
1293 | add_disk(bd->system_disk); | |
1294 | } | |
1295 | #ifndef WESTBRIDGE_NDEBUG | |
1296 | else { | |
1297 | cy_as_hal_print_message( | |
1298 | "%s: system disk already allocated %d\n", | |
0769c38d | 1299 | __func__, bus_num); |
81eb669b DC |
1300 | } |
1301 | #endif | |
1302 | } | |
1303 | out: | |
1304 | return ret; | |
1305 | } | |
1306 | ||
1307 | static struct cyasblkdev_blk_data *cyasblkdev_blk_alloc(void) | |
1308 | { | |
1309 | struct cyasblkdev_blk_data *bd; | |
0769c38d DC |
1310 | int ret = 0; |
1311 | cy_as_return_status_t stat = -1; | |
81eb669b DC |
1312 | int bus_num = 0; |
1313 | int total_media_count = 0; | |
1314 | int devidx = 0; | |
1315 | DBGPRN_FUNC_NAME; | |
1316 | ||
1317 | total_media_count = 0; | |
1318 | devidx = find_first_zero_bit(dev_use, CYASBLKDEV_NUM_MINORS); | |
1319 | if (devidx >= CYASBLKDEV_NUM_MINORS) | |
1320 | return ERR_PTR(-ENOSPC); | |
1321 | ||
1322 | __set_bit(devidx, dev_use); | |
1323 | __set_bit(devidx + 1, dev_use); | |
1324 | ||
1325 | bd = kzalloc(sizeof(struct cyasblkdev_blk_data), GFP_KERNEL); | |
1326 | if (bd) { | |
0769c38d | 1327 | gl_bd = bd; |
81eb669b DC |
1328 | |
1329 | spin_lock_init(&bd->lock); | |
1330 | bd->usage = 1; | |
1331 | ||
1332 | /* setup the block_dev_ops pointer*/ | |
1333 | bd->blkops = &cyasblkdev_bdops; | |
1334 | ||
1335 | /* Get the device handle */ | |
0769c38d | 1336 | bd->dev_handle = cyasdevice_getdevhandle(); |
81eb669b DC |
1337 | if (0 == bd->dev_handle) { |
1338 | #ifndef WESTBRIDGE_NDEBUG | |
1339 | cy_as_hal_print_message( | |
0769c38d | 1340 | "%s: get device failed\n", __func__); |
81eb669b | 1341 | #endif |
0769c38d DC |
1342 | ret = ENODEV; |
1343 | goto out; | |
81eb669b DC |
1344 | } |
1345 | ||
1346 | #ifndef WESTBRIDGE_NDEBUG | |
1347 | cy_as_hal_print_message("%s west bridge device handle:%x\n", | |
1348 | __func__, (uint32_t)bd->dev_handle); | |
1349 | #endif | |
1350 | ||
1351 | /* start the storage api and get a handle to the | |
1352 | * device we are interested in. */ | |
1353 | ||
1354 | /* Error code to use if the conditions are not satisfied. */ | |
0769c38d | 1355 | ret = ENOMEDIUM; |
81eb669b DC |
1356 | |
1357 | stat = cy_as_misc_release_resource(bd->dev_handle, cy_as_bus_0); | |
1358 | if ((stat != CY_AS_ERROR_SUCCESS) && | |
1359 | (stat != CY_AS_ERROR_RESOURCE_NOT_OWNED)) { | |
1360 | #ifndef WESTBRIDGE_NDEBUG | |
1361 | cy_as_hal_print_message("%s: cannot release " | |
1362 | "resource bus 0 - reason code %d\n", | |
0769c38d | 1363 | __func__, stat); |
81eb669b DC |
1364 | #endif |
1365 | } | |
1366 | ||
1367 | stat = cy_as_misc_release_resource(bd->dev_handle, cy_as_bus_1); | |
1368 | if ((stat != CY_AS_ERROR_SUCCESS) && | |
1369 | (stat != CY_AS_ERROR_RESOURCE_NOT_OWNED)) { | |
1370 | #ifndef WESTBRIDGE_NDEBUG | |
1371 | cy_as_hal_print_message("%s: cannot release " | |
1372 | "resource bus 0 - reason code %d\n", | |
0769c38d | 1373 | __func__, stat); |
81eb669b DC |
1374 | #endif |
1375 | } | |
1376 | ||
1377 | /* start storage stack*/ | |
0769c38d | 1378 | stat = cy_as_storage_start(bd->dev_handle, 0, 0x101); |
81eb669b DC |
1379 | if (stat != CY_AS_ERROR_SUCCESS) { |
1380 | #ifndef WESTBRIDGE_NDEBUG | |
1381 | cy_as_hal_print_message("%s: cannot start storage " | |
0769c38d | 1382 | "stack - reason code %d\n", __func__, stat); |
81eb669b DC |
1383 | #endif |
1384 | goto out; | |
1385 | } | |
1386 | ||
1387 | #ifndef WESTBRIDGE_NDEBUG | |
1388 | cy_as_hal_print_message("%s: storage started:%d ok\n", | |
1389 | __func__, stat); | |
1390 | #endif | |
1391 | ||
1392 | stat = cy_as_storage_register_callback(bd->dev_handle, | |
1393 | cyasblkdev_storage_callback); | |
1394 | if (stat != CY_AS_ERROR_SUCCESS) { | |
1395 | #ifndef WESTBRIDGE_NDEBUG | |
1396 | cy_as_hal_print_message("%s: cannot register callback " | |
0769c38d | 1397 | "- reason code %d\n", __func__, stat); |
81eb669b DC |
1398 | #endif |
1399 | goto out; | |
1400 | } | |
1401 | ||
1402 | for (bus_num = 0; bus_num < 2; bus_num++) { | |
1403 | stat = cy_as_storage_query_bus(bd->dev_handle, | |
0769c38d | 1404 | bus_num, &bd->media_count[bus_num], 0, 0); |
81eb669b DC |
1405 | if (stat == CY_AS_ERROR_SUCCESS) { |
1406 | total_media_count = total_media_count + | |
1407 | bd->media_count[bus_num]; | |
1408 | } else { | |
1409 | #ifndef WESTBRIDGE_NDEBUG | |
1410 | cy_as_hal_print_message("%s: cannot query %d, " | |
1411 | "reason code: %d\n", | |
0769c38d | 1412 | __func__, bus_num, stat); |
81eb669b DC |
1413 | #endif |
1414 | goto out; | |
1415 | } | |
1416 | } | |
1417 | ||
1418 | if (total_media_count == 0) { | |
1419 | #ifndef WESTBRIDGE_NDEBUG | |
1420 | cy_as_hal_print_message( | |
0769c38d | 1421 | "%s: no storage media was found\n", __func__); |
81eb669b | 1422 | #endif |
0769c38d | 1423 | goto out; |
81eb669b DC |
1424 | } else if (total_media_count >= 1) { |
1425 | if (bd->user_disk_0 == NULL) { | |
1426 | ||
1427 | bd->user_disk_0 = | |
9bed00f2 | 1428 | alloc_disk(8); |
81eb669b DC |
1429 | if (bd->user_disk_0 == NULL) { |
1430 | kfree(bd); | |
1431 | bd = ERR_PTR(-ENOMEM); | |
0769c38d | 1432 | return bd; |
81eb669b DC |
1433 | } |
1434 | } | |
1435 | #ifndef WESTBRIDGE_NDEBUG | |
1436 | else { | |
1437 | cy_as_hal_print_message("%s: no available " | |
1438 | "gen_disk for disk 0, " | |
1439 | "physically inconsistent\n", __func__); | |
1440 | } | |
1441 | #endif | |
1442 | } | |
1443 | ||
1444 | if (total_media_count == 2) { | |
1445 | if (bd->user_disk_1 == NULL) { | |
1446 | bd->user_disk_1 = | |
9bed00f2 | 1447 | alloc_disk(8); |
81eb669b DC |
1448 | if (bd->user_disk_1 == NULL) { |
1449 | kfree(bd); | |
1450 | bd = ERR_PTR(-ENOMEM); | |
0769c38d | 1451 | return bd; |
81eb669b DC |
1452 | } |
1453 | } | |
1454 | #ifndef WESTBRIDGE_NDEBUG | |
1455 | else { | |
1456 | cy_as_hal_print_message("%s: no available " | |
1457 | "gen_disk for media, " | |
1458 | "physically inconsistent\n", __func__); | |
1459 | } | |
1460 | #endif | |
1461 | } | |
1462 | #ifndef WESTBRIDGE_NDEBUG | |
1463 | else if (total_media_count > 2) { | |
1464 | cy_as_hal_print_message("%s: count corrupted = 0x%d\n", | |
1465 | __func__, total_media_count); | |
1466 | } | |
1467 | #endif | |
1468 | ||
1469 | #ifndef WESTBRIDGE_NDEBUG | |
1470 | cy_as_hal_print_message("%s: %d device(s) found\n", | |
0769c38d | 1471 | __func__, total_media_count); |
81eb669b DC |
1472 | #endif |
1473 | ||
1474 | for (bus_num = 0; bus_num <= 1; bus_num++) { | |
1475 | /*claim storage for cpu */ | |
1476 | stat = cy_as_storage_claim(bd->dev_handle, | |
0769c38d | 1477 | bus_num, 0, 0, 0); |
81eb669b DC |
1478 | if (stat != CY_AS_ERROR_SUCCESS) { |
1479 | cy_as_hal_print_message("%s: cannot claim " | |
1480 | "%d bus - reason code %d\n", | |
0769c38d | 1481 | __func__, bus_num, stat); |
81eb669b DC |
1482 | goto out; |
1483 | } | |
1484 | ||
0769c38d DC |
1485 | dev_data.bus = bus_num; |
1486 | dev_data.device = 0; | |
81eb669b DC |
1487 | |
1488 | stat = cy_as_storage_query_device(bd->dev_handle, | |
0769c38d | 1489 | &dev_data, 0, 0); |
81eb669b DC |
1490 | if (stat == CY_AS_ERROR_SUCCESS) { |
1491 | cyasblkdev_add_disks(bus_num, bd, | |
1492 | total_media_count, devidx); | |
1493 | } else if (stat == CY_AS_ERROR_NO_SUCH_DEVICE) { | |
1494 | #ifndef WESTBRIDGE_NDEBUG | |
1495 | cy_as_hal_print_message( | |
1496 | "%s: no device on bus %d\n", | |
0769c38d | 1497 | __func__, bus_num); |
81eb669b DC |
1498 | #endif |
1499 | } else { | |
1500 | #ifndef WESTBRIDGE_NDEBUG | |
1501 | cy_as_hal_print_message( | |
1502 | "%s: cannot query %d device " | |
1503 | "- reason code %d\n", | |
0769c38d | 1504 | __func__, bus_num, stat); |
81eb669b | 1505 | #endif |
0769c38d | 1506 | goto out; |
81eb669b DC |
1507 | } |
1508 | } /* end for (bus_num = 0; bus_num <= 1; bus_num++)*/ | |
1509 | ||
0769c38d | 1510 | return bd; |
81eb669b DC |
1511 | } |
1512 | out: | |
1513 | #ifndef WESTBRIDGE_NDEBUG | |
1514 | cy_as_hal_print_message( | |
1515 | "%s: bd failed to initialize\n", __func__); | |
1516 | #endif | |
1517 | ||
1518 | kfree(bd); | |
1519 | bd = ERR_PTR(-ret); | |
1520 | return bd; | |
1521 | } | |
1522 | ||
1523 | ||
1524 | /*init west bridge block device */ | |
1525 | static int cyasblkdev_blk_initialize(void) | |
1526 | { | |
1527 | struct cyasblkdev_blk_data *bd; | |
1528 | int res; | |
1529 | ||
1530 | DBGPRN_FUNC_NAME; | |
1531 | ||
1532 | res = register_blkdev(major, "cyasblkdev"); | |
1533 | ||
1534 | if (res < 0) { | |
1535 | #ifndef WESTBRIDGE_NDEBUG | |
1536 | cy_as_hal_print_message(KERN_WARNING | |
1537 | "%s unable to get major %d for cyasblkdev media: %d\n", | |
1538 | __func__, major, res); | |
1539 | #endif | |
1540 | return res; | |
1541 | } | |
1542 | ||
1543 | if (major == 0) | |
1544 | major = res; | |
1545 | ||
1546 | #ifndef WESTBRIDGE_NDEBUG | |
1547 | cy_as_hal_print_message( | |
1548 | "%s cyasblkdev registered with major number: %d\n", | |
0769c38d | 1549 | __func__, major); |
81eb669b DC |
1550 | #endif |
1551 | ||
1552 | bd = cyasblkdev_blk_alloc(); | |
1553 | if (IS_ERR(bd)) | |
1554 | return PTR_ERR(bd); | |
1555 | ||
1556 | return 0; | |
1557 | } | |
1558 | ||
1559 | /* start block device */ | |
1560 | static int __init cyasblkdev_blk_init(void) | |
1561 | { | |
1562 | int res = -ENOMEM; | |
1563 | ||
1564 | DBGPRN_FUNC_NAME; | |
1565 | ||
1566 | /* get the cyasdev handle for future use*/ | |
1567 | cyas_dev_handle = cyasdevice_getdevhandle(); | |
1568 | ||
1569 | if (cyasblkdev_blk_initialize() == 0) | |
1570 | return 0; | |
1571 | ||
1572 | #ifndef WESTBRIDGE_NDEBUG | |
1573 | cy_as_hal_print_message("cyasblkdev init error:%d\n", res); | |
1574 | #endif | |
1575 | return res; | |
1576 | } | |
1577 | ||
1578 | ||
1579 | static void cyasblkdev_blk_deinit(struct cyasblkdev_blk_data *bd) | |
1580 | { | |
1581 | DBGPRN_FUNC_NAME; | |
1582 | ||
1583 | if (bd) { | |
1584 | int devidx; | |
1585 | ||
1586 | if (bd->user_disk_0 != NULL) { | |
1587 | del_gendisk(bd->user_disk_0); | |
1588 | devidx = bd->user_disk_0->first_minor | |
1589 | >> CYASBLKDEV_SHIFT; | |
1590 | __clear_bit(devidx, dev_use); | |
1591 | } | |
1592 | ||
1593 | if (bd->user_disk_1 != NULL) { | |
1594 | del_gendisk(bd->user_disk_1); | |
1595 | devidx = bd->user_disk_1->first_minor | |
1596 | >> CYASBLKDEV_SHIFT; | |
1597 | __clear_bit(devidx, dev_use); | |
1598 | } | |
1599 | ||
1600 | if (bd->system_disk != NULL) { | |
1601 | del_gendisk(bd->system_disk); | |
1602 | devidx = bd->system_disk->first_minor | |
1603 | >> CYASBLKDEV_SHIFT; | |
1604 | __clear_bit(devidx, dev_use); | |
1605 | } | |
1606 | ||
1607 | cyasblkdev_blk_put(bd); | |
1608 | } | |
1609 | } | |
1610 | ||
1611 | /* block device exit */ | |
1612 | static void __exit cyasblkdev_blk_exit(void) | |
1613 | { | |
1614 | DBGPRN_FUNC_NAME; | |
1615 | ||
1616 | cyasblkdev_blk_deinit(gl_bd); | |
1617 | unregister_blkdev(major, "cyasblkdev"); | |
1618 | ||
1619 | } | |
1620 | ||
1621 | module_init(cyasblkdev_blk_init); | |
1622 | module_exit(cyasblkdev_blk_exit); | |
1623 | ||
1624 | MODULE_LICENSE("GPL"); | |
1625 | MODULE_DESCRIPTION("antioch (cyasblkdev) block device driver"); | |
1626 | MODULE_AUTHOR("cypress semiconductor"); | |
1627 | ||
1628 | /*[]*/ |