diff options
Diffstat (limited to 'drivers/ata/libata-acpi.c')
| -rw-r--r-- | drivers/ata/libata-acpi.c | 393 |
1 files changed, 208 insertions, 185 deletions
diff --git a/drivers/ata/libata-acpi.c b/drivers/ata/libata-acpi.c index 8b5ea399a4f..97a14fe47de 100644 --- a/drivers/ata/libata-acpi.c +++ b/drivers/ata/libata-acpi.c @@ -16,11 +16,10 @@ #include <linux/libata.h> #include <linux/pci.h> #include <linux/slab.h> +#include <linux/pm_runtime.h> #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)"); @@ -33,76 +32,34 @@ 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; } +struct ata_acpi_hotplug_context { + struct acpi_hotplug_context hp; + union { + struct ata_port *ap; + struct ata_device *dev; + } data; +}; + +#define ata_hotplug_data(context) (container_of((context), struct ata_acpi_hotplug_context, hp)->data) + /** - * ata_acpi_associate_sata_port - associate SATA port with ACPI objects - * @ap: target SATA port + * ata_dev_acpi_handle - provide the acpi_handle for an ata_device + * @dev: the acpi_handle returned will correspond to this device * - * 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. + * 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. */ -void ata_acpi_associate_sata_port(struct ata_port *ap) -{ - WARN_ON(!(ap->flags & ATA_FLAG_ACPI_SATA)); - - if (!sata_pmp_attached(ap)) { - u64 adr = SATA_ADR(ap->port_no, NO_PORT_MULT); - - 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_for_each_link(link, ap, EDGE) { - u64 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) +acpi_handle ata_dev_acpi_handle(struct ata_device *dev) { - int max_devices, i; - - ap->acpi_handle = acpi_get_child(ap->host->acpi_handle, ap->port_no); - if (!ap->acpi_handle) - return; - - max_devices = 1; - if (ap->flags & ATA_FLAG_SLAVE_POSS) - max_devices++; - - for (i = 0; i < max_devices; i++) { - struct ata_device *dev = &ap->link.device[i]; - - dev->acpi_handle = acpi_get_child(ap->acpi_handle, i); - } - - if (ata_acpi_gtm(ap, &ap->__acpi_init_gtm) == 0) - ap->pflags |= ATA_PFLAG_INIT_GTM_VALID; + return dev->flags & ATA_DFLAG_ACPI_DISABLED ? + NULL : ACPI_HANDLE(&dev->tdev); } /* @ap and @dev are the same as ata_acpi_handle_hotplug() */ @@ -174,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, @@ -207,75 +163,85 @@ 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 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 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; -/** - * ata_acpi_associate - associate ATA host with ACPI objects - * @host: target ATA host - * - * Look up ACPI objects associated with @host and initialize - * acpi_handle fields of @host, its ports and devices accordingly. - * - * LOCKING: - * EH context. - * - * RETURNS: - * 0 on success, -errno on failure. - */ -void ata_acpi_associate(struct ata_host *host) -{ - int i, j; + acpi_preset_companion(&ap->tdev, host_companion, ap->port_no); - if (!is_pci_dev(host->dev) || libata_noacpi) + 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; - host->acpi_handle = DEVICE_ACPI_HANDLE(host->dev); - if (!host->acpi_handle) + context = kzalloc(sizeof(*context), GFP_KERNEL); + if (!context) return; - for (i = 0; i < host->n_ports; i++) { - struct ata_port *ap = host->ports[i]; + 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; - if (host->ports[0]->flags & ATA_FLAG_ACPI_SATA) - ata_acpi_associate_sata_port(ap); + /* + * 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 - ata_acpi_associate_ide_port(ap); + adr = SATA_ADR(ap->port_no, dev->link->pmp); + parent = host_companion; + } else { + adr = dev->devno; + parent = port_companion; + } - if (ap->acpi_handle) { - /* we might be on a docking station */ - register_hotplug_dock_device(ap->acpi_handle, - &ata_acpi_ap_dock_ops, ap); - } + acpi_preset_companion(&dev->tdev, parent, adr); + adev = ACPI_COMPANION(&dev->tdev); + if (!adev || adev->hp) + return; - for (j = 0; j < ata_link_max_devices(&ap->link); j++) { - struct ata_device *dev = &ap->link.device[j]; + context = kzalloc(sizeof(*context), GFP_KERNEL); + if (!context) + return; - if (dev->acpi_handle) { - /* we might be on a docking station */ - register_hotplug_dock_device(dev->acpi_handle, - &ata_acpi_dev_dock_ops, dev); - } - } - } + context->data.dev = dev; + acpi_initialize_hp_context(adev, &context->hp, ata_acpi_dev_notify_dock, + ata_acpi_dev_uevent); } /** @@ -299,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 (ap->acpi_handle && gtm) + if (ACPI_HANDLE(&ap->tdev) && gtm) ata_acpi_stm(ap, gtm); } } @@ -323,8 +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(ap->acpi_handle, "_GTM", NULL, &output); + if (!handle) + return -EINVAL; + + status = acpi_evaluate_object(handle, "_GTM", NULL, &output); rc = -ENOENT; if (status == AE_NOT_FOUND) @@ -332,25 +302,22 @@ int ata_acpi_gtm(struct ata_port *ap, struct ata_acpi_gtm *gtm) rc = -EINVAL; if (ACPI_FAILURE(status)) { - ata_port_printk(ap, KERN_ERR, - "ACPI get timing mode failed (AE 0x%x)\n", - status); + ata_port_err(ap, "ACPI get timing mode failed (AE 0x%x)\n", + status); goto out_free; } out_obj = output.pointer; if (out_obj->type != ACPI_TYPE_BUFFER) { - ata_port_printk(ap, KERN_WARNING, - "_GTM returned unexpected object type 0x%x\n", - out_obj->type); + ata_port_warn(ap, "_GTM returned unexpected object type 0x%x\n", + out_obj->type); goto out_free; } if (out_obj->buffer.length != sizeof(struct ata_acpi_gtm)) { - ata_port_printk(ap, KERN_ERR, - "_GTM returned invalid length %d\n", - out_obj->buffer.length); + ata_port_err(ap, "_GTM returned invalid length %d\n", + out_obj->buffer.length); goto out_free; } @@ -397,13 +364,14 @@ 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(ap->acpi_handle, "_STM", &input, NULL); + status = acpi_evaluate_object(ACPI_HANDLE(&ap->tdev), "_STM", + &input, NULL); if (status == AE_NOT_FOUND) return -ENOENT; if (ACPI_FAILURE(status)) { - ata_port_printk(ap, KERN_ERR, - "ACPI set timing mode failed (status=0x%x)\n", status); + ata_port_err(ap, "ACPI set timing mode failed (status=0x%x)\n", + status); return -EINVAL; } return 0; @@ -450,18 +418,18 @@ static int ata_dev_get_GTF(struct ata_device *dev, struct ata_acpi_gtf **gtf) output.pointer = NULL; /* ACPI-CA sets this; save/free it later */ if (ata_msg_probe(ap)) - ata_dev_printk(dev, KERN_DEBUG, "%s: ENTER: port#: %d\n", - __func__, ap->port_no); + ata_dev_dbg(dev, "%s: ENTER: port#: %d\n", + __func__, ap->port_no); /* _GTF has no input parameters */ - status = acpi_evaluate_object(dev->acpi_handle, "_GTF", NULL, &output); + status = acpi_evaluate_object(ata_dev_acpi_handle(dev), "_GTF", NULL, + &output); out_obj = dev->gtf_cache = output.pointer; if (ACPI_FAILURE(status)) { if (status != AE_NOT_FOUND) { - ata_dev_printk(dev, KERN_WARNING, - "_GTF evaluation failed (AE 0x%x)\n", - status); + ata_dev_warn(dev, "_GTF evaluation failed (AE 0x%x)\n", + status); rc = -EINVAL; } goto out_free; @@ -469,27 +437,24 @@ static int ata_dev_get_GTF(struct ata_device *dev, struct ata_acpi_gtf **gtf) if (!output.length || !output.pointer) { if (ata_msg_probe(ap)) - ata_dev_printk(dev, KERN_DEBUG, "%s: Run _GTF: " - "length or ptr is NULL (0x%llx, 0x%p)\n", - __func__, - (unsigned long long)output.length, - output.pointer); + ata_dev_dbg(dev, "%s: Run _GTF: length or ptr is NULL (0x%llx, 0x%p)\n", + __func__, + (unsigned long long)output.length, + output.pointer); rc = -EINVAL; goto out_free; } if (out_obj->type != ACPI_TYPE_BUFFER) { - ata_dev_printk(dev, KERN_WARNING, - "_GTF unexpected object type 0x%x\n", - out_obj->type); + ata_dev_warn(dev, "_GTF unexpected object type 0x%x\n", + out_obj->type); rc = -EINVAL; goto out_free; } if (out_obj->buffer.length % REGS_PER_GTF) { - ata_dev_printk(dev, KERN_WARNING, - "unexpected _GTF length (%d)\n", - out_obj->buffer.length); + ata_dev_warn(dev, "unexpected _GTF length (%d)\n", + out_obj->buffer.length); rc = -EINVAL; goto out_free; } @@ -499,9 +464,8 @@ static int ata_dev_get_GTF(struct ata_device *dev, struct ata_acpi_gtf **gtf) if (gtf) { *gtf = (void *)out_obj->buffer.pointer; if (ata_msg_probe(ap)) - ata_dev_printk(dev, KERN_DEBUG, - "%s: returning gtf=%p, gtf_count=%d\n", - __func__, *gtf, rc); + ata_dev_dbg(dev, "%s: returning gtf=%p, gtf_count=%d\n", + __func__, *gtf, rc); } return rc; @@ -660,8 +624,7 @@ static int ata_acpi_filter_tf(struct ata_device *dev, * @dev: target ATA device * @gtf: raw ATA taskfile register set (0x1f1 - 0x1f7) * - * Outputs ATA taskfile to standard ATA host controller using MMIO - * or PIO as indicated by the ATA_FLAG_MMIO flag. + * Outputs ATA taskfile to standard ATA host controller. * Writes the control, feature, nsect, lbal, lbam, and lbah registers. * Optionally (ATA_TFLAG_LBA48) writes hob_feature, hob_nsect, * hob_lbal, hob_lbam, and hob_lbah. @@ -812,8 +775,8 @@ static int ata_acpi_push_id(struct ata_device *dev) union acpi_object in_params[1]; if (ata_msg_probe(ap)) - ata_dev_printk(dev, KERN_DEBUG, "%s: ix = %d, port#: %d\n", - __func__, dev->devno, ap->port_no); + ata_dev_dbg(dev, "%s: ix = %d, port#: %d\n", + __func__, dev->devno, ap->port_no); /* Give the drive Identify data to the drive via the _SDD method */ /* _SDD: set up input parameters */ @@ -826,15 +789,15 @@ static int ata_acpi_push_id(struct ata_device *dev) /* It's OK for _SDD to be missing too. */ swap_buf_le16(dev->id, ATA_ID_WORDS); - status = acpi_evaluate_object(dev->acpi_handle, "_SDD", &input, NULL); + status = acpi_evaluate_object(ata_dev_acpi_handle(dev), "_SDD", &input, + NULL); swap_buf_le16(dev->id, ATA_ID_WORDS); if (status == AE_NOT_FOUND) return -ENOENT; if (ACPI_FAILURE(status)) { - ata_dev_printk(dev, KERN_WARNING, - "ACPI _SDD failed (AE 0x%x)\n", status); + ata_dev_warn(dev, "ACPI _SDD failed (AE 0x%x)\n", status); return -EIO; } @@ -877,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 (ap->acpi_handle && gtm) { + if (ACPI_HANDLE(&ap->tdev) && gtm) { /* _GTM valid */ /* restore timing parameters */ @@ -890,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; } @@ -906,34 +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; + + ata_for_each_dev(dev, &ap->link, ENABLED) { + handle = ata_dev_acpi_handle(dev); + if (!handle) + continue; + + 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 { + if (runtime && zpodd_dev_enabled(dev)) + zpodd_disable_run_wake(dev); + acpi_bus_set_power(handle, ACPI_STATE_D0); + } + } +} + +/* 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; - if (!ap->acpi_handle || (ap->flags & ATA_FLAG_ACPI_SATA)) + 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_ON) - acpi_bus_set_power(ap->acpi_handle, ACPI_STATE_D0); + if (state.event & PM_EVENT_RESUME) + acpi_bus_set_power(port_handle, ACPI_STATE_D0); ata_for_each_dev(dev, &ap->link, ENABLED) { - if (dev->acpi_handle) - acpi_bus_set_power(dev->acpi_handle, - state.event == PM_EVENT_ON ? - ACPI_STATE_D0 : ACPI_STATE_D3); + 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_ON) - acpi_bus_set_power(ap->acpi_handle, ACPI_STATE_D3); + + 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); } /** @@ -958,7 +982,7 @@ int ata_acpi_on_devcfg(struct ata_device *dev) int nr_executed = 0; int rc; - if (!dev->acpi_handle) + if (!ata_dev_acpi_handle(dev)) return 0; /* do we need to do _GTF? */ @@ -984,8 +1008,8 @@ int ata_acpi_on_devcfg(struct ata_device *dev) if (nr_executed) { rc = ata_dev_reread_id(dev, 0); if (rc < 0) { - ata_dev_printk(dev, KERN_ERR, "failed to IDENTIFY " - "after ACPI commands\n"); + ata_dev_err(dev, + "failed to IDENTIFY after ACPI commands\n"); return rc; } } @@ -1003,9 +1027,8 @@ int ata_acpi_on_devcfg(struct ata_device *dev) return rc; } - ata_dev_printk(dev, KERN_WARNING, - "ACPI: failed the second time, disabled\n"); - dev->acpi_handle = NULL; + dev->flags |= ATA_DFLAG_ACPI_DISABLED; + ata_dev_warn(dev, "ACPI: failed the second time, disabled\n"); /* We can safely continue if no _GTF command has been executed * and port is not frozen. |
