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.c150
1 files changed, 118 insertions, 32 deletions
diff --git a/drivers/ata/libata-acpi.c b/drivers/ata/libata-acpi.c
index 70b77e0899a8..3ff8b14420d9 100644
--- a/drivers/ata/libata-acpi.c
+++ b/drivers/ata/libata-acpi.c
@@ -118,19 +118,82 @@ static void ata_acpi_associate_ide_port(struct ata_port *ap)
118 ap->pflags |= ATA_PFLAG_INIT_GTM_VALID; 118 ap->pflags |= ATA_PFLAG_INIT_GTM_VALID;
119} 119}
120 120
121static void ata_acpi_eject_device(acpi_handle handle)
122{
123 struct acpi_object_list arg_list;
124 union acpi_object arg;
125
126 arg_list.count = 1;
127 arg_list.pointer = &arg;
128 arg.type = ACPI_TYPE_INTEGER;
129 arg.integer.value = 1;
130
131 if (ACPI_FAILURE(acpi_evaluate_object(handle, "_EJ0",
132 &arg_list, NULL)))
133 printk(KERN_ERR "Failed to evaluate _EJ0!\n");
134}
135
136/* @ap and @dev are the same as ata_acpi_handle_hotplug() */
137static void ata_acpi_detach_device(struct ata_port *ap, struct ata_device *dev)
138{
139 if (dev)
140 dev->flags |= ATA_DFLAG_DETACH;
141 else {
142 struct ata_link *tlink;
143 struct ata_device *tdev;
144
145 ata_port_for_each_link(tlink, ap)
146 ata_link_for_each_dev(tdev, tlink)
147 tdev->flags |= ATA_DFLAG_DETACH;
148 }
149
150 ata_port_schedule_eh(ap);
151}
152
153/**
154 * ata_acpi_handle_hotplug - ACPI event handler backend
155 * @ap: ATA port ACPI event occurred
156 * @dev: ATA device ACPI event occurred (can be NULL)
157 * @event: ACPI event which occurred
158 * @is_dock_event: boolean indicating whether the event was a dock one
159 *
160 * All ACPI bay / device realted events end up in this function. If
161 * the event is port-wide @dev is NULL. If the event is specific to a
162 * device, @dev points to it.
163 *
164 * Hotplug (as opposed to unplug) notification is always handled as
165 * port-wide while unplug only kills the target device on device-wide
166 * event.
167 *
168 * LOCKING:
169 * ACPI notify handler context. May sleep.
170 */
121static void ata_acpi_handle_hotplug(struct ata_port *ap, struct ata_device *dev, 171static void ata_acpi_handle_hotplug(struct ata_port *ap, struct ata_device *dev,
122 u32 event) 172 u32 event, int is_dock_event)
123{ 173{
124 char event_string[12]; 174 char event_string[12];
125 char *envp[] = { event_string, NULL }; 175 char *envp[] = { event_string, NULL };
126 struct ata_eh_info *ehi; 176 struct ata_eh_info *ehi = &ap->link.eh_info;
127 struct kobject *kobj = NULL; 177 struct kobject *kobj = NULL;
128 int wait = 0; 178 int wait = 0;
129 unsigned long flags; 179 unsigned long flags;
180 acpi_handle handle, tmphandle;
181 unsigned long sta;
182 acpi_status status;
183
184 if (dev) {
185 if (dev->sdev)
186 kobj = &dev->sdev->sdev_gendev.kobj;
187 handle = dev->acpi_handle;
188 } else {
189 kobj = &ap->dev->kobj;
190 handle = ap->acpi_handle;
191 }
130 192
131 if (!ap) 193 status = acpi_get_handle(handle, "_EJ0", &tmphandle);
132 ap = dev->link->ap; 194 if (ACPI_FAILURE(status))
133 ehi = &ap->link.eh_info; 195 /* This device does not support hotplug */
196 return;
134 197
135 spin_lock_irqsave(ap->lock, flags); 198 spin_lock_irqsave(ap->lock, flags);
136 199
@@ -138,57 +201,80 @@ static void ata_acpi_handle_hotplug(struct ata_port *ap, struct ata_device *dev,
138 case ACPI_NOTIFY_BUS_CHECK: 201 case ACPI_NOTIFY_BUS_CHECK:
139 case ACPI_NOTIFY_DEVICE_CHECK: 202 case ACPI_NOTIFY_DEVICE_CHECK:
140 ata_ehi_push_desc(ehi, "ACPI event"); 203 ata_ehi_push_desc(ehi, "ACPI event");
141 ata_ehi_hotplugged(ehi);
142 ata_port_freeze(ap);
143 break;
144 204
205 status = acpi_evaluate_integer(handle, "_STA", NULL, &sta);
206 if (ACPI_FAILURE(status)) {
207 ata_port_printk(ap, KERN_ERR,
208 "acpi: failed to determine bay status (0x%x)\n",
209 status);
210 break;
211 }
212
213 if (sta) {
214 ata_ehi_hotplugged(ehi);
215 ata_port_freeze(ap);
216 } else {
217 /* The device has gone - unplug it */
218 ata_acpi_detach_device(ap, dev);
219 wait = 1;
220 }
221 break;
145 case ACPI_NOTIFY_EJECT_REQUEST: 222 case ACPI_NOTIFY_EJECT_REQUEST:
146 ata_ehi_push_desc(ehi, "ACPI event"); 223 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 224
158 ata_port_schedule_eh(ap); 225 if (!is_dock_event)
226 break;
227
228 /* undock event - immediate unplug */
229 ata_acpi_detach_device(ap, dev);
159 wait = 1; 230 wait = 1;
160 break; 231 break;
161 } 232 }
162 233
163 if (dev) { 234 /* make sure kobj doesn't go away while ap->lock is released */
164 if (dev->sdev) 235 kobject_get(kobj);
165 kobj = &dev->sdev->sdev_gendev.kobj; 236
166 } else 237 spin_unlock_irqrestore(ap->lock, flags);
167 kobj = &ap->dev->kobj; 238
239 if (wait) {
240 ata_port_wait_eh(ap);
241 ata_acpi_eject_device(handle);
242 }
168 243
169 if (kobj) { 244 if (kobj && !is_dock_event) {
170 sprintf(event_string, "BAY_EVENT=%d", event); 245 sprintf(event_string, "BAY_EVENT=%d", event);
171 kobject_uevent_env(kobj, KOBJ_CHANGE, envp); 246 kobject_uevent_env(kobj, KOBJ_CHANGE, envp);
172 } 247 }
173 248
174 spin_unlock_irqrestore(ap->lock, flags); 249 kobject_put(kobj);
250}
175 251
176 if (wait) 252static void ata_acpi_dev_notify_dock(acpi_handle handle, u32 event, void *data)
177 ata_port_wait_eh(ap); 253{
254 struct ata_device *dev = data;
255
256 ata_acpi_handle_hotplug(dev->link->ap, dev, event, 1);
257}
258
259static void ata_acpi_ap_notify_dock(acpi_handle handle, u32 event, void *data)
260{
261 struct ata_port *ap = data;
262
263 ata_acpi_handle_hotplug(ap, NULL, event, 1);
178} 264}
179 265
180static void ata_acpi_dev_notify(acpi_handle handle, u32 event, void *data) 266static void ata_acpi_dev_notify(acpi_handle handle, u32 event, void *data)
181{ 267{
182 struct ata_device *dev = data; 268 struct ata_device *dev = data;
183 269
184 ata_acpi_handle_hotplug(NULL, dev, event); 270 ata_acpi_handle_hotplug(dev->link->ap, dev, event, 0);
185} 271}
186 272
187static void ata_acpi_ap_notify(acpi_handle handle, u32 event, void *data) 273static void ata_acpi_ap_notify(acpi_handle handle, u32 event, void *data)
188{ 274{
189 struct ata_port *ap = data; 275 struct ata_port *ap = data;
190 276
191 ata_acpi_handle_hotplug(ap, NULL, event); 277 ata_acpi_handle_hotplug(ap, NULL, event, 0);
192} 278}
193 279
194/** 280/**
@@ -229,7 +315,7 @@ void ata_acpi_associate(struct ata_host *host)
229 ata_acpi_ap_notify, ap); 315 ata_acpi_ap_notify, ap);
230 /* we might be on a docking station */ 316 /* we might be on a docking station */
231 register_hotplug_dock_device(ap->acpi_handle, 317 register_hotplug_dock_device(ap->acpi_handle,
232 ata_acpi_ap_notify, ap); 318 ata_acpi_ap_notify_dock, ap);
233 } 319 }
234 320
235 for (j = 0; j < ata_link_max_devices(&ap->link); j++) { 321 for (j = 0; j < ata_link_max_devices(&ap->link); j++) {
@@ -241,7 +327,7 @@ void ata_acpi_associate(struct ata_host *host)
241 ata_acpi_dev_notify, dev); 327 ata_acpi_dev_notify, dev);
242 /* we might be on a docking station */ 328 /* we might be on a docking station */
243 register_hotplug_dock_device(dev->acpi_handle, 329 register_hotplug_dock_device(dev->acpi_handle,
244 ata_acpi_dev_notify, dev); 330 ata_acpi_dev_notify_dock, dev);
245 } 331 }
246 } 332 }
247 } 333 }