diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2009-04-07 14:06:41 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2009-04-07 14:06:41 -0400 |
commit | 6a5d263866d699ebf6843105497afc86ee53de5b (patch) | |
tree | 439195e272631908cdc2e3e44abaf7e1c3447157 /drivers/block | |
parent | aeeae86859f4319de0a4946b44771d9926eeed54 (diff) | |
parent | ffcd7dca3ab78f9f425971756e5e90024157f6be (diff) |
Merge branch 'for-linus' of git://git.kernel.dk/linux-2.6-block
* 'for-linus' of git://git.kernel.dk/linux-2.6-block:
loop: mutex already unlocked in loop_clr_fd()
cfq-iosched: don't let idling interfere with plugging
block: remove unused REQ_UNPLUG
cfq-iosched: kill two unused cfqq flags
cfq-iosched: change dispatch logic to deal with single requests at the time
mflash: initial support
cciss: change to discover first memory BAR
cciss: kernel scan thread for MSA2012
cciss: fix residual count for block pc requests
block: fix inconsistency in I/O stat accounting code
block: elevator quiescing helpers
Diffstat (limited to 'drivers/block')
-rw-r--r-- | drivers/block/Kconfig | 17 | ||||
-rw-r--r-- | drivers/block/Makefile | 1 | ||||
-rw-r--r-- | drivers/block/cciss.c | 117 | ||||
-rw-r--r-- | drivers/block/cciss.h | 2 | ||||
-rw-r--r-- | drivers/block/cciss_cmd.h | 23 | ||||
-rw-r--r-- | drivers/block/loop.c | 7 | ||||
-rw-r--r-- | drivers/block/mg_disk.c | 1005 |
7 files changed, 1162 insertions, 10 deletions
diff --git a/drivers/block/Kconfig b/drivers/block/Kconfig index e7b8aa0cb47c..ddea8e485cc9 100644 --- a/drivers/block/Kconfig +++ b/drivers/block/Kconfig | |||
@@ -410,6 +410,23 @@ config ATA_OVER_ETH | |||
410 | This driver provides Support for ATA over Ethernet block | 410 | This driver provides Support for ATA over Ethernet block |
411 | devices like the Coraid EtherDrive (R) Storage Blade. | 411 | devices like the Coraid EtherDrive (R) Storage Blade. |
412 | 412 | ||
413 | config MG_DISK | ||
414 | tristate "mGine mflash, gflash support" | ||
415 | depends on ARM && ATA && GPIOLIB | ||
416 | help | ||
417 | mGine mFlash(gFlash) block device driver | ||
418 | |||
419 | config MG_DISK_RES | ||
420 | int "Size of reserved area before MBR" | ||
421 | depends on MG_DISK | ||
422 | default 0 | ||
423 | help | ||
424 | Define size of reserved area that usually used for boot. Unit is KB. | ||
425 | All of the block device operation will be taken this value as start | ||
426 | offset | ||
427 | Examples: | ||
428 | 1024 => 1 MB | ||
429 | |||
413 | config SUNVDC | 430 | config SUNVDC |
414 | tristate "Sun Virtual Disk Client support" | 431 | tristate "Sun Virtual Disk Client support" |
415 | depends on SUN_LDOMS | 432 | depends on SUN_LDOMS |
diff --git a/drivers/block/Makefile b/drivers/block/Makefile index 3145141cef72..7755a5e2a85e 100644 --- a/drivers/block/Makefile +++ b/drivers/block/Makefile | |||
@@ -21,6 +21,7 @@ obj-$(CONFIG_BLK_CPQ_CISS_DA) += cciss.o | |||
21 | obj-$(CONFIG_BLK_DEV_DAC960) += DAC960.o | 21 | obj-$(CONFIG_BLK_DEV_DAC960) += DAC960.o |
22 | obj-$(CONFIG_XILINX_SYSACE) += xsysace.o | 22 | obj-$(CONFIG_XILINX_SYSACE) += xsysace.o |
23 | obj-$(CONFIG_CDROM_PKTCDVD) += pktcdvd.o | 23 | obj-$(CONFIG_CDROM_PKTCDVD) += pktcdvd.o |
24 | obj-$(CONFIG_MG_DISK) += mg_disk.o | ||
24 | obj-$(CONFIG_SUNVDC) += sunvdc.o | 25 | obj-$(CONFIG_SUNVDC) += sunvdc.o |
25 | 26 | ||
26 | obj-$(CONFIG_BLK_DEV_UMEM) += umem.o | 27 | obj-$(CONFIG_BLK_DEV_UMEM) += umem.o |
diff --git a/drivers/block/cciss.c b/drivers/block/cciss.c index a6c55432819b..0ef6f08aa6ea 100644 --- a/drivers/block/cciss.c +++ b/drivers/block/cciss.c | |||
@@ -51,6 +51,7 @@ | |||
51 | #include <scsi/scsi_ioctl.h> | 51 | #include <scsi/scsi_ioctl.h> |
52 | #include <linux/cdrom.h> | 52 | #include <linux/cdrom.h> |
53 | #include <linux/scatterlist.h> | 53 | #include <linux/scatterlist.h> |
54 | #include <linux/kthread.h> | ||
54 | 55 | ||
55 | #define CCISS_DRIVER_VERSION(maj,min,submin) ((maj<<16)|(min<<8)|(submin)) | 56 | #define CCISS_DRIVER_VERSION(maj,min,submin) ((maj<<16)|(min<<8)|(submin)) |
56 | #define DRIVER_NAME "HP CISS Driver (v 3.6.20)" | 57 | #define DRIVER_NAME "HP CISS Driver (v 3.6.20)" |
@@ -186,6 +187,8 @@ static int sendcmd_withirq(__u8 cmd, int ctlr, void *buff, size_t size, | |||
186 | __u8 page_code, int cmd_type); | 187 | __u8 page_code, int cmd_type); |
187 | 188 | ||
188 | static void fail_all_cmds(unsigned long ctlr); | 189 | static void fail_all_cmds(unsigned long ctlr); |
190 | static int scan_thread(void *data); | ||
191 | static int check_for_unit_attention(ctlr_info_t *h, CommandList_struct *c); | ||
189 | 192 | ||
190 | #ifdef CONFIG_PROC_FS | 193 | #ifdef CONFIG_PROC_FS |
191 | static void cciss_procinit(int i); | 194 | static void cciss_procinit(int i); |
@@ -735,6 +738,12 @@ static int cciss_getgeo(struct block_device *bdev, struct hd_geometry *geo) | |||
735 | return 0; | 738 | return 0; |
736 | } | 739 | } |
737 | 740 | ||
741 | static void check_ioctl_unit_attention(ctlr_info_t *host, CommandList_struct *c) | ||
742 | { | ||
743 | if (c->err_info->CommandStatus == CMD_TARGET_STATUS && | ||
744 | c->err_info->ScsiStatus != SAM_STAT_CHECK_CONDITION) | ||
745 | (void)check_for_unit_attention(host, c); | ||
746 | } | ||
738 | /* | 747 | /* |
739 | * ioctl | 748 | * ioctl |
740 | */ | 749 | */ |
@@ -1029,6 +1038,8 @@ static int cciss_ioctl(struct block_device *bdev, fmode_t mode, | |||
1029 | iocommand.buf_size, | 1038 | iocommand.buf_size, |
1030 | PCI_DMA_BIDIRECTIONAL); | 1039 | PCI_DMA_BIDIRECTIONAL); |
1031 | 1040 | ||
1041 | check_ioctl_unit_attention(host, c); | ||
1042 | |||
1032 | /* Copy the error information out */ | 1043 | /* Copy the error information out */ |
1033 | iocommand.error_info = *(c->err_info); | 1044 | iocommand.error_info = *(c->err_info); |
1034 | if (copy_to_user | 1045 | if (copy_to_user |
@@ -1180,6 +1191,7 @@ static int cciss_ioctl(struct block_device *bdev, fmode_t mode, | |||
1180 | (dma_addr_t) temp64.val, buff_size[i], | 1191 | (dma_addr_t) temp64.val, buff_size[i], |
1181 | PCI_DMA_BIDIRECTIONAL); | 1192 | PCI_DMA_BIDIRECTIONAL); |
1182 | } | 1193 | } |
1194 | check_ioctl_unit_attention(host, c); | ||
1183 | /* Copy the error information out */ | 1195 | /* Copy the error information out */ |
1184 | ioc->error_info = *(c->err_info); | 1196 | ioc->error_info = *(c->err_info); |
1185 | if (copy_to_user(argp, ioc, sizeof(*ioc))) { | 1197 | if (copy_to_user(argp, ioc, sizeof(*ioc))) { |
@@ -1287,6 +1299,7 @@ static void cciss_softirq_done(struct request *rq) | |||
1287 | { | 1299 | { |
1288 | CommandList_struct *cmd = rq->completion_data; | 1300 | CommandList_struct *cmd = rq->completion_data; |
1289 | ctlr_info_t *h = hba[cmd->ctlr]; | 1301 | ctlr_info_t *h = hba[cmd->ctlr]; |
1302 | unsigned int nr_bytes; | ||
1290 | unsigned long flags; | 1303 | unsigned long flags; |
1291 | u64bit temp64; | 1304 | u64bit temp64; |
1292 | int i, ddir; | 1305 | int i, ddir; |
@@ -1308,7 +1321,14 @@ static void cciss_softirq_done(struct request *rq) | |||
1308 | printk("Done with %p\n", rq); | 1321 | printk("Done with %p\n", rq); |
1309 | #endif /* CCISS_DEBUG */ | 1322 | #endif /* CCISS_DEBUG */ |
1310 | 1323 | ||
1311 | if (blk_end_request(rq, (rq->errors == 0) ? 0 : -EIO, blk_rq_bytes(rq))) | 1324 | /* |
1325 | * Store the full size and set the residual count for pc requests | ||
1326 | */ | ||
1327 | nr_bytes = blk_rq_bytes(rq); | ||
1328 | if (blk_pc_request(rq)) | ||
1329 | rq->data_len = cmd->err_info->ResidualCnt; | ||
1330 | |||
1331 | if (blk_end_request(rq, (rq->errors == 0) ? 0 : -EIO, nr_bytes)) | ||
1312 | BUG(); | 1332 | BUG(); |
1313 | 1333 | ||
1314 | spin_lock_irqsave(&h->lock, flags); | 1334 | spin_lock_irqsave(&h->lock, flags); |
@@ -2585,12 +2605,14 @@ static inline unsigned int make_status_bytes(unsigned int scsi_status_byte, | |||
2585 | ((driver_byte & 0xff) << 24); | 2605 | ((driver_byte & 0xff) << 24); |
2586 | } | 2606 | } |
2587 | 2607 | ||
2588 | static inline int evaluate_target_status(CommandList_struct *cmd) | 2608 | static inline int evaluate_target_status(ctlr_info_t *h, |
2609 | CommandList_struct *cmd, int *retry_cmd) | ||
2589 | { | 2610 | { |
2590 | unsigned char sense_key; | 2611 | unsigned char sense_key; |
2591 | unsigned char status_byte, msg_byte, host_byte, driver_byte; | 2612 | unsigned char status_byte, msg_byte, host_byte, driver_byte; |
2592 | int error_value; | 2613 | int error_value; |
2593 | 2614 | ||
2615 | *retry_cmd = 0; | ||
2594 | /* If we get in here, it means we got "target status", that is, scsi status */ | 2616 | /* If we get in here, it means we got "target status", that is, scsi status */ |
2595 | status_byte = cmd->err_info->ScsiStatus; | 2617 | status_byte = cmd->err_info->ScsiStatus; |
2596 | driver_byte = DRIVER_OK; | 2618 | driver_byte = DRIVER_OK; |
@@ -2618,6 +2640,11 @@ static inline int evaluate_target_status(CommandList_struct *cmd) | |||
2618 | if (((sense_key == 0x0) || (sense_key == 0x1)) && !blk_pc_request(cmd->rq)) | 2640 | if (((sense_key == 0x0) || (sense_key == 0x1)) && !blk_pc_request(cmd->rq)) |
2619 | error_value = 0; | 2641 | error_value = 0; |
2620 | 2642 | ||
2643 | if (check_for_unit_attention(h, cmd)) { | ||
2644 | *retry_cmd = !blk_pc_request(cmd->rq); | ||
2645 | return 0; | ||
2646 | } | ||
2647 | |||
2621 | if (!blk_pc_request(cmd->rq)) { /* Not SG_IO or similar? */ | 2648 | if (!blk_pc_request(cmd->rq)) { /* Not SG_IO or similar? */ |
2622 | if (error_value != 0) | 2649 | if (error_value != 0) |
2623 | printk(KERN_WARNING "cciss: cmd %p has CHECK CONDITION" | 2650 | printk(KERN_WARNING "cciss: cmd %p has CHECK CONDITION" |
@@ -2657,7 +2684,7 @@ static inline void complete_command(ctlr_info_t *h, CommandList_struct *cmd, | |||
2657 | 2684 | ||
2658 | switch (cmd->err_info->CommandStatus) { | 2685 | switch (cmd->err_info->CommandStatus) { |
2659 | case CMD_TARGET_STATUS: | 2686 | case CMD_TARGET_STATUS: |
2660 | rq->errors = evaluate_target_status(cmd); | 2687 | rq->errors = evaluate_target_status(h, cmd, &retry_cmd); |
2661 | break; | 2688 | break; |
2662 | case CMD_DATA_UNDERRUN: | 2689 | case CMD_DATA_UNDERRUN: |
2663 | if (blk_fs_request(cmd->rq)) { | 2690 | if (blk_fs_request(cmd->rq)) { |
@@ -3008,6 +3035,63 @@ static irqreturn_t do_cciss_intr(int irq, void *dev_id) | |||
3008 | return IRQ_HANDLED; | 3035 | return IRQ_HANDLED; |
3009 | } | 3036 | } |
3010 | 3037 | ||
3038 | static int scan_thread(void *data) | ||
3039 | { | ||
3040 | ctlr_info_t *h = data; | ||
3041 | int rc; | ||
3042 | DECLARE_COMPLETION_ONSTACK(wait); | ||
3043 | h->rescan_wait = &wait; | ||
3044 | |||
3045 | for (;;) { | ||
3046 | rc = wait_for_completion_interruptible(&wait); | ||
3047 | if (kthread_should_stop()) | ||
3048 | break; | ||
3049 | if (!rc) | ||
3050 | rebuild_lun_table(h, 0); | ||
3051 | } | ||
3052 | return 0; | ||
3053 | } | ||
3054 | |||
3055 | static int check_for_unit_attention(ctlr_info_t *h, CommandList_struct *c) | ||
3056 | { | ||
3057 | if (c->err_info->SenseInfo[2] != UNIT_ATTENTION) | ||
3058 | return 0; | ||
3059 | |||
3060 | switch (c->err_info->SenseInfo[12]) { | ||
3061 | case STATE_CHANGED: | ||
3062 | printk(KERN_WARNING "cciss%d: a state change " | ||
3063 | "detected, command retried\n", h->ctlr); | ||
3064 | return 1; | ||
3065 | break; | ||
3066 | case LUN_FAILED: | ||
3067 | printk(KERN_WARNING "cciss%d: LUN failure " | ||
3068 | "detected, action required\n", h->ctlr); | ||
3069 | return 1; | ||
3070 | break; | ||
3071 | case REPORT_LUNS_CHANGED: | ||
3072 | printk(KERN_WARNING "cciss%d: report LUN data " | ||
3073 | "changed\n", h->ctlr); | ||
3074 | if (h->rescan_wait) | ||
3075 | complete(h->rescan_wait); | ||
3076 | return 1; | ||
3077 | break; | ||
3078 | case POWER_OR_RESET: | ||
3079 | printk(KERN_WARNING "cciss%d: a power on " | ||
3080 | "or device reset detected\n", h->ctlr); | ||
3081 | return 1; | ||
3082 | break; | ||
3083 | case UNIT_ATTENTION_CLEARED: | ||
3084 | printk(KERN_WARNING "cciss%d: unit attention " | ||
3085 | "cleared by another initiator\n", h->ctlr); | ||
3086 | return 1; | ||
3087 | break; | ||
3088 | default: | ||
3089 | printk(KERN_WARNING "cciss%d: unknown " | ||
3090 | "unit attention detected\n", h->ctlr); | ||
3091 | return 1; | ||
3092 | } | ||
3093 | } | ||
3094 | |||
3011 | /* | 3095 | /* |
3012 | * We cannot read the structure directly, for portability we must use | 3096 | * We cannot read the structure directly, for portability we must use |
3013 | * the io functions. | 3097 | * the io functions. |
@@ -3181,12 +3265,21 @@ static int __devinit cciss_pci_init(ctlr_info_t *c, struct pci_dev *pdev) | |||
3181 | */ | 3265 | */ |
3182 | cciss_interrupt_mode(c, pdev, board_id); | 3266 | cciss_interrupt_mode(c, pdev, board_id); |
3183 | 3267 | ||
3184 | /* | 3268 | /* find the memory BAR */ |
3185 | * Memory base addr is first addr , the second points to the config | 3269 | for (i = 0; i < DEVICE_COUNT_RESOURCE; i++) { |
3186 | * table | 3270 | if (pci_resource_flags(pdev, i) & IORESOURCE_MEM) |
3187 | */ | 3271 | break; |
3272 | } | ||
3273 | if (i == DEVICE_COUNT_RESOURCE) { | ||
3274 | printk(KERN_WARNING "cciss: No memory BAR found\n"); | ||
3275 | err = -ENODEV; | ||
3276 | goto err_out_free_res; | ||
3277 | } | ||
3278 | |||
3279 | c->paddr = pci_resource_start(pdev, i); /* addressing mode bits | ||
3280 | * already removed | ||
3281 | */ | ||
3188 | 3282 | ||
3189 | c->paddr = pci_resource_start(pdev, 0); /* addressing mode bits already removed */ | ||
3190 | #ifdef CCISS_DEBUG | 3283 | #ifdef CCISS_DEBUG |
3191 | printk("address 0 = %lx\n", c->paddr); | 3284 | printk("address 0 = %lx\n", c->paddr); |
3192 | #endif /* CCISS_DEBUG */ | 3285 | #endif /* CCISS_DEBUG */ |
@@ -3753,6 +3846,11 @@ static int __devinit cciss_init_one(struct pci_dev *pdev, | |||
3753 | hba[i]->busy_initializing = 0; | 3846 | hba[i]->busy_initializing = 0; |
3754 | 3847 | ||
3755 | rebuild_lun_table(hba[i], 1); | 3848 | rebuild_lun_table(hba[i], 1); |
3849 | hba[i]->cciss_scan_thread = kthread_run(scan_thread, hba[i], | ||
3850 | "cciss_scan%02d", i); | ||
3851 | if (IS_ERR(hba[i]->cciss_scan_thread)) | ||
3852 | return PTR_ERR(hba[i]->cciss_scan_thread); | ||
3853 | |||
3756 | return 1; | 3854 | return 1; |
3757 | 3855 | ||
3758 | clean4: | 3856 | clean4: |
@@ -3828,6 +3926,7 @@ static void __devexit cciss_remove_one(struct pci_dev *pdev) | |||
3828 | printk(KERN_ERR "cciss: Unable to remove device \n"); | 3926 | printk(KERN_ERR "cciss: Unable to remove device \n"); |
3829 | return; | 3927 | return; |
3830 | } | 3928 | } |
3929 | |||
3831 | tmp_ptr = pci_get_drvdata(pdev); | 3930 | tmp_ptr = pci_get_drvdata(pdev); |
3832 | i = tmp_ptr->ctlr; | 3931 | i = tmp_ptr->ctlr; |
3833 | if (hba[i] == NULL) { | 3932 | if (hba[i] == NULL) { |
@@ -3836,6 +3935,8 @@ static void __devexit cciss_remove_one(struct pci_dev *pdev) | |||
3836 | return; | 3935 | return; |
3837 | } | 3936 | } |
3838 | 3937 | ||
3938 | kthread_stop(hba[i]->cciss_scan_thread); | ||
3939 | |||
3839 | remove_proc_entry(hba[i]->devname, proc_cciss); | 3940 | remove_proc_entry(hba[i]->devname, proc_cciss); |
3840 | unregister_blkdev(hba[i]->major, hba[i]->devname); | 3941 | unregister_blkdev(hba[i]->major, hba[i]->devname); |
3841 | 3942 | ||
diff --git a/drivers/block/cciss.h b/drivers/block/cciss.h index 15e2b84734e3..703e08038fb9 100644 --- a/drivers/block/cciss.h +++ b/drivers/block/cciss.h | |||
@@ -121,6 +121,8 @@ struct ctlr_info | |||
121 | struct sendcmd_reject_list scsi_rejects; | 121 | struct sendcmd_reject_list scsi_rejects; |
122 | #endif | 122 | #endif |
123 | unsigned char alive; | 123 | unsigned char alive; |
124 | struct completion *rescan_wait; | ||
125 | struct task_struct *cciss_scan_thread; | ||
124 | }; | 126 | }; |
125 | 127 | ||
126 | /* Defining the diffent access_menthods */ | 128 | /* Defining the diffent access_menthods */ |
diff --git a/drivers/block/cciss_cmd.h b/drivers/block/cciss_cmd.h index 24e22dea1a99..40b1b92dae7f 100644 --- a/drivers/block/cciss_cmd.h +++ b/drivers/block/cciss_cmd.h | |||
@@ -25,6 +25,29 @@ | |||
25 | #define CMD_TIMEOUT 0x000B | 25 | #define CMD_TIMEOUT 0x000B |
26 | #define CMD_UNABORTABLE 0x000C | 26 | #define CMD_UNABORTABLE 0x000C |
27 | 27 | ||
28 | /* Unit Attentions ASC's as defined for the MSA2012sa */ | ||
29 | #define POWER_OR_RESET 0x29 | ||
30 | #define STATE_CHANGED 0x2a | ||
31 | #define UNIT_ATTENTION_CLEARED 0x2f | ||
32 | #define LUN_FAILED 0x3e | ||
33 | #define REPORT_LUNS_CHANGED 0x3f | ||
34 | |||
35 | /* Unit Attentions ASCQ's as defined for the MSA2012sa */ | ||
36 | |||
37 | /* These ASCQ's defined for ASC = POWER_OR_RESET */ | ||
38 | #define POWER_ON_RESET 0x00 | ||
39 | #define POWER_ON_REBOOT 0x01 | ||
40 | #define SCSI_BUS_RESET 0x02 | ||
41 | #define MSA_TARGET_RESET 0x03 | ||
42 | #define CONTROLLER_FAILOVER 0x04 | ||
43 | #define TRANSCEIVER_SE 0x05 | ||
44 | #define TRANSCEIVER_LVD 0x06 | ||
45 | |||
46 | /* These ASCQ's defined for ASC = STATE_CHANGED */ | ||
47 | #define RESERVATION_PREEMPTED 0x03 | ||
48 | #define ASYM_ACCESS_CHANGED 0x06 | ||
49 | #define LUN_CAPACITY_CHANGED 0x09 | ||
50 | |||
28 | //transfer direction | 51 | //transfer direction |
29 | #define XFER_NONE 0x00 | 52 | #define XFER_NONE 0x00 |
30 | #define XFER_WRITE 0x01 | 53 | #define XFER_WRITE 0x01 |
diff --git a/drivers/block/loop.c b/drivers/block/loop.c index 40b17d3b55a1..ddae80825899 100644 --- a/drivers/block/loop.c +++ b/drivers/block/loop.c | |||
@@ -1431,6 +1431,7 @@ static int lo_open(struct block_device *bdev, fmode_t mode) | |||
1431 | static int lo_release(struct gendisk *disk, fmode_t mode) | 1431 | static int lo_release(struct gendisk *disk, fmode_t mode) |
1432 | { | 1432 | { |
1433 | struct loop_device *lo = disk->private_data; | 1433 | struct loop_device *lo = disk->private_data; |
1434 | int err; | ||
1434 | 1435 | ||
1435 | mutex_lock(&lo->lo_ctl_mutex); | 1436 | mutex_lock(&lo->lo_ctl_mutex); |
1436 | 1437 | ||
@@ -1442,7 +1443,9 @@ static int lo_release(struct gendisk *disk, fmode_t mode) | |||
1442 | * In autoclear mode, stop the loop thread | 1443 | * In autoclear mode, stop the loop thread |
1443 | * and remove configuration after last close. | 1444 | * and remove configuration after last close. |
1444 | */ | 1445 | */ |
1445 | loop_clr_fd(lo, NULL); | 1446 | err = loop_clr_fd(lo, NULL); |
1447 | if (!err) | ||
1448 | goto out_unlocked; | ||
1446 | } else { | 1449 | } else { |
1447 | /* | 1450 | /* |
1448 | * Otherwise keep thread (if running) and config, | 1451 | * Otherwise keep thread (if running) and config, |
@@ -1453,7 +1456,7 @@ static int lo_release(struct gendisk *disk, fmode_t mode) | |||
1453 | 1456 | ||
1454 | out: | 1457 | out: |
1455 | mutex_unlock(&lo->lo_ctl_mutex); | 1458 | mutex_unlock(&lo->lo_ctl_mutex); |
1456 | 1459 | out_unlocked: | |
1457 | return 0; | 1460 | return 0; |
1458 | } | 1461 | } |
1459 | 1462 | ||
diff --git a/drivers/block/mg_disk.c b/drivers/block/mg_disk.c new file mode 100644 index 000000000000..fb39d9aa3cdc --- /dev/null +++ b/drivers/block/mg_disk.c | |||
@@ -0,0 +1,1005 @@ | |||
1 | /* | ||
2 | * drivers/block/mg_disk.c | ||
3 | * | ||
4 | * Support for the mGine m[g]flash IO mode. | ||
5 | * Based on legacy hd.c | ||
6 | * | ||
7 | * (c) 2008 mGine Co.,LTD | ||
8 | * (c) 2008 unsik Kim <donari75@gmail.com> | ||
9 | * | ||
10 | * This program is free software; you can redistribute it and/or modify | ||
11 | * it under the terms of the GNU General Public License version 2 as | ||
12 | * published by the Free Software Foundation. | ||
13 | */ | ||
14 | |||
15 | #include <linux/kernel.h> | ||
16 | #include <linux/module.h> | ||
17 | #include <linux/fs.h> | ||
18 | #include <linux/blkdev.h> | ||
19 | #include <linux/hdreg.h> | ||
20 | #include <linux/libata.h> | ||
21 | #include <linux/interrupt.h> | ||
22 | #include <linux/delay.h> | ||
23 | #include <linux/platform_device.h> | ||
24 | #include <linux/gpio.h> | ||
25 | #include <linux/mg_disk.h> | ||
26 | |||
27 | #define MG_RES_SEC (CONFIG_MG_DISK_RES << 1) | ||
28 | |||
29 | static void mg_request(struct request_queue *); | ||
30 | |||
31 | static void mg_dump_status(const char *msg, unsigned int stat, | ||
32 | struct mg_host *host) | ||
33 | { | ||
34 | char *name = MG_DISK_NAME; | ||
35 | struct request *req; | ||
36 | |||
37 | if (host->breq) { | ||
38 | req = elv_next_request(host->breq); | ||
39 | if (req) | ||
40 | name = req->rq_disk->disk_name; | ||
41 | } | ||
42 | |||
43 | printk(KERN_ERR "%s: %s: status=0x%02x { ", name, msg, stat & 0xff); | ||
44 | if (stat & MG_REG_STATUS_BIT_BUSY) | ||
45 | printk("Busy "); | ||
46 | if (stat & MG_REG_STATUS_BIT_READY) | ||
47 | printk("DriveReady "); | ||
48 | if (stat & MG_REG_STATUS_BIT_WRITE_FAULT) | ||
49 | printk("WriteFault "); | ||
50 | if (stat & MG_REG_STATUS_BIT_SEEK_DONE) | ||
51 | printk("SeekComplete "); | ||
52 | if (stat & MG_REG_STATUS_BIT_DATA_REQ) | ||
53 | printk("DataRequest "); | ||
54 | if (stat & MG_REG_STATUS_BIT_CORRECTED_ERROR) | ||
55 | printk("CorrectedError "); | ||
56 | if (stat & MG_REG_STATUS_BIT_ERROR) | ||
57 | printk("Error "); | ||
58 | printk("}\n"); | ||
59 | if ((stat & MG_REG_STATUS_BIT_ERROR) == 0) { | ||
60 | host->error = 0; | ||
61 | } else { | ||
62 | host->error = inb((unsigned long)host->dev_base + MG_REG_ERROR); | ||
63 | printk(KERN_ERR "%s: %s: error=0x%02x { ", name, msg, | ||
64 | host->error & 0xff); | ||
65 | if (host->error & MG_REG_ERR_BBK) | ||
66 | printk("BadSector "); | ||
67 | if (host->error & MG_REG_ERR_UNC) | ||
68 | printk("UncorrectableError "); | ||
69 | if (host->error & MG_REG_ERR_IDNF) | ||
70 | printk("SectorIdNotFound "); | ||
71 | if (host->error & MG_REG_ERR_ABRT) | ||
72 | printk("DriveStatusError "); | ||
73 | if (host->error & MG_REG_ERR_AMNF) | ||
74 | printk("AddrMarkNotFound "); | ||
75 | printk("}"); | ||
76 | if (host->error & | ||
77 | (MG_REG_ERR_BBK | MG_REG_ERR_UNC | | ||
78 | MG_REG_ERR_IDNF | MG_REG_ERR_AMNF)) { | ||
79 | if (host->breq) { | ||
80 | req = elv_next_request(host->breq); | ||
81 | if (req) | ||
82 | printk(", sector=%ld", req->sector); | ||
83 | } | ||
84 | |||
85 | } | ||
86 | printk("\n"); | ||
87 | } | ||
88 | } | ||
89 | |||
90 | static unsigned int mg_wait(struct mg_host *host, u32 expect, u32 msec) | ||
91 | { | ||
92 | u8 status; | ||
93 | unsigned long expire, cur_jiffies; | ||
94 | struct mg_drv_data *prv_data = host->dev->platform_data; | ||
95 | |||
96 | host->error = MG_ERR_NONE; | ||
97 | expire = jiffies + msecs_to_jiffies(msec); | ||
98 | |||
99 | status = inb((unsigned long)host->dev_base + MG_REG_STATUS); | ||
100 | |||
101 | do { | ||
102 | cur_jiffies = jiffies; | ||
103 | if (status & MG_REG_STATUS_BIT_BUSY) { | ||
104 | if (expect == MG_REG_STATUS_BIT_BUSY) | ||
105 | break; | ||
106 | } else { | ||
107 | /* Check the error condition! */ | ||
108 | if (status & MG_REG_STATUS_BIT_ERROR) { | ||
109 | mg_dump_status("mg_wait", status, host); | ||
110 | break; | ||
111 | } | ||
112 | |||
113 | if (expect == MG_STAT_READY) | ||
114 | if (MG_READY_OK(status)) | ||
115 | break; | ||
116 | |||
117 | if (expect == MG_REG_STATUS_BIT_DATA_REQ) | ||
118 | if (status & MG_REG_STATUS_BIT_DATA_REQ) | ||
119 | break; | ||
120 | } | ||
121 | if (!msec) { | ||
122 | mg_dump_status("not ready", status, host); | ||
123 | return MG_ERR_INV_STAT; | ||
124 | } | ||
125 | if (prv_data->use_polling) | ||
126 | msleep(1); | ||
127 | |||
128 | status = inb((unsigned long)host->dev_base + MG_REG_STATUS); | ||
129 | } while (time_before(cur_jiffies, expire)); | ||
130 | |||
131 | if (time_after_eq(cur_jiffies, expire) && msec) | ||
132 | host->error = MG_ERR_TIMEOUT; | ||
133 | |||
134 | return host->error; | ||
135 | } | ||
136 | |||
137 | static unsigned int mg_wait_rstout(u32 rstout, u32 msec) | ||
138 | { | ||
139 | unsigned long expire; | ||
140 | |||
141 | expire = jiffies + msecs_to_jiffies(msec); | ||
142 | while (time_before(jiffies, expire)) { | ||
143 | if (gpio_get_value(rstout) == 1) | ||
144 | return MG_ERR_NONE; | ||
145 | msleep(10); | ||
146 | } | ||
147 | |||
148 | return MG_ERR_RSTOUT; | ||
149 | } | ||
150 | |||
151 | static void mg_unexpected_intr(struct mg_host *host) | ||
152 | { | ||
153 | u32 status = inb((unsigned long)host->dev_base + MG_REG_STATUS); | ||
154 | |||
155 | mg_dump_status("mg_unexpected_intr", status, host); | ||
156 | } | ||
157 | |||
158 | static irqreturn_t mg_irq(int irq, void *dev_id) | ||
159 | { | ||
160 | struct mg_host *host = dev_id; | ||
161 | void (*handler)(struct mg_host *) = host->mg_do_intr; | ||
162 | |||
163 | host->mg_do_intr = 0; | ||
164 | del_timer(&host->timer); | ||
165 | if (!handler) | ||
166 | handler = mg_unexpected_intr; | ||
167 | handler(host); | ||
168 | return IRQ_HANDLED; | ||
169 | } | ||
170 | |||
171 | static int mg_get_disk_id(struct mg_host *host) | ||
172 | { | ||
173 | u32 i; | ||
174 | s32 err; | ||
175 | const u16 *id = host->id; | ||
176 | struct mg_drv_data *prv_data = host->dev->platform_data; | ||
177 | char fwrev[ATA_ID_FW_REV_LEN + 1]; | ||
178 | char model[ATA_ID_PROD_LEN + 1]; | ||
179 | char serial[ATA_ID_SERNO_LEN + 1]; | ||
180 | |||
181 | if (!prv_data->use_polling) | ||
182 | outb(MG_REG_CTRL_INTR_DISABLE, | ||
183 | (unsigned long)host->dev_base + | ||
184 | MG_REG_DRV_CTRL); | ||
185 | |||
186 | outb(MG_CMD_ID, (unsigned long)host->dev_base + MG_REG_COMMAND); | ||
187 | err = mg_wait(host, MG_REG_STATUS_BIT_DATA_REQ, MG_TMAX_WAIT_RD_DRQ); | ||
188 | if (err) | ||
189 | return err; | ||
190 | |||
191 | for (i = 0; i < (MG_SECTOR_SIZE >> 1); i++) | ||
192 | host->id[i] = le16_to_cpu(inw((unsigned long)host->dev_base + | ||
193 | MG_BUFF_OFFSET + i * 2)); | ||
194 | |||
195 | outb(MG_CMD_RD_CONF, (unsigned long)host->dev_base + MG_REG_COMMAND); | ||
196 | err = mg_wait(host, MG_STAT_READY, MG_TMAX_CONF_TO_CMD); | ||
197 | if (err) | ||
198 | return err; | ||
199 | |||
200 | if ((id[ATA_ID_FIELD_VALID] & 1) == 0) | ||
201 | return MG_ERR_TRANSLATION; | ||
202 | |||
203 | host->n_sectors = ata_id_u32(id, ATA_ID_LBA_CAPACITY); | ||
204 | host->cyls = id[ATA_ID_CYLS]; | ||
205 | host->heads = id[ATA_ID_HEADS]; | ||
206 | host->sectors = id[ATA_ID_SECTORS]; | ||
207 | |||
208 | if (MG_RES_SEC && host->heads && host->sectors) { | ||
209 | /* modify cyls, n_sectors */ | ||
210 | host->cyls = (host->n_sectors - MG_RES_SEC) / | ||
211 | host->heads / host->sectors; | ||
212 | host->nres_sectors = host->n_sectors - host->cyls * | ||
213 | host->heads * host->sectors; | ||
214 | host->n_sectors -= host->nres_sectors; | ||
215 | } | ||
216 | |||
217 | ata_id_c_string(id, fwrev, ATA_ID_FW_REV, sizeof(fwrev)); | ||
218 | ata_id_c_string(id, model, ATA_ID_PROD, sizeof(model)); | ||
219 | ata_id_c_string(id, serial, ATA_ID_SERNO, sizeof(serial)); | ||
220 | printk(KERN_INFO "mg_disk: model: %s\n", model); | ||
221 | printk(KERN_INFO "mg_disk: firm: %.8s\n", fwrev); | ||
222 | printk(KERN_INFO "mg_disk: serial: %s\n", serial); | ||
223 | printk(KERN_INFO "mg_disk: %d + reserved %d sectors\n", | ||
224 | host->n_sectors, host->nres_sectors); | ||
225 | |||
226 | if (!prv_data->use_polling) | ||
227 | outb(MG_REG_CTRL_INTR_ENABLE, (unsigned long)host->dev_base + | ||
228 | MG_REG_DRV_CTRL); | ||
229 | |||
230 | return err; | ||
231 | } | ||
232 | |||
233 | |||
234 | static int mg_disk_init(struct mg_host *host) | ||
235 | { | ||
236 | struct mg_drv_data *prv_data = host->dev->platform_data; | ||
237 | s32 err; | ||
238 | u8 init_status; | ||
239 | |||
240 | /* hdd rst low */ | ||
241 | gpio_set_value(host->rst, 0); | ||
242 | err = mg_wait(host, MG_REG_STATUS_BIT_BUSY, MG_TMAX_RST_TO_BUSY); | ||
243 | if (err) | ||
244 | return err; | ||
245 | |||
246 | /* hdd rst high */ | ||
247 | gpio_set_value(host->rst, 1); | ||
248 | err = mg_wait(host, MG_STAT_READY, MG_TMAX_HDRST_TO_RDY); | ||
249 | if (err) | ||
250 | return err; | ||
251 | |||
252 | /* soft reset on */ | ||
253 | outb(MG_REG_CTRL_RESET | | ||
254 | (prv_data->use_polling ? MG_REG_CTRL_INTR_DISABLE : | ||
255 | MG_REG_CTRL_INTR_ENABLE), | ||
256 | (unsigned long)host->dev_base + MG_REG_DRV_CTRL); | ||
257 | err = mg_wait(host, MG_REG_STATUS_BIT_BUSY, MG_TMAX_RST_TO_BUSY); | ||
258 | if (err) | ||
259 | return err; | ||
260 | |||
261 | /* soft reset off */ | ||
262 | outb(prv_data->use_polling ? MG_REG_CTRL_INTR_DISABLE : | ||
263 | MG_REG_CTRL_INTR_ENABLE, | ||
264 | (unsigned long)host->dev_base + MG_REG_DRV_CTRL); | ||
265 | err = mg_wait(host, MG_STAT_READY, MG_TMAX_SWRST_TO_RDY); | ||
266 | if (err) | ||
267 | return err; | ||
268 | |||
269 | init_status = inb((unsigned long)host->dev_base + MG_REG_STATUS) & 0xf; | ||
270 | |||
271 | if (init_status == 0xf) | ||
272 | return MG_ERR_INIT_STAT; | ||
273 | |||
274 | return err; | ||
275 | } | ||
276 | |||
277 | static void mg_bad_rw_intr(struct mg_host *host) | ||
278 | { | ||
279 | struct request *req = elv_next_request(host->breq); | ||
280 | if (req != NULL) | ||
281 | if (++req->errors >= MG_MAX_ERRORS || | ||
282 | host->error == MG_ERR_TIMEOUT) | ||
283 | end_request(req, 0); | ||
284 | } | ||
285 | |||
286 | static unsigned int mg_out(struct mg_host *host, | ||
287 | unsigned int sect_num, | ||
288 | unsigned int sect_cnt, | ||
289 | unsigned int cmd, | ||
290 | void (*intr_addr)(struct mg_host *)) | ||
291 | { | ||
292 | struct mg_drv_data *prv_data = host->dev->platform_data; | ||
293 | |||
294 | if (mg_wait(host, MG_STAT_READY, MG_TMAX_CONF_TO_CMD)) | ||
295 | return host->error; | ||
296 | |||
297 | if (!prv_data->use_polling) { | ||
298 | host->mg_do_intr = intr_addr; | ||
299 | mod_timer(&host->timer, jiffies + 3 * HZ); | ||
300 | } | ||
301 | if (MG_RES_SEC) | ||
302 | sect_num += MG_RES_SEC; | ||
303 | outb((u8)sect_cnt, (unsigned long)host->dev_base + MG_REG_SECT_CNT); | ||
304 | outb((u8)sect_num, (unsigned long)host->dev_base + MG_REG_SECT_NUM); | ||
305 | outb((u8)(sect_num >> 8), (unsigned long)host->dev_base + | ||
306 | MG_REG_CYL_LOW); | ||
307 | outb((u8)(sect_num >> 16), (unsigned long)host->dev_base + | ||
308 | MG_REG_CYL_HIGH); | ||
309 | outb((u8)((sect_num >> 24) | MG_REG_HEAD_LBA_MODE), | ||
310 | (unsigned long)host->dev_base + MG_REG_DRV_HEAD); | ||
311 | outb(cmd, (unsigned long)host->dev_base + MG_REG_COMMAND); | ||
312 | return MG_ERR_NONE; | ||
313 | } | ||
314 | |||
315 | static void mg_read(struct request *req) | ||
316 | { | ||
317 | u32 remains, j; | ||
318 | struct mg_host *host = req->rq_disk->private_data; | ||
319 | |||
320 | remains = req->nr_sectors; | ||
321 | |||
322 | if (mg_out(host, req->sector, req->nr_sectors, MG_CMD_RD, 0) != | ||
323 | MG_ERR_NONE) | ||
324 | mg_bad_rw_intr(host); | ||
325 | |||
326 | MG_DBG("requested %d sects (from %ld), buffer=0x%p\n", | ||
327 | remains, req->sector, req->buffer); | ||
328 | |||
329 | while (remains) { | ||
330 | if (mg_wait(host, MG_REG_STATUS_BIT_DATA_REQ, | ||
331 | MG_TMAX_WAIT_RD_DRQ) != MG_ERR_NONE) { | ||
332 | mg_bad_rw_intr(host); | ||
333 | return; | ||
334 | } | ||
335 | for (j = 0; j < MG_SECTOR_SIZE >> 1; j++) { | ||
336 | *(u16 *)req->buffer = | ||
337 | inw((unsigned long)host->dev_base + | ||
338 | MG_BUFF_OFFSET + (j << 1)); | ||
339 | req->buffer += 2; | ||
340 | } | ||
341 | |||
342 | req->sector++; | ||
343 | req->errors = 0; | ||
344 | remains = --req->nr_sectors; | ||
345 | --req->current_nr_sectors; | ||
346 | |||
347 | if (req->current_nr_sectors <= 0) { | ||
348 | MG_DBG("remain : %d sects\n", remains); | ||
349 | end_request(req, 1); | ||
350 | if (remains > 0) | ||
351 | req = elv_next_request(host->breq); | ||
352 | } | ||
353 | |||
354 | outb(MG_CMD_RD_CONF, (unsigned long)host->dev_base + | ||
355 | MG_REG_COMMAND); | ||
356 | } | ||
357 | } | ||
358 | |||
359 | static void mg_write(struct request *req) | ||
360 | { | ||
361 | u32 remains, j; | ||
362 | struct mg_host *host = req->rq_disk->private_data; | ||
363 | |||
364 | remains = req->nr_sectors; | ||
365 | |||
366 | if (mg_out(host, req->sector, req->nr_sectors, MG_CMD_WR, 0) != | ||
367 | MG_ERR_NONE) { | ||
368 | mg_bad_rw_intr(host); | ||
369 | return; | ||
370 | } | ||
371 | |||
372 | |||
373 | MG_DBG("requested %d sects (from %ld), buffer=0x%p\n", | ||
374 | remains, req->sector, req->buffer); | ||
375 | while (remains) { | ||
376 | if (mg_wait(host, MG_REG_STATUS_BIT_DATA_REQ, | ||
377 | MG_TMAX_WAIT_WR_DRQ) != MG_ERR_NONE) { | ||
378 | mg_bad_rw_intr(host); | ||
379 | return; | ||
380 | } | ||
381 | for (j = 0; j < MG_SECTOR_SIZE >> 1; j++) { | ||
382 | outw(*(u16 *)req->buffer, | ||
383 | (unsigned long)host->dev_base + | ||
384 | MG_BUFF_OFFSET + (j << 1)); | ||
385 | req->buffer += 2; | ||
386 | } | ||
387 | req->sector++; | ||
388 | remains = --req->nr_sectors; | ||
389 | --req->current_nr_sectors; | ||
390 | |||
391 | if (req->current_nr_sectors <= 0) { | ||
392 | MG_DBG("remain : %d sects\n", remains); | ||
393 | end_request(req, 1); | ||
394 | if (remains > 0) | ||
395 | req = elv_next_request(host->breq); | ||
396 | } | ||
397 | |||
398 | outb(MG_CMD_WR_CONF, (unsigned long)host->dev_base + | ||
399 | MG_REG_COMMAND); | ||
400 | } | ||
401 | } | ||
402 | |||
403 | static void mg_read_intr(struct mg_host *host) | ||
404 | { | ||
405 | u32 i; | ||
406 | struct request *req; | ||
407 | |||
408 | /* check status */ | ||
409 | do { | ||
410 | i = inb((unsigned long)host->dev_base + MG_REG_STATUS); | ||
411 | if (i & MG_REG_STATUS_BIT_BUSY) | ||
412 | break; | ||
413 | if (!MG_READY_OK(i)) | ||
414 | break; | ||
415 | if (i & MG_REG_STATUS_BIT_DATA_REQ) | ||
416 | goto ok_to_read; | ||
417 | } while (0); | ||
418 | mg_dump_status("mg_read_intr", i, host); | ||
419 | mg_bad_rw_intr(host); | ||
420 | mg_request(host->breq); | ||
421 | return; | ||
422 | |||
423 | ok_to_read: | ||
424 | /* get current segment of request */ | ||
425 | req = elv_next_request(host->breq); | ||
426 | |||
427 | /* read 1 sector */ | ||
428 | for (i = 0; i < MG_SECTOR_SIZE >> 1; i++) { | ||
429 | *(u16 *)req->buffer = | ||
430 | inw((unsigned long)host->dev_base + MG_BUFF_OFFSET + | ||
431 | (i << 1)); | ||
432 | req->buffer += 2; | ||
433 | } | ||
434 | |||
435 | /* manipulate request */ | ||
436 | MG_DBG("sector %ld, remaining=%ld, buffer=0x%p\n", | ||
437 | req->sector, req->nr_sectors - 1, req->buffer); | ||
438 | |||
439 | req->sector++; | ||
440 | req->errors = 0; | ||
441 | i = --req->nr_sectors; | ||
442 | --req->current_nr_sectors; | ||
443 | |||
444 | /* let know if current segment done */ | ||
445 | if (req->current_nr_sectors <= 0) | ||
446 | end_request(req, 1); | ||
447 | |||
448 | /* set handler if read remains */ | ||
449 | if (i > 0) { | ||
450 | host->mg_do_intr = mg_read_intr; | ||
451 | mod_timer(&host->timer, jiffies + 3 * HZ); | ||
452 | } | ||
453 | |||
454 | /* send read confirm */ | ||
455 | outb(MG_CMD_RD_CONF, (unsigned long)host->dev_base + MG_REG_COMMAND); | ||
456 | |||
457 | /* goto next request */ | ||
458 | if (!i) | ||
459 | mg_request(host->breq); | ||
460 | } | ||
461 | |||
462 | static void mg_write_intr(struct mg_host *host) | ||
463 | { | ||
464 | u32 i, j; | ||
465 | u16 *buff; | ||
466 | struct request *req; | ||
467 | |||
468 | /* get current segment of request */ | ||
469 | req = elv_next_request(host->breq); | ||
470 | |||
471 | /* check status */ | ||
472 | do { | ||
473 | i = inb((unsigned long)host->dev_base + MG_REG_STATUS); | ||
474 | if (i & MG_REG_STATUS_BIT_BUSY) | ||
475 | break; | ||
476 | if (!MG_READY_OK(i)) | ||
477 | break; | ||
478 | if ((req->nr_sectors <= 1) || (i & MG_REG_STATUS_BIT_DATA_REQ)) | ||
479 | goto ok_to_write; | ||
480 | } while (0); | ||
481 | mg_dump_status("mg_write_intr", i, host); | ||
482 | mg_bad_rw_intr(host); | ||
483 | mg_request(host->breq); | ||
484 | return; | ||
485 | |||
486 | ok_to_write: | ||
487 | /* manipulate request */ | ||
488 | req->sector++; | ||
489 | i = --req->nr_sectors; | ||
490 | --req->current_nr_sectors; | ||
491 | req->buffer += MG_SECTOR_SIZE; | ||
492 | |||
493 | /* let know if current segment or all done */ | ||
494 | if (!i || (req->bio && req->current_nr_sectors <= 0)) | ||
495 | end_request(req, 1); | ||
496 | |||
497 | /* write 1 sector and set handler if remains */ | ||
498 | if (i > 0) { | ||
499 | buff = (u16 *)req->buffer; | ||
500 | for (j = 0; j < MG_STORAGE_BUFFER_SIZE >> 1; j++) { | ||
501 | outw(*buff, (unsigned long)host->dev_base + | ||
502 | MG_BUFF_OFFSET + (j << 1)); | ||
503 | buff++; | ||
504 | } | ||
505 | MG_DBG("sector %ld, remaining=%ld, buffer=0x%p\n", | ||
506 | req->sector, req->nr_sectors, req->buffer); | ||
507 | host->mg_do_intr = mg_write_intr; | ||
508 | mod_timer(&host->timer, jiffies + 3 * HZ); | ||
509 | } | ||
510 | |||
511 | /* send write confirm */ | ||
512 | outb(MG_CMD_WR_CONF, (unsigned long)host->dev_base + MG_REG_COMMAND); | ||
513 | |||
514 | if (!i) | ||
515 | mg_request(host->breq); | ||
516 | } | ||
517 | |||
518 | void mg_times_out(unsigned long data) | ||
519 | { | ||
520 | struct mg_host *host = (struct mg_host *)data; | ||
521 | char *name; | ||
522 | struct request *req; | ||
523 | |||
524 | req = elv_next_request(host->breq); | ||
525 | if (!req) | ||
526 | return; | ||
527 | |||
528 | host->mg_do_intr = NULL; | ||
529 | |||
530 | name = req->rq_disk->disk_name; | ||
531 | printk(KERN_DEBUG "%s: timeout\n", name); | ||
532 | |||
533 | host->error = MG_ERR_TIMEOUT; | ||
534 | mg_bad_rw_intr(host); | ||
535 | |||
536 | mg_request(host->breq); | ||
537 | } | ||
538 | |||
539 | static void mg_request_poll(struct request_queue *q) | ||
540 | { | ||
541 | struct request *req; | ||
542 | struct mg_host *host; | ||
543 | |||
544 | while ((req = elv_next_request(q)) != NULL) { | ||
545 | host = req->rq_disk->private_data; | ||
546 | if (blk_fs_request(req)) { | ||
547 | switch (rq_data_dir(req)) { | ||
548 | case READ: | ||
549 | mg_read(req); | ||
550 | break; | ||
551 | case WRITE: | ||
552 | mg_write(req); | ||
553 | break; | ||
554 | default: | ||
555 | printk(KERN_WARNING "%s:%d unknown command\n", | ||
556 | __func__, __LINE__); | ||
557 | end_request(req, 0); | ||
558 | break; | ||
559 | } | ||
560 | } | ||
561 | } | ||
562 | } | ||
563 | |||
564 | static unsigned int mg_issue_req(struct request *req, | ||
565 | struct mg_host *host, | ||
566 | unsigned int sect_num, | ||
567 | unsigned int sect_cnt) | ||
568 | { | ||
569 | u16 *buff; | ||
570 | u32 i; | ||
571 | |||
572 | switch (rq_data_dir(req)) { | ||
573 | case READ: | ||
574 | if (mg_out(host, sect_num, sect_cnt, MG_CMD_RD, &mg_read_intr) | ||
575 | != MG_ERR_NONE) { | ||
576 | mg_bad_rw_intr(host); | ||
577 | return host->error; | ||
578 | } | ||
579 | break; | ||
580 | case WRITE: | ||
581 | /* TODO : handler */ | ||
582 | outb(MG_REG_CTRL_INTR_DISABLE, | ||
583 | (unsigned long)host->dev_base + | ||
584 | MG_REG_DRV_CTRL); | ||
585 | if (mg_out(host, sect_num, sect_cnt, MG_CMD_WR, &mg_write_intr) | ||
586 | != MG_ERR_NONE) { | ||
587 | mg_bad_rw_intr(host); | ||
588 | return host->error; | ||
589 | } | ||
590 | del_timer(&host->timer); | ||
591 | mg_wait(host, MG_REG_STATUS_BIT_DATA_REQ, MG_TMAX_WAIT_WR_DRQ); | ||
592 | outb(MG_REG_CTRL_INTR_ENABLE, (unsigned long)host->dev_base + | ||
593 | MG_REG_DRV_CTRL); | ||
594 | if (host->error) { | ||
595 | mg_bad_rw_intr(host); | ||
596 | return host->error; | ||
597 | } | ||
598 | buff = (u16 *)req->buffer; | ||
599 | for (i = 0; i < MG_SECTOR_SIZE >> 1; i++) { | ||
600 | outw(*buff, (unsigned long)host->dev_base + | ||
601 | MG_BUFF_OFFSET + (i << 1)); | ||
602 | buff++; | ||
603 | } | ||
604 | mod_timer(&host->timer, jiffies + 3 * HZ); | ||
605 | outb(MG_CMD_WR_CONF, (unsigned long)host->dev_base + | ||
606 | MG_REG_COMMAND); | ||
607 | break; | ||
608 | default: | ||
609 | printk(KERN_WARNING "%s:%d unknown command\n", | ||
610 | __func__, __LINE__); | ||
611 | end_request(req, 0); | ||
612 | break; | ||
613 | } | ||
614 | return MG_ERR_NONE; | ||
615 | } | ||
616 | |||
617 | /* This function also called from IRQ context */ | ||
618 | static void mg_request(struct request_queue *q) | ||
619 | { | ||
620 | struct request *req; | ||
621 | struct mg_host *host; | ||
622 | u32 sect_num, sect_cnt; | ||
623 | |||
624 | while (1) { | ||
625 | req = elv_next_request(q); | ||
626 | if (!req) | ||
627 | return; | ||
628 | |||
629 | host = req->rq_disk->private_data; | ||
630 | |||
631 | /* check unwanted request call */ | ||
632 | if (host->mg_do_intr) | ||
633 | return; | ||
634 | |||
635 | del_timer(&host->timer); | ||
636 | |||
637 | sect_num = req->sector; | ||
638 | /* deal whole segments */ | ||
639 | sect_cnt = req->nr_sectors; | ||
640 | |||
641 | /* sanity check */ | ||
642 | if (sect_num >= get_capacity(req->rq_disk) || | ||
643 | ((sect_num + sect_cnt) > | ||
644 | get_capacity(req->rq_disk))) { | ||
645 | printk(KERN_WARNING | ||
646 | "%s: bad access: sector=%d, count=%d\n", | ||
647 | req->rq_disk->disk_name, | ||
648 | sect_num, sect_cnt); | ||
649 | end_request(req, 0); | ||
650 | continue; | ||
651 | } | ||
652 | |||
653 | if (!blk_fs_request(req)) | ||
654 | return; | ||
655 | |||
656 | if (!mg_issue_req(req, host, sect_num, sect_cnt)) | ||
657 | return; | ||
658 | } | ||
659 | } | ||
660 | |||
661 | static int mg_getgeo(struct block_device *bdev, struct hd_geometry *geo) | ||
662 | { | ||
663 | struct mg_host *host = bdev->bd_disk->private_data; | ||
664 | |||
665 | geo->cylinders = (unsigned short)host->cyls; | ||
666 | geo->heads = (unsigned char)host->heads; | ||
667 | geo->sectors = (unsigned char)host->sectors; | ||
668 | return 0; | ||
669 | } | ||
670 | |||
671 | static struct block_device_operations mg_disk_ops = { | ||
672 | .getgeo = mg_getgeo | ||
673 | }; | ||
674 | |||
675 | static int mg_suspend(struct platform_device *plat_dev, pm_message_t state) | ||
676 | { | ||
677 | struct mg_drv_data *prv_data = plat_dev->dev.platform_data; | ||
678 | struct mg_host *host = prv_data->host; | ||
679 | |||
680 | if (mg_wait(host, MG_STAT_READY, MG_TMAX_CONF_TO_CMD)) | ||
681 | return -EIO; | ||
682 | |||
683 | if (!prv_data->use_polling) | ||
684 | outb(MG_REG_CTRL_INTR_DISABLE, | ||
685 | (unsigned long)host->dev_base + | ||
686 | MG_REG_DRV_CTRL); | ||
687 | |||
688 | outb(MG_CMD_SLEEP, (unsigned long)host->dev_base + MG_REG_COMMAND); | ||
689 | /* wait until mflash deep sleep */ | ||
690 | msleep(1); | ||
691 | |||
692 | if (mg_wait(host, MG_STAT_READY, MG_TMAX_CONF_TO_CMD)) { | ||
693 | if (!prv_data->use_polling) | ||
694 | outb(MG_REG_CTRL_INTR_ENABLE, | ||
695 | (unsigned long)host->dev_base + | ||
696 | MG_REG_DRV_CTRL); | ||
697 | return -EIO; | ||
698 | } | ||
699 | |||
700 | return 0; | ||
701 | } | ||
702 | |||
703 | static int mg_resume(struct platform_device *plat_dev) | ||
704 | { | ||
705 | struct mg_drv_data *prv_data = plat_dev->dev.platform_data; | ||
706 | struct mg_host *host = prv_data->host; | ||
707 | |||
708 | if (mg_wait(host, MG_STAT_READY, MG_TMAX_CONF_TO_CMD)) | ||
709 | return -EIO; | ||
710 | |||
711 | outb(MG_CMD_WAKEUP, (unsigned long)host->dev_base + MG_REG_COMMAND); | ||
712 | /* wait until mflash wakeup */ | ||
713 | msleep(1); | ||
714 | |||
715 | if (mg_wait(host, MG_STAT_READY, MG_TMAX_CONF_TO_CMD)) | ||
716 | return -EIO; | ||
717 | |||
718 | if (!prv_data->use_polling) | ||
719 | outb(MG_REG_CTRL_INTR_ENABLE, (unsigned long)host->dev_base + | ||
720 | MG_REG_DRV_CTRL); | ||
721 | |||
722 | return 0; | ||
723 | } | ||
724 | |||
725 | static int mg_probe(struct platform_device *plat_dev) | ||
726 | { | ||
727 | struct mg_host *host; | ||
728 | struct resource *rsc; | ||
729 | struct mg_drv_data *prv_data = plat_dev->dev.platform_data; | ||
730 | int err = 0; | ||
731 | |||
732 | if (!prv_data) { | ||
733 | printk(KERN_ERR "%s:%d fail (no driver_data)\n", | ||
734 | __func__, __LINE__); | ||
735 | err = -EINVAL; | ||
736 | goto probe_err; | ||
737 | } | ||
738 | |||
739 | /* alloc mg_host */ | ||
740 | host = kzalloc(sizeof(struct mg_host), GFP_KERNEL); | ||
741 | if (!host) { | ||
742 | printk(KERN_ERR "%s:%d fail (no memory for mg_host)\n", | ||
743 | __func__, __LINE__); | ||
744 | err = -ENOMEM; | ||
745 | goto probe_err; | ||
746 | } | ||
747 | host->major = MG_DISK_MAJ; | ||
748 | |||
749 | /* link each other */ | ||
750 | prv_data->host = host; | ||
751 | host->dev = &plat_dev->dev; | ||
752 | |||
753 | /* io remap */ | ||
754 | rsc = platform_get_resource(plat_dev, IORESOURCE_MEM, 0); | ||
755 | if (!rsc) { | ||
756 | printk(KERN_ERR "%s:%d platform_get_resource fail\n", | ||
757 | __func__, __LINE__); | ||
758 | err = -EINVAL; | ||
759 | goto probe_err_2; | ||
760 | } | ||
761 | host->dev_base = ioremap(rsc->start , rsc->end + 1); | ||
762 | if (!host->dev_base) { | ||
763 | printk(KERN_ERR "%s:%d ioremap fail\n", | ||
764 | __func__, __LINE__); | ||
765 | err = -EIO; | ||
766 | goto probe_err_2; | ||
767 | } | ||
768 | MG_DBG("dev_base = 0x%x\n", (u32)host->dev_base); | ||
769 | |||
770 | /* get reset pin */ | ||
771 | rsc = platform_get_resource_byname(plat_dev, IORESOURCE_IO, | ||
772 | MG_RST_PIN); | ||
773 | if (!rsc) { | ||
774 | printk(KERN_ERR "%s:%d get reset pin fail\n", | ||
775 | __func__, __LINE__); | ||
776 | err = -EIO; | ||
777 | goto probe_err_3; | ||
778 | } | ||
779 | host->rst = rsc->start; | ||
780 | |||
781 | /* init rst pin */ | ||
782 | err = gpio_request(host->rst, MG_RST_PIN); | ||
783 | if (err) | ||
784 | goto probe_err_3; | ||
785 | gpio_direction_output(host->rst, 1); | ||
786 | |||
787 | /* reset out pin */ | ||
788 | if (!(prv_data->dev_attr & MG_DEV_MASK)) | ||
789 | goto probe_err_3a; | ||
790 | |||
791 | if (prv_data->dev_attr != MG_BOOT_DEV) { | ||
792 | rsc = platform_get_resource_byname(plat_dev, IORESOURCE_IO, | ||
793 | MG_RSTOUT_PIN); | ||
794 | if (!rsc) { | ||
795 | printk(KERN_ERR "%s:%d get reset-out pin fail\n", | ||
796 | __func__, __LINE__); | ||
797 | err = -EIO; | ||
798 | goto probe_err_3a; | ||
799 | } | ||
800 | host->rstout = rsc->start; | ||
801 | err = gpio_request(host->rstout, MG_RSTOUT_PIN); | ||
802 | if (err) | ||
803 | goto probe_err_3a; | ||
804 | gpio_direction_input(host->rstout); | ||
805 | } | ||
806 | |||
807 | /* disk reset */ | ||
808 | if (prv_data->dev_attr == MG_STORAGE_DEV) { | ||
809 | /* If POR seq. not yet finised, wait */ | ||
810 | err = mg_wait_rstout(host->rstout, MG_TMAX_RSTOUT); | ||
811 | if (err) | ||
812 | goto probe_err_3b; | ||
813 | err = mg_disk_init(host); | ||
814 | if (err) { | ||
815 | printk(KERN_ERR "%s:%d fail (err code : %d)\n", | ||
816 | __func__, __LINE__, err); | ||
817 | err = -EIO; | ||
818 | goto probe_err_3b; | ||
819 | } | ||
820 | } | ||
821 | |||
822 | /* get irq resource */ | ||
823 | if (!prv_data->use_polling) { | ||
824 | host->irq = platform_get_irq(plat_dev, 0); | ||
825 | if (host->irq == -ENXIO) { | ||
826 | err = host->irq; | ||
827 | goto probe_err_3b; | ||
828 | } | ||
829 | err = request_irq(host->irq, mg_irq, | ||
830 | IRQF_DISABLED | IRQF_TRIGGER_RISING, | ||
831 | MG_DEV_NAME, host); | ||
832 | if (err) { | ||
833 | printk(KERN_ERR "%s:%d fail (request_irq err=%d)\n", | ||
834 | __func__, __LINE__, err); | ||
835 | goto probe_err_3b; | ||
836 | } | ||
837 | |||
838 | } | ||
839 | |||
840 | /* get disk id */ | ||
841 | err = mg_get_disk_id(host); | ||
842 | if (err) { | ||
843 | printk(KERN_ERR "%s:%d fail (err code : %d)\n", | ||
844 | __func__, __LINE__, err); | ||
845 | err = -EIO; | ||
846 | goto probe_err_4; | ||
847 | } | ||
848 | |||
849 | err = register_blkdev(host->major, MG_DISK_NAME); | ||
850 | if (err < 0) { | ||
851 | printk(KERN_ERR "%s:%d register_blkdev fail (err code : %d)\n", | ||
852 | __func__, __LINE__, err); | ||
853 | goto probe_err_4; | ||
854 | } | ||
855 | if (!host->major) | ||
856 | host->major = err; | ||
857 | |||
858 | spin_lock_init(&host->lock); | ||
859 | |||
860 | if (prv_data->use_polling) | ||
861 | host->breq = blk_init_queue(mg_request_poll, &host->lock); | ||
862 | else | ||
863 | host->breq = blk_init_queue(mg_request, &host->lock); | ||
864 | |||
865 | if (!host->breq) { | ||
866 | err = -ENOMEM; | ||
867 | printk(KERN_ERR "%s:%d (blk_init_queue) fail\n", | ||
868 | __func__, __LINE__); | ||
869 | goto probe_err_5; | ||
870 | } | ||
871 | |||
872 | /* mflash is random device, thanx for the noop */ | ||
873 | elevator_exit(host->breq->elevator); | ||
874 | err = elevator_init(host->breq, "noop"); | ||
875 | if (err) { | ||
876 | printk(KERN_ERR "%s:%d (elevator_init) fail\n", | ||
877 | __func__, __LINE__); | ||
878 | goto probe_err_6; | ||
879 | } | ||
880 | blk_queue_max_sectors(host->breq, MG_MAX_SECTS); | ||
881 | blk_queue_hardsect_size(host->breq, MG_SECTOR_SIZE); | ||
882 | |||
883 | init_timer(&host->timer); | ||
884 | host->timer.function = mg_times_out; | ||
885 | host->timer.data = (unsigned long)host; | ||
886 | |||
887 | host->gd = alloc_disk(MG_DISK_MAX_PART); | ||
888 | if (!host->gd) { | ||
889 | printk(KERN_ERR "%s:%d (alloc_disk) fail\n", | ||
890 | __func__, __LINE__); | ||
891 | err = -ENOMEM; | ||
892 | goto probe_err_7; | ||
893 | } | ||
894 | host->gd->major = host->major; | ||
895 | host->gd->first_minor = 0; | ||
896 | host->gd->fops = &mg_disk_ops; | ||
897 | host->gd->queue = host->breq; | ||
898 | host->gd->private_data = host; | ||
899 | sprintf(host->gd->disk_name, MG_DISK_NAME"a"); | ||
900 | |||
901 | set_capacity(host->gd, host->n_sectors); | ||
902 | |||
903 | add_disk(host->gd); | ||
904 | |||
905 | return err; | ||
906 | |||
907 | probe_err_7: | ||
908 | del_timer_sync(&host->timer); | ||
909 | probe_err_6: | ||
910 | blk_cleanup_queue(host->breq); | ||
911 | probe_err_5: | ||
912 | unregister_blkdev(MG_DISK_MAJ, MG_DISK_NAME); | ||
913 | probe_err_4: | ||
914 | if (!prv_data->use_polling) | ||
915 | free_irq(host->irq, host); | ||
916 | probe_err_3b: | ||
917 | gpio_free(host->rstout); | ||
918 | probe_err_3a: | ||
919 | gpio_free(host->rst); | ||
920 | probe_err_3: | ||
921 | iounmap(host->dev_base); | ||
922 | probe_err_2: | ||
923 | kfree(host); | ||
924 | probe_err: | ||
925 | return err; | ||
926 | } | ||
927 | |||
928 | static int mg_remove(struct platform_device *plat_dev) | ||
929 | { | ||
930 | struct mg_drv_data *prv_data = plat_dev->dev.platform_data; | ||
931 | struct mg_host *host = prv_data->host; | ||
932 | int err = 0; | ||
933 | |||
934 | /* delete timer */ | ||
935 | del_timer_sync(&host->timer); | ||
936 | |||
937 | /* remove disk */ | ||
938 | if (host->gd) { | ||
939 | del_gendisk(host->gd); | ||
940 | put_disk(host->gd); | ||
941 | } | ||
942 | /* remove queue */ | ||
943 | if (host->breq) | ||
944 | blk_cleanup_queue(host->breq); | ||
945 | |||
946 | /* unregister blk device */ | ||
947 | unregister_blkdev(host->major, MG_DISK_NAME); | ||
948 | |||
949 | /* free irq */ | ||
950 | if (!prv_data->use_polling) | ||
951 | free_irq(host->irq, host); | ||
952 | |||
953 | /* free reset-out pin */ | ||
954 | if (prv_data->dev_attr != MG_BOOT_DEV) | ||
955 | gpio_free(host->rstout); | ||
956 | |||
957 | /* free rst pin */ | ||
958 | if (host->rst) | ||
959 | gpio_free(host->rst); | ||
960 | |||
961 | /* unmap io */ | ||
962 | if (host->dev_base) | ||
963 | iounmap(host->dev_base); | ||
964 | |||
965 | /* free mg_host */ | ||
966 | kfree(host); | ||
967 | |||
968 | return err; | ||
969 | } | ||
970 | |||
971 | static struct platform_driver mg_disk_driver = { | ||
972 | .probe = mg_probe, | ||
973 | .remove = mg_remove, | ||
974 | .suspend = mg_suspend, | ||
975 | .resume = mg_resume, | ||
976 | .driver = { | ||
977 | .name = MG_DEV_NAME, | ||
978 | .owner = THIS_MODULE, | ||
979 | } | ||
980 | }; | ||
981 | |||
982 | /**************************************************************************** | ||
983 | * | ||
984 | * Module stuff | ||
985 | * | ||
986 | ****************************************************************************/ | ||
987 | |||
988 | static int __init mg_init(void) | ||
989 | { | ||
990 | printk(KERN_INFO "mGine mflash driver, (c) 2008 mGine Co.\n"); | ||
991 | return platform_driver_register(&mg_disk_driver); | ||
992 | } | ||
993 | |||
994 | static void __exit mg_exit(void) | ||
995 | { | ||
996 | printk(KERN_INFO "mflash driver : bye bye\n"); | ||
997 | platform_driver_unregister(&mg_disk_driver); | ||
998 | } | ||
999 | |||
1000 | module_init(mg_init); | ||
1001 | module_exit(mg_exit); | ||
1002 | |||
1003 | MODULE_LICENSE("GPL"); | ||
1004 | MODULE_AUTHOR("unsik Kim <donari75@gmail.com>"); | ||
1005 | MODULE_DESCRIPTION("mGine m[g]flash device driver"); | ||