Re: [PATCH v5 5/6] ata: add ata port system PM callbacks

From: Lin Ming
Date: Wed Dec 07 2011 - 03:38:40 EST


On Mon, Dec 5, 2011 at 9:20 AM, Lin Ming <ming.m.lin@xxxxxxxxx> wrote:
> Change ata_host_request_pm to ata_port_request_pm which performs
> port suspend/resume.
>
> Add ata port type driver which implements port PM callbacks.

Hi Tejun & Alan,

How about this patch?

Lin Ming

>
> Signed-off-by: Lin Ming <ming.m.lin@xxxxxxxxx>
> ---
>  drivers/ata/libata-core.c      |  144 ++++++++++++++++++++-------------------
>  drivers/ata/libata-transport.c |    1 +
>  drivers/ata/libata.h           |    1 +
>  3 files changed, 76 insertions(+), 70 deletions(-)
>
> diff --git a/drivers/ata/libata-core.c b/drivers/ata/libata-core.c
> index c04ad68..04c208e 100644
> --- a/drivers/ata/libata-core.c
> +++ b/drivers/ata/libata-core.c
> @@ -5234,112 +5234,116 @@ bool ata_link_offline(struct ata_link *link)
>  }
>
>  #ifdef CONFIG_PM
> -static int ata_host_request_pm(struct ata_host *host, pm_message_t mesg,
> +static int ata_port_request_pm(struct ata_port *ap, pm_message_t mesg,
>                               unsigned int action, unsigned int ehi_flags,
>                               int wait)
>  {
> +       struct ata_link *link;
>        unsigned long flags;
> -       int i, rc;
> -
> -       for (i = 0; i < host->n_ports; i++) {
> -               struct ata_port *ap = host->ports[i];
> -               struct ata_link *link;
> +       int rc;
>
> -               /* Previous resume operation might still be in
> -                * progress.  Wait for PM_PENDING to clear.
> -                */
> -               if (ap->pflags & ATA_PFLAG_PM_PENDING) {
> -                       ata_port_wait_eh(ap);
> -                       WARN_ON(ap->pflags & ATA_PFLAG_PM_PENDING);
> -               }
> +       /* Previous resume operation might still be in
> +        * progress.  Wait for PM_PENDING to clear.
> +        */
> +       if (ap->pflags & ATA_PFLAG_PM_PENDING) {
> +               ata_port_wait_eh(ap);
> +               WARN_ON(ap->pflags & ATA_PFLAG_PM_PENDING);
> +       }
>
> -               /* request PM ops to EH */
> -               spin_lock_irqsave(ap->lock, flags);
> +       /* request PM ops to EH */
> +       spin_lock_irqsave(ap->lock, flags);
>
> -               ap->pm_mesg = mesg;
> -               if (wait) {
> -                       rc = 0;
> -                       ap->pm_result = &rc;
> -               }
> +       ap->pm_mesg = mesg;
> +       if (wait) {
> +               rc = 0;
> +               ap->pm_result = &rc;
> +       }
>
> -               ap->pflags |= ATA_PFLAG_PM_PENDING;
> -               ata_for_each_link(link, ap, HOST_FIRST) {
> -                       link->eh_info.action |= action;
> -                       link->eh_info.flags |= ehi_flags;
> -               }
> +       ap->pflags |= ATA_PFLAG_PM_PENDING;
> +       ata_for_each_link(link, ap, HOST_FIRST) {
> +               link->eh_info.action |= action;
> +               link->eh_info.flags |= ehi_flags;
> +       }
>
> -               ata_port_schedule_eh(ap);
> +       ata_port_schedule_eh(ap);
>
> -               spin_unlock_irqrestore(ap->lock, flags);
> +       spin_unlock_irqrestore(ap->lock, flags);
>
> -               /* wait and check result */
> -               if (wait) {
> -                       ata_port_wait_eh(ap);
> -                       WARN_ON(ap->pflags & ATA_PFLAG_PM_PENDING);
> -                       if (rc)
> -                               return rc;
> -               }
> +       /* wait and check result */
> +       if (wait) {
> +               ata_port_wait_eh(ap);
> +               WARN_ON(ap->pflags & ATA_PFLAG_PM_PENDING);
>        }
>
> -       return 0;
> +       return rc;
>  }
>
> +#define to_ata_port(d) container_of(d, struct ata_port, tdev)
> +
> +static int ata_port_suspend_common(struct device *dev)
> +{
> +       struct ata_port *ap = to_ata_port(dev);
> +       int rc;
> +
> +       rc = ata_port_request_pm(ap, PMSG_SUSPEND, 0, ATA_EHI_QUIET, 1);
> +       return rc;
> +}
> +
> +static int ata_port_suspend(struct device *dev)
> +{
> +       if (pm_runtime_suspended(dev))
> +               return 0;
> +
> +       return ata_port_suspend_common(dev);
> +}
> +
> +static int ata_port_resume(struct device *dev)
> +{
> +       struct ata_port *ap = to_ata_port(dev);
> +       int rc;
> +
> +       rc = ata_port_request_pm(ap, PMSG_ON, ATA_EH_RESET,
> +               ATA_EHI_NO_AUTOPSY | ATA_EHI_QUIET, 1);
> +       return rc;
> +}
> +
> +static const struct dev_pm_ops ata_port_pm_ops = {
> +       .suspend = ata_port_suspend,
> +       .resume = ata_port_resume,
> +};
> +
>  /**
>  *     ata_host_suspend - suspend host
>  *     @host: host to suspend
>  *     @mesg: PM message
>  *
> - *     Suspend @host.  Actual operation is performed by EH.  This
> - *     function requests EH to perform PM operations and waits for EH
> - *     to finish.
> - *
> - *     LOCKING:
> - *     Kernel thread context (may sleep).
> - *
> - *     RETURNS:
> - *     0 on success, -errno on failure.
> + *     Suspend @host.  Actual operation is performed by port suspend.
>  */
>  int ata_host_suspend(struct ata_host *host, pm_message_t mesg)
>  {
> -       unsigned int ehi_flags = ATA_EHI_QUIET;
> -       int rc;
> -
> -       /*
> -        * On some hardware, device fails to respond after spun down
> -        * for suspend.  As the device won't be used before being
> -        * resumed, we don't need to touch the device.  Ask EH to skip
> -        * the usual stuff and proceed directly to suspend.
> -        *
> -        * http://thread.gmane.org/gmane.linux.ide/46764
> -        */
> -       if (mesg.event == PM_EVENT_SUSPEND)
> -               ehi_flags |= ATA_EHI_NO_AUTOPSY | ATA_EHI_NO_RECOVERY;
> -
> -       rc = ata_host_request_pm(host, mesg, 0, ehi_flags, 1);
> -       if (rc == 0)
> -               host->dev->power.power_state = mesg;
> -       return rc;
> +       host->dev->power.power_state = mesg;
> +       return 0;
>  }
>
>  /**
>  *     ata_host_resume - resume host
>  *     @host: host to resume
>  *
> - *     Resume @host.  Actual operation is performed by EH.  This
> - *     function requests EH to perform PM operations and returns.
> - *     Note that all resume operations are performed parallelly.
> - *
> - *     LOCKING:
> - *     Kernel thread context (may sleep).
> + *     Resume @host.  Actual operation is performed by port resume.
>  */
>  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;
>  }
>  #endif
>
> +struct device_type ata_port_type = {
> +       .name = "ata_port",
> +#ifdef CONFIG_PM
> +       .pm = &ata_port_pm_ops,
> +#endif
> +};
> +
>  /**
>  *     ata_dev_init - Initialize an ata_device structure
>  *     @dev: Device structure to initialize
> diff --git a/drivers/ata/libata-transport.c b/drivers/ata/libata-transport.c
> index ce9dc62..3ceb3d9 100644
> --- a/drivers/ata/libata-transport.c
> +++ b/drivers/ata/libata-transport.c
> @@ -279,6 +279,7 @@ int ata_tport_add(struct device *parent,
>        struct device *dev = &ap->tdev;
>
>        device_initialize(dev);
> +       dev->type = &ata_port_type;
>
>        dev->parent = get_device(parent);
>        dev->release = ata_tport_release;
> diff --git a/drivers/ata/libata.h b/drivers/ata/libata.h
> index 773de97..814486d 100644
> --- a/drivers/ata/libata.h
> +++ b/drivers/ata/libata.h
> @@ -58,6 +58,7 @@ extern int atapi_passthru16;
>  extern int libata_fua;
>  extern int libata_noacpi;
>  extern int libata_allow_tpm;
> +extern struct device_type ata_port_type;
>  extern struct ata_link *ata_dev_phys_link(struct ata_device *dev);
>  extern void ata_force_cbl(struct ata_port *ap);
>  extern u64 ata_tf_to_lba(const struct ata_taskfile *tf);
> --
> 1.7.2.5
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-pm" in
> the body of a message to majordomo@xxxxxxxxxxxxxxx
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/