diff options
Diffstat (limited to 'drivers/ata/libata-acpi.c')
| -rw-r--r-- | drivers/ata/libata-acpi.c | 490 |
1 files changed, 173 insertions, 317 deletions
diff --git a/drivers/ata/libata-acpi.c b/drivers/ata/libata-acpi.c index ef01ac07502..97a14fe47de 100644 --- a/drivers/ata/libata-acpi.c +++ b/drivers/ata/libata-acpi.c @@ -20,8 +20,6 @@ #include <scsi/scsi_device.h> #include "libata.h" -#include <acpi/acpi_bus.h> - unsigned int ata_acpi_gtf_filter = ATA_ACPI_FILTER_DEFAULT; module_param_named(acpi_gtf_filter, ata_acpi_gtf_filter, int, 0644); MODULE_PARM_DESC(acpi_gtf_filter, "filter mask for ACPI _GTF commands, set to filter out (0x1=set xfermode, 0x2=lock/freeze lock, 0x4=DIPM, 0x8=FPDMA non-zero offset, 0x10=FPDMA DMA Setup FIS auto-activate)"); @@ -34,61 +32,35 @@ struct ata_acpi_gtf { u8 tf[REGS_PER_GTF]; /* regs. 0x1f1 - 0x1f7 */ } __packed; -/* - * Helper - belongs in the PCI layer somewhere eventually - */ -static int is_pci_dev(struct device *dev) -{ - return (dev->bus == &pci_bus_type); -} - static void ata_acpi_clear_gtf(struct ata_device *dev) { kfree(dev->gtf_cache); dev->gtf_cache = NULL; } -/** - * ata_ap_acpi_handle - provide the acpi_handle for an ata_port - * @ap: the acpi_handle returned will correspond to this port - * - * Returns the acpi_handle for the ACPI namespace object corresponding to - * the ata_port passed into the function, or NULL if no such object exists - */ -acpi_handle ata_ap_acpi_handle(struct ata_port *ap) -{ - if (ap->flags & ATA_FLAG_ACPI_SATA) - return NULL; +struct ata_acpi_hotplug_context { + struct acpi_hotplug_context hp; + union { + struct ata_port *ap; + struct ata_device *dev; + } data; +}; - return acpi_get_child(DEVICE_ACPI_HANDLE(ap->host->dev), ap->port_no); -} -EXPORT_SYMBOL(ata_ap_acpi_handle); +#define ata_hotplug_data(context) (container_of((context), struct ata_acpi_hotplug_context, hp)->data) /** * ata_dev_acpi_handle - provide the acpi_handle for an ata_device - * @dev: the acpi_device returned will correspond to this port + * @dev: the acpi_handle returned will correspond to this device * * Returns the acpi_handle for the ACPI namespace object corresponding to * the ata_device passed into the function, or NULL if no such object exists + * or ACPI is disabled for this device due to consecutive errors. */ acpi_handle ata_dev_acpi_handle(struct ata_device *dev) { - acpi_integer adr; - struct ata_port *ap = dev->link->ap; - - if (dev->flags & ATA_DFLAG_ACPI_DISABLED) - return NULL; - - 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(ata_ap_acpi_handle(ap), dev->devno); + return dev->flags & ATA_DFLAG_ACPI_DISABLED ? + NULL : ACPI_HANDLE(&dev->tdev); } -EXPORT_SYMBOL(ata_dev_acpi_handle); /* @ap and @dev are the same as ata_acpi_handle_hotplug() */ static void ata_acpi_detach_device(struct ata_port *ap, struct ata_device *dev) @@ -159,18 +131,17 @@ static void ata_acpi_handle_hotplug(struct ata_port *ap, struct ata_device *dev, ata_port_wait_eh(ap); } -static void ata_acpi_dev_notify_dock(acpi_handle handle, u32 event, void *data) +static int ata_acpi_dev_notify_dock(struct acpi_device *adev, u32 event) { - struct ata_device *dev = data; - + struct ata_device *dev = ata_hotplug_data(adev->hp).dev; ata_acpi_handle_hotplug(dev->link->ap, dev, event); + return 0; } -static void ata_acpi_ap_notify_dock(acpi_handle handle, u32 event, void *data) +static int ata_acpi_ap_notify_dock(struct acpi_device *adev, u32 event) { - struct ata_port *ap = data; - - ata_acpi_handle_hotplug(ap, NULL, event); + ata_acpi_handle_hotplug(ata_hotplug_data(adev->hp).ap, NULL, event); + return 0; } static void ata_acpi_uevent(struct ata_port *ap, struct ata_device *dev, @@ -192,26 +163,86 @@ static void ata_acpi_uevent(struct ata_port *ap, struct ata_device *dev, } } -static void ata_acpi_ap_uevent(acpi_handle handle, u32 event, void *data) +static void ata_acpi_ap_uevent(struct acpi_device *adev, u32 event) { - ata_acpi_uevent(data, NULL, event); + ata_acpi_uevent(ata_hotplug_data(adev->hp).ap, NULL, event); } -static void ata_acpi_dev_uevent(acpi_handle handle, u32 event, void *data) +static void ata_acpi_dev_uevent(struct acpi_device *adev, u32 event) { - struct ata_device *dev = data; + struct ata_device *dev = ata_hotplug_data(adev->hp).dev; ata_acpi_uevent(dev->link->ap, dev, event); } -static const struct acpi_dock_ops ata_acpi_dev_dock_ops = { - .handler = ata_acpi_dev_notify_dock, - .uevent = ata_acpi_dev_uevent, -}; +/* bind acpi handle to pata port */ +void ata_acpi_bind_port(struct ata_port *ap) +{ + struct acpi_device *host_companion = ACPI_COMPANION(ap->host->dev); + struct acpi_device *adev; + struct ata_acpi_hotplug_context *context; -static const struct acpi_dock_ops ata_acpi_ap_dock_ops = { - .handler = ata_acpi_ap_notify_dock, - .uevent = ata_acpi_ap_uevent, -}; + if (libata_noacpi || ap->flags & ATA_FLAG_ACPI_SATA || !host_companion) + return; + + acpi_preset_companion(&ap->tdev, host_companion, ap->port_no); + + if (ata_acpi_gtm(ap, &ap->__acpi_init_gtm) == 0) + ap->pflags |= ATA_PFLAG_INIT_GTM_VALID; + + adev = ACPI_COMPANION(&ap->tdev); + if (!adev || adev->hp) + return; + + context = kzalloc(sizeof(*context), GFP_KERNEL); + if (!context) + return; + + context->data.ap = ap; + acpi_initialize_hp_context(adev, &context->hp, ata_acpi_ap_notify_dock, + ata_acpi_ap_uevent); +} + +void ata_acpi_bind_dev(struct ata_device *dev) +{ + struct ata_port *ap = dev->link->ap; + struct acpi_device *port_companion = ACPI_COMPANION(&ap->tdev); + struct acpi_device *host_companion = ACPI_COMPANION(ap->host->dev); + struct acpi_device *parent, *adev; + struct ata_acpi_hotplug_context *context; + u64 adr; + + /* + * For both sata/pata devices, host companion device is required. + * For pata device, port companion device is also required. + */ + if (libata_noacpi || !host_companion || + (!(ap->flags & ATA_FLAG_ACPI_SATA) && !port_companion)) + return; + + 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); + parent = host_companion; + } else { + adr = dev->devno; + parent = port_companion; + } + + acpi_preset_companion(&dev->tdev, parent, adr); + adev = ACPI_COMPANION(&dev->tdev); + if (!adev || adev->hp) + return; + + context = kzalloc(sizeof(*context), GFP_KERNEL); + if (!context) + return; + + context->data.dev = dev; + acpi_initialize_hp_context(adev, &context->hp, ata_acpi_dev_notify_dock, + ata_acpi_dev_uevent); +} /** * ata_acpi_dissociate - dissociate ATA host from ACPI objects @@ -234,7 +265,7 @@ void ata_acpi_dissociate(struct ata_host *host) struct ata_port *ap = host->ports[i]; const struct ata_acpi_gtm *gtm = ata_acpi_init_gtm(ap); - if (ata_ap_acpi_handle(ap) && gtm) + if (ACPI_HANDLE(&ap->tdev) && gtm) ata_acpi_stm(ap, gtm); } } @@ -258,9 +289,12 @@ int ata_acpi_gtm(struct ata_port *ap, struct ata_acpi_gtm *gtm) union acpi_object *out_obj; acpi_status status; int rc = 0; + acpi_handle handle = ACPI_HANDLE(&ap->tdev); - status = acpi_evaluate_object(ata_ap_acpi_handle(ap), "_GTM", NULL, - &output); + if (!handle) + return -EINVAL; + + status = acpi_evaluate_object(handle, "_GTM", NULL, &output); rc = -ENOENT; if (status == AE_NOT_FOUND) @@ -330,8 +364,8 @@ int ata_acpi_stm(struct ata_port *ap, const struct ata_acpi_gtm *stm) input.count = 3; input.pointer = in_params; - status = acpi_evaluate_object(ata_ap_acpi_handle(ap), "_STM", &input, - NULL); + status = acpi_evaluate_object(ACPI_HANDLE(&ap->tdev), "_STM", + &input, NULL); if (status == AE_NOT_FOUND) return -ENOENT; @@ -806,7 +840,7 @@ void ata_acpi_on_resume(struct ata_port *ap) const struct ata_acpi_gtm *gtm = ata_acpi_init_gtm(ap); struct ata_device *dev; - if (ata_ap_acpi_handle(ap) && gtm) { + if (ACPI_HANDLE(&ap->tdev) && gtm) { /* _GTM valid */ /* restore timing parameters */ @@ -819,6 +853,7 @@ void ata_acpi_on_resume(struct ata_port *ap) ata_for_each_dev(dev, &ap->link, ALL) { ata_acpi_clear_gtf(dev); if (ata_dev_enabled(dev) && + ata_dev_acpi_handle(dev) && ata_dev_get_GTF(dev, NULL) >= 0) dev->flags |= ATA_DFLAG_ACPI_PENDING; } @@ -835,50 +870,94 @@ void ata_acpi_on_resume(struct ata_port *ap) } } -/** - * ata_acpi_set_state - set the port power state - * @ap: target ATA port - * @state: state, on/off - * - * This function executes the _PS0/_PS3 ACPI method to set the power state. - * ACPI spec requires _PS0 when IDE power on and _PS3 when power off - */ -void ata_acpi_set_state(struct ata_port *ap, pm_message_t state) +static int ata_acpi_choose_suspend_state(struct ata_device *dev, bool runtime) +{ + int d_max_in = ACPI_STATE_D3_COLD; + if (!runtime) + goto out; + + /* + * For ATAPI, runtime D3 cold is only allowed + * for ZPODD in zero power ready state + */ + if (dev->class == ATA_DEV_ATAPI && + !(zpodd_dev_enabled(dev) && zpodd_zpready(dev))) + d_max_in = ACPI_STATE_D3_HOT; + +out: + return acpi_pm_device_sleep_state(&dev->tdev, NULL, d_max_in); +} + +static void sata_acpi_set_state(struct ata_port *ap, pm_message_t state) { + bool runtime = PMSG_IS_AUTO(state); struct ata_device *dev; acpi_handle handle; int acpi_state; - /* channel first and then drives for power on and vica versa - for power off */ - handle = ata_ap_acpi_handle(ap); - if (handle && state.event == PM_EVENT_ON) - acpi_bus_set_power(handle, ACPI_STATE_D0); - ata_for_each_dev(dev, &ap->link, ENABLED) { handle = ata_dev_acpi_handle(dev); if (!handle) continue; - if (state.event != PM_EVENT_ON) { - acpi_state = acpi_pm_device_sleep_state( - &dev->sdev->sdev_gendev, NULL, ACPI_STATE_D3); - if (acpi_state > 0) - acpi_bus_set_power(handle, acpi_state); - /* TBD: need to check if it's runtime pm request */ - acpi_pm_device_run_wake( - &dev->sdev->sdev_gendev, true); + if (!(state.event & PM_EVENT_RESUME)) { + acpi_state = ata_acpi_choose_suspend_state(dev, runtime); + if (acpi_state == ACPI_STATE_D0) + continue; + if (runtime && zpodd_dev_enabled(dev) && + acpi_state == ACPI_STATE_D3_COLD) + zpodd_enable_run_wake(dev); + acpi_bus_set_power(handle, acpi_state); } else { - /* Ditto */ - acpi_pm_device_run_wake( - &dev->sdev->sdev_gendev, false); + if (runtime && zpodd_dev_enabled(dev)) + zpodd_disable_run_wake(dev); acpi_bus_set_power(handle, ACPI_STATE_D0); } } +} - handle = ata_ap_acpi_handle(ap); - if (handle && state.event != PM_EVENT_ON) - acpi_bus_set_power(handle, ACPI_STATE_D3); +/* ACPI spec requires _PS0 when IDE power on and _PS3 when power off */ +static void pata_acpi_set_state(struct ata_port *ap, pm_message_t state) +{ + struct ata_device *dev; + acpi_handle port_handle; + + port_handle = ACPI_HANDLE(&ap->tdev); + if (!port_handle) + return; + + /* channel first and then drives for power on and vica versa + for power off */ + if (state.event & PM_EVENT_RESUME) + acpi_bus_set_power(port_handle, ACPI_STATE_D0); + + ata_for_each_dev(dev, &ap->link, ENABLED) { + acpi_handle dev_handle = ata_dev_acpi_handle(dev); + if (!dev_handle) + continue; + + acpi_bus_set_power(dev_handle, state.event & PM_EVENT_RESUME ? + ACPI_STATE_D0 : ACPI_STATE_D3_COLD); + } + + if (!(state.event & PM_EVENT_RESUME)) + acpi_bus_set_power(port_handle, ACPI_STATE_D3_COLD); +} + +/** + * ata_acpi_set_state - set the port power state + * @ap: target ATA port + * @state: state, on/off + * + * This function sets a proper ACPI D state for the device on + * system and runtime PM operations. + */ +void ata_acpi_set_state(struct ata_port *ap, pm_message_t state) +{ + if (ap->flags & ATA_FLAG_ACPI_SATA) + sata_acpi_set_state(ap, state); + else + pata_acpi_set_state(ap, state); } /** @@ -973,226 +1052,3 @@ void ata_acpi_on_disable(struct ata_device *dev) { ata_acpi_clear_gtf(dev); } - -static void ata_acpi_wake_dev(acpi_handle handle, u32 event, void *context) -{ - struct ata_device *ata_dev = context; - - if (event == ACPI_NOTIFY_DEVICE_WAKE && ata_dev && - pm_runtime_suspended(&ata_dev->sdev->sdev_gendev)) - scsi_autopm_get_device(ata_dev->sdev); -} - -static void ata_acpi_add_pm_notifier(struct ata_device *dev) -{ - struct acpi_device *acpi_dev; - acpi_handle handle; - acpi_status status; - - handle = ata_dev_acpi_handle(dev); - if (!handle) - return; - - status = acpi_bus_get_device(handle, &acpi_dev); - if (ACPI_FAILURE(status)) - return; - - if (dev->sdev->can_power_off) { - acpi_install_notify_handler(handle, ACPI_SYSTEM_NOTIFY, - ata_acpi_wake_dev, dev); - device_set_run_wake(&dev->sdev->sdev_gendev, true); - } -} - -static void ata_acpi_remove_pm_notifier(struct ata_device *dev) -{ - struct acpi_device *acpi_dev; - acpi_handle handle; - acpi_status status; - - handle = ata_dev_acpi_handle(dev); - if (!handle) - return; - - status = acpi_bus_get_device(handle, &acpi_dev); - if (ACPI_FAILURE(status)) - return; - - if (dev->sdev->can_power_off) { - device_set_run_wake(&dev->sdev->sdev_gendev, false); - acpi_remove_notify_handler(handle, ACPI_SYSTEM_NOTIFY, - ata_acpi_wake_dev); - } -} - -static void ata_acpi_register_power_resource(struct ata_device *dev) -{ - struct scsi_device *sdev = dev->sdev; - acpi_handle handle; - struct device *device; - - handle = ata_dev_acpi_handle(dev); - if (!handle) - return; - - device = &sdev->sdev_gendev; - - acpi_power_resource_register_device(device, handle); -} - -static void ata_acpi_unregister_power_resource(struct ata_device *dev) -{ - struct scsi_device *sdev = dev->sdev; - acpi_handle handle; - struct device *device; - - handle = ata_dev_acpi_handle(dev); - if (!handle) - return; - - device = &sdev->sdev_gendev; - - acpi_power_resource_unregister_device(device, handle); -} - -void ata_acpi_bind(struct ata_device *dev) -{ - ata_acpi_add_pm_notifier(dev); - ata_acpi_register_power_resource(dev); -} - -void ata_acpi_unbind(struct ata_device *dev) -{ - ata_acpi_remove_pm_notifier(dev); - ata_acpi_unregister_power_resource(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; - - if (ata_acpi_gtm(ap, &ap->__acpi_init_gtm) == 0) - ap->pflags |= ATA_PFLAG_INIT_GTM_VALID; - - return 0; -} - -static int ata_acpi_bind_device(struct ata_port *ap, struct scsi_device *sdev, - acpi_handle *handle) -{ - struct ata_device *ata_dev; - acpi_status status; - struct acpi_device *acpi_dev; - struct acpi_device_power_state *states; - - if (ap->flags & ATA_FLAG_ACPI_SATA) { - if (!sata_pmp_attached(ap)) - ata_dev = &ap->link.device[sdev->id]; - else - ata_dev = &ap->pmp_link[sdev->channel].device[sdev->id]; - } - else { - ata_dev = &ap->link.device[sdev->id]; - } - - *handle = ata_dev_acpi_handle(ata_dev); - - if (!*handle) - return -ENODEV; - - status = acpi_bus_get_device(*handle, &acpi_dev); - if (ACPI_FAILURE(status)) - return 0; - - /* - * If firmware has _PS3 or _PR3 for this device, - * and this ata ODD device support device attention, - * it means this device can be powered off - */ - states = acpi_dev->power.states; - if ((states[ACPI_STATE_D3_HOT].flags.valid || - states[ACPI_STATE_D3_COLD].flags.explicit_set) && - ata_dev->flags & ATA_DFLAG_DA) - sdev->can_power_off = 1; - - 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); -} |
