root/drivers/ata/libahci.c
// SPDX-License-Identifier: GPL-2.0-or-later
/*
 *  libahci.c - Common AHCI SATA low-level routines
 *
 *  Maintained by:  Tejun Heo <tj@kernel.org>
 *                  Please ALWAYS copy linux-ide@vger.kernel.org
 *                  on emails.
 *
 *  Copyright 2004-2005 Red Hat, Inc.
 *
 * libata documentation is available via 'make {ps|pdf}docs',
 * as Documentation/driver-api/libata.rst
 *
 * AHCI hardware documentation:
 * http://www.intel.com/technology/serialata/pdf/rev1_0.pdf
 * http://www.intel.com/technology/serialata/pdf/rev1_1.pdf
 */

#include <linux/bitops.h>
#include <linux/kernel.h>
#include <linux/gfp.h>
#include <linux/module.h>
#include <linux/nospec.h>
#include <linux/blkdev.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
#include <linux/dma-mapping.h>
#include <linux/device.h>
#include <scsi/scsi_host.h>
#include <scsi/scsi_cmnd.h>
#include <linux/libata.h>
#include <linux/pci.h>
#include "ahci.h"
#include "libata.h"

static int ahci_skip_host_reset;
int ahci_ignore_sss;
EXPORT_SYMBOL_GPL(ahci_ignore_sss);

module_param_named(skip_host_reset, ahci_skip_host_reset, int, 0444);
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_set_lpm(struct ata_link *link, enum ata_lpm_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);
static ssize_t ahci_transmit_led_message(struct ata_port *ap, u32 state,
                                        ssize_t size);



static int ahci_scr_read(struct ata_link *link, unsigned int sc_reg, u32 *val);
static int ahci_scr_write(struct ata_link *link, unsigned int sc_reg, u32 val);
static void ahci_qc_fill_rtf(struct ata_queued_cmd *qc);
static void ahci_qc_ncq_fill_rtf(struct ata_port *ap, u64 done_mask);
static int ahci_port_start(struct ata_port *ap);
static void ahci_port_stop(struct ata_port *ap);
static enum ata_completion_errors ahci_qc_prep(struct ata_queued_cmd *qc);
static int ahci_pmp_qc_defer(struct ata_queued_cmd *qc);
static void ahci_freeze(struct ata_port *ap);
static void ahci_thaw(struct ata_port *ap);
static void ahci_set_aggressive_devslp(struct ata_port *ap, bool sleep);
static void ahci_enable_fbs(struct ata_port *ap);
static void ahci_disable_fbs(struct ata_port *ap);
static void ahci_pmp_attach(struct ata_port *ap);
static void ahci_pmp_detach(struct ata_port *ap);
static int ahci_softreset(struct ata_link *link, unsigned int *class,
                          unsigned long deadline);
static int ahci_pmp_retry_softreset(struct ata_link *link, unsigned int *class,
                          unsigned long deadline);
static int ahci_hardreset(struct ata_link *link, unsigned int *class,
                          unsigned long deadline);
static void ahci_postreset(struct ata_link *link, unsigned int *class);
static void ahci_post_internal_cmd(struct ata_queued_cmd *qc);
static void ahci_dev_config(struct ata_device *dev);
#ifdef CONFIG_PM
static int ahci_port_suspend(struct ata_port *ap, pm_message_t mesg);
#endif
static ssize_t ahci_activity_show(struct ata_device *dev, char *buf);
static ssize_t ahci_activity_store(struct ata_device *dev,
                                   enum sw_activity val);
static void ahci_init_sw_activity(struct ata_link *link);

static ssize_t ahci_show_host_caps(struct device *dev,
                                   struct device_attribute *attr, char *buf);
static ssize_t ahci_show_host_cap2(struct device *dev,
                                   struct device_attribute *attr, char *buf);
static ssize_t ahci_show_host_version(struct device *dev,
                                      struct device_attribute *attr, char *buf);
static ssize_t ahci_show_port_cmd(struct device *dev,
                                  struct device_attribute *attr, char *buf);
static ssize_t ahci_read_em_buffer(struct device *dev,
                                   struct device_attribute *attr, char *buf);
static ssize_t ahci_store_em_buffer(struct device *dev,
                                    struct device_attribute *attr,
                                    const char *buf, size_t size);
static ssize_t ahci_show_em_supported(struct device *dev,
                                      struct device_attribute *attr, char *buf);
static irqreturn_t ahci_single_level_irq_intr(int irq, void *dev_instance);

static DEVICE_ATTR(ahci_host_caps, S_IRUGO, ahci_show_host_caps, NULL);
static DEVICE_ATTR(ahci_host_cap2, S_IRUGO, ahci_show_host_cap2, NULL);
static DEVICE_ATTR(ahci_host_version, S_IRUGO, ahci_show_host_version, NULL);
static DEVICE_ATTR(ahci_port_cmd, S_IRUGO, ahci_show_port_cmd, NULL);
static DEVICE_ATTR(em_buffer, S_IWUSR | S_IRUGO,
                   ahci_read_em_buffer, ahci_store_em_buffer);
static DEVICE_ATTR(em_message_supported, S_IRUGO, ahci_show_em_supported, NULL);

static struct attribute *ahci_shost_attrs[] = {
        &dev_attr_link_power_management_supported.attr,
        &dev_attr_link_power_management_policy.attr,
        &dev_attr_em_message_type.attr,
        &dev_attr_em_message.attr,
        &dev_attr_ahci_host_caps.attr,
        &dev_attr_ahci_host_cap2.attr,
        &dev_attr_ahci_host_version.attr,
        &dev_attr_ahci_port_cmd.attr,
        &dev_attr_em_buffer.attr,
        &dev_attr_em_message_supported.attr,
        NULL
};

static const struct attribute_group ahci_shost_attr_group = {
        .attrs = ahci_shost_attrs
};

const struct attribute_group *ahci_shost_groups[] = {
        &ahci_shost_attr_group,
        NULL
};
EXPORT_SYMBOL_GPL(ahci_shost_groups);

static struct attribute *ahci_sdev_attrs[] = {
        &dev_attr_sw_activity.attr,
        &dev_attr_unload_heads.attr,
        &dev_attr_ncq_prio_supported.attr,
        &dev_attr_ncq_prio_enable.attr,
        NULL
};

static const struct attribute_group ahci_sdev_attr_group = {
        .attrs = ahci_sdev_attrs
};

const struct attribute_group *ahci_sdev_groups[] = {
        &ahci_sdev_attr_group,
        NULL
};
EXPORT_SYMBOL_GPL(ahci_sdev_groups);

struct ata_port_operations ahci_ops = {
        .inherits               = &sata_pmp_port_ops,

        .qc_defer               = ahci_pmp_qc_defer,
        .qc_prep                = ahci_qc_prep,
        .qc_issue               = ahci_qc_issue,
        .qc_fill_rtf            = ahci_qc_fill_rtf,
        .qc_ncq_fill_rtf        = ahci_qc_ncq_fill_rtf,

        .freeze                 = ahci_freeze,
        .thaw                   = ahci_thaw,
        .reset.softreset        = ahci_softreset,
        .reset.hardreset        = ahci_hardreset,
        .reset.postreset        = ahci_postreset,
        .pmp_reset.softreset    = ahci_softreset,
        .error_handler          = ahci_error_handler,
        .post_internal_cmd      = ahci_post_internal_cmd,
        .dev_config             = ahci_dev_config,

        .scr_read               = ahci_scr_read,
        .scr_write              = ahci_scr_write,
        .pmp_attach             = ahci_pmp_attach,
        .pmp_detach             = ahci_pmp_detach,

        .set_lpm                = ahci_set_lpm,
        .em_show                = ahci_led_show,
        .em_store               = ahci_led_store,
        .sw_activity_show       = ahci_activity_show,
        .sw_activity_store      = ahci_activity_store,
        .transmit_led_message   = ahci_transmit_led_message,
#ifdef CONFIG_PM
        .port_suspend           = ahci_port_suspend,
        .port_resume            = ahci_port_resume,
#endif
        .port_start             = ahci_port_start,
        .port_stop              = ahci_port_stop,
};
EXPORT_SYMBOL_GPL(ahci_ops);

struct ata_port_operations ahci_pmp_retry_srst_ops = {
        .inherits               = &ahci_ops,
        .reset.softreset        = ahci_pmp_retry_softreset,
};
EXPORT_SYMBOL_GPL(ahci_pmp_retry_srst_ops);

static bool ahci_em_messages __read_mostly = true;
module_param(ahci_em_messages, bool, 0444);
/* add other LED protocol types when they become supported */
MODULE_PARM_DESC(ahci_em_messages,
        "AHCI Enclosure Management Message control (0 = off, 1 = on)");

/* device sleep idle timeout in ms */
static int devslp_idle_timeout __read_mostly = 1000;
module_param(devslp_idle_timeout, int, 0644);
MODULE_PARM_DESC(devslp_idle_timeout, "device sleep idle timeout");

static void ahci_enable_ahci(void __iomem *mmio)
{
        int i;
        u32 tmp;

        /* turn on AHCI_EN */
        tmp = readl(mmio + HOST_CTL);
        if (tmp & HOST_AHCI_EN)
                return;

        /* Some controllers need AHCI_EN to be written multiple times.
         * Try a few times before giving up.
         */
        for (i = 0; i < 5; i++) {
                tmp |= HOST_AHCI_EN;
                writel(tmp, mmio + HOST_CTL);
                tmp = readl(mmio + HOST_CTL);   /* flush && sanity check */
                if (tmp & HOST_AHCI_EN)
                        return;
                msleep(10);
        }

        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)
{
        struct Scsi_Host *shost = class_to_shost(dev);
        struct ata_port *ap = ata_shost_to_port(shost);
        struct ahci_host_priv *hpriv = ap->host->private_data;

        return sprintf(buf, "%x\n", hpriv->cap);
}

static ssize_t ahci_show_host_cap2(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);
        struct ahci_host_priv *hpriv = ap->host->private_data;

        return sprintf(buf, "%x\n", hpriv->cap2);
}

static ssize_t ahci_show_host_version(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);
        struct ahci_host_priv *hpriv = ap->host->private_data;

        return sprintf(buf, "%x\n", hpriv->version);
}

static ssize_t ahci_show_port_cmd(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);
        void __iomem *port_mmio = ahci_port_base(ap);
        ssize_t ret;

        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,
                                   struct device_attribute *attr, char *buf)
{
        struct Scsi_Host *shost = class_to_shost(dev);
        struct ata_port *ap = ata_shost_to_port(shost);
        struct ahci_host_priv *hpriv = ap->host->private_data;
        void __iomem *mmio = hpriv->mmio;
        void __iomem *em_mmio = mmio + hpriv->em_loc;
        u32 em_ctl, msg;
        unsigned long flags;
        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;
        }

        if (!(em_ctl & EM_CTL_SMB))
                em_mmio += hpriv->em_buf_sz;

        count = hpriv->em_buf_sz;

        /* the count should not be larger than PAGE_SIZE */
        if (count > PAGE_SIZE) {
                if (printk_ratelimit())
                        ata_port_warn(ap,
                                      "EM read buffer size too large: "
                                      "buffer size %u, page size %lu\n",
                                      hpriv->em_buf_sz, PAGE_SIZE);
                count = PAGE_SIZE;
        }

        for (i = 0; i < count; i += 4) {
                msg = readl(em_mmio + i);
                buf[i] = msg & 0xff;
                buf[i + 1] = (msg >> 8) & 0xff;
                buf[i + 2] = (msg >> 16) & 0xff;
                buf[i + 3] = (msg >> 24) & 0xff;
        }

        spin_unlock_irqrestore(ap->lock, flags);
        ahci_rpm_put_port(ap);

        return i;
}

static ssize_t ahci_store_em_buffer(struct device *dev,
                                    struct device_attribute *attr,
                                    const char *buf, size_t size)
{
        struct Scsi_Host *shost = class_to_shost(dev);
        struct ata_port *ap = ata_shost_to_port(shost);
        struct ahci_host_priv *hpriv = ap->host->private_data;
        void __iomem *mmio = hpriv->mmio;
        void __iomem *em_mmio = mmio + hpriv->em_loc;
        const unsigned char *msg_buf = buf;
        u32 em_ctl, msg;
        unsigned long flags;
        int i;

        /* check size validity */
        if (!(ap->flags & ATA_FLAG_EM) ||
            !(hpriv->em_msg_type & EM_MSG_TYPE_SGPIO) ||
            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;
        }

        for (i = 0; i < size; i += 4) {
                msg = msg_buf[i] | msg_buf[i + 1] << 8 |
                      msg_buf[i + 2] << 16 | msg_buf[i + 3] << 24;
                writel(msg, em_mmio + i);
        }

        writel(em_ctl | EM_CTL_TM, mmio + HOST_EM_CTL);

        spin_unlock_irqrestore(ap->lock, flags);
        ahci_rpm_put_port(ap);

        return size;
}

static ssize_t ahci_show_em_supported(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);
        struct ahci_host_priv *hpriv = ap->host->private_data;
        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 " : "",
                       em_ctl & EM_CTL_SAFTE ? "saf-te " : "",
                       em_ctl & EM_CTL_SES ? "ses-2 " : "",
                       em_ctl & EM_CTL_SGPIO ? "sgpio " : "");
}

/**
 *      ahci_save_initial_config - Save and fixup initial config values
 *      @dev: target AHCI device
 *      @hpriv: host private area to store config values
 *
 *      Some registers containing configuration info might be setup by
 *      BIOS and might be cleared on reset.  This function saves the
 *      initial values of those registers into @hpriv such that they
 *      can be restored after controller reset.
 *
 *      If inconsistent, config values are fixed up by this function.
 *
 *      If it is not set already this function sets hpriv->start_engine to
 *      ahci_start_engine.
 *
 *      LOCKING:
 *      None.
 */
void ahci_save_initial_config(struct device *dev, struct ahci_host_priv *hpriv)
{
        void __iomem *mmio = hpriv->mmio;
        void __iomem *port_mmio;
        unsigned long port_map;
        u32 cap, cap2, vers;
        int i;

        /* make sure AHCI mode is enabled before accessing CAP */
        ahci_enable_ahci(mmio);

        /*
         * Values prefixed with saved_ are written back to the HBA and ports
         * registers after reset. Values without are used for driver operation.
         */

        /*
         * Override HW-init HBA capability fields with the platform-specific
         * values. The rest of the HBA capabilities are defined as Read-only
         * and can't be modified in CSR anyway.
         */
        cap = readl(mmio + HOST_CAP);
        if (hpriv->saved_cap)
                cap = (cap & ~(HOST_CAP_SSS | HOST_CAP_MPS)) | hpriv->saved_cap;
        hpriv->saved_cap = cap;

        /* CAP2 register is only defined for AHCI 1.2 and later */
        vers = readl(mmio + HOST_VERSION);
        if ((vers >> 16) > 1 ||
           ((vers >> 16) == 1 && (vers & 0xFFFF) >= 0x200))
                hpriv->saved_cap2 = cap2 = readl(mmio + HOST_CAP2);
        else
                hpriv->saved_cap2 = cap2 = 0;

        /* some chips have errata preventing 64bit use */
        if ((cap & HOST_CAP_64) && (hpriv->flags & AHCI_HFLAG_32BIT_ONLY)) {
                dev_info(dev, "controller can't do 64bit DMA, forcing 32bit\n");
                cap &= ~HOST_CAP_64;
        }

        if ((cap & HOST_CAP_NCQ) && (hpriv->flags & AHCI_HFLAG_NO_NCQ)) {
                dev_info(dev, "controller can't do NCQ, turning off CAP_NCQ\n");
                cap &= ~HOST_CAP_NCQ;
        }

        if (!(cap & HOST_CAP_NCQ) && (hpriv->flags & AHCI_HFLAG_YES_NCQ)) {
                dev_info(dev, "controller can do NCQ, turning on CAP_NCQ\n");
                cap |= HOST_CAP_NCQ;
        }

        if ((cap & HOST_CAP_PMP) && (hpriv->flags & AHCI_HFLAG_NO_PMP)) {
                dev_info(dev, "controller can't do PMP, turning off CAP_PMP\n");
                cap &= ~HOST_CAP_PMP;
        }

        if ((cap & HOST_CAP_SNTF) && (hpriv->flags & AHCI_HFLAG_NO_SNTF)) {
                dev_info(dev,
                         "controller can't do SNTF, turning off CAP_SNTF\n");
                cap &= ~HOST_CAP_SNTF;
        }

        if ((cap2 & HOST_CAP2_SDS) && (hpriv->flags & AHCI_HFLAG_NO_DEVSLP)) {
                dev_info(dev,
                         "controller can't do DEVSLP, turning off\n");
                cap2 &= ~HOST_CAP2_SDS;
                cap2 &= ~HOST_CAP2_SADM;
        }

        if (!(cap & HOST_CAP_FBS) && (hpriv->flags & AHCI_HFLAG_YES_FBS)) {
                dev_info(dev, "controller can do FBS, turning on CAP_FBS\n");
                cap |= HOST_CAP_FBS;
        }

        if ((cap & HOST_CAP_FBS) && (hpriv->flags & AHCI_HFLAG_NO_FBS)) {
                dev_info(dev, "controller can't do FBS, turning off CAP_FBS\n");
                cap &= ~HOST_CAP_FBS;
        }

        if (!(cap & HOST_CAP_ALPM) && (hpriv->flags & AHCI_HFLAG_YES_ALPM)) {
                dev_info(dev, "controller can do ALPM, turning on CAP_ALPM\n");
                cap |= HOST_CAP_ALPM;
        }

        if ((cap & HOST_CAP_SXS) && (hpriv->flags & AHCI_HFLAG_NO_SXS)) {
                dev_info(dev, "controller does not support SXS, disabling CAP_SXS\n");
                cap &= ~HOST_CAP_SXS;
        }

        /* Override the HBA ports mapping if the platform needs it */
        port_map = readl(mmio + HOST_PORTS_IMPL);
        if (hpriv->saved_port_map && port_map != hpriv->saved_port_map) {
                dev_info(dev, "forcing port_map 0x%lx -> 0x%x\n",
                         port_map, hpriv->saved_port_map);
                port_map = hpriv->saved_port_map;
        } else {
                hpriv->saved_port_map = port_map;
        }

        /* mask_port_map not set means that all ports are available */
        if (hpriv->mask_port_map) {
                dev_warn(dev, "masking port_map 0x%lx -> 0x%lx\n",
                        port_map,
                        port_map & hpriv->mask_port_map);
                port_map &= hpriv->mask_port_map;
        }

        /* cross check port_map and cap.n_ports */
        if (port_map) {
                int map_ports = hweight_long(port_map);

                /* If PI has more ports than n_ports, whine, clear
                 * port_map and let it be generated from n_ports.
                 */
                if (map_ports > ahci_nr_ports(cap)) {
                        dev_warn(dev,
                                 "implemented port map (0x%lx) contains more ports than nr_ports (%u), using nr_ports\n",
                                 port_map, ahci_nr_ports(cap));
                        port_map = 0;
                }
        }

        /* fabricate port_map from cap.nr_ports for < AHCI 1.3 */
        if (!port_map && vers < 0x10300) {
                port_map = (1 << ahci_nr_ports(cap)) - 1;
                dev_warn(dev, "forcing PORTS_IMPL to 0x%lx\n", port_map);

                /* write the fixed up value to the PI register */
                hpriv->saved_port_map = port_map;
        }

        /*
         * Preserve the ports capabilities defined by the platform. Note there
         * is no need in storing the rest of the P#.CMD fields since they are
         * volatile.
         */
        for_each_set_bit(i, &port_map, AHCI_MAX_PORTS) {
                if (hpriv->saved_port_cap[i])
                        continue;

                port_mmio = __ahci_port_base(hpriv, i);
                hpriv->saved_port_cap[i] =
                        readl(port_mmio + PORT_CMD) & PORT_CMD_CAP;
        }

        /* record values to use during operation */
        hpriv->cap = cap;
        hpriv->cap2 = cap2;
        hpriv->version = vers;
        hpriv->port_map = port_map;

        if (!hpriv->start_engine)
                hpriv->start_engine = ahci_start_engine;

        if (!hpriv->stop_engine)
                hpriv->stop_engine = ahci_stop_engine;

        if (!hpriv->irq_handler)
                hpriv->irq_handler = ahci_single_level_irq_intr;
}
EXPORT_SYMBOL_GPL(ahci_save_initial_config);

/**
 *      ahci_restore_initial_config - Restore initial config
 *      @host: target ATA host
 *
 *      Restore initial config stored by ahci_save_initial_config().
 *
 *      LOCKING:
 *      None.
 */
static void ahci_restore_initial_config(struct ata_host *host)
{
        struct ahci_host_priv *hpriv = host->private_data;
        unsigned long port_map = hpriv->port_map;
        void __iomem *mmio = hpriv->mmio;
        void __iomem *port_mmio;
        int i;

        writel(hpriv->saved_cap, mmio + HOST_CAP);
        if (hpriv->saved_cap2)
                writel(hpriv->saved_cap2, mmio + HOST_CAP2);
        writel(hpriv->saved_port_map, mmio + HOST_PORTS_IMPL);
        (void) readl(mmio + HOST_PORTS_IMPL);   /* flush */

        for_each_set_bit(i, &port_map, AHCI_MAX_PORTS) {
                port_mmio = __ahci_port_base(hpriv, i);
                writel(hpriv->saved_port_cap[i], port_mmio + PORT_CMD);
        }
}

static unsigned ahci_scr_offset(struct ata_port *ap, unsigned int sc_reg)
{
        static const int offset[] = {
                [SCR_STATUS]            = PORT_SCR_STAT,
                [SCR_CONTROL]           = PORT_SCR_CTL,
                [SCR_ERROR]             = PORT_SCR_ERR,
                [SCR_ACTIVE]            = PORT_SCR_ACT,
                [SCR_NOTIFICATION]      = PORT_SCR_NTF,
        };
        struct ahci_host_priv *hpriv = ap->host->private_data;

        if (sc_reg < ARRAY_SIZE(offset) &&
            (sc_reg != SCR_NOTIFICATION || (hpriv->cap & HOST_CAP_SNTF)))
                return offset[sc_reg];
        return 0;
}

static int ahci_scr_read(struct ata_link *link, unsigned int sc_reg, u32 *val)
{
        void __iomem *port_mmio = ahci_port_base(link->ap);
        int offset = ahci_scr_offset(link->ap, sc_reg);

        if (offset) {
                *val = readl(port_mmio + offset);
                return 0;
        }
        return -EINVAL;
}

static int ahci_scr_write(struct ata_link *link, unsigned int sc_reg, u32 val)
{
        void __iomem *port_mmio = ahci_port_base(link->ap);
        int offset = ahci_scr_offset(link->ap, sc_reg);

        if (offset) {
                writel(val, port_mmio + offset);
                return 0;
        }
        return -EINVAL;
}

void ahci_start_engine(struct ata_port *ap)
{
        void __iomem *port_mmio = ahci_port_base(ap);
        u32 tmp;

        /* start DMA */
        tmp = readl(port_mmio + PORT_CMD);
        tmp |= PORT_CMD_START;
        writel(tmp, port_mmio + PORT_CMD);
        readl(port_mmio + PORT_CMD); /* flush */
}
EXPORT_SYMBOL_GPL(ahci_start_engine);

int ahci_stop_engine(struct ata_port *ap)
{
        void __iomem *port_mmio = ahci_port_base(ap);
        struct ahci_host_priv *hpriv = ap->host->private_data;
        u32 tmp;

        /*
         * On some controllers, stopping a port's DMA engine while the port
         * is in ALPM state (partial or slumber) results in failures on
         * subsequent DMA engine starts.  For those controllers, put the
         * port back in active state before stopping its DMA engine.
         */
        if ((hpriv->flags & AHCI_HFLAG_WAKE_BEFORE_STOP) &&
            (ap->link.lpm_policy > ATA_LPM_MAX_POWER) &&
            ahci_set_lpm(&ap->link, ATA_LPM_MAX_POWER, ATA_LPM_WAKE_ONLY)) {
                dev_err(ap->host->dev, "Failed to wake up port before engine stop\n");
                return -EIO;
        }

        tmp = readl(port_mmio + PORT_CMD);

        /* check if the HBA is idle */
        if ((tmp & (PORT_CMD_START | PORT_CMD_LIST_ON)) == 0)
                return 0;

        /*
         * Don't try to issue commands but return with ENODEV if the
         * AHCI controller not available anymore (e.g. due to PCIe hot
         * unplugging). Otherwise a 500ms delay for each port is added.
         */
        if (tmp == 0xffffffff) {
                dev_err(ap->host->dev, "AHCI controller unavailable!\n");
                return -ENODEV;
        }

        /* setting HBA to idle */
        tmp &= ~PORT_CMD_START;
        writel(tmp, port_mmio + PORT_CMD);

        /* wait for engine to stop. This could be as long as 500 msec */
        tmp = ata_wait_register(ap, port_mmio + PORT_CMD,
                                PORT_CMD_LIST_ON, PORT_CMD_LIST_ON, 1, 500);
        if (tmp & PORT_CMD_LIST_ON)
                return -EIO;

        return 0;
}
EXPORT_SYMBOL_GPL(ahci_stop_engine);

void ahci_start_fis_rx(struct ata_port *ap)
{
        void __iomem *port_mmio = ahci_port_base(ap);
        struct ahci_host_priv *hpriv = ap->host->private_data;
        struct ahci_port_priv *pp = ap->private_data;
        u32 tmp;

        /* set FIS registers */
        if (hpriv->cap & HOST_CAP_64)
                writel((pp->cmd_slot_dma >> 16) >> 16,
                       port_mmio + PORT_LST_ADDR_HI);
        writel(pp->cmd_slot_dma & 0xffffffff, port_mmio + PORT_LST_ADDR);

        if (hpriv->cap & HOST_CAP_64)
                writel((pp->rx_fis_dma >> 16) >> 16,
                       port_mmio + PORT_FIS_ADDR_HI);
        writel(pp->rx_fis_dma & 0xffffffff, port_mmio + PORT_FIS_ADDR);

        /* enable FIS reception */
        tmp = readl(port_mmio + PORT_CMD);
        tmp |= PORT_CMD_FIS_RX;
        writel(tmp, port_mmio + PORT_CMD);

        /* flush */
        readl(port_mmio + PORT_CMD);
}
EXPORT_SYMBOL_GPL(ahci_start_fis_rx);

static int ahci_stop_fis_rx(struct ata_port *ap)
{
        void __iomem *port_mmio = ahci_port_base(ap);
        u32 tmp;

        /* disable FIS reception */
        tmp = readl(port_mmio + PORT_CMD);
        tmp &= ~PORT_CMD_FIS_RX;
        writel(tmp, port_mmio + PORT_CMD);

        /* wait for completion, spec says 500ms, give it 1000 */
        tmp = ata_wait_register(ap, port_mmio + PORT_CMD, PORT_CMD_FIS_ON,
                                PORT_CMD_FIS_ON, 10, 1000);
        if (tmp & PORT_CMD_FIS_ON)
                return -EBUSY;

        return 0;
}

static void ahci_power_up(struct ata_port *ap)
{
        struct ahci_host_priv *hpriv = ap->host->private_data;
        void __iomem *port_mmio = ahci_port_base(ap);
        u32 cmd;

        cmd = readl(port_mmio + PORT_CMD) & ~PORT_CMD_ICC_MASK;

        /* spin up device */
        if (hpriv->cap & HOST_CAP_SSS) {
                cmd |= PORT_CMD_SPIN_UP;
                writel(cmd, port_mmio + PORT_CMD);
        }

        /* wake up link */
        writel(cmd | PORT_CMD_ICC_ACTIVE, port_mmio + PORT_CMD);
}

static int ahci_set_lpm(struct ata_link *link, enum ata_lpm_policy policy,
                        unsigned int hints)
{
        struct ata_port *ap = link->ap;
        struct ahci_host_priv *hpriv = ap->host->private_data;
        struct ahci_port_priv *pp = ap->private_data;
        void __iomem *port_mmio = ahci_port_base(ap);

        if (policy != ATA_LPM_MAX_POWER) {
                /* wakeup flag only applies to the max power policy */
                hints &= ~ATA_LPM_WAKE_ONLY;

                /*
                 * Disable interrupts on Phy Ready. This keeps us from
                 * getting woken up due to spurious phy ready
                 * interrupts.
                 */
                pp->intr_mask &= ~PORT_IRQ_PHYRDY;
                writel(pp->intr_mask, port_mmio + PORT_IRQ_MASK);

                sata_link_scr_lpm(link, policy, false);
        }

        if (hpriv->cap & HOST_CAP_ALPM) {
                u32 cmd = readl(port_mmio + PORT_CMD);

                if (policy == ATA_LPM_MAX_POWER || !(hints & ATA_LPM_HIPM)) {
                        if (!(hints & ATA_LPM_WAKE_ONLY))
                                cmd &= ~(PORT_CMD_ASP | PORT_CMD_ALPE);
                        cmd |= PORT_CMD_ICC_ACTIVE;

                        writel(cmd, port_mmio + PORT_CMD);
                        readl(port_mmio + PORT_CMD);

                        /* wait 10ms to be sure we've come out of LPM state */
                        ata_msleep(ap, 10);

                        if (hints & ATA_LPM_WAKE_ONLY)
                                return 0;
                } else {
                        cmd |= PORT_CMD_ALPE;
                        if (policy == ATA_LPM_MIN_POWER)
                                cmd |= PORT_CMD_ASP;
                        else if (policy == ATA_LPM_MIN_POWER_WITH_PARTIAL)
                                cmd &= ~PORT_CMD_ASP;

                        /* write out new cmd value */
                        writel(cmd, port_mmio + PORT_CMD);
                }
        }

        /* set aggressive device sleep */
        if ((hpriv->cap2 & HOST_CAP2_SDS) &&
            (hpriv->cap2 & HOST_CAP2_SADM) &&
            (link->device->flags & ATA_DFLAG_DEVSLP)) {
                if (policy == ATA_LPM_MIN_POWER ||
                    policy == ATA_LPM_MIN_POWER_WITH_PARTIAL)
                        ahci_set_aggressive_devslp(ap, true);
                else
                        ahci_set_aggressive_devslp(ap, false);
        }

        if (policy == ATA_LPM_MAX_POWER) {
                sata_link_scr_lpm(link, policy, false);

                /* turn PHYRDY IRQ back on */
                pp->intr_mask |= PORT_IRQ_PHYRDY;
                writel(pp->intr_mask, port_mmio + PORT_IRQ_MASK);
        }

        return 0;
}

#ifdef CONFIG_PM
static void ahci_power_down(struct ata_port *ap)
{
        struct ahci_host_priv *hpriv = ap->host->private_data;
        void __iomem *port_mmio = ahci_port_base(ap);
        u32 cmd, scontrol;

        if (!(hpriv->cap & HOST_CAP_SSS))
                return;

        /* put device into listen mode, first set PxSCTL.DET to 0 */
        scontrol = readl(port_mmio + PORT_SCR_CTL);
        scontrol &= ~0xf;
        writel(scontrol, port_mmio + PORT_SCR_CTL);

        /* then set PxCMD.SUD to 0 */
        cmd = readl(port_mmio + PORT_CMD) & ~PORT_CMD_ICC_MASK;
        cmd &= ~PORT_CMD_SPIN_UP;
        writel(cmd, port_mmio + PORT_CMD);
}
#endif

static void ahci_start_port(struct ata_port *ap)
{
        struct ahci_host_priv *hpriv = ap->host->private_data;
        struct ahci_port_priv *pp = ap->private_data;
        struct ata_link *link;
        struct ahci_em_priv *emp;
        ssize_t rc;
        int i;

        /* enable FIS reception */
        ahci_start_fis_rx(ap);

        /* enable DMA */
        if (!(hpriv->flags & AHCI_HFLAG_DELAY_ENGINE))
                hpriv->start_engine(ap);

        /* turn on LEDs */
        if (ap->flags & ATA_FLAG_EM) {
                ata_for_each_link(link, ap, EDGE) {
                        emp = &pp->em_priv[link->pmp];

                        /* EM Transmit bit maybe busy during init */
                        for (i = 0; i < EM_MAX_RETRY; i++) {
                                rc = ap->ops->transmit_led_message(ap,
                                                               emp->led_state,
                                                               4);
                                /*
                                 * If busy, give a breather but do not
                                 * release EH ownership by using msleep()
                                 * instead of ata_msleep().  EM Transmit
                                 * bit is busy for the whole host and
                                 * releasing ownership will cause other
                                 * ports to fail the same way.
                                 */
                                if (rc == -EBUSY)
                                        msleep(1);
                                else
                                        break;
                        }
                }
        }

        if (ap->flags & ATA_FLAG_SW_ACTIVITY)
                ata_for_each_link(link, ap, EDGE)
                        ahci_init_sw_activity(link);

}

static int ahci_deinit_port(struct ata_port *ap, const char **emsg)
{
        int rc;
        struct ahci_host_priv *hpriv = ap->host->private_data;

        /* disable DMA */
        rc = hpriv->stop_engine(ap);
        if (rc) {
                *emsg = "failed to stop engine";
                return rc;
        }

        /* disable FIS reception */
        rc = ahci_stop_fis_rx(ap);
        if (rc) {
                *emsg = "failed stop FIS RX";
                return rc;
        }

        return 0;
}

int ahci_reset_controller(struct ata_host *host)
{
        struct ahci_host_priv *hpriv = host->private_data;
        void __iomem *mmio = hpriv->mmio;
        u32 tmp;

        /*
         * We must be in AHCI mode, before using anything AHCI-specific, such
         * as HOST_RESET.
         */
        ahci_enable_ahci(mmio);

        /* Global controller reset */
        if (ahci_skip_host_reset) {
                dev_info(host->dev, "Skipping global host reset\n");
                return 0;
        }

        tmp = readl(mmio + HOST_CTL);
        if (!(tmp & HOST_RESET)) {
                writel(tmp | HOST_RESET, mmio + HOST_CTL);
                readl(mmio + HOST_CTL); /* flush */
        }

        /*
         * To perform host reset, OS should set HOST_RESET and poll until this
         * bit is read to be "0". Reset must complete within 1 second, or the
         * hardware should be considered fried.
         */
        tmp = ata_wait_register(NULL, mmio + HOST_CTL, HOST_RESET,
                                HOST_RESET, 10, 1000);
        if (tmp & HOST_RESET) {
                dev_err(host->dev, "Controller reset failed (0x%x)\n",
                        tmp);
                return -EIO;
        }

        /* Turn on AHCI mode */
        ahci_enable_ahci(mmio);

        /* Some registers might be cleared on reset. Restore initial values. */
        if (!(hpriv->flags & AHCI_HFLAG_NO_WRITE_TO_RO))
                ahci_restore_initial_config(host);

        return 0;
}
EXPORT_SYMBOL_GPL(ahci_reset_controller);

static void ahci_sw_activity(struct ata_link *link)
{
        struct ata_port *ap = link->ap;
        struct ahci_port_priv *pp = ap->private_data;
        struct ahci_em_priv *emp = &pp->em_priv[link->pmp];

        if (!(link->flags & ATA_LFLAG_SW_ACTIVITY))
                return;

        emp->activity++;
        if (!timer_pending(&emp->timer))
                mod_timer(&emp->timer, jiffies + msecs_to_jiffies(10));
}

static void ahci_sw_activity_blink(struct timer_list *t)
{
        struct ahci_em_priv *emp = timer_container_of(emp, t, timer);
        struct ata_link *link = emp->link;
        struct ata_port *ap = link->ap;

        unsigned long led_message = emp->led_state;
        u32 activity_led_state;
        unsigned long flags;

        led_message &= EM_MSG_LED_VALUE;
        led_message |= ap->port_no | (link->pmp << 8);

        /* check to see if we've had activity.  If so,
         * toggle state of LED and reset timer.  If not,
         * turn LED to desired idle state.
         */
        spin_lock_irqsave(ap->lock, flags);
        if (emp->saved_activity != emp->activity) {
                emp->saved_activity = emp->activity;
                /* get the current LED state */
                activity_led_state = led_message & EM_MSG_LED_VALUE_ON;

                if (activity_led_state)
                        activity_led_state = 0;
                else
                        activity_led_state = 1;

                /* clear old state */
                led_message &= ~EM_MSG_LED_VALUE_ACTIVITY;

                /* toggle state */
                led_message |= (activity_led_state << 16);
                mod_timer(&emp->timer, jiffies + msecs_to_jiffies(100));
        } else {
                /* switch to idle */
                led_message &= ~EM_MSG_LED_VALUE_ACTIVITY;
                if (emp->blink_policy == BLINK_OFF)
                        led_message |= (1 << 16);
        }
        spin_unlock_irqrestore(ap->lock, flags);
        ap->ops->transmit_led_message(ap, led_message, 4);
}

static void ahci_init_sw_activity(struct ata_link *link)
{
        struct ata_port *ap = link->ap;
        struct ahci_port_priv *pp = ap->private_data;
        struct ahci_em_priv *emp = &pp->em_priv[link->pmp];

        /* init activity stats, setup timer */
        emp->saved_activity = emp->activity = 0;
        emp->link = link;
        timer_setup(&emp->timer, ahci_sw_activity_blink, 0);

        /* check our blink policy and set flag for link if it's enabled */
        if (emp->blink_policy)
                link->flags |= ATA_LFLAG_SW_ACTIVITY;
}

int ahci_reset_em(struct ata_host *host)
{
        struct ahci_host_priv *hpriv = host->private_data;
        void __iomem *mmio = hpriv->mmio;
        u32 em_ctl;

        em_ctl = readl(mmio + HOST_EM_CTL);
        if ((em_ctl & EM_CTL_TM) || (em_ctl & EM_CTL_RST))
                return -EINVAL;

        writel(em_ctl | EM_CTL_RST, mmio + HOST_EM_CTL);
        return 0;
}
EXPORT_SYMBOL_GPL(ahci_reset_em);

static ssize_t ahci_transmit_led_message(struct ata_port *ap, u32 state,
                                        ssize_t size)
{
        struct ahci_host_priv *hpriv = ap->host->private_data;
        struct ahci_port_priv *pp = ap->private_data;
        void __iomem *mmio = hpriv->mmio;
        u32 em_ctl;
        u32 message[] = {0, 0};
        unsigned long flags;
        int pmp;
        struct ahci_em_priv *emp;

        /* get the slot number from the message */
        pmp = (state & EM_MSG_LED_PMP_SLOT) >> 8;
        if (pmp < EM_MAX_SLOTS)
                emp = &pp->em_priv[pmp];
        else
                return -EINVAL;

        ahci_rpm_get_port(ap);
        spin_lock_irqsave(ap->lock, flags);

        /*
         * if we are still busy transmitting a previous message,
         * do not allow
         */
        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;
        }

        if (hpriv->em_msg_type & EM_MSG_TYPE_LED) {
                /*
                 * create message header - this is all zero except for
                 * the message size, which is 4 bytes.
                 */
                message[0] |= (4 << 8);

                /* ignore 0:4 of byte zero, fill in port info yourself */
                message[1] = ((state & ~EM_MSG_LED_HBA_PORT) | ap->port_no);

                /* write message to EM_LOC */
                writel(message[0], mmio + hpriv->em_loc);
                writel(message[1], mmio + hpriv->em_loc+4);

                /*
                 * tell hardware to transmit the message
                 */
                writel(em_ctl | EM_CTL_TM, mmio + HOST_EM_CTL);
        }

        /* save off new led state for port/slot */
        emp->led_state = state;

        spin_unlock_irqrestore(ap->lock, flags);
        ahci_rpm_put_port(ap);

        return size;
}

static ssize_t ahci_led_show(struct ata_port *ap, char *buf)
{
        struct ahci_port_priv *pp = ap->private_data;
        struct ata_link *link;
        struct ahci_em_priv *emp;
        int rc = 0;

        ata_for_each_link(link, ap, EDGE) {
                emp = &pp->em_priv[link->pmp];
                rc += sprintf(buf, "%lx\n", emp->led_state);
        }
        return rc;
}

static ssize_t ahci_led_store(struct ata_port *ap, const char *buf,
                                size_t size)
{
        unsigned int state;
        int pmp;
        struct ahci_port_priv *pp = ap->private_data;
        struct ahci_em_priv *emp;

        if (kstrtouint(buf, 0, &state) < 0)
                return -EINVAL;

        /* get the slot number from the message */
        pmp = (state & EM_MSG_LED_PMP_SLOT) >> 8;
        if (pmp < EM_MAX_SLOTS) {
                pmp = array_index_nospec(pmp, EM_MAX_SLOTS);
                emp = &pp->em_priv[pmp];
        } else {
                return -EINVAL;
        }

        /* mask off the activity bits if we are in sw_activity
         * mode, user should turn off sw_activity before setting
         * activity led through em_message
         */
        if (emp->blink_policy)
                state &= ~EM_MSG_LED_VALUE_ACTIVITY;

        return ap->ops->transmit_led_message(ap, state, size);
}

static ssize_t ahci_activity_store(struct ata_device *dev, enum sw_activity val)
{
        struct ata_link *link = dev->link;
        struct ata_port *ap = link->ap;
        struct ahci_port_priv *pp = ap->private_data;
        struct ahci_em_priv *emp = &pp->em_priv[link->pmp];
        u32 port_led_state = emp->led_state;

        /* save the desired Activity LED behavior */
        if (val == OFF) {
                /* clear LFLAG */
                link->flags &= ~(ATA_LFLAG_SW_ACTIVITY);

                /* set the LED to OFF */
                port_led_state &= EM_MSG_LED_VALUE_OFF;
                port_led_state |= (ap->port_no | (link->pmp << 8));
                ap->ops->transmit_led_message(ap, port_led_state, 4);
        } else {
                link->flags |= ATA_LFLAG_SW_ACTIVITY;
                if (val == BLINK_OFF) {
                        /* set LED to ON for idle */
                        port_led_state &= EM_MSG_LED_VALUE_OFF;
                        port_led_state |= (ap->port_no | (link->pmp << 8));
                        port_led_state |= EM_MSG_LED_VALUE_ON; /* check this */
                        ap->ops->transmit_led_message(ap, port_led_state, 4);
                }
        }
        emp->blink_policy = val;
        return 0;
}

static ssize_t ahci_activity_show(struct ata_device *dev, char *buf)
{
        struct ata_link *link = dev->link;
        struct ata_port *ap = link->ap;
        struct ahci_port_priv *pp = ap->private_data;
        struct ahci_em_priv *emp = &pp->em_priv[link->pmp];

        /* display the saved value of activity behavior for this
         * disk.
         */
        return sprintf(buf, "%d\n", emp->blink_policy);
}

static void ahci_port_clear_pending_irq(struct ata_port *ap)
{
        struct ahci_host_priv *hpriv = ap->host->private_data;
        void __iomem *port_mmio = ahci_port_base(ap);
        u32 tmp;

        /* clear SError */
        tmp = readl(port_mmio + PORT_SCR_ERR);
        dev_dbg(ap->host->dev, "PORT_SCR_ERR 0x%x\n", tmp);
        writel(tmp, port_mmio + PORT_SCR_ERR);

        /* clear port IRQ */
        tmp = readl(port_mmio + PORT_IRQ_STAT);
        dev_dbg(ap->host->dev, "PORT_IRQ_STAT 0x%x\n", tmp);
        if (tmp)
                writel(tmp, port_mmio + PORT_IRQ_STAT);

        writel(1 << ap->port_no, hpriv->mmio + HOST_IRQ_STAT);
}

static void ahci_port_init(struct device *dev, struct ata_port *ap,
                           int port_no, void __iomem *mmio,
                           void __iomem *port_mmio)
{
        const char *emsg = NULL;
        int rc;

        /* make sure port is not active */
        rc = ahci_deinit_port(ap, &emsg);
        if (rc)
                dev_warn(dev, "%s (%d)\n", emsg, rc);

        ahci_port_clear_pending_irq(ap);
}

void ahci_init_controller(struct ata_host *host)
{
        struct ahci_host_priv *hpriv = host->private_data;
        void __iomem *mmio = hpriv->mmio;
        int i;
        void __iomem *port_mmio;
        u32 tmp;

        for (i = 0; i < host->n_ports; i++) {
                struct ata_port *ap = host->ports[i];

                port_mmio = ahci_port_base(ap);
                if (ata_port_is_dummy(ap))
                        continue;

                ahci_port_init(host->dev, ap, i, mmio, port_mmio);
        }

        tmp = readl(mmio + HOST_CTL);
        dev_dbg(host->dev, "HOST_CTL 0x%x\n", tmp);
        writel(tmp | HOST_IRQ_EN, mmio + HOST_CTL);
        tmp = readl(mmio + HOST_CTL);
        dev_dbg(host->dev, "HOST_CTL 0x%x\n", tmp);
}
EXPORT_SYMBOL_GPL(ahci_init_controller);

static void ahci_dev_config(struct ata_device *dev)
{
        struct ahci_host_priv *hpriv = dev->link->ap->host->private_data;

        if ((dev->class == ATA_DEV_ATAPI) &&
            (hpriv->flags & AHCI_HFLAG_ATAPI_DMA_QUIRK))
                dev->quirks |= ATA_QUIRK_ATAPI_MOD16_DMA;

        if (hpriv->flags & AHCI_HFLAG_SECT255) {
                dev->max_sectors = 255;
                ata_dev_info(dev,
                             "SB600 AHCI: limiting to 255 sectors per cmd\n");
        }
}

unsigned int ahci_dev_classify(struct ata_port *ap)
{
        void __iomem *port_mmio = ahci_port_base(ap);
        struct ata_taskfile tf;
        u32 tmp;

        tmp = readl(port_mmio + PORT_SIG);
        tf.lbah         = (tmp >> 24)   & 0xff;
        tf.lbam         = (tmp >> 16)   & 0xff;
        tf.lbal         = (tmp >> 8)    & 0xff;
        tf.nsect        = (tmp)         & 0xff;

        return ata_port_classify(ap, &tf);
}
EXPORT_SYMBOL_GPL(ahci_dev_classify);

void ahci_fill_cmd_slot(struct ahci_port_priv *pp, unsigned int tag,
                        u32 opts)
{
        dma_addr_t cmd_tbl_dma;

        cmd_tbl_dma = pp->cmd_tbl_dma + tag * AHCI_CMD_TBL_SZ;

        pp->cmd_slot[tag].opts = cpu_to_le32(opts);
        pp->cmd_slot[tag].status = 0;
        pp->cmd_slot[tag].tbl_addr = cpu_to_le32(cmd_tbl_dma & 0xffffffff);
        pp->cmd_slot[tag].tbl_addr_hi = cpu_to_le32((cmd_tbl_dma >> 16) >> 16);
}
EXPORT_SYMBOL_GPL(ahci_fill_cmd_slot);

int ahci_kick_engine(struct ata_port *ap)
{
        void __iomem *port_mmio = ahci_port_base(ap);
        struct ahci_host_priv *hpriv = ap->host->private_data;
        u8 status = readl(port_mmio + PORT_TFDATA) & 0xFF;
        u32 tmp;
        int busy, rc;

        /* stop engine */
        rc = hpriv->stop_engine(ap);
        if (rc)
                goto out_restart;

        /* need to do CLO?
         * always do CLO if PMP is attached (AHCI-1.3 9.2)
         */
        busy = status & (ATA_BUSY | ATA_DRQ);
        if (!busy && !sata_pmp_attached(ap)) {
                rc = 0;
                goto out_restart;
        }

        if (!(hpriv->cap & HOST_CAP_CLO)) {
                rc = -EOPNOTSUPP;
                goto out_restart;
        }

        /* perform CLO */
        tmp = readl(port_mmio + PORT_CMD);
        tmp |= PORT_CMD_CLO;
        writel(tmp, port_mmio + PORT_CMD);

        rc = 0;
        tmp = ata_wait_register(ap, port_mmio + PORT_CMD,
                                PORT_CMD_CLO, PORT_CMD_CLO, 1, 500);
        if (tmp & PORT_CMD_CLO)
                rc = -EIO;

        /* restart engine */
 out_restart:
        hpriv->start_engine(ap);
        return rc;
}
EXPORT_SYMBOL_GPL(ahci_kick_engine);

static int ahci_exec_polled_cmd(struct ata_port *ap, int pmp,
                                struct ata_taskfile *tf, int is_cmd, u16 flags,
                                unsigned int timeout_msec)
{
        const u32 cmd_fis_len = 5; /* five dwords */
        struct ahci_port_priv *pp = ap->private_data;
        void __iomem *port_mmio = ahci_port_base(ap);
        u8 *fis = pp->cmd_tbl;
        u32 tmp;

        /* prep the command */
        ata_tf_to_fis(tf, pmp, is_cmd, fis);
        ahci_fill_cmd_slot(pp, 0, cmd_fis_len | flags | (pmp << 12));

        /* set port value for softreset of Port Multiplier */
        if (pp->fbs_enabled && pp->fbs_last_dev != pmp) {
                tmp = readl(port_mmio + PORT_FBS);
                tmp &= ~(PORT_FBS_DEV_MASK | PORT_FBS_DEC);
                tmp |= pmp << PORT_FBS_DEV_OFFSET;
                writel(tmp, port_mmio + PORT_FBS);
                pp->fbs_last_dev = pmp;
        }

        /* issue & wait */
        writel(1, port_mmio + PORT_CMD_ISSUE);

        if (timeout_msec) {
                tmp = ata_wait_register(ap, port_mmio + PORT_CMD_ISSUE,
                                        0x1, 0x1, 1, timeout_msec);
                if (tmp & 0x1) {
                        ahci_kick_engine(ap);
                        return -EBUSY;
                }
        } else
                readl(port_mmio + PORT_CMD_ISSUE);      /* flush */

        return 0;
}

int ahci_do_softreset(struct ata_link *link, unsigned int *class,
                      int pmp, unsigned long deadline,
                      int (*check_ready)(struct ata_link *link))
{
        struct ata_port *ap = link->ap;
        struct ahci_host_priv *hpriv = ap->host->private_data;
        struct ahci_port_priv *pp = ap->private_data;
        const char *reason = NULL;
        unsigned long now;
        unsigned int msecs;
        struct ata_taskfile tf;
        bool fbs_disabled = false;
        int rc;

        /* prepare for SRST (AHCI-1.1 10.4.1) */
        rc = ahci_kick_engine(ap);
        if (rc && rc != -EOPNOTSUPP)
                ata_link_warn(link, "failed to reset engine (errno=%d)\n", rc);

        /*
         * According to AHCI-1.2 9.3.9: if FBS is enable, software shall
         * clear PxFBS.EN to '0' prior to issuing software reset to devices
         * that is attached to port multiplier.
         */
        if (!ata_is_host_link(link) && pp->fbs_enabled) {
                ahci_disable_fbs(ap);
                fbs_disabled = true;
        }

        ata_tf_init(link->device, &tf);

        /* issue the first H2D Register FIS */
        msecs = 0;
        now = jiffies;
        if (time_after(deadline, now))
                msecs = jiffies_to_msecs(deadline - now);

        tf.ctl |= ATA_SRST;
        if (ahci_exec_polled_cmd(ap, pmp, &tf, 0,
                                 AHCI_CMD_RESET | AHCI_CMD_CLR_BUSY, msecs)) {
                rc = -EIO;
                reason = "1st FIS failed";
                goto fail;
        }

        /* spec says at least 5us, but be generous and sleep for 1ms */
        ata_msleep(ap, 1);

        /* issue the second H2D Register FIS */
        tf.ctl &= ~ATA_SRST;
        ahci_exec_polled_cmd(ap, pmp, &tf, 0, 0, 0);

        /* wait for link to become ready */
        rc = ata_wait_after_reset(link, deadline, check_ready);
        if (rc == -EBUSY && hpriv->flags & AHCI_HFLAG_SRST_TOUT_IS_OFFLINE) {
                /*
                 * Workaround for cases where link online status can't
                 * be trusted.  Treat device readiness timeout as link
                 * offline.
                 */
                ata_link_info(link, "device not ready, treating as offline\n");
                *class = ATA_DEV_NONE;
        } else if (rc) {
                /* link occupied, -ENODEV too is an error */
                reason = "device not ready";
                goto fail;
        } else
                *class = ahci_dev_classify(ap);

        /* re-enable FBS if disabled before */
        if (fbs_disabled)
                ahci_enable_fbs(ap);

        return 0;

 fail:
        ata_link_err(link, "softreset failed (%s)\n", reason);
        return rc;
}
EXPORT_SYMBOL_GPL(ahci_do_softreset);

int ahci_check_ready(struct ata_link *link)
{
        void __iomem *port_mmio = ahci_port_base(link->ap);
        u8 status = readl(port_mmio + PORT_TFDATA) & 0xFF;

        return ata_check_ready(status);
}
EXPORT_SYMBOL_GPL(ahci_check_ready);

static int ahci_softreset(struct ata_link *link, unsigned int *class,
                          unsigned long deadline)
{
        int pmp = sata_srst_pmp(link);

        return ahci_do_softreset(link, class, pmp, deadline, ahci_check_ready);
}

static int ahci_bad_pmp_check_ready(struct ata_link *link)
{
        void __iomem *port_mmio = ahci_port_base(link->ap);
        u8 status = readl(port_mmio + PORT_TFDATA) & 0xFF;
        u32 irq_status = readl(port_mmio + PORT_IRQ_STAT);

        /*
         * There is no need to check TFDATA if BAD PMP is found due to HW bug,
         * which can save timeout delay.
         */
        if (irq_status & PORT_IRQ_BAD_PMP)
                return -EIO;

        return ata_check_ready(status);
}

static int ahci_pmp_retry_softreset(struct ata_link *link, unsigned int *class,
                                    unsigned long deadline)
{
        struct ata_port *ap = link->ap;
        void __iomem *port_mmio = ahci_port_base(ap);
        int pmp = sata_srst_pmp(link);
        int rc;
        u32 irq_sts;

        rc = ahci_do_softreset(link, class, pmp, deadline,
                               ahci_bad_pmp_check_ready);

        /*
         * Soft reset fails with IPMS set when PMP is enabled but
         * SATA HDD/ODD is connected to SATA port, do soft reset
         * again to port 0.
         */
        if (rc == -EIO) {
                irq_sts = readl(port_mmio + PORT_IRQ_STAT);
                if (irq_sts & PORT_IRQ_BAD_PMP) {
                        ata_link_warn(link,
                                        "applying PMP SRST workaround "
                                        "and retrying\n");
                        rc = ahci_do_softreset(link, class, 0, deadline,
                                               ahci_check_ready);
                }
        }

        return rc;
}

int ahci_do_hardreset(struct ata_link *link, unsigned int *class,
                      unsigned long deadline, bool *online)
{
        const unsigned int *timing = sata_ehc_deb_timing(&link->eh_context);
        struct ata_port *ap = link->ap;
        struct ahci_port_priv *pp = ap->private_data;
        struct ahci_host_priv *hpriv = ap->host->private_data;
        u8 *d2h_fis = pp->rx_fis + RX_FIS_D2H_REG;
        struct ata_taskfile tf;
        int rc;

        hpriv->stop_engine(ap);

        /* clear D2H reception area to properly wait for D2H FIS */
        ata_tf_init(link->device, &tf);
        tf.status = ATA_BUSY;
        ata_tf_to_fis(&tf, 0, 0, d2h_fis);

        ahci_port_clear_pending_irq(ap);

        rc = sata_link_hardreset(link, timing, deadline, online,
                                 ahci_check_ready);

        hpriv->start_engine(ap);

        if (*online)
                *class = ahci_dev_classify(ap);

        return rc;
}
EXPORT_SYMBOL_GPL(ahci_do_hardreset);

static int ahci_hardreset(struct ata_link *link, unsigned int *class,
                          unsigned long deadline)
{
        bool online;

        return ahci_do_hardreset(link, class, deadline, &online);
}

static void ahci_postreset(struct ata_link *link, unsigned int *class)
{
        struct ata_port *ap = link->ap;
        void __iomem *port_mmio = ahci_port_base(ap);
        u32 new_tmp, tmp;

        ata_std_postreset(link, class);

        /* Make sure port's ATAPI bit is set appropriately */
        new_tmp = tmp = readl(port_mmio + PORT_CMD);
        if (*class == ATA_DEV_ATAPI)
                new_tmp |= PORT_CMD_ATAPI;
        else
                new_tmp &= ~PORT_CMD_ATAPI;
        if (new_tmp != tmp) {
                writel(new_tmp, port_mmio + PORT_CMD);
                readl(port_mmio + PORT_CMD); /* flush */
        }
}

static unsigned int ahci_fill_sg(struct ata_queued_cmd *qc, void *cmd_tbl)
{
        struct scatterlist *sg;
        struct ahci_sg *ahci_sg = cmd_tbl + AHCI_CMD_TBL_HDR_SZ;
        unsigned int si;

        /*
         * Next, the S/G list.
         */
        for_each_sg(qc->sg, sg, qc->n_elem, si) {
                dma_addr_t addr = sg_dma_address(sg);
                u32 sg_len = sg_dma_len(sg);

                ahci_sg[si].addr = cpu_to_le32(addr & 0xffffffff);
                ahci_sg[si].addr_hi = cpu_to_le32((addr >> 16) >> 16);
                ahci_sg[si].flags_size = cpu_to_le32(sg_len - 1);
        }

        return si;
}

static int ahci_pmp_qc_defer(struct ata_queued_cmd *qc)
{
        struct ata_port *ap = qc->ap;
        struct ahci_port_priv *pp = ap->private_data;

        if (!sata_pmp_attached(ap) || pp->fbs_enabled)
                return ata_std_qc_defer(qc);
        else
                return sata_pmp_qc_defer_cmd_switch(qc);
}

static enum ata_completion_errors ahci_qc_prep(struct ata_queued_cmd *qc)
{
        struct ata_port *ap = qc->ap;
        struct ahci_port_priv *pp = ap->private_data;
        int is_atapi = ata_is_atapi(qc->tf.protocol);
        void *cmd_tbl;
        u32 opts;
        const u32 cmd_fis_len = 5; /* five dwords */
        unsigned int n_elem;

        /*
         * Fill in command table information.  First, the header,
         * a SATA Register - Host to Device command FIS.
         */
        cmd_tbl = pp->cmd_tbl + qc->hw_tag * AHCI_CMD_TBL_SZ;

        ata_tf_to_fis(&qc->tf, qc->dev->link->pmp, 1, cmd_tbl);
        if (is_atapi) {
                memset(cmd_tbl + AHCI_CMD_TBL_CDB, 0, 32);
                memcpy(cmd_tbl + AHCI_CMD_TBL_CDB, qc->cdb, qc->dev->cdb_len);
        }

        n_elem = 0;
        if (qc->flags & ATA_QCFLAG_DMAMAP)
                n_elem = ahci_fill_sg(qc, cmd_tbl);

        /*
         * Fill in command slot information.
         */
        opts = cmd_fis_len | n_elem << 16 | (qc->dev->link->pmp << 12);
        if (qc->tf.flags & ATA_TFLAG_WRITE)
                opts |= AHCI_CMD_WRITE;
        if (is_atapi)
                opts |= AHCI_CMD_ATAPI | AHCI_CMD_PREFETCH;

        ahci_fill_cmd_slot(pp, qc->hw_tag, opts);

        return AC_ERR_OK;
}

static void ahci_fbs_dec_intr(struct ata_port *ap)
{
        struct ahci_port_priv *pp = ap->private_data;
        void __iomem *port_mmio = ahci_port_base(ap);
        u32 fbs = readl(port_mmio + PORT_FBS);
        int retries = 3;

        BUG_ON(!pp->fbs_enabled);

        /* time to wait for DEC is not specified by AHCI spec,
         * add a retry loop for safety.
         */
        writel(fbs | PORT_FBS_DEC, port_mmio + PORT_FBS);
        fbs = readl(port_mmio + PORT_FBS);
        while ((fbs & PORT_FBS_DEC) && retries--) {
                udelay(1);
                fbs = readl(port_mmio + PORT_FBS);
        }

        if (fbs & PORT_FBS_DEC)
                dev_err(ap->host->dev, "failed to clear device error\n");
}

static void ahci_error_intr(struct ata_port *ap, u32 irq_stat)
{
        struct ahci_host_priv *hpriv = ap->host->private_data;
        struct ahci_port_priv *pp = ap->private_data;
        struct ata_eh_info *host_ehi = &ap->link.eh_info;
        struct ata_link *link = NULL;
        struct ata_queued_cmd *active_qc;
        struct ata_eh_info *active_ehi;
        bool fbs_need_dec = false;
        u32 serror;

        /* determine active link with error */
        if (pp->fbs_enabled) {
                void __iomem *port_mmio = ahci_port_base(ap);
                u32 fbs = readl(port_mmio + PORT_FBS);
                int pmp = fbs >> PORT_FBS_DWE_OFFSET;

                if ((fbs & PORT_FBS_SDE) && (pmp < ap->nr_pmp_links)) {
                        link = &ap->pmp_link[pmp];
                        fbs_need_dec = true;
                }

        } else
                ata_for_each_link(link, ap, EDGE)
                        if (ata_link_active(link))
                                break;

        if (!link)
                link = &ap->link;

        active_qc = ata_qc_from_tag(ap, link->active_tag);
        active_ehi = &link->eh_info;

        /* record irq stat */
        ata_ehi_clear_desc(host_ehi);
        ata_ehi_push_desc(host_ehi, "irq_stat 0x%08x", irq_stat);

        /* AHCI needs SError cleared; otherwise, it might lock up */
        ahci_scr_read(&ap->link, SCR_ERROR, &serror);
        ahci_scr_write(&ap->link, SCR_ERROR, serror);
        host_ehi->serror |= serror;

        /* some controllers set IRQ_IF_ERR on device errors, ignore it */
        if (hpriv->flags & AHCI_HFLAG_IGN_IRQ_IF_ERR)
                irq_stat &= ~PORT_IRQ_IF_ERR;

        if (irq_stat & PORT_IRQ_TF_ERR) {
                /* If qc is active, charge it; otherwise, the active
                 * link.  There's no active qc on NCQ errors.  It will
                 * be determined by EH by reading log page 10h.
                 */
                if (active_qc)
                        active_qc->err_mask |= AC_ERR_DEV;
                else
                        active_ehi->err_mask |= AC_ERR_DEV;

                if (hpriv->flags & AHCI_HFLAG_IGN_SERR_INTERNAL)
                        host_ehi->serror &= ~SERR_INTERNAL;
        }

        if (irq_stat & PORT_IRQ_UNK_FIS) {
                u32 *unk = pp->rx_fis + RX_FIS_UNK;

                active_ehi->err_mask |= AC_ERR_HSM;
                active_ehi->action |= ATA_EH_RESET;
                ata_ehi_push_desc(active_ehi,
                                  "unknown FIS %08x %08x %08x %08x" ,
                                  unk[0], unk[1], unk[2], unk[3]);
        }

        if (sata_pmp_attached(ap) && (irq_stat & PORT_IRQ_BAD_PMP)) {
                active_ehi->err_mask |= AC_ERR_HSM;
                active_ehi->action |= ATA_EH_RESET;
                ata_ehi_push_desc(active_ehi, "incorrect PMP");
        }

        if (irq_stat & (PORT_IRQ_HBUS_ERR | PORT_IRQ_HBUS_DATA_ERR)) {
                host_ehi->err_mask |= AC_ERR_HOST_BUS;
                host_ehi->action |= ATA_EH_RESET;
                ata_ehi_push_desc(host_ehi, "host bus error");
        }

        if (irq_stat & PORT_IRQ_IF_ERR) {
                if (fbs_need_dec)
                        active_ehi->err_mask |= AC_ERR_DEV;
                else {
                        host_ehi->err_mask |= AC_ERR_ATA_BUS;
                        host_ehi->action |= ATA_EH_RESET;
                }

                ata_ehi_push_desc(host_ehi, "interface fatal error");
        }

        if (irq_stat & (PORT_IRQ_CONNECT | PORT_IRQ_PHYRDY)) {
                ata_ehi_hotplugged(host_ehi);
                ata_ehi_push_desc(host_ehi, "%s",
                        irq_stat & PORT_IRQ_CONNECT ?
                        "connection status changed" : "PHY RDY changed");
        }

        /* okay, let's hand over to EH */

        if (irq_stat & PORT_IRQ_FREEZE)
                ata_port_freeze(ap);
        else if (fbs_need_dec) {
                ata_link_abort(link);
                ahci_fbs_dec_intr(ap);
        } else
                ata_port_abort(ap);
}

static void ahci_qc_complete(struct ata_port *ap, void __iomem *port_mmio)
{
        struct ata_eh_info *ehi = &ap->link.eh_info;
        struct ahci_port_priv *pp = ap->private_data;
        u32 qc_active = 0;
        int rc;

        /*
         * pp->active_link is not reliable once FBS is enabled, both
         * PORT_SCR_ACT and PORT_CMD_ISSUE should be checked because
         * NCQ and non-NCQ commands may be in flight at the same time.
         */
        if (pp->fbs_enabled) {
                if (ap->qc_active) {
                        qc_active = readl(port_mmio + PORT_SCR_ACT);
                        qc_active |= readl(port_mmio + PORT_CMD_ISSUE);
                }
        } else {
                /* pp->active_link is valid iff any command is in flight */
                if (ap->qc_active && pp->active_link->sactive)
                        qc_active = readl(port_mmio + PORT_SCR_ACT);
                else
                        qc_active = readl(port_mmio + PORT_CMD_ISSUE);
        }

        rc = ata_qc_complete_multiple(ap, qc_active);
        if (unlikely(rc < 0 && !(ap->pflags & ATA_PFLAG_RESETTING))) {
                ehi->err_mask |= AC_ERR_HSM;
                ehi->action |= ATA_EH_RESET;
                ata_port_freeze(ap);
        }
}

static void ahci_handle_port_interrupt(struct ata_port *ap,
                                       void __iomem *port_mmio, u32 status)
{
        struct ahci_port_priv *pp = ap->private_data;
        struct ahci_host_priv *hpriv = ap->host->private_data;

        /* ignore BAD_PMP while resetting */
        if (unlikely(ap->pflags & ATA_PFLAG_RESETTING))
                status &= ~PORT_IRQ_BAD_PMP;

        if (sata_lpm_ignore_phy_events(&ap->link)) {
                status &= ~PORT_IRQ_PHYRDY;
                ahci_scr_write(&ap->link, SCR_ERROR, SERR_PHYRDY_CHG);
        }

        if (unlikely(status & PORT_IRQ_ERROR)) {
                /*
                 * Before getting the error notification, we may have
                 * received SDB FISes notifying successful completions.
                 * Handle these first and then handle the error.
                 */
                ahci_qc_complete(ap, port_mmio);
                ahci_error_intr(ap, status);
                return;
        }

        if (status & PORT_IRQ_SDB_FIS) {
                /* If SNotification is available, leave notification
                 * handling to sata_async_notification().  If not,
                 * emulate it by snooping SDB FIS RX area.
                 *
                 * Snooping FIS RX area is probably cheaper than
                 * poking SNotification but some constrollers which
                 * implement SNotification, ICH9 for example, don't
                 * store AN SDB FIS into receive area.
                 */
                if (hpriv->cap & HOST_CAP_SNTF)
                        sata_async_notification(ap);
                else {
                        /* If the 'N' bit in word 0 of the FIS is set,
                         * we just received asynchronous notification.
                         * Tell libata about it.
                         *
                         * Lack of SNotification should not appear in
                         * ahci 1.2, so the workaround is unnecessary
                         * when FBS is enabled.
                         */
                        if (pp->fbs_enabled)
                                WARN_ON_ONCE(1);
                        else {
                                const __le32 *f = pp->rx_fis + RX_FIS_SDB;
                                u32 f0 = le32_to_cpu(f[0]);
                                if (f0 & (1 << 15))
                                        sata_async_notification(ap);
                        }
                }
        }

        /* Handle completed commands */
        ahci_qc_complete(ap, port_mmio);
}

static void ahci_port_intr(struct ata_port *ap)
{
        void __iomem *port_mmio = ahci_port_base(ap);
        u32 status;

        status = readl(port_mmio + PORT_IRQ_STAT);
        writel(status, port_mmio + PORT_IRQ_STAT);

        ahci_handle_port_interrupt(ap, port_mmio, status);
}

static irqreturn_t ahci_multi_irqs_intr_hard(int irq, void *dev_instance)
{
        struct ata_port *ap = dev_instance;
        void __iomem *port_mmio = ahci_port_base(ap);
        u32 status;

        status = readl(port_mmio + PORT_IRQ_STAT);
        writel(status, port_mmio + PORT_IRQ_STAT);

        spin_lock(ap->lock);
        ahci_handle_port_interrupt(ap, port_mmio, status);
        spin_unlock(ap->lock);

        return IRQ_HANDLED;
}

u32 ahci_handle_port_intr(struct ata_host *host, u32 irq_masked)
{
        unsigned int i, handled = 0;

        for (i = 0; i < host->n_ports; i++) {
                struct ata_port *ap;

                if (!(irq_masked & (1 << i)))
                        continue;

                ap = host->ports[i];
                if (ap) {
                        ahci_port_intr(ap);
                } else {
                        if (ata_ratelimit())
                                dev_warn(host->dev,
                                         "interrupt on disabled port %u\n", i);
                }

                handled = 1;
        }

        return handled;
}
EXPORT_SYMBOL_GPL(ahci_handle_port_intr);

static irqreturn_t ahci_single_level_irq_intr(int irq, void *dev_instance)
{
        struct ata_host *host = dev_instance;
        struct ahci_host_priv *hpriv;
        unsigned int rc = 0;
        void __iomem *mmio;
        u32 irq_stat, irq_masked;

        hpriv = host->private_data;
        mmio = hpriv->mmio;

        /* sigh.  0xffffffff is a valid return from h/w */
        irq_stat = readl(mmio + HOST_IRQ_STAT);
        if (!irq_stat)
                return IRQ_NONE;

        irq_masked = irq_stat & hpriv->port_map;

        spin_lock(&host->lock);

        rc = ahci_handle_port_intr(host, irq_masked);

        /* HOST_IRQ_STAT behaves as level triggered latch meaning that
         * it should be cleared after all the port events are cleared;
         * otherwise, it will raise a spurious interrupt after each
         * valid one.  Please read section 10.6.2 of ahci 1.1 for more
         * information.
         *
         * Also, use the unmasked value to clear interrupt as spurious
         * pending event on a dummy port might cause screaming IRQ.
         */
        writel(irq_stat, mmio + HOST_IRQ_STAT);

        spin_unlock(&host->lock);

        return IRQ_RETVAL(rc);
}

unsigned int ahci_qc_issue(struct ata_queued_cmd *qc)
{
        struct ata_port *ap = qc->ap;
        void __iomem *port_mmio = ahci_port_base(ap);
        struct ahci_port_priv *pp = ap->private_data;

        /* Keep track of the currently active link.  It will be used
         * in completion path to determine whether NCQ phase is in
         * progress.
         */
        pp->active_link = qc->dev->link;

        if (ata_is_ncq(qc->tf.protocol))
                writel(1 << qc->hw_tag, port_mmio + PORT_SCR_ACT);

        if (pp->fbs_enabled && pp->fbs_last_dev != qc->dev->link->pmp) {
                u32 fbs = readl(port_mmio + PORT_FBS);
                fbs &= ~(PORT_FBS_DEV_MASK | PORT_FBS_DEC);
                fbs |= qc->dev->link->pmp << PORT_FBS_DEV_OFFSET;
                writel(fbs, port_mmio + PORT_FBS);
                pp->fbs_last_dev = qc->dev->link->pmp;
        }

        writel(1 << qc->hw_tag, port_mmio + PORT_CMD_ISSUE);

        ahci_sw_activity(qc->dev->link);

        return 0;
}
EXPORT_SYMBOL_GPL(ahci_qc_issue);

static void ahci_qc_fill_rtf(struct ata_queued_cmd *qc)
{
        struct ahci_port_priv *pp = qc->ap->private_data;
        u8 *rx_fis = pp->rx_fis;

        if (pp->fbs_enabled)
                rx_fis += qc->dev->link->pmp * AHCI_RX_FIS_SZ;

        /*
         * After a successful execution of an ATA PIO data-in command,
         * the device doesn't send D2H Reg FIS to update the TF and
         * the host should take TF and E_Status from the preceding PIO
         * Setup FIS.
         */
        if (qc->tf.protocol == ATA_PROT_PIO && qc->dma_dir == DMA_FROM_DEVICE &&
            !(qc->flags & ATA_QCFLAG_EH)) {
                ata_tf_from_fis(rx_fis + RX_FIS_PIO_SETUP, &qc->result_tf);
                qc->result_tf.status = (rx_fis + RX_FIS_PIO_SETUP)[15];
                return;
        }

        /*
         * For NCQ commands, we never get a D2H FIS, so reading the D2H Register
         * FIS area of the Received FIS Structure (which contains a copy of the
         * last D2H FIS received) will contain an outdated status code.
         * For NCQ commands, we instead get a SDB FIS, so read the SDB FIS area
         * instead. However, the SDB FIS does not contain the LBA, so we can't
         * use the ata_tf_from_fis() helper.
         */
        if (ata_is_ncq(qc->tf.protocol)) {
                const u8 *fis = rx_fis + RX_FIS_SDB;

                /*
                 * Successful NCQ commands have been filled already.
                 * A failed NCQ command will read the status here.
                 * (Note that a failed NCQ command will get a more specific
                 * error when reading the NCQ Command Error log.)
                 */
                qc->result_tf.status = fis[2];
                qc->result_tf.error = fis[3];
                return;
        }

        ata_tf_from_fis(rx_fis + RX_FIS_D2H_REG, &qc->result_tf);
}

static void ahci_qc_ncq_fill_rtf(struct ata_port *ap, u64 done_mask)
{
        struct ahci_port_priv *pp = ap->private_data;
        const u8 *fis;

        /* No outstanding commands. */
        if (!ap->qc_active)
                return;

        /*
         * FBS not enabled, so read status and error once, since they are shared
         * for all QCs.
         */
        if (!pp->fbs_enabled) {
                u8 status, error;

                /* No outstanding NCQ commands. */
                if (!pp->active_link->sactive)
                        return;

                fis = pp->rx_fis + RX_FIS_SDB;
                status = fis[2];
                error = fis[3];

                while (done_mask) {
                        struct ata_queued_cmd *qc;
                        unsigned int tag = __ffs64(done_mask);

                        qc = ata_qc_from_tag(ap, tag);
                        if (qc && ata_is_ncq(qc->tf.protocol)) {
                                qc->result_tf.status = status;
                                qc->result_tf.error = error;
                                qc->result_tf.flags = qc->tf.flags;
                                qc->flags |= ATA_QCFLAG_RTF_FILLED;
                        }
                        done_mask &= ~(1ULL << tag);
                }

                return;
        }

        /*
         * FBS enabled, so read the status and error for each QC, since the QCs
         * can belong to different PMP links. (Each PMP link has its own FIS
         * Receive Area.)
         */
        while (done_mask) {
                struct ata_queued_cmd *qc;
                unsigned int tag = __ffs64(done_mask);

                qc = ata_qc_from_tag(ap, tag);
                if (qc && ata_is_ncq(qc->tf.protocol)) {
                        fis = pp->rx_fis;
                        fis += qc->dev->link->pmp * AHCI_RX_FIS_SZ;
                        fis += RX_FIS_SDB;
                        qc->result_tf.status = fis[2];
                        qc->result_tf.error = fis[3];
                        qc->result_tf.flags = qc->tf.flags;
                        qc->flags |= ATA_QCFLAG_RTF_FILLED;
                }
                done_mask &= ~(1ULL << tag);
        }
}

static void ahci_freeze(struct ata_port *ap)
{
        void __iomem *port_mmio = ahci_port_base(ap);

        /* turn IRQ off */
        writel(0, port_mmio + PORT_IRQ_MASK);
}

static void ahci_thaw(struct ata_port *ap)
{
        struct ahci_host_priv *hpriv = ap->host->private_data;
        void __iomem *mmio = hpriv->mmio;
        void __iomem *port_mmio = ahci_port_base(ap);
        u32 tmp;
        struct ahci_port_priv *pp = ap->private_data;

        /* clear IRQ */
        tmp = readl(port_mmio + PORT_IRQ_STAT);
        writel(tmp, port_mmio + PORT_IRQ_STAT);
        writel(1 << ap->port_no, mmio + HOST_IRQ_STAT);

        /* turn IRQ back on */
        writel(pp->intr_mask, port_mmio + PORT_IRQ_MASK);
}

void ahci_error_handler(struct ata_port *ap)
        __must_hold(&ap->host->eh_mutex)
{
        struct ahci_host_priv *hpriv = ap->host->private_data;

        if (!ata_port_is_frozen(ap)) {
                /* restart engine */
                hpriv->stop_engine(ap);
                hpriv->start_engine(ap);
        }

        sata_pmp_error_handler(ap);

        if (!ata_dev_enabled(ap->link.device))
                hpriv->stop_engine(ap);
}
EXPORT_SYMBOL_GPL(ahci_error_handler);

static void ahci_post_internal_cmd(struct ata_queued_cmd *qc)
{
        struct ata_port *ap = qc->ap;

        /* make DMA engine forget about the failed command */
        if (qc->flags & ATA_QCFLAG_EH)
                ahci_kick_engine(ap);
}

static void ahci_set_aggressive_devslp(struct ata_port *ap, bool sleep)
{
        struct ahci_host_priv *hpriv = ap->host->private_data;
        void __iomem *port_mmio = ahci_port_base(ap);
        struct ata_device *dev = ap->link.device;
        u32 devslp, dm, dito, mdat, deto, dito_conf;
        int rc;
        unsigned int err_mask;

        devslp = readl(port_mmio + PORT_DEVSLP);
        if (!(devslp & PORT_DEVSLP_DSP)) {
                dev_info(ap->host->dev, "port does not support device sleep\n");
                return;
        }

        /* disable device sleep */
        if (!sleep) {
                if (devslp & PORT_DEVSLP_ADSE) {
                        writel(devslp & ~PORT_DEVSLP_ADSE,
                               port_mmio + PORT_DEVSLP);
                        err_mask = ata_dev_set_feature(dev,
                                                       SETFEATURES_SATA_DISABLE,
                                                       SATA_DEVSLP);
                        if (err_mask && err_mask != AC_ERR_DEV)
                                ata_dev_warn(dev, "failed to disable DEVSLP\n");
                }
                return;
        }

        dm = (devslp & PORT_DEVSLP_DM_MASK) >> PORT_DEVSLP_DM_OFFSET;
        dito = devslp_idle_timeout / (dm + 1);
        if (dito > 0x3ff)
                dito = 0x3ff;

        dito_conf = (devslp >> PORT_DEVSLP_DITO_OFFSET) & 0x3FF;

        /* device sleep was already enabled and same dito */
        if ((devslp & PORT_DEVSLP_ADSE) && (dito_conf == dito))
                return;

        /* set DITO, MDAT, DETO and enable DevSlp, need to stop engine first */
        rc = hpriv->stop_engine(ap);
        if (rc)
                return;

        /* Use the nominal value 10 ms if the read MDAT is zero,
         * the nominal value of DETO is 20 ms.
         */
        if (dev->devslp_timing[ATA_LOG_DEVSLP_VALID] &
            ATA_LOG_DEVSLP_VALID_MASK) {
                mdat = dev->devslp_timing[ATA_LOG_DEVSLP_MDAT] &
                       ATA_LOG_DEVSLP_MDAT_MASK;
                if (!mdat)
                        mdat = 10;
                deto = dev->devslp_timing[ATA_LOG_DEVSLP_DETO];
                if (!deto)
                        deto = 20;
        } else {
                mdat = 10;
                deto = 20;
        }

        /* Make dito, mdat, deto bits to 0s */
        devslp &= ~GENMASK_ULL(24, 2);
        devslp |= ((dito << PORT_DEVSLP_DITO_OFFSET) |
                   (mdat << PORT_DEVSLP_MDAT_OFFSET) |
                   (deto << PORT_DEVSLP_DETO_OFFSET) |
                   PORT_DEVSLP_ADSE);
        writel(devslp, port_mmio + PORT_DEVSLP);

        hpriv->start_engine(ap);

        /* enable device sleep feature for the drive */
        err_mask = ata_dev_set_feature(dev,
                                       SETFEATURES_SATA_ENABLE,
                                       SATA_DEVSLP);
        if (err_mask && err_mask != AC_ERR_DEV)
                ata_dev_warn(dev, "failed to enable DEVSLP\n");
}

static void ahci_enable_fbs(struct ata_port *ap)
{
        struct ahci_host_priv *hpriv = ap->host->private_data;
        struct ahci_port_priv *pp = ap->private_data;
        void __iomem *port_mmio = ahci_port_base(ap);
        u32 fbs;
        int rc;

        if (!pp->fbs_supported)
                return;

        fbs = readl(port_mmio + PORT_FBS);
        if (fbs & PORT_FBS_EN) {
                pp->fbs_enabled = true;
                pp->fbs_last_dev = -1; /* initialization */
                return;
        }

        rc = hpriv->stop_engine(ap);
        if (rc)
                return;

        writel(fbs | PORT_FBS_EN, port_mmio + PORT_FBS);
        fbs = readl(port_mmio + PORT_FBS);
        if (fbs & PORT_FBS_EN) {
                dev_info(ap->host->dev, "FBS is enabled\n");
                pp->fbs_enabled = true;
                pp->fbs_last_dev = -1; /* initialization */
        } else
                dev_err(ap->host->dev, "Failed to enable FBS\n");

        hpriv->start_engine(ap);
}

static void ahci_disable_fbs(struct ata_port *ap)
{
        struct ahci_host_priv *hpriv = ap->host->private_data;
        struct ahci_port_priv *pp = ap->private_data;
        void __iomem *port_mmio = ahci_port_base(ap);
        u32 fbs;
        int rc;

        if (!pp->fbs_supported)
                return;

        fbs = readl(port_mmio + PORT_FBS);
        if ((fbs & PORT_FBS_EN) == 0) {
                pp->fbs_enabled = false;
                return;
        }

        rc = hpriv->stop_engine(ap);
        if (rc)
                return;

        writel(fbs & ~PORT_FBS_EN, port_mmio + PORT_FBS);
        fbs = readl(port_mmio + PORT_FBS);
        if (fbs & PORT_FBS_EN)
                dev_err(ap->host->dev, "Failed to disable FBS\n");
        else {
                dev_info(ap->host->dev, "FBS is disabled\n");
                pp->fbs_enabled = false;
        }

        hpriv->start_engine(ap);
}

static void ahci_pmp_attach(struct ata_port *ap)
{
        void __iomem *port_mmio = ahci_port_base(ap);
        struct ahci_port_priv *pp = ap->private_data;
        u32 cmd;

        cmd = readl(port_mmio + PORT_CMD);
        cmd |= PORT_CMD_PMP;
        writel(cmd, port_mmio + PORT_CMD);

        ahci_enable_fbs(ap);

        pp->intr_mask |= PORT_IRQ_BAD_PMP;

        /*
         * We must not change the port interrupt mask register if the
         * port is marked frozen, the value in pp->intr_mask will be
         * restored later when the port is thawed.
         *
         * Note that during initialization, the port is marked as
         * frozen since the irq handler is not yet registered.
         */
        if (!ata_port_is_frozen(ap))
                writel(pp->intr_mask, port_mmio + PORT_IRQ_MASK);
}

static void ahci_pmp_detach(struct ata_port *ap)
{
        void __iomem *port_mmio = ahci_port_base(ap);
        struct ahci_port_priv *pp = ap->private_data;
        u32 cmd;

        ahci_disable_fbs(ap);

        cmd = readl(port_mmio + PORT_CMD);
        cmd &= ~PORT_CMD_PMP;
        writel(cmd, port_mmio + PORT_CMD);

        pp->intr_mask &= ~PORT_IRQ_BAD_PMP;

        /* see comment above in ahci_pmp_attach() */
        if (!ata_port_is_frozen(ap))
                writel(pp->intr_mask, port_mmio + PORT_IRQ_MASK);
}

int ahci_port_resume(struct ata_port *ap)
{
        ahci_rpm_get_port(ap);

        ahci_power_up(ap);
        ahci_start_port(ap);

        if (sata_pmp_attached(ap))
                ahci_pmp_attach(ap);
        else
                ahci_pmp_detach(ap);

        return 0;
}
EXPORT_SYMBOL_GPL(ahci_port_resume);

#ifdef CONFIG_PM
static void ahci_handle_s2idle(struct ata_port *ap)
{
        void __iomem *port_mmio = ahci_port_base(ap);
        u32 devslp;

        if (pm_suspend_via_firmware())
                return;
        devslp = readl(port_mmio + PORT_DEVSLP);
        if ((devslp & PORT_DEVSLP_ADSE))
                ata_msleep(ap, devslp_idle_timeout);
}

static int ahci_port_suspend(struct ata_port *ap, pm_message_t mesg)
{
        const char *emsg = NULL;
        int rc;

        rc = ahci_deinit_port(ap, &emsg);
        if (rc == 0)
                ahci_power_down(ap);
        else {
                ata_port_err(ap, "%s (%d)\n", emsg, rc);
                ata_port_freeze(ap);
        }

        if (acpi_storage_d3(ap->host->dev))
                ahci_handle_s2idle(ap);

        ahci_rpm_put_port(ap);
        return rc;
}
#endif

static int ahci_port_start(struct ata_port *ap)
{
        struct ahci_host_priv *hpriv = ap->host->private_data;
        struct device *dev = ap->host->dev;
        struct ahci_port_priv *pp;
        void *mem;
        dma_addr_t mem_dma;
        size_t dma_sz, rx_fis_sz;

        pp = devm_kzalloc(dev, sizeof(*pp), GFP_KERNEL);
        if (!pp)
                return -ENOMEM;

        if (ap->host->n_ports > 1) {
                pp->irq_desc = devm_kzalloc(dev, 8, GFP_KERNEL);
                if (!pp->irq_desc) {
                        devm_kfree(dev, pp);
                        return -ENOMEM;
                }
                snprintf(pp->irq_desc, 8,
                         "%s%d", dev_driver_string(dev), ap->port_no);
        }

        /* check FBS capability */
        if ((hpriv->cap & HOST_CAP_FBS) && sata_pmp_supported(ap)) {
                void __iomem *port_mmio = ahci_port_base(ap);
                u32 cmd = readl(port_mmio + PORT_CMD);
                if (cmd & PORT_CMD_FBSCP)
                        pp->fbs_supported = true;
                else if (hpriv->flags & AHCI_HFLAG_YES_FBS) {
                        dev_info(dev, "port %d can do FBS, forcing FBSCP\n",
                                 ap->port_no);
                        pp->fbs_supported = true;
                } else
                        dev_warn(dev, "port %d is not capable of FBS\n",
                                 ap->port_no);
        }

        if (pp->fbs_supported) {
                dma_sz = AHCI_PORT_PRIV_FBS_DMA_SZ;
                rx_fis_sz = AHCI_RX_FIS_SZ * 16;
        } else {
                dma_sz = AHCI_PORT_PRIV_DMA_SZ;
                rx_fis_sz = AHCI_RX_FIS_SZ;
        }

        mem = dmam_alloc_coherent(dev, dma_sz, &mem_dma, GFP_KERNEL);
        if (!mem)
                return -ENOMEM;

        /*
         * First item in chunk of DMA memory: 32-slot command table,
         * 32 bytes each in size
         */
        pp->cmd_slot = mem;
        pp->cmd_slot_dma = mem_dma;

        mem += AHCI_CMD_SLOT_SZ;
        mem_dma += AHCI_CMD_SLOT_SZ;

        /*
         * Second item: Received-FIS area
         */
        pp->rx_fis = mem;
        pp->rx_fis_dma = mem_dma;

        mem += rx_fis_sz;
        mem_dma += rx_fis_sz;

        /*
         * Third item: data area for storing a single command
         * and its scatter-gather table
         */
        pp->cmd_tbl = mem;
        pp->cmd_tbl_dma = mem_dma;

        /*
         * Save off initial list of interrupts to be enabled.
         * This could be changed later
         */
        pp->intr_mask = DEF_PORT_IRQ;

        /*
         * Switch to per-port locking in case each port has its own MSI vector.
         */
        if (hpriv->flags & AHCI_HFLAG_MULTI_MSI) {
                spin_lock_init(&pp->lock);
                ap->lock = &pp->lock;
        }

        ap->private_data = pp;

        /* engage engines, captain */
        return ahci_port_resume(ap);
}

static void ahci_port_stop(struct ata_port *ap)
{
        const char *emsg = NULL;
        struct ahci_host_priv *hpriv = ap->host->private_data;
        void __iomem *host_mmio = hpriv->mmio;
        int rc;

        /* de-initialize port */
        rc = ahci_deinit_port(ap, &emsg);
        if (rc)
                ata_port_warn(ap, "%s (%d)\n", emsg, rc);

        /*
         * Clear GHC.IS to prevent stuck INTx after disabling MSI and
         * re-enabling INTx.
         */
        writel(1 << ap->port_no, host_mmio + HOST_IRQ_STAT);

        ahci_rpm_put_port(ap);
}

void ahci_print_info(struct ata_host *host, const char *scc_s)
{
        struct ahci_host_priv *hpriv = host->private_data;
        u32 vers, cap, cap2, impl, speed;
        const char *speed_s;

        vers = hpriv->version;
        cap = hpriv->cap;
        cap2 = hpriv->cap2;
        impl = hpriv->port_map;

        speed = (cap >> 20) & 0xf;
        if (speed == 1)
                speed_s = "1.5";
        else if (speed == 2)
                speed_s = "3";
        else if (speed == 3)
                speed_s = "6";
        else
                speed_s = "?";

        dev_info(host->dev,
                "AHCI vers %02x%02x.%02x%02x, "
                "%u command slots, %s Gbps, %s mode\n"
                ,

                (vers >> 24) & 0xff,
                (vers >> 16) & 0xff,
                (vers >> 8) & 0xff,
                vers & 0xff,

                ((cap >> 8) & 0x1f) + 1,
                speed_s,
                scc_s);

        dev_info(host->dev,
                "%u/%u ports implemented (port mask 0x%x)\n"
                ,

                hweight32(impl),
                ahci_nr_ports(cap),
                impl);

        dev_info(host->dev,
                "flags: "
                "%s%s%s%s%s%s%s"
                "%s%s%s%s%s%s%s"
                "%s%s%s%s%s%s%s"
                "%s%s\n"
                ,

                cap & HOST_CAP_64 ? "64bit " : "",
                cap & HOST_CAP_NCQ ? "ncq " : "",
                cap & HOST_CAP_SNTF ? "sntf " : "",
                cap & HOST_CAP_MPS ? "ilck " : "",
                cap & HOST_CAP_SSS ? "stag " : "",
                cap & HOST_CAP_ALPM ? "pm " : "",
                cap & HOST_CAP_LED ? "led " : "",
                cap & HOST_CAP_CLO ? "clo " : "",
                cap & HOST_CAP_ONLY ? "only " : "",
                cap & HOST_CAP_PMP ? "pmp " : "",
                cap & HOST_CAP_FBS ? "fbs " : "",
                cap & HOST_CAP_PIO_MULTI ? "pio " : "",
                cap & HOST_CAP_SSC ? "slum " : "",
                cap & HOST_CAP_PART ? "part " : "",
                cap & HOST_CAP_CCC ? "ccc " : "",
                cap & HOST_CAP_EMS ? "ems " : "",
                cap & HOST_CAP_SXS ? "sxs " : "",
                cap2 & HOST_CAP2_DESO ? "deso " : "",
                cap2 & HOST_CAP2_SADM ? "sadm " : "",
                cap2 & HOST_CAP2_SDS ? "sds " : "",
                cap2 & HOST_CAP2_APST ? "apst " : "",
                cap2 & HOST_CAP2_NVMHCI ? "nvmp " : "",
                cap2 & HOST_CAP2_BOH ? "boh " : ""
                );
}
EXPORT_SYMBOL_GPL(ahci_print_info);

void ahci_set_em_messages(struct ahci_host_priv *hpriv,
                          struct ata_port_info *pi)
{
        u8 messages;
        void __iomem *mmio = hpriv->mmio;
        u32 em_loc = readl(mmio + HOST_EM_LOC);
        u32 em_ctl = readl(mmio + HOST_EM_CTL);

        if (!ahci_em_messages || !(hpriv->cap & HOST_CAP_EMS))
                return;

        messages = (em_ctl & EM_CTRL_MSG_TYPE) >> 16;

        if (messages) {
                /* store em_loc */
                hpriv->em_loc = ((em_loc >> 16) * 4);
                hpriv->em_buf_sz = ((em_loc & 0xff) * 4);
                hpriv->em_msg_type = messages;
                pi->flags |= ATA_FLAG_EM;
                if (!(em_ctl & EM_CTL_ALHD))
                        pi->flags |= ATA_FLAG_SW_ACTIVITY;
        }
}
EXPORT_SYMBOL_GPL(ahci_set_em_messages);

static int ahci_host_activate_multi_irqs(struct ata_host *host,
                                         const struct scsi_host_template *sht)
{
        struct ahci_host_priv *hpriv = host->private_data;
        int i, rc;

        rc = ata_host_start(host);
        if (rc)
                return rc;
        /*
         * Requests IRQs according to AHCI-1.1 when multiple MSIs were
         * allocated. That is one MSI per port, starting from @irq.
         */
        for (i = 0; i < host->n_ports; i++) {
                struct ahci_port_priv *pp = host->ports[i]->private_data;
                int irq = hpriv->get_irq_vector(host, i);

                /* Do not receive interrupts sent by dummy ports */
                if (!pp) {
                        disable_irq(irq);
                        continue;
                }

                rc = devm_request_irq(host->dev, irq, ahci_multi_irqs_intr_hard,
                                0, pp->irq_desc, host->ports[i]);

                if (rc)
                        return rc;
                ata_port_desc_misc(host->ports[i], irq);
        }

        return ata_host_register(host, sht);
}

/**
 *      ahci_host_activate - start AHCI host, request IRQs and register it
 *      @host: target ATA host
 *      @sht: scsi_host_template to use when registering the host
 *
 *      LOCKING:
 *      Inherited from calling layer (may sleep).
 *
 *      RETURNS:
 *      0 on success, -errno otherwise.
 */
int ahci_host_activate(struct ata_host *host, const struct scsi_host_template *sht)
{
        struct ahci_host_priv *hpriv = host->private_data;
        int irq = hpriv->irq;
        int rc;

        if (hpriv->flags & AHCI_HFLAG_MULTI_MSI) {
                if (hpriv->irq_handler &&
                    hpriv->irq_handler != ahci_single_level_irq_intr)
                        dev_warn(host->dev,
                                 "both AHCI_HFLAG_MULTI_MSI flag set and custom irq handler implemented\n");
                if (!hpriv->get_irq_vector) {
                        dev_err(host->dev,
                                "AHCI_HFLAG_MULTI_MSI requires ->get_irq_vector!\n");
                        return -EIO;
                }

                rc = ahci_host_activate_multi_irqs(host, sht);
        } else {
                rc = ata_host_activate(host, irq, hpriv->irq_handler,
                                       IRQF_SHARED, sht);
        }


        return rc;
}
EXPORT_SYMBOL_GPL(ahci_host_activate);

MODULE_AUTHOR("Jeff Garzik");
MODULE_DESCRIPTION("Common AHCI SATA low-level routines");
MODULE_LICENSE("GPL");