Commit 237d8440 authored by Matthew Garrett's avatar Matthew Garrett Committed by Jeff Garzik

libata: Integrate ACPI-based PATA/SATA hotplug - version 5

Modern laptops with hotswap bays still tend to utilise a PATA interface
on a SATA bridge, generally with the host controller in some legacy
emulation mode rather than AHCI. This means that the existing hotplug
code in libata is unable to work. The ACPI specification states that
these devices can send notifications when hotswapped, which avoids the
need to obtain notification from the controller. This patch uses the
existing libata-acpi code and simply registers a notification in order
to trigger a rescan whenever the firmware signals an event.
Signed-off-by: default avatarMatthew Garrett <mjg59@srcf.ucam.org>
Signed-off-by: default avatarJeff Garzik <jeff@garzik.org>
parent b3a70601
...@@ -14,6 +14,7 @@ ...@@ -14,6 +14,7 @@
#include <linux/acpi.h> #include <linux/acpi.h>
#include <linux/libata.h> #include <linux/libata.h>
#include <linux/pci.h> #include <linux/pci.h>
#include <scsi/scsi_device.h>
#include "libata.h" #include "libata.h"
#include <acpi/acpi_bus.h> #include <acpi/acpi_bus.h>
...@@ -95,6 +96,47 @@ static void ata_acpi_associate_ide_port(struct ata_port *ap) ...@@ -95,6 +96,47 @@ static void ata_acpi_associate_ide_port(struct ata_port *ap)
} }
} }
static void ata_acpi_handle_hotplug (struct ata_port *ap, struct kobject *kobj,
u32 event)
{
char event_string[12];
char *envp[] = { event_string, NULL };
struct ata_eh_info *ehi = &ap->link.eh_info;
if (event == 0 || event == 1) {
unsigned long flags;
spin_lock_irqsave(ap->lock, flags);
ata_ehi_clear_desc(ehi);
ata_ehi_push_desc(ehi, "ACPI event");
ata_ehi_hotplugged(ehi);
ata_port_freeze(ap);
spin_unlock_irqrestore(ap->lock, flags);
}
if (kobj) {
sprintf(event_string, "BAY_EVENT=%d", event);
kobject_uevent_env(kobj, KOBJ_CHANGE, envp);
}
}
static void ata_acpi_dev_notify(acpi_handle handle, u32 event, void *data)
{
struct ata_device *dev = data;
struct kobject *kobj = NULL;
if (dev->sdev)
kobj = &dev->sdev->sdev_gendev.kobj;
ata_acpi_handle_hotplug (dev->link->ap, kobj, event);
}
static void ata_acpi_ap_notify(acpi_handle handle, u32 event, void *data)
{
struct ata_port *ap = data;
ata_acpi_handle_hotplug (ap, &ap->dev->kobj, event);
}
/** /**
* ata_acpi_associate - associate ATA host with ACPI objects * ata_acpi_associate - associate ATA host with ACPI objects
* @host: target ATA host * @host: target ATA host
...@@ -110,7 +152,7 @@ static void ata_acpi_associate_ide_port(struct ata_port *ap) ...@@ -110,7 +152,7 @@ static void ata_acpi_associate_ide_port(struct ata_port *ap)
*/ */
void ata_acpi_associate(struct ata_host *host) void ata_acpi_associate(struct ata_host *host)
{ {
int i; int i, j;
if (!is_pci_dev(host->dev) || libata_noacpi) if (!is_pci_dev(host->dev) || libata_noacpi)
return; return;
...@@ -126,6 +168,22 @@ void ata_acpi_associate(struct ata_host *host) ...@@ -126,6 +168,22 @@ void ata_acpi_associate(struct ata_host *host)
ata_acpi_associate_sata_port(ap); ata_acpi_associate_sata_port(ap);
else else
ata_acpi_associate_ide_port(ap); ata_acpi_associate_ide_port(ap);
if (ap->acpi_handle)
acpi_install_notify_handler (ap->acpi_handle,
ACPI_SYSTEM_NOTIFY,
ata_acpi_ap_notify,
ap);
for (j = 0; j < ata_link_max_devices(&ap->link); j++) {
struct ata_device *dev = &ap->link.device[j];
if (dev->acpi_handle)
acpi_install_notify_handler (dev->acpi_handle,
ACPI_SYSTEM_NOTIFY,
ata_acpi_dev_notify,
dev);
}
} }
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment