From 6b66d95895c149cbc04d4fac5a2f5477c543a8ae Mon Sep 17 00:00:00 2001 From: Matthew Garrett Date: Mon, 25 Jun 2012 16:13:03 +0800 Subject: libata: bind the Linux device tree to the ACPI device tree Associate the ACPI device tree and libata devices. This patch uses the generic ACPI glue framework to do so. Signed-off-by: Matthew Garrett Signed-off-by: Holger Macht Signed-off-by: Lin Ming Signed-off-by: Jeff Garzik --- drivers/ata/libata-acpi.c | 137 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 137 insertions(+) (limited to 'drivers/ata/libata-acpi.c') diff --git a/drivers/ata/libata-acpi.c b/drivers/ata/libata-acpi.c index bb7c5f1..b3025a7 100644 --- a/drivers/ata/libata-acpi.c +++ b/drivers/ata/libata-acpi.c @@ -47,6 +47,39 @@ static void ata_acpi_clear_gtf(struct ata_device *dev) dev->gtf_cache = NULL; } +static acpi_handle ap_acpi_handle(struct ata_port *ap) +{ + if (ap->flags & ATA_FLAG_ACPI_SATA) + return NULL; + + /* + * If acpi bind operation has already happened, we can get the handle + * for the port by checking the corresponding scsi_host device's + * firmware node, otherwise we will need to find out the handle from + * its parent's acpi node. + */ + if (ap->scsi_host) + return DEVICE_ACPI_HANDLE(&ap->scsi_host->shost_gendev); + else + return acpi_get_child(DEVICE_ACPI_HANDLE(ap->host->dev), + ap->port_no); +} + +static acpi_handle dev_acpi_handle(struct ata_device *dev) +{ + acpi_integer adr; + struct ata_port *ap = dev->link->ap; + + if (ap->flags & ATA_FLAG_ACPI_SATA) { + if (!sata_pmp_attached(ap)) + adr = SATA_ADR(ap->port_no, NO_PORT_MULT); + else + adr = SATA_ADR(ap->port_no, dev->link->pmp); + return acpi_get_child(DEVICE_ACPI_HANDLE(ap->host->dev), adr); + } else + return acpi_get_child(ap_acpi_handle(ap), dev->devno); +} + /** * ata_acpi_associate_sata_port - associate SATA port with ACPI objects * @ap: target SATA port @@ -1018,3 +1051,107 @@ void ata_acpi_on_disable(struct ata_device *dev) { ata_acpi_clear_gtf(dev); } + +static int compat_pci_ata(struct ata_port *ap) +{ + struct device *dev = ap->tdev.parent; + struct pci_dev *pdev; + + if (!is_pci_dev(dev)) + return 0; + + pdev = to_pci_dev(dev); + + if ((pdev->class >> 8) != PCI_CLASS_STORAGE_SATA && + (pdev->class >> 8) != PCI_CLASS_STORAGE_IDE) + return 0; + + return 1; +} + +static int ata_acpi_bind_host(struct ata_port *ap, acpi_handle *handle) +{ + if (ap->flags & ATA_FLAG_ACPI_SATA) + return -ENODEV; + + *handle = acpi_get_child(DEVICE_ACPI_HANDLE(ap->tdev.parent), + ap->port_no); + + if (!*handle) + return -ENODEV; + + return 0; +} + +static int ata_acpi_bind_device(struct ata_port *ap, struct scsi_device *sdev, + acpi_handle *handle) +{ + struct ata_device *ata_dev; + + if (ap->flags & ATA_FLAG_ACPI_SATA) + ata_dev = &ap->link.device[sdev->channel]; + else + ata_dev = &ap->link.device[sdev->id]; + + *handle = dev_acpi_handle(ata_dev); + + if (!*handle) + return -ENODEV; + + return 0; +} + +static int is_ata_port(const struct device *dev) +{ + return dev->type == &ata_port_type; +} + +static struct ata_port *dev_to_ata_port(struct device *dev) +{ + while (!is_ata_port(dev)) { + if (!dev->parent) + return NULL; + dev = dev->parent; + } + return to_ata_port(dev); +} + +static int ata_acpi_find_device(struct device *dev, acpi_handle *handle) +{ + struct ata_port *ap = dev_to_ata_port(dev); + + if (!ap) + return -ENODEV; + + if (!compat_pci_ata(ap)) + return -ENODEV; + + if (scsi_is_host_device(dev)) + return ata_acpi_bind_host(ap, handle); + else if (scsi_is_sdev_device(dev)) { + struct scsi_device *sdev = to_scsi_device(dev); + + return ata_acpi_bind_device(ap, sdev, handle); + } else + return -ENODEV; +} + +static int ata_acpi_find_dummy(struct device *dev, acpi_handle *handle) +{ + return -ENODEV; +} + +static struct acpi_bus_type ata_acpi_bus = { + .find_bridge = ata_acpi_find_dummy, + .find_device = ata_acpi_find_device, +}; + +int ata_acpi_register(void) +{ + return scsi_register_acpi_bus_type(&ata_acpi_bus); +} + +void ata_acpi_unregister(void) +{ + scsi_unregister_acpi_bus_type(&ata_acpi_bus); +} -- cgit v1.1