Skip to content

Commit

Permalink
libata: Integrate ACPI-based PATA/SATA hotplug - version 5
Browse files Browse the repository at this point in the history
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: Matthew Garrett <mjg59@srcf.ucam.org>
Signed-off-by: Jeff Garzik <jeff@garzik.org>
  • Loading branch information
Matthew Garrett authored and Jeff Garzik committed Oct 12, 2007
1 parent b3a7060 commit 237d844
Showing 1 changed file with 59 additions and 1 deletion.
60 changes: 59 additions & 1 deletion drivers/ata/libata-acpi.c
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
#include <linux/acpi.h>
#include <linux/libata.h>
#include <linux/pci.h>
#include <scsi/scsi_device.h>
#include "libata.h"

#include <acpi/acpi_bus.h>
Expand Down Expand Up @@ -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
* @host: target ATA host
Expand All @@ -110,7 +152,7 @@ static void ata_acpi_associate_ide_port(struct ata_port *ap)
*/
void ata_acpi_associate(struct ata_host *host)
{
int i;
int i, j;

if (!is_pci_dev(host->dev) || libata_noacpi)
return;
Expand All @@ -126,6 +168,22 @@ void ata_acpi_associate(struct ata_host *host)
ata_acpi_associate_sata_port(ap);
else
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);
}
}
}

Expand Down

0 comments on commit 237d844

Please sign in to comment.