diff options
| -rw-r--r-- | drivers/ide/Makefile | 2 | ||||
| -rw-r--r-- | drivers/ide/ide-io.c | 29 | ||||
| -rw-r--r-- | drivers/ide/ide-iops.c | 29 | ||||
| -rw-r--r-- | drivers/ide/ide-park.c | 121 | ||||
| -rw-r--r-- | drivers/ide/ide-probe.c | 5 | ||||
| -rw-r--r-- | drivers/ide/ide-taskfile.c | 11 | ||||
| -rw-r--r-- | drivers/ide/ide.c | 1 | ||||
| -rw-r--r-- | include/linux/ide.h | 13 |
8 files changed, 206 insertions, 5 deletions
diff --git a/drivers/ide/Makefile b/drivers/ide/Makefile index 0c30adb115c8..ceaf779054ea 100644 --- a/drivers/ide/Makefile +++ b/drivers/ide/Makefile | |||
| @@ -5,7 +5,7 @@ | |||
| 5 | EXTRA_CFLAGS += -Idrivers/ide | 5 | EXTRA_CFLAGS += -Idrivers/ide |
| 6 | 6 | ||
| 7 | ide-core-y += ide.o ide-ioctls.o ide-io.o ide-iops.o ide-lib.o ide-probe.o \ | 7 | ide-core-y += ide.o ide-ioctls.o ide-io.o ide-iops.o ide-lib.o ide-probe.o \ |
| 8 | ide-taskfile.o ide-pio-blacklist.o | 8 | ide-taskfile.o ide-park.o ide-pio-blacklist.o |
| 9 | 9 | ||
| 10 | # core IDE code | 10 | # core IDE code |
| 11 | ide-core-$(CONFIG_IDE_TIMINGS) += ide-timings.o | 11 | ide-core-$(CONFIG_IDE_TIMINGS) += ide-timings.o |
diff --git a/drivers/ide/ide-io.c b/drivers/ide/ide-io.c index e205f46c3c7a..77c6eaeacefa 100644 --- a/drivers/ide/ide-io.c +++ b/drivers/ide/ide-io.c | |||
| @@ -672,7 +672,32 @@ EXPORT_SYMBOL_GPL(ide_devset_execute); | |||
| 672 | 672 | ||
| 673 | static ide_startstop_t ide_special_rq(ide_drive_t *drive, struct request *rq) | 673 | static ide_startstop_t ide_special_rq(ide_drive_t *drive, struct request *rq) |
| 674 | { | 674 | { |
| 675 | switch (rq->cmd[0]) { | 675 | u8 cmd = rq->cmd[0]; |
| 676 | |||
| 677 | if (cmd == REQ_PARK_HEADS || cmd == REQ_UNPARK_HEADS) { | ||
| 678 | ide_task_t task; | ||
| 679 | struct ide_taskfile *tf = &task.tf; | ||
| 680 | |||
| 681 | memset(&task, 0, sizeof(task)); | ||
| 682 | if (cmd == REQ_PARK_HEADS) { | ||
| 683 | drive->sleep = *(unsigned long *)rq->special; | ||
| 684 | drive->dev_flags |= IDE_DFLAG_SLEEPING; | ||
| 685 | tf->command = ATA_CMD_IDLEIMMEDIATE; | ||
| 686 | tf->feature = 0x44; | ||
| 687 | tf->lbal = 0x4c; | ||
| 688 | tf->lbam = 0x4e; | ||
| 689 | tf->lbah = 0x55; | ||
| 690 | task.tf_flags |= IDE_TFLAG_CUSTOM_HANDLER; | ||
| 691 | } else /* cmd == REQ_UNPARK_HEADS */ | ||
| 692 | tf->command = ATA_CMD_CHK_POWER; | ||
| 693 | |||
| 694 | task.tf_flags |= IDE_TFLAG_TF | IDE_TFLAG_DEVICE; | ||
| 695 | task.rq = rq; | ||
| 696 | drive->hwif->data_phase = task.data_phase = TASKFILE_NO_DATA; | ||
| 697 | return do_rw_taskfile(drive, &task); | ||
| 698 | } | ||
| 699 | |||
| 700 | switch (cmd) { | ||
| 676 | case REQ_DEVSET_EXEC: | 701 | case REQ_DEVSET_EXEC: |
| 677 | { | 702 | { |
| 678 | int err, (*setfunc)(ide_drive_t *, int) = rq->special; | 703 | int err, (*setfunc)(ide_drive_t *, int) = rq->special; |
| @@ -1008,7 +1033,7 @@ static void ide_do_request (ide_hwgroup_t *hwgroup, int masked_irq) | |||
| 1008 | } | 1033 | } |
| 1009 | hwgroup->hwif = hwif; | 1034 | hwgroup->hwif = hwif; |
| 1010 | hwgroup->drive = drive; | 1035 | hwgroup->drive = drive; |
| 1011 | drive->dev_flags &= ~IDE_DFLAG_SLEEPING; | 1036 | drive->dev_flags &= ~(IDE_DFLAG_SLEEPING | IDE_DFLAG_PARKED); |
| 1012 | drive->service_start = jiffies; | 1037 | drive->service_start = jiffies; |
| 1013 | 1038 | ||
| 1014 | if (blk_queue_plugged(drive->queue)) { | 1039 | if (blk_queue_plugged(drive->queue)) { |
diff --git a/drivers/ide/ide-iops.c b/drivers/ide/ide-iops.c index 91182ebed468..b762deb2dacb 100644 --- a/drivers/ide/ide-iops.c +++ b/drivers/ide/ide-iops.c | |||
| @@ -1020,6 +1020,7 @@ static void ide_disk_pre_reset(ide_drive_t *drive) | |||
| 1020 | drive->special.b.recalibrate = legacy; | 1020 | drive->special.b.recalibrate = legacy; |
| 1021 | 1021 | ||
| 1022 | drive->mult_count = 0; | 1022 | drive->mult_count = 0; |
| 1023 | drive->dev_flags &= ~IDE_DFLAG_PARKED; | ||
| 1023 | 1024 | ||
| 1024 | if ((drive->dev_flags & IDE_DFLAG_KEEP_SETTINGS) == 0 && | 1025 | if ((drive->dev_flags & IDE_DFLAG_KEEP_SETTINGS) == 0 && |
| 1025 | (drive->dev_flags & IDE_DFLAG_USING_DMA) == 0) | 1026 | (drive->dev_flags & IDE_DFLAG_USING_DMA) == 0) |
| @@ -1079,12 +1080,13 @@ static void pre_reset(ide_drive_t *drive) | |||
| 1079 | static ide_startstop_t do_reset1 (ide_drive_t *drive, int do_not_try_atapi) | 1080 | static ide_startstop_t do_reset1 (ide_drive_t *drive, int do_not_try_atapi) |
| 1080 | { | 1081 | { |
| 1081 | unsigned int unit; | 1082 | unsigned int unit; |
| 1082 | unsigned long flags; | 1083 | unsigned long flags, timeout; |
| 1083 | ide_hwif_t *hwif; | 1084 | ide_hwif_t *hwif; |
| 1084 | ide_hwgroup_t *hwgroup; | 1085 | ide_hwgroup_t *hwgroup; |
| 1085 | struct ide_io_ports *io_ports; | 1086 | struct ide_io_ports *io_ports; |
| 1086 | const struct ide_tp_ops *tp_ops; | 1087 | const struct ide_tp_ops *tp_ops; |
| 1087 | const struct ide_port_ops *port_ops; | 1088 | const struct ide_port_ops *port_ops; |
| 1089 | DEFINE_WAIT(wait); | ||
| 1088 | 1090 | ||
| 1089 | spin_lock_irqsave(&ide_lock, flags); | 1091 | spin_lock_irqsave(&ide_lock, flags); |
| 1090 | hwif = HWIF(drive); | 1092 | hwif = HWIF(drive); |
| @@ -1111,6 +1113,31 @@ static ide_startstop_t do_reset1 (ide_drive_t *drive, int do_not_try_atapi) | |||
| 1111 | return ide_started; | 1113 | return ide_started; |
| 1112 | } | 1114 | } |
| 1113 | 1115 | ||
| 1116 | /* We must not disturb devices in the IDE_DFLAG_PARKED state. */ | ||
| 1117 | do { | ||
| 1118 | unsigned long now; | ||
| 1119 | |||
| 1120 | prepare_to_wait(&ide_park_wq, &wait, TASK_UNINTERRUPTIBLE); | ||
| 1121 | timeout = jiffies; | ||
| 1122 | for (unit = 0; unit < MAX_DRIVES; unit++) { | ||
| 1123 | ide_drive_t *tdrive = &hwif->drives[unit]; | ||
| 1124 | |||
| 1125 | if (tdrive->dev_flags & IDE_DFLAG_PRESENT && | ||
| 1126 | tdrive->dev_flags & IDE_DFLAG_PARKED && | ||
| 1127 | time_after(tdrive->sleep, timeout)) | ||
| 1128 | timeout = tdrive->sleep; | ||
| 1129 | } | ||
| 1130 | |||
| 1131 | now = jiffies; | ||
| 1132 | if (time_before_eq(timeout, now)) | ||
| 1133 | break; | ||
| 1134 | |||
| 1135 | spin_unlock_irqrestore(&ide_lock, flags); | ||
| 1136 | timeout = schedule_timeout_uninterruptible(timeout - now); | ||
| 1137 | spin_lock_irqsave(&ide_lock, flags); | ||
| 1138 | } while (timeout); | ||
| 1139 | finish_wait(&ide_park_wq, &wait); | ||
| 1140 | |||
| 1114 | /* | 1141 | /* |
| 1115 | * First, reset any device state data we were maintaining | 1142 | * First, reset any device state data we were maintaining |
| 1116 | * for any of the drives on this interface. | 1143 | * for any of the drives on this interface. |
diff --git a/drivers/ide/ide-park.c b/drivers/ide/ide-park.c new file mode 100644 index 000000000000..03b00e57e93f --- /dev/null +++ b/drivers/ide/ide-park.c | |||
| @@ -0,0 +1,121 @@ | |||
| 1 | #include <linux/kernel.h> | ||
| 2 | #include <linux/ide.h> | ||
| 3 | #include <linux/jiffies.h> | ||
| 4 | #include <linux/blkdev.h> | ||
| 5 | |||
| 6 | DECLARE_WAIT_QUEUE_HEAD(ide_park_wq); | ||
| 7 | |||
| 8 | static void issue_park_cmd(ide_drive_t *drive, unsigned long timeout) | ||
| 9 | { | ||
| 10 | struct request_queue *q = drive->queue; | ||
| 11 | struct request *rq; | ||
| 12 | int rc; | ||
| 13 | |||
| 14 | timeout += jiffies; | ||
| 15 | spin_lock_irq(&ide_lock); | ||
| 16 | if (drive->dev_flags & IDE_DFLAG_PARKED) { | ||
| 17 | ide_hwgroup_t *hwgroup = drive->hwif->hwgroup; | ||
| 18 | int reset_timer; | ||
| 19 | |||
| 20 | reset_timer = time_before(timeout, drive->sleep); | ||
| 21 | drive->sleep = timeout; | ||
| 22 | wake_up_all(&ide_park_wq); | ||
| 23 | if (reset_timer && hwgroup->sleeping && | ||
| 24 | del_timer(&hwgroup->timer)) { | ||
| 25 | hwgroup->sleeping = 0; | ||
| 26 | hwgroup->busy = 0; | ||
| 27 | blk_start_queueing(q); | ||
| 28 | } | ||
| 29 | spin_unlock_irq(&ide_lock); | ||
| 30 | return; | ||
| 31 | } | ||
| 32 | spin_unlock_irq(&ide_lock); | ||
| 33 | |||
| 34 | rq = blk_get_request(q, READ, __GFP_WAIT); | ||
| 35 | rq->cmd[0] = REQ_PARK_HEADS; | ||
| 36 | rq->cmd_len = 1; | ||
| 37 | rq->cmd_type = REQ_TYPE_SPECIAL; | ||
| 38 | rq->special = &timeout; | ||
| 39 | rc = blk_execute_rq(q, NULL, rq, 1); | ||
| 40 | blk_put_request(rq); | ||
| 41 | if (rc) | ||
| 42 | goto out; | ||
| 43 | |||
| 44 | /* | ||
| 45 | * Make sure that *some* command is sent to the drive after the | ||
| 46 | * timeout has expired, so power management will be reenabled. | ||
| 47 | */ | ||
| 48 | rq = blk_get_request(q, READ, GFP_NOWAIT); | ||
| 49 | if (unlikely(!rq)) | ||
| 50 | goto out; | ||
| 51 | |||
| 52 | rq->cmd[0] = REQ_UNPARK_HEADS; | ||
| 53 | rq->cmd_len = 1; | ||
| 54 | rq->cmd_type = REQ_TYPE_SPECIAL; | ||
| 55 | elv_add_request(q, rq, ELEVATOR_INSERT_FRONT, 1); | ||
| 56 | |||
| 57 | out: | ||
| 58 | return; | ||
| 59 | } | ||
| 60 | |||
| 61 | ssize_t ide_park_show(struct device *dev, struct device_attribute *attr, | ||
| 62 | char *buf) | ||
| 63 | { | ||
| 64 | ide_drive_t *drive = to_ide_device(dev); | ||
| 65 | unsigned long now; | ||
| 66 | unsigned int msecs; | ||
| 67 | |||
| 68 | if (drive->dev_flags & IDE_DFLAG_NO_UNLOAD) | ||
| 69 | return -EOPNOTSUPP; | ||
| 70 | |||
| 71 | spin_lock_irq(&ide_lock); | ||
| 72 | now = jiffies; | ||
| 73 | if (drive->dev_flags & IDE_DFLAG_PARKED && | ||
| 74 | time_after(drive->sleep, now)) | ||
| 75 | msecs = jiffies_to_msecs(drive->sleep - now); | ||
| 76 | else | ||
| 77 | msecs = 0; | ||
| 78 | spin_unlock_irq(&ide_lock); | ||
| 79 | |||
| 80 | return snprintf(buf, 20, "%u\n", msecs); | ||
| 81 | } | ||
| 82 | |||
| 83 | ssize_t ide_park_store(struct device *dev, struct device_attribute *attr, | ||
| 84 | const char *buf, size_t len) | ||
| 85 | { | ||
| 86 | #define MAX_PARK_TIMEOUT 30000 | ||
| 87 | ide_drive_t *drive = to_ide_device(dev); | ||
| 88 | long int input; | ||
| 89 | int rc; | ||
| 90 | |||
| 91 | rc = strict_strtol(buf, 10, &input); | ||
| 92 | if (rc || input < -2) | ||
| 93 | return -EINVAL; | ||
| 94 | if (input > MAX_PARK_TIMEOUT) { | ||
| 95 | input = MAX_PARK_TIMEOUT; | ||
| 96 | rc = -EOVERFLOW; | ||
| 97 | } | ||
| 98 | |||
| 99 | mutex_lock(&ide_setting_mtx); | ||
| 100 | if (input >= 0) { | ||
| 101 | if (drive->dev_flags & IDE_DFLAG_NO_UNLOAD) | ||
| 102 | rc = -EOPNOTSUPP; | ||
| 103 | else if (input || drive->dev_flags & IDE_DFLAG_PARKED) | ||
| 104 | issue_park_cmd(drive, msecs_to_jiffies(input)); | ||
| 105 | } else { | ||
| 106 | if (drive->media == ide_disk) | ||
| 107 | switch (input) { | ||
| 108 | case -1: | ||
| 109 | drive->dev_flags &= ~IDE_DFLAG_NO_UNLOAD; | ||
| 110 | break; | ||
| 111 | case -2: | ||
| 112 | drive->dev_flags |= IDE_DFLAG_NO_UNLOAD; | ||
| 113 | break; | ||
| 114 | } | ||
| 115 | else | ||
| 116 | rc = -EOPNOTSUPP; | ||
| 117 | } | ||
| 118 | mutex_unlock(&ide_setting_mtx); | ||
| 119 | |||
| 120 | return rc ? rc : len; | ||
| 121 | } | ||
diff --git a/drivers/ide/ide-probe.c b/drivers/ide/ide-probe.c index de8edd306c79..f27baa5f140e 100644 --- a/drivers/ide/ide-probe.c +++ b/drivers/ide/ide-probe.c | |||
| @@ -208,6 +208,8 @@ static inline void do_identify (ide_drive_t *drive, u8 cmd) | |||
| 208 | drive->ready_stat = 0; | 208 | drive->ready_stat = 0; |
| 209 | if (ata_id_cdb_intr(id)) | 209 | if (ata_id_cdb_intr(id)) |
| 210 | drive->atapi_flags |= IDE_AFLAG_DRQ_INTERRUPT; | 210 | drive->atapi_flags |= IDE_AFLAG_DRQ_INTERRUPT; |
| 211 | /* we don't do head unloading on ATAPI devices */ | ||
| 212 | drive->dev_flags |= IDE_DFLAG_NO_UNLOAD; | ||
| 211 | return; | 213 | return; |
| 212 | } | 214 | } |
| 213 | 215 | ||
| @@ -223,6 +225,9 @@ static inline void do_identify (ide_drive_t *drive, u8 cmd) | |||
| 223 | 225 | ||
| 224 | drive->media = ide_disk; | 226 | drive->media = ide_disk; |
| 225 | 227 | ||
| 228 | if (!ata_id_has_unload(drive->id)) | ||
| 229 | drive->dev_flags |= IDE_DFLAG_NO_UNLOAD; | ||
| 230 | |||
| 226 | printk(KERN_CONT "%s DISK drive\n", is_cfa ? "CFA" : "ATA"); | 231 | printk(KERN_CONT "%s DISK drive\n", is_cfa ? "CFA" : "ATA"); |
| 227 | 232 | ||
| 228 | return; | 233 | return; |
diff --git a/drivers/ide/ide-taskfile.c b/drivers/ide/ide-taskfile.c index a4c2d91179b3..bf4fb9d8d176 100644 --- a/drivers/ide/ide-taskfile.c +++ b/drivers/ide/ide-taskfile.c | |||
| @@ -152,7 +152,16 @@ static ide_startstop_t task_no_data_intr(ide_drive_t *drive) | |||
| 152 | 152 | ||
| 153 | if (!custom) | 153 | if (!custom) |
| 154 | ide_end_drive_cmd(drive, stat, ide_read_error(drive)); | 154 | ide_end_drive_cmd(drive, stat, ide_read_error(drive)); |
| 155 | else if (tf->command == ATA_CMD_SET_MULTI) | 155 | else if (tf->command == ATA_CMD_IDLEIMMEDIATE) { |
| 156 | hwif->tp_ops->tf_read(drive, task); | ||
| 157 | if (tf->lbal != 0xc4) { | ||
| 158 | printk(KERN_ERR "%s: head unload failed!\n", | ||
| 159 | drive->name); | ||
| 160 | ide_tf_dump(drive->name, tf); | ||
| 161 | } else | ||
| 162 | drive->dev_flags |= IDE_DFLAG_PARKED; | ||
| 163 | ide_end_drive_cmd(drive, stat, ide_read_error(drive)); | ||
| 164 | } else if (tf->command == ATA_CMD_SET_MULTI) | ||
| 156 | drive->mult_count = drive->mult_req; | 165 | drive->mult_count = drive->mult_req; |
| 157 | 166 | ||
| 158 | return ide_stopped; | 167 | return ide_stopped; |
diff --git a/drivers/ide/ide.c b/drivers/ide/ide.c index 083783e851d1..04f8f13cb9d7 100644 --- a/drivers/ide/ide.c +++ b/drivers/ide/ide.c | |||
| @@ -587,6 +587,7 @@ static struct device_attribute ide_dev_attrs[] = { | |||
| 587 | __ATTR_RO(model), | 587 | __ATTR_RO(model), |
| 588 | __ATTR_RO(firmware), | 588 | __ATTR_RO(firmware), |
| 589 | __ATTR(serial, 0400, serial_show, NULL), | 589 | __ATTR(serial, 0400, serial_show, NULL), |
| 590 | __ATTR(unload_heads, 0644, ide_park_show, ide_park_store), | ||
| 590 | __ATTR_NULL | 591 | __ATTR_NULL |
| 591 | }; | 592 | }; |
| 592 | 593 | ||
diff --git a/include/linux/ide.h b/include/linux/ide.h index 8247b286838d..c47e371554c1 100644 --- a/include/linux/ide.h +++ b/include/linux/ide.h | |||
| @@ -156,6 +156,8 @@ enum { | |||
| 156 | */ | 156 | */ |
| 157 | #define REQ_DRIVE_RESET 0x20 | 157 | #define REQ_DRIVE_RESET 0x20 |
| 158 | #define REQ_DEVSET_EXEC 0x21 | 158 | #define REQ_DEVSET_EXEC 0x21 |
| 159 | #define REQ_PARK_HEADS 0x22 | ||
| 160 | #define REQ_UNPARK_HEADS 0x23 | ||
| 159 | 161 | ||
| 160 | /* | 162 | /* |
| 161 | * Check for an interrupt and acknowledge the interrupt status | 163 | * Check for an interrupt and acknowledge the interrupt status |
| @@ -573,6 +575,10 @@ enum { | |||
| 573 | /* retrying in PIO */ | 575 | /* retrying in PIO */ |
| 574 | IDE_DFLAG_DMA_PIO_RETRY = (1 << 25), | 576 | IDE_DFLAG_DMA_PIO_RETRY = (1 << 25), |
| 575 | IDE_DFLAG_LBA = (1 << 26), | 577 | IDE_DFLAG_LBA = (1 << 26), |
| 578 | /* don't unload heads */ | ||
| 579 | IDE_DFLAG_NO_UNLOAD = (1 << 27), | ||
| 580 | /* heads unloaded, please don't reset port */ | ||
| 581 | IDE_DFLAG_PARKED = (1 << 28) | ||
| 576 | }; | 582 | }; |
| 577 | 583 | ||
| 578 | struct ide_drive_s { | 584 | struct ide_drive_s { |
| @@ -1207,6 +1213,13 @@ int ide_check_atapi_device(ide_drive_t *, const char *); | |||
| 1207 | 1213 | ||
| 1208 | void ide_init_pc(struct ide_atapi_pc *); | 1214 | void ide_init_pc(struct ide_atapi_pc *); |
| 1209 | 1215 | ||
| 1216 | /* Disk head parking */ | ||
| 1217 | extern wait_queue_head_t ide_park_wq; | ||
| 1218 | ssize_t ide_park_show(struct device *dev, struct device_attribute *attr, | ||
| 1219 | char *buf); | ||
| 1220 | ssize_t ide_park_store(struct device *dev, struct device_attribute *attr, | ||
| 1221 | const char *buf, size_t len); | ||
| 1222 | |||
| 1210 | /* | 1223 | /* |
| 1211 | * Special requests for ide-tape block device strategy routine. | 1224 | * Special requests for ide-tape block device strategy routine. |
| 1212 | * | 1225 | * |
