#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>
#include <acpi/actypes.h>
#define NO_PORT_MULT 0xffff
-#define SATA_ADR(root,pmp) (((root) << 16) | (pmp))
+#define SATA_ADR(root, pmp) (((root) << 16) | (pmp))
#define REGS_PER_GTF 7
struct ata_acpi_gtf {
return (dev->bus == &pci_bus_type);
}
-static void ata_acpi_associate_sata_port(struct ata_port *ap)
+/**
+ * ata_acpi_associate_sata_port - associate SATA port with ACPI objects
+ * @ap: target SATA port
+ *
+ * Look up ACPI objects associated with @ap and initialize acpi_handle
+ * fields of @ap, the port and devices accordingly.
+ *
+ * LOCKING:
+ * EH context.
+ *
+ * RETURNS:
+ * 0 on success, -errno on failure.
+ */
+void ata_acpi_associate_sata_port(struct ata_port *ap)
{
- acpi_integer adr = SATA_ADR(ap->port_no, NO_PORT_MULT);
+ WARN_ON(!(ap->flags & ATA_FLAG_ACPI_SATA));
+
+ if (!ap->nr_pmp_links) {
+ acpi_integer adr = SATA_ADR(ap->port_no, NO_PORT_MULT);
- ap->link.device->acpi_handle =
- acpi_get_child(ap->host->acpi_handle, adr);
+ ap->link.device->acpi_handle =
+ acpi_get_child(ap->host->acpi_handle, adr);
+ } else {
+ struct ata_link *link;
+
+ ap->link.device->acpi_handle = NULL;
+
+ ata_port_for_each_link(link, ap) {
+ acpi_integer adr = SATA_ADR(ap->port_no, link->pmp);
+
+ link->device->acpi_handle =
+ acpi_get_child(ap->host->acpi_handle, adr);
+ }
+ }
}
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
*/
void ata_acpi_associate(struct ata_host *host)
{
- int i;
+ int i, j;
if (!is_pci_dev(host->dev) || libata_noacpi)
return;
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);
+ }
}
}
* RETURNS:
* 0 on success, -ENOENT if _GTM doesn't exist, -errno on failure.
*/
-static int ata_acpi_gtm(const struct ata_port *ap, struct ata_acpi_gtm *gtm)
+int ata_acpi_gtm(const struct ata_port *ap, struct ata_acpi_gtm *gtm)
{
struct acpi_buffer output = { .length = ACPI_ALLOCATE_BUFFER };
union acpi_object *out_obj;
return rc;
}
+EXPORT_SYMBOL_GPL(ata_acpi_gtm);
+
/**
* ata_acpi_stm - execute _STM
* @ap: target ATA port
* RETURNS:
* 0 on success, -ENOENT if _STM doesn't exist, -errno on failure.
*/
-static int ata_acpi_stm(const struct ata_port *ap, struct ata_acpi_gtm *stm)
+int ata_acpi_stm(const struct ata_port *ap, struct ata_acpi_gtm *stm)
{
acpi_status status;
struct acpi_object_list input;
return 0;
}
+EXPORT_SYMBOL_GPL(ata_acpi_stm);
+
/**
* ata_dev_get_GTF - get the drive bootup default taskfile settings
* @dev: target ATA device
*
* RETURNS:
* Number of taskfiles on success, 0 if _GTF doesn't exist or doesn't
- * contain valid data. -errno on other errors.
+ * contain valid data.
*/
static int ata_dev_get_GTF(struct ata_device *dev, struct ata_acpi_gtf **gtf,
void **ptr_to_free)
ata_dev_printk(dev, KERN_WARNING,
"_GTF evaluation failed (AE 0x%x)\n",
status);
- rc = -EIO;
}
goto out_free;
}
ata_dev_printk(dev, KERN_WARNING,
"_GTF unexpected object type 0x%x\n",
out_obj->type);
- rc = -EINVAL;
goto out_free;
}
ata_dev_printk(dev, KERN_WARNING,
"unexpected _GTF length (%d)\n",
out_obj->buffer.length);
- rc = -EINVAL;
goto out_free;
}
{
struct ata_acpi_gtm gtm;
int valid = 0;
-
+
/* No _GTM data, no information */
if (ata_acpi_gtm(ap, >m) < 0)
return 0;
-
+
/* Split timing, DMA enabled */
if ((gtm.flags & 0x11) == 0x11 && gtm.drive[0].dma < 55)
valid |= 1;
tf.lbal, tf.lbam, tf.lbah, tf.device);
rtf = tf;
- err_mask = ata_exec_internal(dev, &rtf, NULL, DMA_NONE, NULL, 0);
+ err_mask = ata_exec_internal(dev, &rtf, NULL, DMA_NONE, NULL, 0, 0);
if (err_mask) {
ata_dev_printk(dev, KERN_ERR,
"ACPI cmd %02x/%02x:%02x:%02x:%02x:%02x:%02x failed "
int gtf_count, i, rc;
/* get taskfiles */
- rc = ata_dev_get_GTF(dev, >f, &ptr_to_free);
- if (rc < 0)
- return rc;
- gtf_count = rc;
+ gtf_count = ata_dev_get_GTF(dev, >f, &ptr_to_free);
/* execute them */
for (i = 0, rc = 0; i < gtf_count; i++) {