aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMatthew Garrett <mjg59@srcf.ucam.org>2007-10-02 20:24:16 -0400
committerJeff Garzik <jeff@garzik.org>2007-10-12 14:55:45 -0400
commit237d8440cb2b104a3b97fc971a9bce67960bb616 (patch)
tree1199f27b0858c67f2f327c70be108ec5b03448fb
parentb3a706014e56b1356e7b275fd25b833c63175bf0 (diff)
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: Matthew Garrett <mjg59@srcf.ucam.org> Signed-off-by: Jeff Garzik <jeff@garzik.org>
-rw-r--r--drivers/ata/libata-acpi.c60
1 files changed, 59 insertions, 1 deletions
diff --git a/drivers/ata/libata-acpi.c b/drivers/ata/libata-acpi.c
index a276c06dda95..5ebbf16f3af1 100644
--- a/drivers/ata/libata-acpi.c
+++ b/drivers/ata/libata-acpi.c
@@ -14,6 +14,7 @@
14#include <linux/acpi.h> 14#include <linux/acpi.h>
15#include <linux/libata.h> 15#include <linux/libata.h>
16#include <linux/pci.h> 16#include <linux/pci.h>
17#include <scsi/scsi_device.h>
17#include "libata.h" 18#include "libata.h"
18 19
19#include <acpi/acpi_bus.h> 20#include <acpi/acpi_bus.h>
@@ -95,6 +96,47 @@ static void ata_acpi_associate_ide_port(struct ata_port *ap)
95 } 96 }
96} 97}
97 98
99static void ata_acpi_handle_hotplug (struct ata_port *ap, struct kobject *kobj,
100 u32 event)
101{
102 char event_string[12];
103 char *envp[] = { event_string, NULL };
104 struct ata_eh_info *ehi = &ap->link.eh_info;
105
106 if (event == 0 || event == 1) {
107 unsigned long flags;
108 spin_lock_irqsave(ap->lock, flags);
109 ata_ehi_clear_desc(ehi);
110 ata_ehi_push_desc(ehi, "ACPI event");
111 ata_ehi_hotplugged(ehi);
112 ata_port_freeze(ap);
113 spin_unlock_irqrestore(ap->lock, flags);
114 }
115
116 if (kobj) {
117 sprintf(event_string, "BAY_EVENT=%d", event);
118 kobject_uevent_env(kobj, KOBJ_CHANGE, envp);
119 }
120}
121
122static void ata_acpi_dev_notify(acpi_handle handle, u32 event, void *data)
123{
124 struct ata_device *dev = data;
125 struct kobject *kobj = NULL;
126
127 if (dev->sdev)
128 kobj = &dev->sdev->sdev_gendev.kobj;
129
130 ata_acpi_handle_hotplug (dev->link->ap, kobj, event);
131}
132
133static void ata_acpi_ap_notify(acpi_handle handle, u32 event, void *data)
134{
135 struct ata_port *ap = data;
136
137 ata_acpi_handle_hotplug (ap, &ap->dev->kobj, event);
138}
139
98/** 140/**
99 * ata_acpi_associate - associate ATA host with ACPI objects 141 * ata_acpi_associate - associate ATA host with ACPI objects
100 * @host: target ATA host 142 * @host: target ATA host
@@ -110,7 +152,7 @@ static void ata_acpi_associate_ide_port(struct ata_port *ap)
110 */ 152 */
111void ata_acpi_associate(struct ata_host *host) 153void ata_acpi_associate(struct ata_host *host)
112{ 154{
113 int i; 155 int i, j;
114 156
115 if (!is_pci_dev(host->dev) || libata_noacpi) 157 if (!is_pci_dev(host->dev) || libata_noacpi)
116 return; 158 return;
@@ -126,6 +168,22 @@ void ata_acpi_associate(struct ata_host *host)
126 ata_acpi_associate_sata_port(ap); 168 ata_acpi_associate_sata_port(ap);
127 else 169 else
128 ata_acpi_associate_ide_port(ap); 170 ata_acpi_associate_ide_port(ap);
171
172 if (ap->acpi_handle)
173 acpi_install_notify_handler (ap->acpi_handle,
174 ACPI_SYSTEM_NOTIFY,
175 ata_acpi_ap_notify,
176 ap);
177
178 for (j = 0; j < ata_link_max_devices(&ap->link); j++) {
179 struct ata_device *dev = &ap->link.device[j];
180
181 if (dev->acpi_handle)
182 acpi_install_notify_handler (dev->acpi_handle,
183 ACPI_SYSTEM_NOTIFY,
184 ata_acpi_dev_notify,
185 dev);
186 }
129 } 187 }
130} 188}
131 189