lkml.org 
[lkml]   [2010]   [Aug]   [5]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
    Patch in this message
    /
    Date
    From
    SubjectRe: [PATCH] SATA / AHCI: Do not play with the link PM during suspend to RAM
    Hello, Rafael.

    Can you please try the following patch and see whether the problem
    goes away?

    Thanks.

    drivers/ata/ahci.c | 3
    drivers/ata/ahci.h | 1
    drivers/ata/ahci_platform.c | 3
    drivers/ata/ata_piix.c | 24 +++
    drivers/ata/libahci.c | 161 +++++++-------------------
    drivers/ata/libata-core.c | 269 ++++++++++----------------------------------
    drivers/ata/libata-eh.c | 176 +++++++++++++++++++++++++---
    drivers/ata/libata-pmp.c | 49 +++++++-
    drivers/ata/libata-scsi.c | 74 ++++--------
    drivers/ata/libata.h | 12 +
    include/linux/libata.h | 40 +++---
    11 files changed, 393 insertions(+), 419 deletions(-)

    diff --git a/drivers/ata/ahci.c b/drivers/ata/ahci.c
    index f252253..cfdc22b 100644
    --- a/drivers/ata/ahci.c
    +++ b/drivers/ata/ahci.c
    @@ -1190,9 +1190,6 @@ static int ahci_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
    ata_port_pbar_desc(ap, AHCI_PCI_BAR,
    0x100 + ap->port_no * 0x80, "port");

    - /* set initial link pm policy */
    - ap->pm_policy = NOT_AVAILABLE;
    -
    /* set enclosure management message type */
    if (ap->flags & ATA_FLAG_EM)
    ap->em_message_type = hpriv->em_msg_type;
    diff --git a/drivers/ata/ahci.h b/drivers/ata/ahci.h
    index 7113c57..6d07948 100644
    --- a/drivers/ata/ahci.h
    +++ b/drivers/ata/ahci.h
    @@ -201,7 +201,6 @@ enum {
    AHCI_HFLAG_MV_PATA = (1 << 4), /* PATA port */
    AHCI_HFLAG_NO_MSI = (1 << 5), /* no PCI MSI */
    AHCI_HFLAG_NO_PMP = (1 << 6), /* no PMP */
    - AHCI_HFLAG_NO_HOTPLUG = (1 << 7), /* ignore PxSERR.DIAG.N */
    AHCI_HFLAG_SECT255 = (1 << 8), /* max 255 sectors */
    AHCI_HFLAG_YES_NCQ = (1 << 9), /* force NCQ cap on */
    AHCI_HFLAG_NO_SUSPEND = (1 << 10), /* don't suspend */
    diff --git a/drivers/ata/ahci_platform.c b/drivers/ata/ahci_platform.c
    index 5e11b16..0f69afe 100644
    --- a/drivers/ata/ahci_platform.c
    +++ b/drivers/ata/ahci_platform.c
    @@ -120,9 +120,6 @@ static int __init ahci_probe(struct platform_device *pdev)
    ata_port_desc(ap, "mmio %pR", mem);
    ata_port_desc(ap, "port 0x%x", 0x100 + ap->port_no * 0x80);

    - /* set initial link pm policy */
    - ap->pm_policy = NOT_AVAILABLE;
    -
    /* set enclosure management message type */
    if (ap->flags & ATA_FLAG_EM)
    ap->em_message_type = hpriv->em_msg_type;
    diff --git a/drivers/ata/ata_piix.c b/drivers/ata/ata_piix.c
    index 7409f98..0df0477 100644
    --- a/drivers/ata/ata_piix.c
    +++ b/drivers/ata/ata_piix.c
    @@ -174,6 +174,8 @@ static int piix_sidpr_scr_read(struct ata_link *link,
    unsigned int reg, u32 *val);
    static int piix_sidpr_scr_write(struct ata_link *link,
    unsigned int reg, u32 val);
    +static int piix_sidpr_set_ipm(struct ata_link *link, enum ata_ipm_policy policy,
    + unsigned hints);
    static bool piix_irq_check(struct ata_port *ap);
    #ifdef CONFIG_PM
    static int piix_pci_device_suspend(struct pci_dev *pdev, pm_message_t mesg);
    @@ -343,11 +345,22 @@ static struct ata_port_operations ich_pata_ops = {
    .set_dmamode = ich_set_dmamode,
    };

    +static struct device_attribute *piix_sidpr_shost_attrs[] = {
    + &dev_attr_link_power_management_policy,
    + NULL
    +};
    +
    +static struct scsi_host_template piix_sidpr_sht = {
    + ATA_BMDMA_SHT(DRV_NAME),
    + .shost_attrs = piix_sidpr_shost_attrs,
    +};
    +
    static struct ata_port_operations piix_sidpr_sata_ops = {
    .inherits = &piix_sata_ops,
    .hardreset = sata_std_hardreset,
    .scr_read = piix_sidpr_scr_read,
    .scr_write = piix_sidpr_scr_write,
    + .set_ipm = piix_sidpr_set_ipm,
    };

    static const struct piix_map_db ich5_map_db = {
    @@ -973,6 +986,12 @@ static int piix_sidpr_scr_write(struct ata_link *link,
    return 0;
    }

    +static int piix_sidpr_set_ipm(struct ata_link *link, enum ata_ipm_policy policy,
    + unsigned hints)
    +{
    + return sata_link_scr_ipm(link, policy, false);
    +}
    +
    static bool piix_irq_check(struct ata_port *ap)
    {
    if (unlikely(!ap->ioaddr.bmdma_addr))
    @@ -1532,6 +1551,7 @@ static int __devinit piix_init_one(struct pci_dev *pdev,
    struct device *dev = &pdev->dev;
    struct ata_port_info port_info[2];
    const struct ata_port_info *ppi[] = { &port_info[0], &port_info[1] };
    + struct scsi_host_template *sht = &piix_sht;
    unsigned long port_flags;
    struct ata_host *host;
    struct piix_host_priv *hpriv;
    @@ -1600,6 +1620,8 @@ static int __devinit piix_init_one(struct pci_dev *pdev,
    rc = piix_init_sidpr(host);
    if (rc)
    return rc;
    + if (host->ports[0]->ops == &piix_sidpr_sata_ops)
    + sht = &piix_sidpr_sht;
    }

    /* apply IOCFG bit18 quirk */
    @@ -1626,7 +1648,7 @@ static int __devinit piix_init_one(struct pci_dev *pdev,
    host->flags |= ATA_HOST_PARALLEL_SCAN;

    pci_set_master(pdev);
    - return ata_pci_sff_activate_host(host, ata_bmdma_interrupt, &piix_sht);
    + return ata_pci_sff_activate_host(host, ata_bmdma_interrupt, sht);
    }

    static void piix_remove_one(struct pci_dev *pdev)
    diff --git a/drivers/ata/libahci.c b/drivers/ata/libahci.c
    index 81e772a..2c5f3df 100644
    --- a/drivers/ata/libahci.c
    +++ b/drivers/ata/libahci.c
    @@ -56,9 +56,8 @@ MODULE_PARM_DESC(skip_host_reset, "skip global host reset (0=don't skip, 1=skip)
    module_param_named(ignore_sss, ahci_ignore_sss, int, 0444);
    MODULE_PARM_DESC(ignore_sss, "Ignore staggered spinup flag (0=don't ignore, 1=ignore)");

    -static int ahci_enable_alpm(struct ata_port *ap,
    - enum link_pm policy);
    -static void ahci_disable_alpm(struct ata_port *ap);
    +static int ahci_set_ipm(struct ata_link *link, enum ata_ipm_policy policy,
    + unsigned hints);
    static ssize_t ahci_led_show(struct ata_port *ap, char *buf);
    static ssize_t ahci_led_store(struct ata_port *ap, const char *buf,
    size_t size);
    @@ -172,8 +171,7 @@ struct ata_port_operations ahci_ops = {
    .pmp_attach = ahci_pmp_attach,
    .pmp_detach = ahci_pmp_detach,

    - .enable_pm = ahci_enable_alpm,
    - .disable_pm = ahci_disable_alpm,
    + .set_ipm = ahci_set_ipm,
    .em_show = ahci_led_show,
    .em_store = ahci_led_store,
    .sw_activity_show = ahci_activity_show,
    @@ -644,127 +642,59 @@ static void ahci_power_up(struct ata_port *ap)
    writel(cmd | PORT_CMD_ICC_ACTIVE, port_mmio + PORT_CMD);
    }

    -static void ahci_disable_alpm(struct ata_port *ap)
    +static int ahci_set_ipm(struct ata_link *link, enum ata_ipm_policy policy,
    + unsigned int hints)
    {
    + struct ata_port *ap = link->ap;
    struct ahci_host_priv *hpriv = ap->host->private_data;
    - void __iomem *port_mmio = ahci_port_base(ap);
    - u32 cmd;
    struct ahci_port_priv *pp = ap->private_data;
    -
    - /* IPM bits should be disabled by libata-core */
    - /* get the existing command bits */
    - cmd = readl(port_mmio + PORT_CMD);
    -
    - /* disable ALPM and ASP */
    - cmd &= ~PORT_CMD_ASP;
    - cmd &= ~PORT_CMD_ALPE;
    -
    - /* force the interface back to active */
    - cmd |= PORT_CMD_ICC_ACTIVE;
    -
    - /* write out new cmd value */
    - writel(cmd, port_mmio + PORT_CMD);
    - cmd = readl(port_mmio + PORT_CMD);
    -
    - /* wait 10ms to be sure we've come out of any low power state */
    - msleep(10);
    -
    - /* clear out any PhyRdy stuff from interrupt status */
    - writel(PORT_IRQ_PHYRDY, port_mmio + PORT_IRQ_STAT);
    -
    - /* go ahead and clean out PhyRdy Change from Serror too */
    - ahci_scr_write(&ap->link, SCR_ERROR, ((1 << 16) | (1 << 18)));
    -
    - /*
    - * Clear flag to indicate that we should ignore all PhyRdy
    - * state changes
    - */
    - hpriv->flags &= ~AHCI_HFLAG_NO_HOTPLUG;
    -
    - /*
    - * Enable interrupts on Phy Ready.
    - */
    - pp->intr_mask |= PORT_IRQ_PHYRDY;
    - writel(pp->intr_mask, port_mmio + PORT_IRQ_MASK);
    -
    - /*
    - * don't change the link pm policy - we can be called
    - * just to turn of link pm temporarily
    - */
    -}
    -
    -static int ahci_enable_alpm(struct ata_port *ap,
    - enum link_pm policy)
    -{
    - struct ahci_host_priv *hpriv = ap->host->private_data;
    void __iomem *port_mmio = ahci_port_base(ap);
    - u32 cmd;
    - struct ahci_port_priv *pp = ap->private_data;
    - u32 asp;

    - /* Make sure the host is capable of link power management */
    - if (!(hpriv->cap & HOST_CAP_ALPM))
    - return -EINVAL;
    + ata_link_printk(link, KERN_INFO, "XXX ahci_set_ipm: pol=%d hints=%x\n",
    + policy, hints);

    - switch (policy) {
    - case MAX_PERFORMANCE:
    - case NOT_AVAILABLE:
    + if (policy != ATA_IPM_MAX_POWER) {
    /*
    - * if we came here with NOT_AVAILABLE,
    - * it just means this is the first time we
    - * have tried to enable - default to max performance,
    - * and let the user go to lower power modes on request.
    + * Disable interrupts on Phy Ready. This keeps us from
    + * getting woken up due to spurious phy ready
    + * interrupts.
    */
    - ahci_disable_alpm(ap);
    - return 0;
    - case MIN_POWER:
    - /* configure HBA to enter SLUMBER */
    - asp = PORT_CMD_ASP;
    - break;
    - case MEDIUM_POWER:
    - /* configure HBA to enter PARTIAL */
    - asp = 0;
    - break;
    - default:
    - return -EINVAL;
    + pp->intr_mask &= ~PORT_IRQ_PHYRDY;
    + writel(pp->intr_mask, port_mmio + PORT_IRQ_MASK);
    +
    + sata_link_scr_ipm(link, policy, false);
    }

    - /*
    - * Disable interrupts on Phy Ready. This keeps us from
    - * getting woken up due to spurious phy ready interrupts
    - * TBD - Hot plug should be done via polling now, is
    - * that even supported?
    - */
    - pp->intr_mask &= ~PORT_IRQ_PHYRDY;
    - writel(pp->intr_mask, port_mmio + PORT_IRQ_MASK);
    + if (hpriv->cap & HOST_CAP_ALPM) {
    + u32 cmd = readl(port_mmio + PORT_CMD);

    - /*
    - * Set a flag to indicate that we should ignore all PhyRdy
    - * state changes since these can happen now whenever we
    - * change link state
    - */
    - hpriv->flags |= AHCI_HFLAG_NO_HOTPLUG;
    + if (policy == ATA_IPM_MAX_POWER || !(hints & ATA_IPM_HIPM)) {
    + cmd &= ~(PORT_CMD_ASP | PORT_CMD_ALPE);
    + cmd |= PORT_CMD_ICC_ACTIVE;

    - /* get the existing command bits */
    - cmd = readl(port_mmio + PORT_CMD);
    + writel(cmd, port_mmio + PORT_CMD);
    + readl(port_mmio + PORT_CMD);

    - /*
    - * Set ASP based on Policy
    - */
    - cmd |= asp;
    + /* wait 10ms to be sure we've come out of IPM state */
    + msleep(10);
    + } else {
    + cmd |= PORT_CMD_ALPE;
    + if (policy == ATA_IPM_MIN_POWER)
    + cmd |= PORT_CMD_ASP;

    - /*
    - * Setting this bit will instruct the HBA to aggressively
    - * enter a lower power link state when it's appropriate and
    - * based on the value set above for ASP
    - */
    - cmd |= PORT_CMD_ALPE;
    + /* write out new cmd value */
    + writel(cmd, port_mmio + PORT_CMD);
    + }
    + }

    - /* write out new cmd value */
    - writel(cmd, port_mmio + PORT_CMD);
    - cmd = readl(port_mmio + PORT_CMD);
    + if (policy == ATA_IPM_MAX_POWER) {
    + sata_link_scr_ipm(link, policy, false);
    +
    + /* turn PHYRDY IRQ back on */
    + pp->intr_mask |= PORT_IRQ_PHYRDY;
    + writel(pp->intr_mask, port_mmio + PORT_IRQ_MASK);
    + }

    - /* IPM bits should be set by libata-core */
    return 0;
    }

    @@ -1662,15 +1592,10 @@ static void ahci_port_intr(struct ata_port *ap)
    if (unlikely(resetting))
    status &= ~PORT_IRQ_BAD_PMP;

    - /* If we are getting PhyRdy, this is
    - * just a power state change, we should
    - * clear out this, plus the PhyRdy/Comm
    - * Wake bits from Serror
    - */
    - if ((hpriv->flags & AHCI_HFLAG_NO_HOTPLUG) &&
    - (status & PORT_IRQ_PHYRDY)) {
    + /* if IPM is enabled, PHYRDY doesn't mean anything */
    + if (ap->link.ipm_policy > ATA_IPM_MAX_POWER) {
    status &= ~PORT_IRQ_PHYRDY;
    - ahci_scr_write(&ap->link, SCR_ERROR, ((1 << 16) | (1 << 18)));
    + ahci_scr_write(&ap->link, SCR_ERROR, SERR_PHYRDY_CHG);
    }

    if (unlikely(status & PORT_IRQ_ERROR)) {
    diff --git a/drivers/ata/libata-core.c b/drivers/ata/libata-core.c
    index ddf8e48..5d1eeb1 100644
    --- a/drivers/ata/libata-core.c
    +++ b/drivers/ata/libata-core.c
    @@ -91,8 +91,6 @@ const struct ata_port_operations sata_port_ops = {
    static unsigned int ata_dev_init_params(struct ata_device *dev,
    u16 heads, u16 sectors);
    static unsigned int ata_dev_set_xfermode(struct ata_device *dev);
    -static unsigned int ata_dev_set_feature(struct ata_device *dev,
    - u8 enable, u8 feature);
    static void ata_dev_xfermask(struct ata_device *dev);
    static unsigned long ata_dev_blacklisted(const struct ata_device *dev);

    @@ -1032,182 +1030,6 @@ static const char *sata_spd_string(unsigned int spd)
    return spd_str[spd - 1];
    }

    -static int ata_dev_set_dipm(struct ata_device *dev, enum link_pm policy)
    -{
    - struct ata_link *link = dev->link;
    - struct ata_port *ap = link->ap;
    - u32 scontrol;
    - unsigned int err_mask;
    - int rc;
    -
    - /*
    - * disallow DIPM for drivers which haven't set
    - * ATA_FLAG_IPM. This is because when DIPM is enabled,
    - * phy ready will be set in the interrupt status on
    - * state changes, which will cause some drivers to
    - * think there are errors - additionally drivers will
    - * need to disable hot plug.
    - */
    - if (!(ap->flags & ATA_FLAG_IPM) || !ata_dev_enabled(dev)) {
    - ap->pm_policy = NOT_AVAILABLE;
    - return -EINVAL;
    - }
    -
    - /*
    - * For DIPM, we will only enable it for the
    - * min_power setting.
    - *
    - * Why? Because Disks are too stupid to know that
    - * If the host rejects a request to go to SLUMBER
    - * they should retry at PARTIAL, and instead it
    - * just would give up. So, for medium_power to
    - * work at all, we need to only allow HIPM.
    - */
    - rc = sata_scr_read(link, SCR_CONTROL, &scontrol);
    - if (rc)
    - return rc;
    -
    - switch (policy) {
    - case MIN_POWER:
    - /* no restrictions on IPM transitions */
    - scontrol &= ~(0x3 << 8);
    - rc = sata_scr_write(link, SCR_CONTROL, scontrol);
    - if (rc)
    - return rc;
    -
    - /* enable DIPM */
    - if (dev->flags & ATA_DFLAG_DIPM)
    - err_mask = ata_dev_set_feature(dev,
    - SETFEATURES_SATA_ENABLE, SATA_DIPM);
    - break;
    - case MEDIUM_POWER:
    - /* allow IPM to PARTIAL */
    - scontrol &= ~(0x1 << 8);
    - scontrol |= (0x2 << 8);
    - rc = sata_scr_write(link, SCR_CONTROL, scontrol);
    - if (rc)
    - return rc;
    -
    - /*
    - * we don't have to disable DIPM since IPM flags
    - * disallow transitions to SLUMBER, which effectively
    - * disable DIPM if it does not support PARTIAL
    - */
    - break;
    - case NOT_AVAILABLE:
    - case MAX_PERFORMANCE:
    - /* disable all IPM transitions */
    - scontrol |= (0x3 << 8);
    - rc = sata_scr_write(link, SCR_CONTROL, scontrol);
    - if (rc)
    - return rc;
    -
    - /*
    - * we don't have to disable DIPM since IPM flags
    - * disallow all transitions which effectively
    - * disable DIPM anyway.
    - */
    - break;
    - }
    -
    - /* FIXME: handle SET FEATURES failure */
    - (void) err_mask;
    -
    - return 0;
    -}
    -
    -/**
    - * ata_dev_enable_pm - enable SATA interface power management
    - * @dev: device to enable power management
    - * @policy: the link power management policy
    - *
    - * Enable SATA Interface power management. This will enable
    - * Device Interface Power Management (DIPM) for min_power
    - * policy, and then call driver specific callbacks for
    - * enabling Host Initiated Power management.
    - *
    - * Locking: Caller.
    - * Returns: -EINVAL if IPM is not supported, 0 otherwise.
    - */
    -void ata_dev_enable_pm(struct ata_device *dev, enum link_pm policy)
    -{
    - int rc = 0;
    - struct ata_port *ap = dev->link->ap;
    -
    - /* set HIPM first, then DIPM */
    - if (ap->ops->enable_pm)
    - rc = ap->ops->enable_pm(ap, policy);
    - if (rc)
    - goto enable_pm_out;
    - rc = ata_dev_set_dipm(dev, policy);
    -
    -enable_pm_out:
    - if (rc)
    - ap->pm_policy = MAX_PERFORMANCE;
    - else
    - ap->pm_policy = policy;
    - return /* rc */; /* hopefully we can use 'rc' eventually */
    -}
    -
    -#ifdef CONFIG_PM
    -/**
    - * ata_dev_disable_pm - disable SATA interface power management
    - * @dev: device to disable power management
    - *
    - * Disable SATA Interface power management. This will disable
    - * Device Interface Power Management (DIPM) without changing
    - * policy, call driver specific callbacks for disabling Host
    - * Initiated Power management.
    - *
    - * Locking: Caller.
    - * Returns: void
    - */
    -static void ata_dev_disable_pm(struct ata_device *dev)
    -{
    - struct ata_port *ap = dev->link->ap;
    -
    - ata_dev_set_dipm(dev, MAX_PERFORMANCE);
    - if (ap->ops->disable_pm)
    - ap->ops->disable_pm(ap);
    -}
    -#endif /* CONFIG_PM */
    -
    -void ata_lpm_schedule(struct ata_port *ap, enum link_pm policy)
    -{
    - ap->pm_policy = policy;
    - ap->link.eh_info.action |= ATA_EH_LPM;
    - ap->link.eh_info.flags |= ATA_EHI_NO_AUTOPSY;
    - ata_port_schedule_eh(ap);
    -}
    -
    -#ifdef CONFIG_PM
    -static void ata_lpm_enable(struct ata_host *host)
    -{
    - struct ata_link *link;
    - struct ata_port *ap;
    - struct ata_device *dev;
    - int i;
    -
    - for (i = 0; i < host->n_ports; i++) {
    - ap = host->ports[i];
    - ata_for_each_link(link, ap, EDGE) {
    - ata_for_each_dev(dev, link, ALL)
    - ata_dev_disable_pm(dev);
    - }
    - }
    -}
    -
    -static void ata_lpm_disable(struct ata_host *host)
    -{
    - int i;
    -
    - for (i = 0; i < host->n_ports; i++) {
    - struct ata_port *ap = host->ports[i];
    - ata_lpm_schedule(ap, ap->pm_policy);
    - }
    -}
    -#endif /* CONFIG_PM */
    -
    /**
    * ata_dev_classify - determine device type based on ATA-spec signature
    * @tf: ATA taskfile register set for device to be identified
    @@ -2566,13 +2388,6 @@ int ata_dev_configure(struct ata_device *dev)
    if (dev->flags & ATA_DFLAG_LBA48)
    dev->max_sectors = ATA_MAX_SECTORS_LBA48;

    - if (!(dev->horkage & ATA_HORKAGE_IPM)) {
    - if (ata_id_has_hipm(dev->id))
    - dev->flags |= ATA_DFLAG_HIPM;
    - if (ata_id_has_dipm(dev->id))
    - dev->flags |= ATA_DFLAG_DIPM;
    - }
    -
    /* Limit PATA drive on SATA cable bridge transfers to udma5,
    200 sectors */
    if (ata_dev_knobble(dev)) {
    @@ -2593,13 +2408,6 @@ int ata_dev_configure(struct ata_device *dev)
    dev->max_sectors = min_t(unsigned int, ATA_MAX_SECTORS_128,
    dev->max_sectors);

    - if (ata_dev_blacklisted(dev) & ATA_HORKAGE_IPM) {
    - dev->horkage |= ATA_HORKAGE_IPM;
    -
    - /* reset link pm_policy for this port to no pm */
    - ap->pm_policy = MAX_PERFORMANCE;
    - }
    -
    if (ap->ops->dev_config)
    ap->ops->dev_config(dev);

    @@ -3630,7 +3438,7 @@ int ata_wait_after_reset(struct ata_link *link, unsigned long deadline,
    * @params: timing parameters { interval, duratinon, timeout } in msec
    * @deadline: deadline jiffies for the operation
    *
    -* Make sure SStatus of @link reaches stable state, determined by
    + * Make sure SStatus of @link reaches stable state, determined by
    * holding the same value where DET is not 1 for @duration polled
    * every @interval, before @timeout. Timeout constraints the
    * beginning of the stable state. Because DET gets stuck at 1 on
    @@ -3761,6 +3569,65 @@ int sata_link_resume(struct ata_link *link, const unsigned long *params,
    return rc != -EINVAL ? rc : 0;
    }

    +int sata_link_scr_ipm(struct ata_link *link, enum ata_ipm_policy policy,
    + bool spm_wakeup)
    +{
    + struct ata_eh_context *ehc = &link->eh_context;
    + bool woken_up = false;
    + u32 scontrol;
    + int rc;
    +
    + ata_link_printk(link, KERN_INFO,
    + "XXX sata_link_scr_ipm: pol=%d spm_wakeup=%d\n",
    + policy, spm_wakeup);
    + rc = sata_scr_read(link, SCR_CONTROL, &scontrol);
    + if (rc)
    + return rc;
    +
    + switch (policy) {
    + case ATA_IPM_MAX_POWER:
    + /* disable all IPM transitions */
    + scontrol |= (0x3 << 8);
    + /* initiate transition to active state */
    + if (spm_wakeup) {
    + scontrol |= (0x4 << 12);
    + woken_up = true;
    + }
    + break;
    + case ATA_IPM_MED_POWER:
    + /* allow IPM to PARTIAL */
    + scontrol &= ~(0x1 << 8);
    + scontrol |= (0x2 << 8);
    + break;
    + case ATA_IPM_MIN_POWER:
    + /* no restrictions on IPM transitions */
    + scontrol &= ~(0x3 << 8);
    + break;
    + default:
    + WARN_ON(1);
    + }
    +
    + ata_link_printk(link, KERN_INFO,
    + "XXX sata_link_scr_ipm: updating sctl to %x\n",
    + scontrol);
    + rc = sata_scr_write(link, SCR_CONTROL, scontrol);
    + if (rc)
    + return rc;
    +
    + /* give the link time to transit out of IPM state */
    + if (woken_up) {
    + msleep(10);
    + ata_link_printk(link, KERN_INFO,
    + "XXX sata_link_scr_ipm: sleeping 10msec\n");
    + }
    +
    + /* clear PHYRDY_CHG from SError */
    + ata_link_printk(link, KERN_INFO,
    + "XXX sata_link_scr_ipm: clearing serr\n");
    + ehc->i.serror &= ~SERR_PHYRDY_CHG;
    + return sata_scr_write(link, SCR_ERROR, SERR_PHYRDY_CHG);
    +}
    +
    /**
    * ata_std_prereset - prepare for reset
    * @link: ATA link to be reset
    @@ -4570,6 +4437,7 @@ static unsigned int ata_dev_set_xfermode(struct ata_device *dev)
    DPRINTK("EXIT, err_mask=%x\n", err_mask);
    return err_mask;
    }
    +
    /**
    * ata_dev_set_feature - Issue SET FEATURES - SATA FEATURES
    * @dev: Device to which command will be sent
    @@ -4585,8 +4453,7 @@ static unsigned int ata_dev_set_xfermode(struct ata_device *dev)
    * RETURNS:
    * 0 on success, AC_ERR_* mask otherwise.
    */
    -static unsigned int ata_dev_set_feature(struct ata_device *dev, u8 enable,
    - u8 feature)
    +unsigned int ata_dev_set_feature(struct ata_device *dev, u8 enable, u8 feature)
    {
    struct ata_taskfile tf;
    unsigned int err_mask;
    @@ -5436,12 +5303,6 @@ int ata_host_suspend(struct ata_host *host, pm_message_t mesg)
    {
    int rc;

    - /*
    - * disable link pm on all ports before requesting
    - * any pm activity
    - */
    - ata_lpm_enable(host);
    -
    rc = ata_host_request_pm(host, mesg, 0, ATA_EHI_QUIET, 1);
    if (rc == 0)
    host->dev->power.power_state = mesg;
    @@ -5464,9 +5325,6 @@ void ata_host_resume(struct ata_host *host)
    ata_host_request_pm(host, PMSG_ON, ATA_EH_RESET,
    ATA_EHI_NO_AUTOPSY | ATA_EHI_QUIET, 0);
    host->dev->power.power_state = PMSG_ON;
    -
    - /* reenable link pm */
    - ata_lpm_disable(host);
    }
    #endif

    @@ -6025,7 +5883,7 @@ static void async_port_probe(void *data, async_cookie_t cookie)
    spin_lock_irqsave(ap->lock, flags);

    ehi->probe_mask |= ATA_ALL_DEVICES;
    - ehi->action |= ATA_EH_RESET | ATA_EH_LPM;
    + ehi->action |= ATA_EH_RESET;
    ehi->flags |= ATA_EHI_NO_AUTOPSY | ATA_EHI_QUIET;

    ap->pflags &= ~ATA_PFLAG_INITIALIZING;
    @@ -6698,6 +6556,7 @@ EXPORT_SYMBOL_GPL(sata_set_spd);
    EXPORT_SYMBOL_GPL(ata_wait_after_reset);
    EXPORT_SYMBOL_GPL(sata_link_debounce);
    EXPORT_SYMBOL_GPL(sata_link_resume);
    +EXPORT_SYMBOL_GPL(sata_link_scr_ipm);
    EXPORT_SYMBOL_GPL(ata_std_prereset);
    EXPORT_SYMBOL_GPL(sata_link_hardreset);
    EXPORT_SYMBOL_GPL(sata_std_hardreset);
    diff --git a/drivers/ata/libata-eh.c b/drivers/ata/libata-eh.c
    index f77a673..bd77d94 100644
    --- a/drivers/ata/libata-eh.c
    +++ b/drivers/ata/libata-eh.c
    @@ -1568,14 +1568,15 @@ static void ata_eh_analyze_serror(struct ata_link *link)
    action |= ATA_EH_RESET;
    }

    - /* Determine whether a hotplug event has occurred. Both
    + /*
    + * Determine whether a hotplug event has occurred. Both
    * SError.N/X are considered hotplug events for enabled or
    * host links. For disabled PMP links, only N bit is
    * considered as X bit is left at 1 for link plugging.
    */
    - hotplug_mask = 0;
    -
    - if (!(link->flags & ATA_LFLAG_DISABLED) || ata_is_host_link(link))
    + if (link->ipm_policy != ATA_IPM_MAX_POWER)
    + hotplug_mask = 0; /* hotplug doesn't work w/ IPM */
    + else if (!(link->flags & ATA_LFLAG_DISABLED) || ata_is_host_link(link))
    hotplug_mask = SERR_PHYRDY_CHG | SERR_DEV_XCHG;
    else
    hotplug_mask = SERR_PHYRDY_CHG;
    @@ -2776,8 +2777,9 @@ int ata_eh_reset(struct ata_link *link, int classify,
    ata_eh_done(link, NULL, ATA_EH_RESET);
    if (slave)
    ata_eh_done(slave, NULL, ATA_EH_RESET);
    - ehc->last_reset = jiffies; /* update to completion time */
    + ehc->last_reset = jiffies; /* update to completion time */
    ehc->i.action |= ATA_EH_REVALIDATE;
    + link->ipm_policy = ATA_IPM_UNKNOWN; /* reset IPM state */

    rc = 0;
    out:
    @@ -3203,6 +3205,124 @@ static int ata_eh_maybe_retry_flush(struct ata_device *dev)
    return rc;
    }

    +/**
    + * ata_eh_set_ipm - configure SATA interface power management
    + * @link: link to configure power management
    + * @policy: the link power management policy
    + * @r_failed_dev: out parameter for failed device
    + *
    + * Enable SATA Interface power management. This will enable
    + * Device Interface Power Management (DIPM) for min_power
    + * policy, and then call driver specific callbacks for
    + * enabling Host Initiated Power management.
    + *
    + * LOCKING:
    + * EH context.
    + *
    + * RETURNS:
    + * 0 on success, -errno on failure.
    + */
    +static int ata_eh_set_ipm(struct ata_link *link, enum ata_ipm_policy policy,
    + struct ata_device **r_failed_dev)
    +{
    + struct ata_port *ap = ata_is_host_link(link) ? link->ap : NULL;
    + struct ata_eh_context *ehc = &link->eh_context;
    + struct ata_device *dev, *link_dev = NULL, *ipm_dev = NULL;
    + unsigned int hints = ATA_IPM_EMPTY | ATA_IPM_HIPM;
    + unsigned int err_mask;
    + int rc;
    +
    + /* if the link or host doesn't do IPM, noop */
    + if ((link->flags & ATA_LFLAG_NO_IPM) || (ap && !ap->ops->set_ipm))
    + return 0;
    +
    + /*
    + * DIPM is enabled only for MIN_POWER as some devices
    + * misbehave when the host NACKs transition to SLUMBER. Order
    + * device and link configurations such that the host always
    + * allows DIPM requests.
    + */
    + ata_for_each_dev(dev, link, ENABLED) {
    + bool hipm = ata_id_has_hipm(dev->id);
    + bool dipm = ata_id_has_dipm(dev->id);
    +
    + /* find the first enabled and IPM enabled devices */
    + if (!link_dev)
    + link_dev = dev;
    +
    + if (!ipm_dev && (hipm || dipm))
    + ipm_dev = dev;
    +
    + hints &= ~ATA_IPM_EMPTY;
    + if (!hipm)
    + hints &= ~ATA_IPM_HIPM;
    +
    + /* disable DIPM before changing link config */
    + if (policy != ATA_IPM_MIN_POWER && dipm) {
    + ata_dev_printk(dev, KERN_INFO, "XXX ata_eh_set_ipm: disabling DIPM\n");
    + err_mask = ata_dev_set_feature(dev,
    + SETFEATURES_SATA_DISABLE, SATA_DIPM);
    + if (err_mask && err_mask != AC_ERR_DEV) {
    + ata_dev_printk(dev, KERN_WARNING,
    + "error while disabling DIPM\n");
    + rc = -EIO;
    + goto fail;
    + }
    + }
    + }
    +
    + if (ap) {
    + rc = ap->ops->set_ipm(link, policy, hints);
    + if (!rc && ap->slave_link)
    + rc = ap->ops->set_ipm(ap->slave_link, policy, hints);
    + } else
    + rc = sata_pmp_set_ipm(link, policy, hints);
    +
    + /*
    + * Attribute link config failure to the first (IPM) enabled
    + * device on the link.
    + */
    + if (rc) {
    + if (rc == -EOPNOTSUPP) {
    + link->flags |= ATA_LFLAG_NO_IPM;
    + return 0;
    + }
    + dev = ipm_dev ? ipm_dev : link_dev;
    + goto fail;
    + }
    +
    + /* host config updated, enable DIPM if transitioning to MIN_POWER */
    + ata_for_each_dev(dev, link, ENABLED) {
    + if (policy == ATA_IPM_MIN_POWER && ata_id_has_dipm(dev->id)) {
    + ata_dev_printk(dev, KERN_INFO, "XXX ata_eh_set_ipm: enabling DIPM\n");
    + err_mask = ata_dev_set_feature(dev,
    + SETFEATURES_SATA_ENABLE, SATA_DIPM);
    + if (err_mask && err_mask != AC_ERR_DEV) {
    + ata_dev_printk(dev, KERN_WARNING,
    + "error while enabling DIPM\n");
    + rc = -EIO;
    + goto fail;
    + }
    + }
    + }
    +
    + link->ipm_policy = policy;
    + if (ap && ap->slave_link)
    + ap->slave_link->ipm_policy = policy;
    + return 0;
    +
    +fail:
    + /* if no device or the last chance for the device, disable IPM */
    + if (!dev || ehc->tries[dev->devno] == 1) {
    + ata_link_printk(link, KERN_WARNING,
    + "disabling IPM on the link\n");
    + link->flags |= ATA_LFLAG_NO_IPM;
    + }
    + if (r_failed_dev)
    + *r_failed_dev = dev;
    + return rc;
    +}
    +
    static int ata_link_nr_enabled(struct ata_link *link)
    {
    struct ata_device *dev;
    @@ -3283,6 +3403,16 @@ static int ata_eh_schedule_probe(struct ata_device *dev)
    ehc->saved_xfer_mode[dev->devno] = 0;
    ehc->saved_ncq_enabled &= ~(1 << dev->devno);

    + /* the link maybe in a deep sleep, wake it up */
    + if (link->ipm_policy > ATA_IPM_MAX_POWER) {
    + if (ata_is_host_link(link))
    + link->ap->ops->set_ipm(link, ATA_IPM_MAX_POWER,
    + ATA_IPM_EMPTY);
    + else
    + sata_pmp_set_ipm(link, ATA_IPM_MAX_POWER,
    + ATA_IPM_EMPTY);
    + }
    +
    /* Record and count probe trials on the ering. The specific
    * error mask used is irrelevant. Because a successful device
    * detection clears the ering, this count accumulates only if
    @@ -3384,8 +3514,7 @@ int ata_eh_recover(struct ata_port *ap, ata_prereset_fn_t prereset,
    {
    struct ata_link *link;
    struct ata_device *dev;
    - int nr_failed_devs;
    - int rc;
    + int rc, nr_fails;
    unsigned long flags, deadline;

    DPRINTK("ENTER\n");
    @@ -3426,7 +3555,6 @@ int ata_eh_recover(struct ata_port *ap, ata_prereset_fn_t prereset,

    retry:
    rc = 0;
    - nr_failed_devs = 0;

    /* if UNLOADING, finish immediately */
    if (ap->pflags & ATA_PFLAG_UNLOADING)
    @@ -3511,13 +3639,17 @@ int ata_eh_recover(struct ata_port *ap, ata_prereset_fn_t prereset,
    }

    /* the rest */
    - ata_for_each_link(link, ap, EDGE) {
    + nr_fails = 0;
    + ata_for_each_link(link, ap, PMP_FIRST) {
    struct ata_eh_context *ehc = &link->eh_context;

    + if (sata_pmp_attached(ap) && ata_is_host_link(link))
    + goto config_ipm;
    +
    /* revalidate existing devices and attach new ones */
    rc = ata_eh_revalidate_and_attach(link, &dev);
    if (rc)
    - goto dev_fail;
    + goto rest_fail;

    /* if PMP got attached, return, pmp EH will take care of it */
    if (link->device->class == ATA_DEV_PMP) {
    @@ -3529,7 +3661,7 @@ int ata_eh_recover(struct ata_port *ap, ata_prereset_fn_t prereset,
    if (ehc->i.flags & ATA_EHI_SETMODE) {
    rc = ata_set_mode(link, &dev);
    if (rc)
    - goto dev_fail;
    + goto rest_fail;
    ehc->i.flags &= ~ATA_EHI_SETMODE;
    }

    @@ -3542,7 +3674,7 @@ int ata_eh_recover(struct ata_port *ap, ata_prereset_fn_t prereset,
    continue;
    rc = atapi_eh_clear_ua(dev);
    if (rc)
    - goto dev_fail;
    + goto rest_fail;
    }
    }

    @@ -3552,21 +3684,25 @@ int ata_eh_recover(struct ata_port *ap, ata_prereset_fn_t prereset,
    continue;
    rc = ata_eh_maybe_retry_flush(dev);
    if (rc)
    - goto dev_fail;
    + goto rest_fail;
    }

    + config_ipm:
    /* configure link power saving */
    - if (ehc->i.action & ATA_EH_LPM)
    - ata_for_each_dev(dev, link, ALL)
    - ata_dev_enable_pm(dev, ap->pm_policy);
    + if (link->ipm_policy != ap->target_ipm_policy) {
    + rc = ata_eh_set_ipm(link, ap->target_ipm_policy, &dev);
    + if (rc)
    + goto rest_fail;
    + }

    /* this link is okay now */
    ehc->i.flags = 0;
    continue;

    -dev_fail:
    - nr_failed_devs++;
    - ata_eh_handle_dev_fail(dev, rc);
    + rest_fail:
    + nr_fails++;
    + if (dev)
    + ata_eh_handle_dev_fail(dev, rc);

    if (ap->pflags & ATA_PFLAG_FROZEN) {
    /* PMP reset requires working host port.
    @@ -3578,7 +3714,7 @@ dev_fail:
    }
    }

    - if (nr_failed_devs)
    + if (nr_fails)
    goto retry;

    out:
    diff --git a/drivers/ata/libata-pmp.c b/drivers/ata/libata-pmp.c
    index 224faab..06a66ca 100644
    --- a/drivers/ata/libata-pmp.c
    +++ b/drivers/ata/libata-pmp.c
    @@ -185,6 +185,27 @@ int sata_pmp_scr_write(struct ata_link *link, int reg, u32 val)
    }

    /**
    + * sata_pmp_set_ipm - configure IPM for a PMP link
    + * @link: PMP link to configure IPM for
    + * @policy: target IPM policy
    + * @hints: IPM hints
    + *
    + * Configure IPM for @link. This function will contain any PMP
    + * specific workarounds if necessary.
    + *
    + * LOCKING:
    + * EH context.
    + *
    + * RETURNS:
    + * 0 on success, -errno on failure.
    + */
    +int sata_pmp_set_ipm(struct ata_link *link, enum ata_ipm_policy policy,
    + unsigned hints)
    +{
    + return sata_link_scr_ipm(link, policy, true);
    +}
    +
    +/**
    * sata_pmp_read_gscr - read GSCR block of SATA PMP
    * @dev: PMP device
    * @gscr: buffer to read GSCR block into
    @@ -351,6 +372,9 @@ static void sata_pmp_quirks(struct ata_port *ap)
    if (vendor == 0x1095 && devid == 0x3726) {
    /* sil3726 quirks */
    ata_for_each_link(link, ap, EDGE) {
    + /* link reports offline after IPM */
    + link->flags |= ATA_LFLAG_NO_IPM;
    +
    /* Class code report is unreliable and SRST
    * times out under certain configurations.
    */
    @@ -366,6 +390,9 @@ static void sata_pmp_quirks(struct ata_port *ap)
    } else if (vendor == 0x1095 && devid == 0x4723) {
    /* sil4723 quirks */
    ata_for_each_link(link, ap, EDGE) {
    + /* link reports offline after IPM */
    + link->flags |= ATA_LFLAG_NO_IPM;
    +
    /* class code report is unreliable */
    if (link->pmp < 2)
    link->flags |= ATA_LFLAG_ASSUME_ATA;
    @@ -378,6 +405,9 @@ static void sata_pmp_quirks(struct ata_port *ap)
    } else if (vendor == 0x1095 && devid == 0x4726) {
    /* sil4726 quirks */
    ata_for_each_link(link, ap, EDGE) {
    + /* link reports offline after IPM */
    + link->flags |= ATA_LFLAG_NO_IPM;
    +
    /* Class code report is unreliable and SRST
    * times out under certain configurations.
    * Config device can be at port 0 or 5 and
    @@ -938,15 +968,26 @@ static int sata_pmp_eh_recover(struct ata_port *ap)
    if (rc)
    goto link_fail;

    - /* Connection status might have changed while resetting other
    - * links, check SATA_PMP_GSCR_ERROR before returning.
    - */
    -
    +
    /* clear SNotification */
    rc = sata_scr_read(&ap->link, SCR_NOTIFICATION, &sntf);
    if (rc == 0)
    sata_scr_write(&ap->link, SCR_NOTIFICATION, sntf);

    + /*
    + * If IPM is active on any fan-out port, hotplug wouldn't
    + * work. Return w/ PHY event notification disabled.
    + */
    + ata_for_each_link(link, ap, EDGE)
    + if (link->ipm_policy > ATA_IPM_MAX_POWER)
    + return 0;
    +
    + /*
    + * Connection status might have changed while resetting other
    + * links, enable notification and check SATA_PMP_GSCR_ERROR
    + * before returning.
    + */
    +
    /* enable notification */
    if (pmp_dev->flags & ATA_DFLAG_AN) {
    gscr[SATA_PMP_GSCR_FEAT_EN] |= SATA_PMP_FEAT_NOTIFY;
    diff --git a/drivers/ata/libata-scsi.c b/drivers/ata/libata-scsi.c
    index a54273d..8801342 100644
    --- a/drivers/ata/libata-scsi.c
    +++ b/drivers/ata/libata-scsi.c
    @@ -116,73 +116,55 @@ static struct scsi_transport_template ata_scsi_transport_template = {
    .user_scan = ata_scsi_user_scan,
    };

    -
    -static const struct {
    - enum link_pm value;
    - const char *name;
    -} link_pm_policy[] = {
    - { NOT_AVAILABLE, "max_performance" },
    - { MIN_POWER, "min_power" },
    - { MAX_PERFORMANCE, "max_performance" },
    - { MEDIUM_POWER, "medium_power" },
    +static const char *ata_ipm_policy_names[] = {
    + [ATA_IPM_UNKNOWN] = "max_performance",
    + [ATA_IPM_MAX_POWER] = "max_performance",
    + [ATA_IPM_MED_POWER] = "medium_power",
    + [ATA_IPM_MIN_POWER] = "min_power",
    };

    -static const char *ata_scsi_lpm_get(enum link_pm policy)
    -{
    - int i;
    -
    - for (i = 0; i < ARRAY_SIZE(link_pm_policy); i++)
    - if (link_pm_policy[i].value == policy)
    - return link_pm_policy[i].name;
    -
    - return NULL;
    -}
    -
    -static ssize_t ata_scsi_lpm_put(struct device *dev,
    - struct device_attribute *attr,
    - const char *buf, size_t count)
    +static ssize_t ata_scsi_ipm_store(struct device *dev,
    + struct device_attribute *attr,
    + const char *buf, size_t count)
    {
    struct Scsi_Host *shost = class_to_shost(dev);
    struct ata_port *ap = ata_shost_to_port(shost);
    - enum link_pm policy = 0;
    - int i;
    + enum ata_ipm_policy policy;
    + unsigned long flags;

    - /*
    - * we are skipping array location 0 on purpose - this
    - * is because a value of NOT_AVAILABLE is displayed
    - * to the user as max_performance, but when the user
    - * writes "max_performance", they actually want the
    - * value to match MAX_PERFORMANCE.
    - */
    - for (i = 1; i < ARRAY_SIZE(link_pm_policy); i++) {
    - const int len = strlen(link_pm_policy[i].name);
    - if (strncmp(link_pm_policy[i].name, buf, len) == 0) {
    - policy = link_pm_policy[i].value;
    + /* UNKNOWN is internal state, iterate from MAX_POWER */
    + for (policy = ATA_IPM_MAX_POWER;
    + policy < ARRAY_SIZE(ata_ipm_policy_names); policy++) {
    + const char *name = ata_ipm_policy_names[policy];
    +
    + if (strncmp(name, buf, strlen(name)) == 0)
    break;
    - }
    }
    - if (!policy)
    + if (policy == ARRAY_SIZE(ata_ipm_policy_names))
    return -EINVAL;

    - ata_lpm_schedule(ap, policy);
    + spin_lock_irqsave(ap->lock, flags);
    + ap->target_ipm_policy = policy;
    + ata_port_schedule_eh(ap);
    + spin_unlock_irqrestore(ap->lock, flags);
    +
    return count;
    }

    -static ssize_t
    -ata_scsi_lpm_show(struct device *dev, struct device_attribute *attr, char *buf)
    +static ssize_t ata_scsi_ipm_show(struct device *dev,
    + struct device_attribute *attr, char *buf)
    {
    struct Scsi_Host *shost = class_to_shost(dev);
    struct ata_port *ap = ata_shost_to_port(shost);
    - const char *policy =
    - ata_scsi_lpm_get(ap->pm_policy);

    - if (!policy)
    + if (ap->target_ipm_policy >= ARRAY_SIZE(ata_ipm_policy_names))
    return -EINVAL;

    - return snprintf(buf, 23, "%s\n", policy);
    + return snprintf(buf, PAGE_SIZE, "%s\n",
    + ata_ipm_policy_names[ap->target_ipm_policy]);
    }
    DEVICE_ATTR(link_power_management_policy, S_IRUGO | S_IWUSR,
    - ata_scsi_lpm_show, ata_scsi_lpm_put);
    + ata_scsi_ipm_show, ata_scsi_ipm_store);
    EXPORT_SYMBOL_GPL(dev_attr_link_power_management_policy);

    static ssize_t ata_scsi_park_show(struct device *device,
    diff --git a/drivers/ata/libata.h b/drivers/ata/libata.h
    index 4b84ed6..2dd0dfe 100644
    --- a/drivers/ata/libata.h
    +++ b/drivers/ata/libata.h
    @@ -87,6 +87,8 @@ extern int ata_dev_revalidate(struct ata_device *dev, unsigned int new_class,
    extern int ata_dev_configure(struct ata_device *dev);
    extern int sata_down_spd_limit(struct ata_link *link, u32 spd_limit);
    extern int ata_down_xfermask_limit(struct ata_device *dev, unsigned int sel);
    +extern unsigned int ata_dev_set_feature(struct ata_device *dev,
    + u8 enable, u8 feature);
    extern void ata_sg_clean(struct ata_queued_cmd *qc);
    extern void ata_qc_free(struct ata_queued_cmd *qc);
    extern void ata_qc_issue(struct ata_queued_cmd *qc);
    @@ -101,8 +103,6 @@ extern int sata_link_init_spd(struct ata_link *link);
    extern int ata_task_ioctl(struct scsi_device *scsidev, void __user *arg);
    extern int ata_cmd_ioctl(struct scsi_device *scsidev, void __user *arg);
    extern struct ata_port *ata_port_alloc(struct ata_host *host);
    -extern void ata_dev_enable_pm(struct ata_device *dev, enum link_pm policy);
    -extern void ata_lpm_schedule(struct ata_port *ap, enum link_pm);

    /* libata-acpi.c */
    #ifdef CONFIG_ATA_ACPI
    @@ -170,6 +170,8 @@ extern void ata_eh_finish(struct ata_port *ap);
    #ifdef CONFIG_SATA_PMP
    extern int sata_pmp_scr_read(struct ata_link *link, int reg, u32 *val);
    extern int sata_pmp_scr_write(struct ata_link *link, int reg, u32 val);
    +extern int sata_pmp_set_ipm(struct ata_link *link, enum ata_ipm_policy policy,
    + unsigned hints);
    extern int sata_pmp_attach(struct ata_device *dev);
    #else /* CONFIG_SATA_PMP */
    static inline int sata_pmp_scr_read(struct ata_link *link, int reg, u32 *val)
    @@ -182,6 +184,12 @@ static inline int sata_pmp_scr_write(struct ata_link *link, int reg, u32 val)
    return -EINVAL;
    }

    +static inline int sata_pmp_set_ipm(struct ata_link *link,
    + enum ata_ipm_policy policy, unsigned hints)
    +{
    + return -EINVAL;
    +}
    +
    static inline int sata_pmp_attach(struct ata_device *dev)
    {
    return -EINVAL;
    diff --git a/include/linux/libata.h b/include/linux/libata.h
    index b85f3ff..1f90dc5 100644
    --- a/include/linux/libata.h
    +++ b/include/linux/libata.h
    @@ -172,6 +172,7 @@ enum {
    ATA_LFLAG_NO_RETRY = (1 << 5), /* don't retry this link */
    ATA_LFLAG_DISABLED = (1 << 6), /* link is disabled */
    ATA_LFLAG_SW_ACTIVITY = (1 << 7), /* keep activity stats */
    + ATA_LFLAG_NO_IPM = (1 << 8), /* disable IPM on this link */

    /* struct ata_port flags */
    ATA_FLAG_SLAVE_POSS = (1 << 0), /* host supports slave dev */
    @@ -324,12 +325,11 @@ enum {
    ATA_EH_HARDRESET = (1 << 2), /* meaningful only in ->prereset */
    ATA_EH_RESET = ATA_EH_SOFTRESET | ATA_EH_HARDRESET,
    ATA_EH_ENABLE_LINK = (1 << 3),
    - ATA_EH_LPM = (1 << 4), /* link power management action */
    ATA_EH_PARK = (1 << 5), /* unload heads and stop I/O */

    ATA_EH_PERDEV_MASK = ATA_EH_REVALIDATE | ATA_EH_PARK,
    ATA_EH_ALL_ACTIONS = ATA_EH_REVALIDATE | ATA_EH_RESET |
    - ATA_EH_ENABLE_LINK | ATA_EH_LPM,
    + ATA_EH_ENABLE_LINK,

    /* ata_eh_info->flags */
    ATA_EHI_HOTPLUGGED = (1 << 0), /* could have been hotplugged */
    @@ -376,7 +376,6 @@ enum {
    ATA_HORKAGE_BROKEN_HPA = (1 << 4), /* Broken HPA */
    ATA_HORKAGE_DISABLE = (1 << 5), /* Disable it */
    ATA_HORKAGE_HPA_SIZE = (1 << 6), /* native size off by one */
    - ATA_HORKAGE_IPM = (1 << 7), /* Link PM problems */
    ATA_HORKAGE_IVB = (1 << 8), /* cbl det validity bit bugs */
    ATA_HORKAGE_STUCK_ERR = (1 << 9), /* stuck ERR on next PACKET */
    ATA_HORKAGE_BRIDGE_OK = (1 << 10), /* no bridge limits */
    @@ -463,6 +462,22 @@ enum ata_completion_errors {
    AC_ERR_NCQ = (1 << 10), /* marker for offending NCQ qc */
    };

    +/*
    + * Link pm policy: If you alter this, you also need to alter
    + * libata-scsi.c (for the ascii descriptions)
    + */
    +enum ata_ipm_policy {
    + ATA_IPM_UNKNOWN,
    + ATA_IPM_MAX_POWER,
    + ATA_IPM_MED_POWER,
    + ATA_IPM_MIN_POWER,
    +};
    +
    +enum ata_ipm_hints {
    + ATA_IPM_EMPTY = (1 << 0), /* port empty/probing */
    + ATA_IPM_HIPM = (1 << 1), /* may use HIPM */
    +};
    +
    /* forward declarations */
    struct scsi_device;
    struct ata_port_operations;
    @@ -477,16 +492,6 @@ typedef int (*ata_reset_fn_t)(struct ata_link *link, unsigned int *classes,
    unsigned long deadline);
    typedef void (*ata_postreset_fn_t)(struct ata_link *link, unsigned int *classes);

    -/*
    - * host pm policy: If you alter this, you also need to alter libata-scsi.c
    - * (for the ascii descriptions)
    - */
    -enum link_pm {
    - NOT_AVAILABLE,
    - MIN_POWER,
    - MAX_PERFORMANCE,
    - MEDIUM_POWER,
    -};
    extern struct device_attribute dev_attr_link_power_management_policy;
    extern struct device_attribute dev_attr_unload_heads;
    extern struct device_attribute dev_attr_em_message_type;
    @@ -698,6 +703,7 @@ struct ata_link {
    unsigned int hw_sata_spd_limit;
    unsigned int sata_spd_limit;
    unsigned int sata_spd; /* current SATA PHY speed */
    + enum ata_ipm_policy ipm_policy;

    /* record runtime error info, protected by host_set lock */
    struct ata_eh_info eh_info;
    @@ -764,7 +770,7 @@ struct ata_port {

    pm_message_t pm_mesg;
    int *pm_result;
    - enum link_pm pm_policy;
    + enum ata_ipm_policy target_ipm_policy;

    struct timer_list fastdrain_timer;
    unsigned long fastdrain_cnt;
    @@ -830,8 +836,8 @@ struct ata_port_operations {
    int (*scr_write)(struct ata_link *link, unsigned int sc_reg, u32 val);
    void (*pmp_attach)(struct ata_port *ap);
    void (*pmp_detach)(struct ata_port *ap);
    - int (*enable_pm)(struct ata_port *ap, enum link_pm policy);
    - void (*disable_pm)(struct ata_port *ap);
    + int (*set_ipm)(struct ata_link *link, enum ata_ipm_policy policy,
    + unsigned hints);

    /*
    * Start, stop, suspend and resume
    @@ -943,6 +949,8 @@ extern int sata_link_debounce(struct ata_link *link,
    const unsigned long *params, unsigned long deadline);
    extern int sata_link_resume(struct ata_link *link, const unsigned long *params,
    unsigned long deadline);
    +extern int sata_link_scr_ipm(struct ata_link *link, enum ata_ipm_policy policy,
    + bool spm_wakeup);
    extern int sata_link_hardreset(struct ata_link *link,
    const unsigned long *timing, unsigned long deadline,
    bool *online, int (*check_ready)(struct ata_link *));

    \
     
     \ /
      Last update: 2010-08-05 18:11    [W:0.091 / U:63.132 seconds]
    ©2003-2016 Jasper Spaans. hosted at Digital OceanAdvertise on this site