diff options
Diffstat (limited to 'drivers/ata/libata-acpi.c')
-rw-r--r-- | drivers/ata/libata-acpi.c | 167 |
1 files changed, 133 insertions, 34 deletions
diff --git a/drivers/ata/libata-acpi.c b/drivers/ata/libata-acpi.c index 70b77e0899a8..9330b7922f62 100644 --- a/drivers/ata/libata-acpi.c +++ b/drivers/ata/libata-acpi.c | |||
@@ -29,14 +29,16 @@ | |||
29 | enum { | 29 | enum { |
30 | ATA_ACPI_FILTER_SETXFER = 1 << 0, | 30 | ATA_ACPI_FILTER_SETXFER = 1 << 0, |
31 | ATA_ACPI_FILTER_LOCK = 1 << 1, | 31 | ATA_ACPI_FILTER_LOCK = 1 << 1, |
32 | ATA_ACPI_FILTER_DIPM = 1 << 2, | ||
32 | 33 | ||
33 | ATA_ACPI_FILTER_DEFAULT = ATA_ACPI_FILTER_SETXFER | | 34 | ATA_ACPI_FILTER_DEFAULT = ATA_ACPI_FILTER_SETXFER | |
34 | ATA_ACPI_FILTER_LOCK, | 35 | ATA_ACPI_FILTER_LOCK | |
36 | ATA_ACPI_FILTER_DIPM, | ||
35 | }; | 37 | }; |
36 | 38 | ||
37 | static unsigned int ata_acpi_gtf_filter = ATA_ACPI_FILTER_DEFAULT; | 39 | static unsigned int ata_acpi_gtf_filter = ATA_ACPI_FILTER_DEFAULT; |
38 | module_param_named(acpi_gtf_filter, ata_acpi_gtf_filter, int, 0644); | 40 | module_param_named(acpi_gtf_filter, ata_acpi_gtf_filter, int, 0644); |
39 | MODULE_PARM_DESC(acpi_gtf_filter, "filter mask for ACPI _GTF commands, set to filter out (0x1=set xfermode, 0x2=lock/freeze lock)"); | 41 | MODULE_PARM_DESC(acpi_gtf_filter, "filter mask for ACPI _GTF commands, set to filter out (0x1=set xfermode, 0x2=lock/freeze lock, 0x4=DIPM)"); |
40 | 42 | ||
41 | #define NO_PORT_MULT 0xffff | 43 | #define NO_PORT_MULT 0xffff |
42 | #define SATA_ADR(root, pmp) (((root) << 16) | (pmp)) | 44 | #define SATA_ADR(root, pmp) (((root) << 16) | (pmp)) |
@@ -118,19 +120,86 @@ static void ata_acpi_associate_ide_port(struct ata_port *ap) | |||
118 | ap->pflags |= ATA_PFLAG_INIT_GTM_VALID; | 120 | ap->pflags |= ATA_PFLAG_INIT_GTM_VALID; |
119 | } | 121 | } |
120 | 122 | ||
123 | static void ata_acpi_eject_device(acpi_handle handle) | ||
124 | { | ||
125 | struct acpi_object_list arg_list; | ||
126 | union acpi_object arg; | ||
127 | |||
128 | arg_list.count = 1; | ||
129 | arg_list.pointer = &arg; | ||
130 | arg.type = ACPI_TYPE_INTEGER; | ||
131 | arg.integer.value = 1; | ||
132 | |||
133 | if (ACPI_FAILURE(acpi_evaluate_object(handle, "_EJ0", | ||
134 | &arg_list, NULL))) | ||
135 | printk(KERN_ERR "Failed to evaluate _EJ0!\n"); | ||
136 | } | ||
137 | |||
138 | /* @ap and @dev are the same as ata_acpi_handle_hotplug() */ | ||
139 | static void ata_acpi_detach_device(struct ata_port *ap, struct ata_device *dev) | ||
140 | { | ||
141 | if (dev) | ||
142 | dev->flags |= ATA_DFLAG_DETACH; | ||
143 | else { | ||
144 | struct ata_link *tlink; | ||
145 | struct ata_device *tdev; | ||
146 | |||
147 | ata_port_for_each_link(tlink, ap) | ||
148 | ata_link_for_each_dev(tdev, tlink) | ||
149 | tdev->flags |= ATA_DFLAG_DETACH; | ||
150 | } | ||
151 | |||
152 | ata_port_schedule_eh(ap); | ||
153 | } | ||
154 | |||
155 | /** | ||
156 | * ata_acpi_handle_hotplug - ACPI event handler backend | ||
157 | * @ap: ATA port ACPI event occurred | ||
158 | * @dev: ATA device ACPI event occurred (can be NULL) | ||
159 | * @event: ACPI event which occurred | ||
160 | * @is_dock_event: boolean indicating whether the event was a dock one | ||
161 | * | ||
162 | * All ACPI bay / device realted events end up in this function. If | ||
163 | * the event is port-wide @dev is NULL. If the event is specific to a | ||
164 | * device, @dev points to it. | ||
165 | * | ||
166 | * Hotplug (as opposed to unplug) notification is always handled as | ||
167 | * port-wide while unplug only kills the target device on device-wide | ||
168 | * event. | ||
169 | * | ||
170 | * LOCKING: | ||
171 | * ACPI notify handler context. May sleep. | ||
172 | */ | ||
121 | static void ata_acpi_handle_hotplug(struct ata_port *ap, struct ata_device *dev, | 173 | static void ata_acpi_handle_hotplug(struct ata_port *ap, struct ata_device *dev, |
122 | u32 event) | 174 | u32 event, int is_dock_event) |
123 | { | 175 | { |
124 | char event_string[12]; | 176 | char event_string[12]; |
125 | char *envp[] = { event_string, NULL }; | 177 | char *envp[] = { event_string, NULL }; |
126 | struct ata_eh_info *ehi; | 178 | struct ata_eh_info *ehi = &ap->link.eh_info; |
127 | struct kobject *kobj = NULL; | 179 | struct kobject *kobj = NULL; |
128 | int wait = 0; | 180 | int wait = 0; |
129 | unsigned long flags; | 181 | unsigned long flags; |
182 | acpi_handle handle, tmphandle; | ||
183 | unsigned long sta; | ||
184 | acpi_status status; | ||
130 | 185 | ||
131 | if (!ap) | 186 | if (dev) { |
132 | ap = dev->link->ap; | 187 | if (dev->sdev) |
133 | ehi = &ap->link.eh_info; | 188 | kobj = &dev->sdev->sdev_gendev.kobj; |
189 | handle = dev->acpi_handle; | ||
190 | } else { | ||
191 | kobj = &ap->dev->kobj; | ||
192 | handle = ap->acpi_handle; | ||
193 | } | ||
194 | |||
195 | status = acpi_get_handle(handle, "_EJ0", &tmphandle); | ||
196 | if (ACPI_FAILURE(status)) | ||
197 | /* This device does not support hotplug */ | ||
198 | return; | ||
199 | |||
200 | if (event == ACPI_NOTIFY_BUS_CHECK || | ||
201 | event == ACPI_NOTIFY_DEVICE_CHECK) | ||
202 | status = acpi_evaluate_integer(handle, "_STA", NULL, &sta); | ||
134 | 203 | ||
135 | spin_lock_irqsave(ap->lock, flags); | 204 | spin_lock_irqsave(ap->lock, flags); |
136 | 205 | ||
@@ -138,57 +207,79 @@ static void ata_acpi_handle_hotplug(struct ata_port *ap, struct ata_device *dev, | |||
138 | case ACPI_NOTIFY_BUS_CHECK: | 207 | case ACPI_NOTIFY_BUS_CHECK: |
139 | case ACPI_NOTIFY_DEVICE_CHECK: | 208 | case ACPI_NOTIFY_DEVICE_CHECK: |
140 | ata_ehi_push_desc(ehi, "ACPI event"); | 209 | ata_ehi_push_desc(ehi, "ACPI event"); |
141 | ata_ehi_hotplugged(ehi); | ||
142 | ata_port_freeze(ap); | ||
143 | break; | ||
144 | 210 | ||
211 | if (ACPI_FAILURE(status)) { | ||
212 | ata_port_printk(ap, KERN_ERR, | ||
213 | "acpi: failed to determine bay status (0x%x)\n", | ||
214 | status); | ||
215 | break; | ||
216 | } | ||
217 | |||
218 | if (sta) { | ||
219 | ata_ehi_hotplugged(ehi); | ||
220 | ata_port_freeze(ap); | ||
221 | } else { | ||
222 | /* The device has gone - unplug it */ | ||
223 | ata_acpi_detach_device(ap, dev); | ||
224 | wait = 1; | ||
225 | } | ||
226 | break; | ||
145 | case ACPI_NOTIFY_EJECT_REQUEST: | 227 | case ACPI_NOTIFY_EJECT_REQUEST: |
146 | ata_ehi_push_desc(ehi, "ACPI event"); | 228 | ata_ehi_push_desc(ehi, "ACPI event"); |
147 | if (dev) | ||
148 | dev->flags |= ATA_DFLAG_DETACH; | ||
149 | else { | ||
150 | struct ata_link *tlink; | ||
151 | struct ata_device *tdev; | ||
152 | |||
153 | ata_port_for_each_link(tlink, ap) | ||
154 | ata_link_for_each_dev(tdev, tlink) | ||
155 | tdev->flags |= ATA_DFLAG_DETACH; | ||
156 | } | ||
157 | 229 | ||
158 | ata_port_schedule_eh(ap); | 230 | if (!is_dock_event) |
231 | break; | ||
232 | |||
233 | /* undock event - immediate unplug */ | ||
234 | ata_acpi_detach_device(ap, dev); | ||
159 | wait = 1; | 235 | wait = 1; |
160 | break; | 236 | break; |
161 | } | 237 | } |
162 | 238 | ||
163 | if (dev) { | 239 | /* make sure kobj doesn't go away while ap->lock is released */ |
164 | if (dev->sdev) | 240 | kobject_get(kobj); |
165 | kobj = &dev->sdev->sdev_gendev.kobj; | 241 | |
166 | } else | 242 | spin_unlock_irqrestore(ap->lock, flags); |
167 | kobj = &ap->dev->kobj; | ||
168 | 243 | ||
169 | if (kobj) { | 244 | if (wait) { |
245 | ata_port_wait_eh(ap); | ||
246 | ata_acpi_eject_device(handle); | ||
247 | } | ||
248 | |||
249 | if (kobj && !is_dock_event) { | ||
170 | sprintf(event_string, "BAY_EVENT=%d", event); | 250 | sprintf(event_string, "BAY_EVENT=%d", event); |
171 | kobject_uevent_env(kobj, KOBJ_CHANGE, envp); | 251 | kobject_uevent_env(kobj, KOBJ_CHANGE, envp); |
172 | } | 252 | } |
173 | 253 | ||
174 | spin_unlock_irqrestore(ap->lock, flags); | 254 | kobject_put(kobj); |
255 | } | ||
175 | 256 | ||
176 | if (wait) | 257 | static void ata_acpi_dev_notify_dock(acpi_handle handle, u32 event, void *data) |
177 | ata_port_wait_eh(ap); | 258 | { |
259 | struct ata_device *dev = data; | ||
260 | |||
261 | ata_acpi_handle_hotplug(dev->link->ap, dev, event, 1); | ||
262 | } | ||
263 | |||
264 | static void ata_acpi_ap_notify_dock(acpi_handle handle, u32 event, void *data) | ||
265 | { | ||
266 | struct ata_port *ap = data; | ||
267 | |||
268 | ata_acpi_handle_hotplug(ap, NULL, event, 1); | ||
178 | } | 269 | } |
179 | 270 | ||
180 | static void ata_acpi_dev_notify(acpi_handle handle, u32 event, void *data) | 271 | static void ata_acpi_dev_notify(acpi_handle handle, u32 event, void *data) |
181 | { | 272 | { |
182 | struct ata_device *dev = data; | 273 | struct ata_device *dev = data; |
183 | 274 | ||
184 | ata_acpi_handle_hotplug(NULL, dev, event); | 275 | ata_acpi_handle_hotplug(dev->link->ap, dev, event, 0); |
185 | } | 276 | } |
186 | 277 | ||
187 | static void ata_acpi_ap_notify(acpi_handle handle, u32 event, void *data) | 278 | static void ata_acpi_ap_notify(acpi_handle handle, u32 event, void *data) |
188 | { | 279 | { |
189 | struct ata_port *ap = data; | 280 | struct ata_port *ap = data; |
190 | 281 | ||
191 | ata_acpi_handle_hotplug(ap, NULL, event); | 282 | ata_acpi_handle_hotplug(ap, NULL, event, 0); |
192 | } | 283 | } |
193 | 284 | ||
194 | /** | 285 | /** |
@@ -229,7 +320,7 @@ void ata_acpi_associate(struct ata_host *host) | |||
229 | ata_acpi_ap_notify, ap); | 320 | ata_acpi_ap_notify, ap); |
230 | /* we might be on a docking station */ | 321 | /* we might be on a docking station */ |
231 | register_hotplug_dock_device(ap->acpi_handle, | 322 | register_hotplug_dock_device(ap->acpi_handle, |
232 | ata_acpi_ap_notify, ap); | 323 | ata_acpi_ap_notify_dock, ap); |
233 | } | 324 | } |
234 | 325 | ||
235 | for (j = 0; j < ata_link_max_devices(&ap->link); j++) { | 326 | for (j = 0; j < ata_link_max_devices(&ap->link); j++) { |
@@ -241,7 +332,7 @@ void ata_acpi_associate(struct ata_host *host) | |||
241 | ata_acpi_dev_notify, dev); | 332 | ata_acpi_dev_notify, dev); |
242 | /* we might be on a docking station */ | 333 | /* we might be on a docking station */ |
243 | register_hotplug_dock_device(dev->acpi_handle, | 334 | register_hotplug_dock_device(dev->acpi_handle, |
244 | ata_acpi_dev_notify, dev); | 335 | ata_acpi_dev_notify_dock, dev); |
245 | } | 336 | } |
246 | } | 337 | } |
247 | } | 338 | } |
@@ -604,6 +695,14 @@ static int ata_acpi_filter_tf(const struct ata_taskfile *tf, | |||
604 | return 1; | 695 | return 1; |
605 | } | 696 | } |
606 | 697 | ||
698 | if (ata_acpi_gtf_filter & ATA_ACPI_FILTER_DIPM) { | ||
699 | /* inhibit enabling DIPM */ | ||
700 | if (tf->command == ATA_CMD_SET_FEATURES && | ||
701 | tf->feature == SETFEATURES_SATA_ENABLE && | ||
702 | tf->nsect == SATA_DIPM) | ||
703 | return 1; | ||
704 | } | ||
705 | |||
607 | return 0; | 706 | return 0; |
608 | } | 707 | } |
609 | 708 | ||