Skip to content

Commit

Permalink
Merge branch 'upstream-linus' of git://git.kernel.org/pub/scm/linux/k…
Browse files Browse the repository at this point in the history
…ernel/git/jgarzik/libata-dev

* 'upstream-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/jgarzik/libata-dev:
  libata-sff: Fix oops reported in kerneloops.org for pnp devices with no ctl
  libata: kill unused constants
  sata_mv: PHY_MODE4 cleanups
  [libata] ata_piix: more acer short cable quirks
  [libata] ACPI: Properly handle bay devices in dock stations
  • Loading branch information
Linus Torvalds committed Jun 4, 2008
2 parents 2d9b57f + a57c1ba commit 246dd41
Show file tree
Hide file tree
Showing 8 changed files with 239 additions and 97 deletions.
2 changes: 2 additions & 0 deletions drivers/ata/ata_piix.c
Original file line number Diff line number Diff line change
Expand Up @@ -574,6 +574,8 @@ static const struct ich_laptop ich_laptop[] = {
{ 0x27DF, 0x1043, 0x1267 }, /* ICH7 on Asus W5F */
{ 0x27DF, 0x103C, 0x30A1 }, /* ICH7 on HP Compaq nc2400 */
{ 0x24CA, 0x1025, 0x0061 }, /* ICH4 on ACER Aspire 2023WLMi */
{ 0x24CA, 0x1025, 0x003d }, /* ICH4 on ACER TM290 */
{ 0x266F, 0x1025, 0x0066 }, /* ICH6 on ACER Aspire 1694WLMi */
{ 0x2653, 0x1043, 0x82D8 }, /* ICH6M on Asus Eee 701 */
/* end marker */
{ 0, }
Expand Down
165 changes: 114 additions & 51 deletions drivers/ata/libata-acpi.c
Original file line number Diff line number Diff line change
Expand Up @@ -118,100 +118,163 @@ static void ata_acpi_associate_ide_port(struct ata_port *ap)
ap->pflags |= ATA_PFLAG_INIT_GTM_VALID;
}

static void ata_acpi_handle_hotplug(struct ata_port *ap, struct ata_device
*dev, u32 event)
static void ata_acpi_eject_device(acpi_handle handle)
{
struct acpi_object_list arg_list;
union acpi_object arg;

arg_list.count = 1;
arg_list.pointer = &arg;
arg.type = ACPI_TYPE_INTEGER;
arg.integer.value = 1;

if (ACPI_FAILURE(acpi_evaluate_object(handle, "_EJ0",
&arg_list, NULL)))
printk(KERN_ERR "Failed to evaluate _EJ0!\n");
}

/* @ap and @dev are the same as ata_acpi_handle_hotplug() */
static void ata_acpi_detach_device(struct ata_port *ap, struct ata_device *dev)
{
if (dev)
dev->flags |= ATA_DFLAG_DETACH;
else {
struct ata_link *tlink;
struct ata_device *tdev;

ata_port_for_each_link(tlink, ap)
ata_link_for_each_dev(tdev, tlink)
tdev->flags |= ATA_DFLAG_DETACH;
}

ata_port_schedule_eh(ap);
}

/**
* ata_acpi_handle_hotplug - ACPI event handler backend
* @ap: ATA port ACPI event occurred
* @dev: ATA device ACPI event occurred (can be NULL)
* @event: ACPI event which occurred
* @is_dock_event: boolean indicating whether the event was a dock one
*
* All ACPI bay / device realted events end up in this function. If
* the event is port-wide @dev is NULL. If the event is specific to a
* device, @dev points to it.
*
* Hotplug (as opposed to unplug) notification is always handled as
* port-wide while unplug only kills the target device on device-wide
* event.
*
* LOCKING:
* ACPI notify handler context. May sleep.
*/
static void ata_acpi_handle_hotplug(struct ata_port *ap, struct ata_device *dev,
u32 event, int is_dock_event)
{
char event_string[12];
char *envp[] = { event_string, NULL };
struct ata_eh_info *ehi;
struct ata_eh_info *ehi = &ap->link.eh_info;
struct kobject *kobj = NULL;
int wait = 0;
unsigned long flags;
acpi_handle handle, tmphandle;
unsigned long sta;
acpi_status status;

if (!ap)
ap = dev->link->ap;
ehi = &ap->link.eh_info;

spin_lock_irqsave(ap->lock, flags);

if (dev)
if (dev) {
if (dev->sdev)
kobj = &dev->sdev->sdev_gendev.kobj;
handle = dev->acpi_handle;
else
} else {
kobj = &ap->dev->kobj;
handle = ap->acpi_handle;
}

status = acpi_get_handle(handle, "_EJ0", &tmphandle);
if (ACPI_FAILURE(status)) {
/* This device is not ejectable */
spin_unlock_irqrestore(ap->lock, flags);
if (ACPI_FAILURE(status))
/* This device does not support hotplug */
return;
}

status = acpi_evaluate_integer(handle, "_STA", NULL, &sta);
if (ACPI_FAILURE(status)) {
printk ("Unable to determine bay status\n");
spin_unlock_irqrestore(ap->lock, flags);
return;
}
spin_lock_irqsave(ap->lock, flags);

switch (event) {
case ACPI_NOTIFY_BUS_CHECK:
case ACPI_NOTIFY_DEVICE_CHECK:
ata_ehi_push_desc(ehi, "ACPI event");
if (!sta) {
/* Device has been unplugged */
if (dev)
dev->flags |= ATA_DFLAG_DETACH;
else {
struct ata_link *tlink;
struct ata_device *tdev;

ata_port_for_each_link(tlink, ap) {
ata_link_for_each_dev(tdev, tlink) {
tdev->flags |=
ATA_DFLAG_DETACH;
}
}
}
ata_port_schedule_eh(ap);
wait = 1;
} else {

status = acpi_evaluate_integer(handle, "_STA", NULL, &sta);
if (ACPI_FAILURE(status)) {
ata_port_printk(ap, KERN_ERR,
"acpi: failed to determine bay status (0x%x)\n",
status);
break;
}

if (sta) {
ata_ehi_hotplugged(ehi);
ata_port_freeze(ap);
} else {
/* The device has gone - unplug it */
ata_acpi_detach_device(ap, dev);
wait = 1;
}
break;
case ACPI_NOTIFY_EJECT_REQUEST:
ata_ehi_push_desc(ehi, "ACPI event");

if (!is_dock_event)
break;

/* undock event - immediate unplug */
ata_acpi_detach_device(ap, dev);
wait = 1;
break;
}

/* make sure kobj doesn't go away while ap->lock is released */
kobject_get(kobj);

spin_unlock_irqrestore(ap->lock, flags);

if (wait)
if (wait) {
ata_port_wait_eh(ap);
ata_acpi_eject_device(handle);
}

if (dev) {
if (dev->sdev)
kobj = &dev->sdev->sdev_gendev.kobj;
} else
kobj = &ap->dev->kobj;

if (kobj) {
if (kobj && !is_dock_event) {
sprintf(event_string, "BAY_EVENT=%d", event);
kobject_uevent_env(kobj, KOBJ_CHANGE, envp);
}

kobject_put(kobj);
}

static void ata_acpi_dev_notify_dock(acpi_handle handle, u32 event, void *data)
{
struct ata_device *dev = data;

ata_acpi_handle_hotplug(dev->link->ap, dev, event, 1);
}

static void ata_acpi_ap_notify_dock(acpi_handle handle, u32 event, void *data)
{
struct ata_port *ap = data;

ata_acpi_handle_hotplug(ap, NULL, event, 1);
}

static void ata_acpi_dev_notify(acpi_handle handle, u32 event, void *data)
{
struct ata_device *dev = data;

ata_acpi_handle_hotplug(NULL, dev, event);
ata_acpi_handle_hotplug(dev->link->ap, dev, event, 0);
}

static void ata_acpi_ap_notify(acpi_handle handle, u32 event, void *data)
{
struct ata_port *ap = data;

ata_acpi_handle_hotplug(ap, NULL, event);
ata_acpi_handle_hotplug(ap, NULL, event, 0);
}

/**
Expand Down Expand Up @@ -252,7 +315,7 @@ void ata_acpi_associate(struct ata_host *host)
ata_acpi_ap_notify, ap);
/* we might be on a docking station */
register_hotplug_dock_device(ap->acpi_handle,
ata_acpi_ap_notify, ap);
ata_acpi_ap_notify_dock, ap);
}

for (j = 0; j < ata_link_max_devices(&ap->link); j++) {
Expand All @@ -264,7 +327,7 @@ void ata_acpi_associate(struct ata_host *host)
ata_acpi_dev_notify, dev);
/* we might be on a docking station */
register_hotplug_dock_device(dev->acpi_handle,
ata_acpi_dev_notify, dev);
ata_acpi_dev_notify_dock, dev);
}
}
}
Expand Down
Loading

0 comments on commit 246dd41

Please sign in to comment.