]> err.no Git - linux-2.6/blobdiff - drivers/ata/ahci.c
ahci: implement PMP support
[linux-2.6] / drivers / ata / ahci.c
index baf7d05621907755312e461f346614d4346c9d8e..b697da483b70266490021a7256a3e4448fcadbe0 100644 (file)
@@ -46,7 +46,7 @@
 #include <linux/libata.h>
 
 #define DRV_NAME       "ahci"
-#define DRV_VERSION    "2.3"
+#define DRV_VERSION    "3.0"
 
 
 enum {
@@ -77,11 +77,10 @@ enum {
        RX_FIS_UNK              = 0x60, /* offset of Unknown FIS data */
 
        board_ahci              = 0,
-       board_ahci_pi           = 1,
-       board_ahci_vt8251       = 2,
-       board_ahci_ign_iferr    = 3,
-       board_ahci_sb600        = 4,
-       board_ahci_mv           = 5,
+       board_ahci_vt8251       = 1,
+       board_ahci_ign_iferr    = 2,
+       board_ahci_sb600        = 3,
+       board_ahci_mv           = 4,
 
        /* global controller registers */
        HOST_CAP                = 0x00, /* host capabilities */
@@ -97,6 +96,7 @@ enum {
 
        /* HOST_CAP bits */
        HOST_CAP_SSC            = (1 << 14), /* Slumber capable */
+       HOST_CAP_PMP            = (1 << 17), /* Port Multiplier support */
        HOST_CAP_CLO            = (1 << 24), /* Command List Override support */
        HOST_CAP_SSS            = (1 << 27), /* Staggered Spin-up */
        HOST_CAP_SNTF           = (1 << 29), /* SNotification register */
@@ -144,7 +144,8 @@ enum {
                                  PORT_IRQ_IF_ERR |
                                  PORT_IRQ_CONNECT |
                                  PORT_IRQ_PHYRDY |
-                                 PORT_IRQ_UNK_FIS,
+                                 PORT_IRQ_UNK_FIS |
+                                 PORT_IRQ_BAD_PMP,
        PORT_IRQ_ERROR          = PORT_IRQ_FREEZE |
                                  PORT_IRQ_TF_ERR |
                                  PORT_IRQ_HBUS_DATA_ERR,
@@ -154,6 +155,7 @@ enum {
 
        /* PORT_CMD bits */
        PORT_CMD_ATAPI          = (1 << 24), /* Device is ATAPI */
+       PORT_CMD_PMP            = (1 << 17), /* PMP attached */
        PORT_CMD_LIST_ON        = (1 << 15), /* cmd list DMA engine running */
        PORT_CMD_FIS_ON         = (1 << 14), /* FIS DMA engine running */
        PORT_CMD_FIS_RX         = (1 << 4), /* Enable FIS receive DMA engine */
@@ -170,7 +172,6 @@ enum {
        /* ap->flags bits */
        AHCI_FLAG_NO_NCQ                = (1 << 24),
        AHCI_FLAG_IGN_IRQ_IF_ERR        = (1 << 25), /* ignore IRQ_IF_ERR */
-       AHCI_FLAG_HONOR_PI              = (1 << 26), /* honor PORTS_IMPL */
        AHCI_FLAG_IGN_SERR_INTERNAL     = (1 << 27), /* ignore SERR_INTERNAL */
        AHCI_FLAG_32BIT_ONLY            = (1 << 28), /* force 32bit */
        AHCI_FLAG_MV_PATA               = (1 << 29), /* PATA port */
@@ -179,7 +180,7 @@ enum {
 
        AHCI_FLAG_COMMON                = ATA_FLAG_SATA | ATA_FLAG_NO_LEGACY |
                                          ATA_FLAG_MMIO | ATA_FLAG_PIO_DMA |
-                                         ATA_FLAG_ACPI_SATA,
+                                         ATA_FLAG_ACPI_SATA | ATA_FLAG_AN,
        AHCI_LFLAG_COMMON               = ATA_LFLAG_SKIP_D2H_BSY,
 };
 
@@ -206,6 +207,7 @@ struct ahci_host_priv {
 };
 
 struct ahci_port_priv {
+       struct ata_link         *active_link;
        struct ahci_cmd_hdr     *cmd_slot;
        dma_addr_t              cmd_slot_dma;
        void                    *cmd_tbl;
@@ -231,6 +233,10 @@ static void ahci_qc_prep(struct ata_queued_cmd *qc);
 static u8 ahci_check_status(struct ata_port *ap);
 static void ahci_freeze(struct ata_port *ap);
 static void ahci_thaw(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_pmp_read(struct ata_device *dev, int pmp, int reg, u32 *r_val);
+static int ahci_pmp_write(struct ata_device *dev, int pmp, int reg, u32 val);
 static void ahci_error_handler(struct ata_port *ap);
 static void ahci_vt8251_error_handler(struct ata_port *ap);
 static void ahci_post_internal_cmd(struct ata_queued_cmd *qc);
@@ -264,20 +270,17 @@ static struct scsi_host_template ahci_sht = {
 };
 
 static const struct ata_port_operations ahci_ops = {
-       .port_disable           = ata_port_disable,
-
        .check_status           = ahci_check_status,
        .check_altstatus        = ahci_check_status,
        .dev_select             = ata_noop_dev_select,
 
        .tf_read                = ahci_tf_read,
 
+       .qc_defer               = sata_pmp_qc_defer_cmd_switch,
        .qc_prep                = ahci_qc_prep,
        .qc_issue               = ahci_qc_issue,
 
        .irq_clear              = ahci_irq_clear,
-       .irq_on                 = ata_dummy_irq_on,
-       .irq_ack                = ata_dummy_irq_ack,
 
        .scr_read               = ahci_scr_read,
        .scr_write              = ahci_scr_write,
@@ -288,6 +291,11 @@ static const struct ata_port_operations ahci_ops = {
        .error_handler          = ahci_error_handler,
        .post_internal_cmd      = ahci_post_internal_cmd,
 
+       .pmp_attach             = ahci_pmp_attach,
+       .pmp_detach             = ahci_pmp_detach,
+       .pmp_read               = ahci_pmp_read,
+       .pmp_write              = ahci_pmp_write,
+
 #ifdef CONFIG_PM
        .port_suspend           = ahci_port_suspend,
        .port_resume            = ahci_port_resume,
@@ -298,20 +306,17 @@ static const struct ata_port_operations ahci_ops = {
 };
 
 static const struct ata_port_operations ahci_vt8251_ops = {
-       .port_disable           = ata_port_disable,
-
        .check_status           = ahci_check_status,
        .check_altstatus        = ahci_check_status,
        .dev_select             = ata_noop_dev_select,
 
        .tf_read                = ahci_tf_read,
 
+       .qc_defer               = sata_pmp_qc_defer_cmd_switch,
        .qc_prep                = ahci_qc_prep,
        .qc_issue               = ahci_qc_issue,
 
        .irq_clear              = ahci_irq_clear,
-       .irq_on                 = ata_dummy_irq_on,
-       .irq_ack                = ata_dummy_irq_ack,
 
        .scr_read               = ahci_scr_read,
        .scr_write              = ahci_scr_write,
@@ -322,6 +327,11 @@ static const struct ata_port_operations ahci_vt8251_ops = {
        .error_handler          = ahci_vt8251_error_handler,
        .post_internal_cmd      = ahci_post_internal_cmd,
 
+       .pmp_attach             = ahci_pmp_attach,
+       .pmp_detach             = ahci_pmp_detach,
+       .pmp_read               = ahci_pmp_read,
+       .pmp_write              = ahci_pmp_write,
+
 #ifdef CONFIG_PM
        .port_suspend           = ahci_port_suspend,
        .port_resume            = ahci_port_resume,
@@ -340,14 +350,6 @@ static const struct ata_port_info ahci_port_info[] = {
                .udma_mask      = ATA_UDMA6,
                .port_ops       = &ahci_ops,
        },
-       /* board_ahci_pi */
-       {
-               .flags          = AHCI_FLAG_COMMON | AHCI_FLAG_HONOR_PI,
-               .link_flags     = AHCI_LFLAG_COMMON,
-               .pio_mask       = 0x1f, /* pio0-4 */
-               .udma_mask      = ATA_UDMA6,
-               .port_ops       = &ahci_ops,
-       },
        /* board_ahci_vt8251 */
        {
                .flags          = AHCI_FLAG_COMMON | AHCI_FLAG_NO_NCQ,
@@ -379,8 +381,8 @@ static const struct ata_port_info ahci_port_info[] = {
                .sht            = &ahci_sht,
                .flags          = ATA_FLAG_SATA | ATA_FLAG_NO_LEGACY |
                                  ATA_FLAG_MMIO | ATA_FLAG_PIO_DMA |
-                                 AHCI_FLAG_HONOR_PI | AHCI_FLAG_NO_NCQ |
-                                 AHCI_FLAG_NO_MSI | AHCI_FLAG_MV_PATA,
+                                 AHCI_FLAG_NO_NCQ | AHCI_FLAG_NO_MSI |
+                                 AHCI_FLAG_MV_PATA,
                .link_flags     = AHCI_LFLAG_COMMON,
                .pio_mask       = 0x1f, /* pio0-4 */
                .udma_mask      = ATA_UDMA6,
@@ -400,23 +402,25 @@ static const struct pci_device_id ahci_pci_tbl[] = {
        { PCI_VDEVICE(INTEL, 0x2682), board_ahci }, /* ESB2 */
        { PCI_VDEVICE(INTEL, 0x2683), board_ahci }, /* ESB2 */
        { PCI_VDEVICE(INTEL, 0x27c6), board_ahci }, /* ICH7-M DH */
-       { PCI_VDEVICE(INTEL, 0x2821), board_ahci_pi }, /* ICH8 */
-       { PCI_VDEVICE(INTEL, 0x2822), board_ahci_pi }, /* ICH8 */
-       { PCI_VDEVICE(INTEL, 0x2824), board_ahci_pi }, /* ICH8 */
-       { PCI_VDEVICE(INTEL, 0x2829), board_ahci_pi }, /* ICH8M */
-       { PCI_VDEVICE(INTEL, 0x282a), board_ahci_pi }, /* ICH8M */
-       { PCI_VDEVICE(INTEL, 0x2922), board_ahci_pi }, /* ICH9 */
-       { PCI_VDEVICE(INTEL, 0x2923), board_ahci_pi }, /* ICH9 */
-       { PCI_VDEVICE(INTEL, 0x2924), board_ahci_pi }, /* ICH9 */
-       { PCI_VDEVICE(INTEL, 0x2925), board_ahci_pi }, /* ICH9 */
-       { PCI_VDEVICE(INTEL, 0x2927), board_ahci_pi }, /* ICH9 */
-       { PCI_VDEVICE(INTEL, 0x2929), board_ahci_pi }, /* ICH9M */
-       { PCI_VDEVICE(INTEL, 0x292a), board_ahci_pi }, /* ICH9M */
-       { PCI_VDEVICE(INTEL, 0x292b), board_ahci_pi }, /* ICH9M */
-       { PCI_VDEVICE(INTEL, 0x292c), board_ahci_pi }, /* ICH9M */
-       { PCI_VDEVICE(INTEL, 0x292f), board_ahci_pi }, /* ICH9M */
-       { PCI_VDEVICE(INTEL, 0x294d), board_ahci_pi }, /* ICH9 */
-       { PCI_VDEVICE(INTEL, 0x294e), board_ahci_pi }, /* ICH9M */
+       { PCI_VDEVICE(INTEL, 0x2821), board_ahci }, /* ICH8 */
+       { PCI_VDEVICE(INTEL, 0x2822), board_ahci }, /* ICH8 */
+       { PCI_VDEVICE(INTEL, 0x2824), board_ahci }, /* ICH8 */
+       { PCI_VDEVICE(INTEL, 0x2829), board_ahci }, /* ICH8M */
+       { PCI_VDEVICE(INTEL, 0x282a), board_ahci }, /* ICH8M */
+       { PCI_VDEVICE(INTEL, 0x2922), board_ahci }, /* ICH9 */
+       { PCI_VDEVICE(INTEL, 0x2923), board_ahci }, /* ICH9 */
+       { PCI_VDEVICE(INTEL, 0x2924), board_ahci }, /* ICH9 */
+       { PCI_VDEVICE(INTEL, 0x2925), board_ahci }, /* ICH9 */
+       { PCI_VDEVICE(INTEL, 0x2927), board_ahci }, /* ICH9 */
+       { PCI_VDEVICE(INTEL, 0x2929), board_ahci }, /* ICH9M */
+       { PCI_VDEVICE(INTEL, 0x292a), board_ahci }, /* ICH9M */
+       { PCI_VDEVICE(INTEL, 0x292b), board_ahci }, /* ICH9M */
+       { PCI_VDEVICE(INTEL, 0x292c), board_ahci }, /* ICH9M */
+       { PCI_VDEVICE(INTEL, 0x292f), board_ahci }, /* ICH9M */
+       { PCI_VDEVICE(INTEL, 0x294d), board_ahci }, /* ICH9 */
+       { PCI_VDEVICE(INTEL, 0x294e), board_ahci }, /* ICH9M */
+       { PCI_VDEVICE(INTEL, 0x502a), board_ahci }, /* Tolapai */
+       { PCI_VDEVICE(INTEL, 0x502b), board_ahci }, /* Tolapai */
 
        /* JMicron 360/1/3/5/6, match class to avoid IDE function */
        { PCI_VENDOR_ID_JMICRON, PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID,
@@ -570,16 +574,6 @@ static void ahci_save_initial_config(struct pci_dev *pdev,
                cap &= ~HOST_CAP_NCQ;
        }
 
-       /* fixup zero port_map */
-       if (!port_map) {
-               port_map = (1 << ahci_nr_ports(cap)) - 1;
-               dev_printk(KERN_WARNING, &pdev->dev,
-                          "PORTS_IMPL is zero, forcing 0x%x\n", port_map);
-
-               /* write the fixed up value to the PI register */
-               hpriv->saved_port_map = port_map;
-       }
-
        /*
         * Temporary Marvell 6145 hack: PATA port presence
         * is asserted through the standard AHCI port
@@ -595,7 +589,7 @@ static void ahci_save_initial_config(struct pci_dev *pdev,
        }
 
        /* cross check port_map and cap.n_ports */
-       if (pi->flags & AHCI_FLAG_HONOR_PI) {
+       if (port_map) {
                u32 tmp_port_map = port_map;
                int n_ports = ahci_nr_ports(cap);
 
@@ -606,17 +600,26 @@ static void ahci_save_initial_config(struct pci_dev *pdev,
                        }
                }
 
-               /* Whine if inconsistent.  No need to update cap.
-                * port_map is used to determine number of ports.
+               /* If n_ports and port_map are inconsistent, whine and
+                * clear port_map and let it be generated from n_ports.
                 */
-               if (n_ports || tmp_port_map)
+               if (n_ports || tmp_port_map) {
                        dev_printk(KERN_WARNING, &pdev->dev,
                                   "nr_ports (%u) and implemented port map "
-                                  "(0x%x) don't match\n",
+                                  "(0x%x) don't match, using nr_ports\n",
                                   ahci_nr_ports(cap), port_map);
-       } else {
-               /* fabricate port_map from cap.nr_ports */
+                       port_map = 0;
+               }
+       }
+
+       /* fabricate port_map from cap.nr_ports */
+       if (!port_map) {
                port_map = (1 << ahci_nr_ports(cap)) - 1;
+               dev_printk(KERN_WARNING, &pdev->dev,
+                          "forcing PORTS_IMPL to 0x%x\n", port_map);
+
+               /* write the fixed up value to the PI register */
+               hpriv->saved_port_map = port_map;
        }
 
        /* record values to use during operation */
@@ -842,8 +845,14 @@ static int ahci_reset_controller(struct ata_host *host)
        void __iomem *mmio = host->iomap[AHCI_PCI_BAR];
        u32 tmp;
 
-       /* global controller reset */
+       /* we must be in AHCI mode, before using anything
+        * AHCI-specific, such as HOST_RESET.
+        */
        tmp = readl(mmio + HOST_CTL);
+       if (!(tmp & HOST_AHCI_EN))
+               writel(tmp | HOST_AHCI_EN, mmio + HOST_CTL);
+
+       /* global controller reset */
        if ((tmp & HOST_RESET) == 0) {
                writel(tmp | HOST_RESET, mmio + HOST_CTL);
                readl(mmio + HOST_CTL); /* flush */
@@ -1123,7 +1132,12 @@ static int ahci_do_softreset(struct ata_link *link, unsigned int *class,
 static int ahci_softreset(struct ata_link *link, unsigned int *class,
                          unsigned long deadline)
 {
-       return ahci_do_softreset(link, class, 0, deadline);
+       int pmp = 0;
+
+       if (link->ap->flags & ATA_FLAG_PMP)
+               pmp = SATA_PMP_CTRL_PORT;
+
+       return ahci_do_softreset(link, class, pmp, deadline);
 }
 
 static int ahci_hardreset(struct ata_link *link, unsigned int *class,
@@ -1150,7 +1164,7 @@ static int ahci_hardreset(struct ata_link *link, unsigned int *class,
 
        if (rc == 0 && ata_link_online(link))
                *class = ahci_dev_classify(ap);
-       if (*class == ATA_DEV_UNKNOWN)
+       if (rc != -EAGAIN && *class == ATA_DEV_UNKNOWN)
                *class = ATA_DEV_NONE;
 
        DPRINTK("EXIT, rc=%d, class=%u\n", rc, *class);
@@ -1205,6 +1219,12 @@ static void ahci_postreset(struct ata_link *link, unsigned int *class)
        }
 }
 
+static int ahci_pmp_softreset(struct ata_link *link, unsigned int *class,
+                             unsigned long deadline)
+{
+       return ahci_do_softreset(link, class, link->pmp, deadline);
+}
+
 static u8 ahci_check_status(struct ata_port *ap)
 {
        void __iomem *mmio = ap->ioaddr.cmd_addr;
@@ -1263,7 +1283,7 @@ static void ahci_qc_prep(struct ata_queued_cmd *qc)
         */
        cmd_tbl = pp->cmd_tbl + qc->tag * AHCI_CMD_TBL_SZ;
 
-       ata_tf_to_fis(&qc->tf, 0, 1, cmd_tbl);
+       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);
@@ -1276,7 +1296,7 @@ static void ahci_qc_prep(struct ata_queued_cmd *qc)
        /*
         * Fill in command slot information.
         */
-       opts = cmd_fis_len | n_elem << 16;
+       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)
@@ -1288,65 +1308,85 @@ static void ahci_qc_prep(struct ata_queued_cmd *qc)
 static void ahci_error_intr(struct ata_port *ap, u32 irq_stat)
 {
        struct ahci_port_priv *pp = ap->private_data;
-       struct ata_eh_info *ehi = &ap->link.eh_info;
-       unsigned int err_mask = 0, action = 0;
-       struct ata_queued_cmd *qc;
+       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;
        u32 serror;
 
-       ata_ehi_clear_desc(ehi);
+       /* determine active link */
+       ata_port_for_each_link(link, ap)
+               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, SCR_ERROR, &serror);
        ahci_scr_write(ap, SCR_ERROR, serror);
-
-       /* analyze @irq_stat */
-       ata_ehi_push_desc(ehi, "irq_stat 0x%08x", irq_stat);
+       host_ehi->serror |= serror;
 
        /* some controllers set IRQ_IF_ERR on device errors, ignore it */
        if (ap->flags & AHCI_FLAG_IGN_IRQ_IF_ERR)
                irq_stat &= ~PORT_IRQ_IF_ERR;
 
        if (irq_stat & PORT_IRQ_TF_ERR) {
-               err_mask |= AC_ERR_DEV;
+               /* 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 (ap->flags & AHCI_FLAG_IGN_SERR_INTERNAL)
-                       serror &= ~SERR_INTERNAL;
+                       host_ehi->serror &= ~SERR_INTERNAL;
+       }
+
+       if (irq_stat & PORT_IRQ_UNK_FIS) {
+               u32 *unk = (u32 *)(pp->rx_fis + RX_FIS_UNK);
+
+               active_ehi->err_mask |= AC_ERR_HSM;
+               active_ehi->action |= ATA_EH_SOFTRESET;
+               ata_ehi_push_desc(active_ehi,
+                                 "unknown FIS %08x %08x %08x %08x" ,
+                                 unk[0], unk[1], unk[2], unk[3]);
+       }
+
+       if (ap->nr_pmp_links && (irq_stat & PORT_IRQ_BAD_PMP)) {
+               active_ehi->err_mask |= AC_ERR_HSM;
+               active_ehi->action |= ATA_EH_SOFTRESET;
+               ata_ehi_push_desc(active_ehi, "incorrect PMP");
        }
 
        if (irq_stat & (PORT_IRQ_HBUS_ERR | PORT_IRQ_HBUS_DATA_ERR)) {
-               err_mask |= AC_ERR_HOST_BUS;
-               action |= ATA_EH_SOFTRESET;
+               host_ehi->err_mask |= AC_ERR_HOST_BUS;
+               host_ehi->action |= ATA_EH_SOFTRESET;
+               ata_ehi_push_desc(host_ehi, "host bus error");
        }
 
        if (irq_stat & PORT_IRQ_IF_ERR) {
-               err_mask |= AC_ERR_ATA_BUS;
-               action |= ATA_EH_SOFTRESET;
-               ata_ehi_push_desc(ehi, "interface fatal error");
+               host_ehi->err_mask |= AC_ERR_ATA_BUS;
+               host_ehi->action |= ATA_EH_SOFTRESET;
+               ata_ehi_push_desc(host_ehi, "interface fatal error");
        }
 
        if (irq_stat & (PORT_IRQ_CONNECT | PORT_IRQ_PHYRDY)) {
-               ata_ehi_hotplugged(ehi);
-               ata_ehi_push_desc(ehi, "%s", irq_stat & PORT_IRQ_CONNECT ?
+               ata_ehi_hotplugged(host_ehi);
+               ata_ehi_push_desc(host_ehi, "%s",
+                       irq_stat & PORT_IRQ_CONNECT ?
                        "connection status changed" : "PHY RDY changed");
        }
 
-       if (irq_stat & PORT_IRQ_UNK_FIS) {
-               u32 *unk = (u32 *)(pp->rx_fis + RX_FIS_UNK);
-
-               err_mask |= AC_ERR_HSM;
-               action |= ATA_EH_SOFTRESET;
-               ata_ehi_push_desc(ehi, "unknown FIS %08x %08x %08x %08x",
-                                 unk[0], unk[1], unk[2], unk[3]);
-       }
-
        /* okay, let's hand over to EH */
-       ehi->serror |= serror;
-       ehi->action |= action;
-
-       qc = ata_qc_from_tag(ap, ap->link.active_tag);
-       if (qc)
-               qc->err_mask |= err_mask;
-       else
-               ehi->err_mask |= err_mask;
 
        if (irq_stat & PORT_IRQ_FREEZE)
                ata_port_freeze(ap);
@@ -1371,30 +1411,21 @@ static void ahci_port_intr(struct ata_port *ap)
        }
 
        if (status & PORT_IRQ_SDB_FIS) {
-               /*
-                * if this is an ATAPI device with AN turned on,
-                * then we should interrogate the device to
-                * determine the cause of the interrupt
-                *
-                * for AN - this we should check the SDB FIS
-                * and find the I and N bits set
+               /* If the 'N' bit in word 0 of the FIS is set, we just
+                * received asynchronous notification.  Tell libata
+                * about it.  Note that as the SDB FIS itself is
+                * accessible, SNotification can be emulated by the
+                * driver but don't bother for the time being.
                 */
                const __le32 *f = pp->rx_fis + RX_FIS_SDB;
                u32 f0 = le32_to_cpu(f[0]);
 
-               /* check the 'N' bit in word 0 of the FIS */
-               if (f0 & (1 << 15)) {
-                       int port_addr = ((f0 & 0x00000f00) >> 8);
-                       struct ata_device *adev;
-                       if (port_addr < ATA_MAX_DEVICES) {
-                               adev = &ap->link.device[port_addr];
-                               if (adev->flags & ATA_DFLAG_AN)
-                                       ata_scsi_media_change_notify(adev);
-                       }
-               }
+               if (f0 & (1 << 15))
+                       sata_async_notification(ap);
        }
 
-       if (ap->link.sactive)
+       /* 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);
@@ -1532,6 +1563,13 @@ static 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 (qc->tf.protocol == ATA_PROT_NCQ)
                writel(1 << qc->tag, port_mmio + PORT_SCR_ACT);
@@ -1561,8 +1599,11 @@ static void ahci_thaw(struct ata_port *ap)
        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);
+       /* turn IRQ back on, ignore BAD_PMP if PMP isn't attached */
+       tmp = pp->intr_mask;
+       if (!ap->nr_pmp_links)
+               tmp &= ~PORT_IRQ_BAD_PMP;
+       writel(tmp, port_mmio + PORT_IRQ_MASK);
 }
 
 static void ahci_error_handler(struct ata_port *ap)
@@ -1574,8 +1615,10 @@ static void ahci_error_handler(struct ata_port *ap)
        }
 
        /* perform recovery */
-       ata_do_eh(ap, ata_std_prereset, ahci_softreset, ahci_hardreset,
-                 ahci_postreset);
+       sata_pmp_do_eh(ap, ata_std_prereset, ahci_softreset,
+                      ahci_hardreset, ahci_postreset,
+                      sata_pmp_std_prereset, ahci_pmp_softreset,
+                      sata_pmp_std_hardreset, sata_pmp_std_postreset);
 }
 
 static void ahci_vt8251_error_handler(struct ata_port *ap)
@@ -1600,11 +1643,74 @@ static void ahci_post_internal_cmd(struct ata_queued_cmd *qc)
                ahci_kick_engine(ap, 1);
 }
 
+static void ahci_pmp_attach(struct ata_port *ap)
+{
+       void __iomem *port_mmio = ahci_port_base(ap);
+       u32 cmd;
+
+       cmd = readl(port_mmio + PORT_CMD);
+       cmd |= PORT_CMD_PMP;
+       writel(cmd, port_mmio + PORT_CMD);
+}
+
+static void ahci_pmp_detach(struct ata_port *ap)
+{
+       void __iomem *port_mmio = ahci_port_base(ap);
+       struct ahci_host_priv *hpriv = ap->host->private_data;
+       unsigned long flags;
+       u32 cmd;
+
+       cmd = readl(port_mmio + PORT_CMD);
+       cmd &= ~PORT_CMD_PMP;
+       writel(cmd, port_mmio + PORT_CMD);
+
+       if (hpriv->cap & HOST_CAP_NCQ) {
+               spin_lock_irqsave(ap->lock, flags);
+               ap->flags |= ATA_FLAG_NCQ;
+               spin_unlock_irqrestore(ap->lock, flags);
+       }
+}
+
+static int ahci_pmp_read(struct ata_device *dev, int pmp, int reg, u32 *r_val)
+{
+       struct ata_port *ap = dev->link->ap;
+       struct ata_taskfile tf;
+       int rc;
+
+       ahci_kick_engine(ap, 0);
+
+       sata_pmp_read_init_tf(&tf, dev, pmp, reg);
+       rc = ahci_exec_polled_cmd(ap, SATA_PMP_CTRL_PORT, &tf, 1, 0,
+                                 SATA_PMP_SCR_TIMEOUT);
+       if (rc == 0) {
+               ahci_tf_read(ap, &tf);
+               *r_val = sata_pmp_read_val(&tf);
+       }
+       return rc;
+}
+
+static int ahci_pmp_write(struct ata_device *dev, int pmp, int reg, u32 val)
+{
+       struct ata_port *ap = dev->link->ap;
+       struct ata_taskfile tf;
+
+       ahci_kick_engine(ap, 0);
+
+       sata_pmp_write_init_tf(&tf, dev, pmp, reg, val);
+       return ahci_exec_polled_cmd(ap, SATA_PMP_CTRL_PORT, &tf, 1, 0,
+                                   SATA_PMP_SCR_TIMEOUT);
+}
+
 static int ahci_port_resume(struct ata_port *ap)
 {
        ahci_power_up(ap);
        ahci_start_port(ap);
 
+       if (ap->nr_pmp_links)
+               ahci_pmp_attach(ap);
+       else
+               ahci_pmp_detach(ap);
+
        return 0;
 }
 
@@ -1885,6 +1991,9 @@ static int ahci_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
        if (hpriv->cap & HOST_CAP_NCQ)
                pi.flags |= ATA_FLAG_NCQ;
 
+       if (hpriv->cap & HOST_CAP_PMP)
+               pi.flags |= ATA_FLAG_PMP;
+
        host = ata_host_alloc_pinfo(&pdev->dev, ppi, fls(hpriv->port_map));
        if (!host)
                return -ENOMEM;
@@ -1895,6 +2004,10 @@ static int ahci_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
                struct ata_port *ap = host->ports[i];
                void __iomem *port_mmio = ahci_port_base(ap);
 
+               ata_port_pbar_desc(ap, AHCI_PCI_BAR, -1, "abar");
+               ata_port_pbar_desc(ap, AHCI_PCI_BAR,
+                                  0x100 + ap->port_no * 0x80, "port");
+
                /* standard SATA port setup */
                if (hpriv->port_map & (1 << i))
                        ap->ioaddr.cmd_addr = port_mmio;