aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/ata/libata-acpi.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/ata/libata-acpi.c')
-rw-r--r--drivers/ata/libata-acpi.c167
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 @@
29enum { 29enum {
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
37static unsigned int ata_acpi_gtf_filter = ATA_ACPI_FILTER_DEFAULT; 39static unsigned int ata_acpi_gtf_filter = ATA_ACPI_FILTER_DEFAULT;
38module_param_named(acpi_gtf_filter, ata_acpi_gtf_filter, int, 0644); 40module_param_named(acpi_gtf_filter, ata_acpi_gtf_filter, int, 0644);
39MODULE_PARM_DESC(acpi_gtf_filter, "filter mask for ACPI _GTF commands, set to filter out (0x1=set xfermode, 0x2=lock/freeze lock)"); 41MODULE_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
123static 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() */
139static 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 */
121static void ata_acpi_handle_hotplug(struct ata_port *ap, struct ata_device *dev, 173static 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) 257static 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
264static 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
180static void ata_acpi_dev_notify(acpi_handle handle, u32 event, void *data) 271static 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
187static void ata_acpi_ap_notify(acpi_handle handle, u32 event, void *data) 278static 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