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 | * |