diff options
Diffstat (limited to 'drivers/ata/libata-acpi.c')
-rw-r--r-- | drivers/ata/libata-acpi.c | 150 |
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 | ||
121 | static 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() */ | ||
137 | static 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 | */ | ||
121 | static void ata_acpi_handle_hotplug(struct ata_port *ap, struct ata_device *dev, | 171 | static 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) | 252 | static 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 | |||
259 | static 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 | ||
180 | static void ata_acpi_dev_notify(acpi_handle handle, u32 event, void *data) | 266 | static 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 | ||
187 | static void ata_acpi_ap_notify(acpi_handle handle, u32 event, void *data) | 273 | static 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 | } |