From bb03c640697155639b2e15e2aaa4c10f60bf0d5e Mon Sep 17 00:00:00 2001 From: Mika Westerberg Date: Thu, 18 Feb 2016 10:54:16 +0200 Subject: ahci: Add functions to manage runtime PM of AHCI ports Add new functions ahci_rpm_get_port()/ahci_rpm_put_port() that change runtime PM status of AHCI ports. Depending if the AHCI host has runtime PM enabled or disabled calling these may trigger runtime suspend/resume of the host controller. We also call these functions in appropriate places to make sure host controller registers are available before using them. Signed-off-by: Mika Westerberg Signed-off-by: Tejun Heo --- drivers/ata/libahci.c | 48 +++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 47 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/ata/libahci.c b/drivers/ata/libahci.c index 8cdf0afd551d..2d21b46db714 100644 --- a/drivers/ata/libahci.c +++ b/drivers/ata/libahci.c @@ -225,6 +225,31 @@ static void ahci_enable_ahci(void __iomem *mmio) WARN_ON(1); } +/** + * ahci_rpm_get_port - Make sure the port is powered on + * @ap: Port to power on + * + * Whenever there is need to access the AHCI host registers outside of + * normal execution paths, call this function to make sure the host is + * actually powered on. + */ +static int ahci_rpm_get_port(struct ata_port *ap) +{ + return pm_runtime_get_sync(ap->dev); +} + +/** + * ahci_rpm_put_port - Undoes ahci_rpm_get_port() + * @ap: Port to power down + * + * Undoes ahci_rpm_get_port() and possibly powers down the AHCI host + * if it has no more active users. + */ +static void ahci_rpm_put_port(struct ata_port *ap) +{ + pm_runtime_put(ap->dev); +} + static ssize_t ahci_show_host_caps(struct device *dev, struct device_attribute *attr, char *buf) { @@ -261,8 +286,13 @@ static ssize_t ahci_show_port_cmd(struct device *dev, struct Scsi_Host *shost = class_to_shost(dev); struct ata_port *ap = ata_shost_to_port(shost); void __iomem *port_mmio = ahci_port_base(ap); + ssize_t ret; - return sprintf(buf, "%x\n", readl(port_mmio + PORT_CMD)); + ahci_rpm_get_port(ap); + ret = sprintf(buf, "%x\n", readl(port_mmio + PORT_CMD)); + ahci_rpm_put_port(ap); + + return ret; } static ssize_t ahci_read_em_buffer(struct device *dev, @@ -278,17 +308,20 @@ static ssize_t ahci_read_em_buffer(struct device *dev, size_t count; int i; + ahci_rpm_get_port(ap); spin_lock_irqsave(ap->lock, flags); em_ctl = readl(mmio + HOST_EM_CTL); if (!(ap->flags & ATA_FLAG_EM) || em_ctl & EM_CTL_XMT || !(hpriv->em_msg_type & EM_MSG_TYPE_SGPIO)) { spin_unlock_irqrestore(ap->lock, flags); + ahci_rpm_put_port(ap); return -EINVAL; } if (!(em_ctl & EM_CTL_MR)) { spin_unlock_irqrestore(ap->lock, flags); + ahci_rpm_put_port(ap); return -EAGAIN; } @@ -316,6 +349,7 @@ static ssize_t ahci_read_em_buffer(struct device *dev, } spin_unlock_irqrestore(ap->lock, flags); + ahci_rpm_put_port(ap); return i; } @@ -340,11 +374,13 @@ static ssize_t ahci_store_em_buffer(struct device *dev, size % 4 || size > hpriv->em_buf_sz) return -EINVAL; + ahci_rpm_get_port(ap); spin_lock_irqsave(ap->lock, flags); em_ctl = readl(mmio + HOST_EM_CTL); if (em_ctl & EM_CTL_TM) { spin_unlock_irqrestore(ap->lock, flags); + ahci_rpm_put_port(ap); return -EBUSY; } @@ -357,6 +393,7 @@ static ssize_t ahci_store_em_buffer(struct device *dev, writel(em_ctl | EM_CTL_TM, mmio + HOST_EM_CTL); spin_unlock_irqrestore(ap->lock, flags); + ahci_rpm_put_port(ap); return size; } @@ -370,7 +407,9 @@ static ssize_t ahci_show_em_supported(struct device *dev, void __iomem *mmio = hpriv->mmio; u32 em_ctl; + ahci_rpm_get_port(ap); em_ctl = readl(mmio + HOST_EM_CTL); + ahci_rpm_put_port(ap); return sprintf(buf, "%s%s%s%s\n", em_ctl & EM_CTL_LED ? "led " : "", @@ -1014,6 +1053,7 @@ static ssize_t ahci_transmit_led_message(struct ata_port *ap, u32 state, else return -EINVAL; + ahci_rpm_get_port(ap); spin_lock_irqsave(ap->lock, flags); /* @@ -1023,6 +1063,7 @@ static ssize_t ahci_transmit_led_message(struct ata_port *ap, u32 state, em_ctl = readl(mmio + HOST_EM_CTL); if (em_ctl & EM_CTL_TM) { spin_unlock_irqrestore(ap->lock, flags); + ahci_rpm_put_port(ap); return -EBUSY; } @@ -1050,6 +1091,8 @@ static ssize_t ahci_transmit_led_message(struct ata_port *ap, u32 state, emp->led_state = state; spin_unlock_irqrestore(ap->lock, flags); + ahci_rpm_put_port(ap); + return size; } @@ -2216,6 +2259,8 @@ static void ahci_pmp_detach(struct ata_port *ap) int ahci_port_resume(struct ata_port *ap) { + ahci_rpm_get_port(ap); + ahci_power_up(ap); ahci_start_port(ap); @@ -2242,6 +2287,7 @@ static int ahci_port_suspend(struct ata_port *ap, pm_message_t mesg) ata_port_freeze(ap); } + ahci_rpm_put_port(ap); return rc; } #endif -- cgit v1.2.3