X-Git-Url: http://pilppa.org/gitweb/?a=blobdiff_plain;f=drivers%2Fata%2Fahci.c;h=35ede5c31b9fc8c83c323776c4ad220774b335ca;hb=417a1a6d3d7abad4c5288135f640e6e38e7a65c5;hp=ca5229d24d8ed8f0692b64afed88d8e14cfbac33;hpb=f436ab43614f105a4685c976a85a171bc112c802;p=linux-2.6-omap-h63xx.git diff --git a/drivers/ata/ahci.c b/drivers/ata/ahci.c index ca5229d24d8..35ede5c31b9 100644 --- a/drivers/ata/ahci.c +++ b/drivers/ata/ahci.c @@ -46,7 +46,7 @@ #include #define DRV_NAME "ahci" -#define DRV_VERSION "2.2" +#define DRV_VERSION "3.0" enum { @@ -54,7 +54,7 @@ enum { AHCI_MAX_PORTS = 32, AHCI_MAX_SG = 168, /* hardware max is 64K */ AHCI_DMA_BOUNDARY = 0xffffffff, - AHCI_USE_CLUSTERING = 0, + AHCI_USE_CLUSTERING = 1, AHCI_MAX_CMDS = 32, AHCI_CMD_SZ = 32, AHCI_CMD_SLOT_SZ = AHCI_MAX_CMDS * AHCI_CMD_SZ, @@ -77,10 +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_vt8251 = 1, + board_ahci_ign_iferr = 2, + board_ahci_sb600 = 3, + board_ahci_mv = 4, /* global controller registers */ HOST_CAP = 0x00, /* host capabilities */ @@ -96,8 +96,10 @@ 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 */ HOST_CAP_NCQ = (1 << 30), /* Native Command Queueing */ HOST_CAP_64 = (1 << 31), /* PCI DAC (64-bit DMA) support */ @@ -112,11 +114,11 @@ enum { PORT_TFDATA = 0x20, /* taskfile data */ PORT_SIG = 0x24, /* device TF signature */ PORT_CMD_ISSUE = 0x38, /* command issue */ - PORT_SCR = 0x28, /* SATA phy register block */ PORT_SCR_STAT = 0x28, /* SATA phy register: SStatus */ PORT_SCR_CTL = 0x2c, /* SATA phy register: SControl */ PORT_SCR_ERR = 0x30, /* SATA phy register: SError */ PORT_SCR_ACT = 0x34, /* SATA phy register: SActive */ + PORT_SCR_NTF = 0x3c, /* SATA phy register: SNotification */ /* PORT_IRQ_{STAT,MASK} bits */ PORT_IRQ_COLD_PRES = (1 << 31), /* cold presence detect */ @@ -142,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, @@ -152,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 */ @@ -165,17 +169,21 @@ enum { PORT_CMD_ICC_PARTIAL = (0x2 << 28), /* Put i/f in partial state */ PORT_CMD_ICC_SLUMBER = (0x6 << 28), /* Put i/f in slumber state */ + /* hpriv->flags bits */ + AHCI_HFLAG_NO_NCQ = (1 << 0), + AHCI_HFLAG_IGN_IRQ_IF_ERR = (1 << 1), /* ignore IRQ_IF_ERR */ + AHCI_HFLAG_IGN_SERR_INTERNAL = (1 << 2), /* ignore SERR_INTERNAL */ + AHCI_HFLAG_32BIT_ONLY = (1 << 3), /* force 32bit */ + AHCI_HFLAG_MV_PATA = (1 << 4), /* PATA port */ + AHCI_HFLAG_NO_MSI = (1 << 5), /* no PCI MSI */ + /* 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_NO_HOTPLUG = (1 << 24), /* ignore PxSERR.DIAG.N */ AHCI_FLAG_COMMON = ATA_FLAG_SATA | ATA_FLAG_NO_LEGACY | ATA_FLAG_MMIO | ATA_FLAG_PIO_DMA | - ATA_FLAG_SKIP_D2H_BSY | - ATA_FLAG_ACPI_SATA, + ATA_FLAG_ACPI_SATA | ATA_FLAG_AN, + AHCI_LFLAG_COMMON = ATA_LFLAG_SKIP_D2H_BSY, }; struct ahci_cmd_hdr { @@ -194,6 +202,7 @@ struct ahci_sg { }; struct ahci_host_priv { + unsigned int flags; /* AHCI_HFLAG_* */ u32 cap; /* cap to use */ u32 port_map; /* port map to use */ u32 saved_cap; /* saved initial cap */ @@ -201,6 +210,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; @@ -211,10 +221,11 @@ struct ahci_port_priv { unsigned int ncq_saw_d2h:1; unsigned int ncq_saw_dmas:1; unsigned int ncq_saw_sdb:1; + u32 intr_mask; /* interrupts to enable */ }; -static u32 ahci_scr_read (struct ata_port *ap, unsigned int sc_reg); -static void ahci_scr_write (struct ata_port *ap, unsigned int sc_reg, u32 val); +static int ahci_scr_read(struct ata_port *ap, unsigned int sc_reg, u32 *val); +static int ahci_scr_write(struct ata_port *ap, unsigned int sc_reg, u32 val); static int ahci_init_one (struct pci_dev *pdev, const struct pci_device_id *ent); static unsigned int ahci_qc_issue(struct ata_queued_cmd *qc); static void ahci_irq_clear(struct ata_port *ap); @@ -225,12 +236,19 @@ 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); +static int ahci_port_resume(struct ata_port *ap); +static unsigned int ahci_fill_sg(struct ata_queued_cmd *qc, void *cmd_tbl); +static void ahci_fill_cmd_slot(struct ahci_port_priv *pp, unsigned int tag, + u32 opts); #ifdef CONFIG_PM static int ahci_port_suspend(struct ata_port *ap, pm_message_t mesg); -static int ahci_port_resume(struct ata_port *ap); static int ahci_pci_device_suspend(struct pci_dev *pdev, pm_message_t mesg); static int ahci_pci_device_resume(struct pci_dev *pdev); #endif @@ -255,20 +273,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, @@ -279,6 +294,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, @@ -289,20 +309,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, @@ -313,6 +330,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, @@ -322,43 +344,54 @@ static const struct ata_port_operations ahci_vt8251_ops = { .port_stop = ahci_port_stop, }; +#define AHCI_HFLAGS(flags) .private_data = (void *)(flags) + static const struct ata_port_info ahci_port_info[] = { /* board_ahci */ { .flags = AHCI_FLAG_COMMON, + .link_flags = AHCI_LFLAG_COMMON, .pio_mask = 0x1f, /* pio0-4 */ - .udma_mask = 0x7f, /* udma0-6 ; FIXME */ - .port_ops = &ahci_ops, - }, - /* board_ahci_pi */ - { - .flags = AHCI_FLAG_COMMON | AHCI_FLAG_HONOR_PI, - .pio_mask = 0x1f, /* pio0-4 */ - .udma_mask = 0x7f, /* udma0-6 ; FIXME */ + .udma_mask = ATA_UDMA6, .port_ops = &ahci_ops, }, /* board_ahci_vt8251 */ { - .flags = AHCI_FLAG_COMMON | ATA_FLAG_HRST_TO_RESUME | - AHCI_FLAG_NO_NCQ, + AHCI_HFLAGS (AHCI_HFLAG_NO_NCQ), + .flags = AHCI_FLAG_COMMON, + .link_flags = AHCI_LFLAG_COMMON | ATA_LFLAG_HRST_TO_RESUME, .pio_mask = 0x1f, /* pio0-4 */ - .udma_mask = 0x7f, /* udma0-6 ; FIXME */ + .udma_mask = ATA_UDMA6, .port_ops = &ahci_vt8251_ops, }, /* board_ahci_ign_iferr */ { - .flags = AHCI_FLAG_COMMON | AHCI_FLAG_IGN_IRQ_IF_ERR, + AHCI_HFLAGS (AHCI_HFLAG_IGN_IRQ_IF_ERR), + .flags = AHCI_FLAG_COMMON, + .link_flags = AHCI_LFLAG_COMMON, .pio_mask = 0x1f, /* pio0-4 */ - .udma_mask = 0x7f, /* udma0-6 ; FIXME */ + .udma_mask = ATA_UDMA6, .port_ops = &ahci_ops, }, /* board_ahci_sb600 */ { - .flags = AHCI_FLAG_COMMON | - AHCI_FLAG_IGN_SERR_INTERNAL | - AHCI_FLAG_32BIT_ONLY, + AHCI_HFLAGS (AHCI_HFLAG_IGN_SERR_INTERNAL | + AHCI_HFLAG_32BIT_ONLY), + .flags = AHCI_FLAG_COMMON, + .link_flags = AHCI_LFLAG_COMMON, + .pio_mask = 0x1f, /* pio0-4 */ + .udma_mask = ATA_UDMA6, + .port_ops = &ahci_ops, + }, + /* board_ahci_mv */ + { + AHCI_HFLAGS (AHCI_HFLAG_NO_NCQ | AHCI_HFLAG_NO_MSI | + AHCI_HFLAG_MV_PATA), + .flags = ATA_FLAG_SATA | ATA_FLAG_NO_LEGACY | + ATA_FLAG_MMIO | ATA_FLAG_PIO_DMA, + .link_flags = AHCI_LFLAG_COMMON, .pio_mask = 0x1f, /* pio0-4 */ - .udma_mask = 0x7f, /* udma0-6 ; FIXME */ + .udma_mask = ATA_UDMA6, .port_ops = &ahci_ops, }, }; @@ -375,23 +408,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, @@ -399,7 +434,12 @@ static const struct pci_device_id ahci_pci_tbl[] = { /* ATI */ { PCI_VDEVICE(ATI, 0x4380), board_ahci_sb600 }, /* ATI SB600 */ - { PCI_VDEVICE(ATI, 0x4390), board_ahci_sb600 }, /* ATI SB700 */ + { PCI_VDEVICE(ATI, 0x4390), board_ahci_sb600 }, /* ATI SB700/800 */ + { PCI_VDEVICE(ATI, 0x4391), board_ahci_sb600 }, /* ATI SB700/800 */ + { PCI_VDEVICE(ATI, 0x4392), board_ahci_sb600 }, /* ATI SB700/800 */ + { PCI_VDEVICE(ATI, 0x4393), board_ahci_sb600 }, /* ATI SB700/800 */ + { PCI_VDEVICE(ATI, 0x4394), board_ahci_sb600 }, /* ATI SB700/800 */ + { PCI_VDEVICE(ATI, 0x4395), board_ahci_sb600 }, /* ATI SB700/800 */ /* VIA */ { PCI_VDEVICE(VIA, 0x3349), board_ahci_vt8251 }, /* VIA VT8251 */ @@ -456,6 +496,9 @@ static const struct pci_device_id ahci_pci_tbl[] = { { PCI_VDEVICE(SI, 0x1185), board_ahci }, /* SiS 966 */ { PCI_VDEVICE(SI, 0x0186), board_ahci }, /* SiS 968 */ + /* Marvell */ + { PCI_VDEVICE(MARVELL, 0x6145), board_ahci_mv }, /* 6145 */ + /* Generic, PCI class code for AHCI */ { PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID, PCI_CLASS_STORAGE_SATA_AHCI, 0xffffff, board_ahci }, @@ -481,17 +524,22 @@ static inline int ahci_nr_ports(u32 cap) return (cap & 0x1f) + 1; } -static inline void __iomem *ahci_port_base(struct ata_port *ap) +static inline void __iomem *__ahci_port_base(struct ata_host *host, + unsigned int port_no) { - void __iomem *mmio = ap->host->iomap[AHCI_PCI_BAR]; + void __iomem *mmio = host->iomap[AHCI_PCI_BAR]; - return mmio + 0x100 + (ap->port_no * 0x80); + return mmio + 0x100 + (port_no * 0x80); +} + +static inline void __iomem *ahci_port_base(struct ata_port *ap) +{ + return __ahci_port_base(ap->host, ap->port_no); } /** * ahci_save_initial_config - Save and fixup initial config values * @pdev: target PCI device - * @pi: associated ATA port info * @hpriv: host private area to store config values * * Some registers containing configuration info might be setup by @@ -505,7 +553,6 @@ static inline void __iomem *ahci_port_base(struct ata_port *ap) * None. */ static void ahci_save_initial_config(struct pci_dev *pdev, - const struct ata_port_info *pi, struct ahci_host_priv *hpriv) { void __iomem *mmio = pcim_iomap_table(pdev)[AHCI_PCI_BAR]; @@ -518,25 +565,35 @@ static void ahci_save_initial_config(struct pci_dev *pdev, hpriv->saved_cap = cap = readl(mmio + HOST_CAP); hpriv->saved_port_map = port_map = readl(mmio + HOST_PORTS_IMPL); - /* some chips lie about 64bit support */ - if ((cap & HOST_CAP_64) && (pi->flags & AHCI_FLAG_32BIT_ONLY)) { + /* some chips have errata preventing 64bit use */ + if ((cap & HOST_CAP_64) && (hpriv->flags & AHCI_HFLAG_32BIT_ONLY)) { dev_printk(KERN_INFO, &pdev->dev, "controller can't do 64bit DMA, forcing 32bit\n"); cap &= ~HOST_CAP_64; } - /* 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); + if ((cap & HOST_CAP_NCQ) && (hpriv->flags & AHCI_HFLAG_NO_NCQ)) { + dev_printk(KERN_INFO, &pdev->dev, + "controller can't do NCQ, turning off CAP_NCQ\n"); + cap &= ~HOST_CAP_NCQ; + } - /* 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 + * presence register, as bit 4 (counting from 0) + */ + if (hpriv->flags & AHCI_HFLAG_MV_PATA) { + dev_printk(KERN_ERR, &pdev->dev, + "MV_AHCI HACK: port_map %x -> %x\n", + hpriv->port_map, + hpriv->port_map & 0xf); + + port_map &= 0xf; } /* 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); @@ -547,17 +604,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 */ @@ -584,38 +650,45 @@ static void ahci_restore_initial_config(struct ata_host *host) (void) readl(mmio + HOST_PORTS_IMPL); /* flush */ } -static u32 ahci_scr_read (struct ata_port *ap, unsigned int sc_reg_in) +static unsigned ahci_scr_offset(struct ata_port *ap, unsigned int sc_reg) { - unsigned int sc_reg; - - switch (sc_reg_in) { - case SCR_STATUS: sc_reg = 0; break; - case SCR_CONTROL: sc_reg = 1; break; - case SCR_ERROR: sc_reg = 2; break; - case SCR_ACTIVE: sc_reg = 3; break; - default: - return 0xffffffffU; - } + 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; - return readl(ap->ioaddr.scr_addr + (sc_reg * 4)); + if (sc_reg < ARRAY_SIZE(offset) && + (sc_reg != SCR_NOTIFICATION || (hpriv->cap & HOST_CAP_SNTF))) + return offset[sc_reg]; + return 0; } - -static void ahci_scr_write (struct ata_port *ap, unsigned int sc_reg_in, - u32 val) +static int ahci_scr_read(struct ata_port *ap, unsigned int sc_reg, u32 *val) { - unsigned int sc_reg; - - switch (sc_reg_in) { - case SCR_STATUS: sc_reg = 0; break; - case SCR_CONTROL: sc_reg = 1; break; - case SCR_ERROR: sc_reg = 2; break; - case SCR_ACTIVE: sc_reg = 3; break; - default: - return; + void __iomem *port_mmio = ahci_port_base(ap); + int offset = ahci_scr_offset(ap, sc_reg); + + if (offset) { + *val = readl(port_mmio + offset); + return 0; } + return -EINVAL; +} - writel(val, ap->ioaddr.scr_addr + (sc_reg * 4)); +static int ahci_scr_write(struct ata_port *ap, unsigned int sc_reg, u32 val) +{ + void __iomem *port_mmio = ahci_port_base(ap); + int offset = ahci_scr_offset(ap, sc_reg); + + if (offset) { + writel(val, port_mmio + offset); + return 0; + } + return -EINVAL; } static void ahci_start_engine(struct ata_port *ap) @@ -740,7 +813,7 @@ static void ahci_power_down(struct ata_port *ap) } #endif -static void ahci_init_port(struct ata_port *ap) +static void ahci_start_port(struct ata_port *ap) { /* enable FIS reception */ ahci_start_fis_rx(ap); @@ -776,8 +849,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 */ @@ -814,39 +893,63 @@ static int ahci_reset_controller(struct ata_host *host) return 0; } +static void ahci_port_init(struct pci_dev *pdev, struct ata_port *ap, + int port_no, void __iomem *mmio, + void __iomem *port_mmio) +{ + const char *emsg = NULL; + int rc; + u32 tmp; + + /* make sure port is not active */ + rc = ahci_deinit_port(ap, &emsg); + if (rc) + dev_printk(KERN_WARNING, &pdev->dev, + "%s (%d)\n", emsg, rc); + + /* clear SError */ + tmp = readl(port_mmio + PORT_SCR_ERR); + VPRINTK("PORT_SCR_ERR 0x%x\n", tmp); + writel(tmp, port_mmio + PORT_SCR_ERR); + + /* clear port IRQ */ + tmp = readl(port_mmio + PORT_IRQ_STAT); + VPRINTK("PORT_IRQ_STAT 0x%x\n", tmp); + if (tmp) + writel(tmp, port_mmio + PORT_IRQ_STAT); + + writel(1 << port_no, mmio + HOST_IRQ_STAT); +} + static void ahci_init_controller(struct ata_host *host) { + struct ahci_host_priv *hpriv = host->private_data; struct pci_dev *pdev = to_pci_dev(host->dev); void __iomem *mmio = host->iomap[AHCI_PCI_BAR]; - int i, rc; + int i; + void __iomem *port_mmio; u32 tmp; - for (i = 0; i < host->n_ports; i++) { - struct ata_port *ap = host->ports[i]; - void __iomem *port_mmio = ahci_port_base(ap); - const char *emsg = NULL; - - if (ata_port_is_dummy(ap)) - continue; - - /* make sure port is not active */ - rc = ahci_deinit_port(ap, &emsg); - if (rc) - dev_printk(KERN_WARNING, &pdev->dev, - "%s (%d)\n", emsg, rc); + if (hpriv->flags & AHCI_HFLAG_MV_PATA) { + port_mmio = __ahci_port_base(host, 4); - /* clear SError */ - tmp = readl(port_mmio + PORT_SCR_ERR); - VPRINTK("PORT_SCR_ERR 0x%x\n", tmp); - writel(tmp, port_mmio + PORT_SCR_ERR); + writel(0, port_mmio + PORT_IRQ_MASK); /* clear port IRQ */ tmp = readl(port_mmio + PORT_IRQ_STAT); VPRINTK("PORT_IRQ_STAT 0x%x\n", tmp); if (tmp) writel(tmp, port_mmio + PORT_IRQ_STAT); + } - writel(1 << i, mmio + HOST_IRQ_STAT); + 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(pdev, ap, i, mmio, port_mmio); } tmp = readl(mmio + HOST_CTL); @@ -884,85 +987,115 @@ static void ahci_fill_cmd_slot(struct ahci_port_priv *pp, unsigned int tag, pp->cmd_slot[tag].tbl_addr_hi = cpu_to_le32((cmd_tbl_dma >> 16) >> 16); } -static int ahci_clo(struct ata_port *ap) +static int ahci_kick_engine(struct ata_port *ap, int force_restart) { void __iomem *port_mmio = ap->ioaddr.cmd_addr; struct ahci_host_priv *hpriv = ap->host->private_data; u32 tmp; + int busy, rc; + + /* do we need to kick the port? */ + busy = ahci_check_status(ap) & (ATA_BUSY | ATA_DRQ); + if (!busy && !force_restart) + return 0; + + /* stop engine */ + rc = ahci_stop_engine(ap); + if (rc) + goto out_restart; + + /* need to do CLO? */ + if (!busy) { + rc = 0; + goto out_restart; + } - if (!(hpriv->cap & HOST_CAP_CLO)) - return -EOPNOTSUPP; + 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(port_mmio + PORT_CMD, PORT_CMD_CLO, PORT_CMD_CLO, 1, 500); if (tmp & PORT_CMD_CLO) - return -EIO; + rc = -EIO; - return 0; + /* restart engine */ + out_restart: + ahci_start_engine(ap); + return rc; } -static int ahci_softreset(struct ata_port *ap, unsigned int *class, - unsigned long deadline) +static int ahci_exec_polled_cmd(struct ata_port *ap, int pmp, + struct ata_taskfile *tf, int is_cmd, u16 flags, + unsigned long 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); - const u32 cmd_fis_len = 5; /* five dwords */ + 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)); + + /* issue & wait */ + writel(1, port_mmio + PORT_CMD_ISSUE); + + if (timeout_msec) { + tmp = ata_wait_register(port_mmio + PORT_CMD_ISSUE, 0x1, 0x1, + 1, timeout_msec); + if (tmp & 0x1) { + ahci_kick_engine(ap, 1); + return -EBUSY; + } + } else + readl(port_mmio + PORT_CMD_ISSUE); /* flush */ + + return 0; +} + +static int ahci_do_softreset(struct ata_link *link, unsigned int *class, + int pmp, unsigned long deadline) +{ + struct ata_port *ap = link->ap; const char *reason = NULL; + unsigned long now, msecs; struct ata_taskfile tf; - u32 tmp; - u8 *fis; int rc; DPRINTK("ENTER\n"); - if (ata_port_offline(ap)) { + if (ata_link_offline(link)) { DPRINTK("PHY reports no device\n"); *class = ATA_DEV_NONE; return 0; } /* prepare for SRST (AHCI-1.1 10.4.1) */ - rc = ahci_stop_engine(ap); - if (rc) { - reason = "failed to stop engine"; - goto fail_restart; - } - - /* check BUSY/DRQ, perform Command List Override if necessary */ - if (ahci_check_status(ap) & (ATA_BUSY | ATA_DRQ)) { - rc = ahci_clo(ap); - - if (rc == -EOPNOTSUPP) { - reason = "port busy but CLO unavailable"; - goto fail_restart; - } else if (rc) { - reason = "port busy but CLO failed"; - goto fail_restart; - } - } - - /* restart engine */ - ahci_start_engine(ap); + rc = ahci_kick_engine(ap, 1); + if (rc) + ata_link_printk(link, KERN_WARNING, + "failed to reset engine (errno=%d)", rc); - ata_tf_init(ap->device, &tf); - fis = pp->cmd_tbl; + ata_tf_init(link->device, &tf); /* issue the first D2H Register FIS */ - ahci_fill_cmd_slot(pp, 0, - cmd_fis_len | AHCI_CMD_RESET | AHCI_CMD_CLR_BUSY); + msecs = 0; + now = jiffies; + if (time_after(now, deadline)) + msecs = jiffies_to_msecs(deadline - now); tf.ctl |= ATA_SRST; - ata_tf_to_fis(&tf, fis, 0); - fis[1] &= ~(1 << 7); /* turn off Command FIS bit */ - - writel(1, port_mmio + PORT_CMD_ISSUE); - - tmp = ata_wait_register(port_mmio + PORT_CMD_ISSUE, 0x1, 0x1, 1, 500); - if (tmp & 0x1) { + 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; @@ -972,14 +1105,8 @@ static int ahci_softreset(struct ata_port *ap, unsigned int *class, msleep(1); /* issue the second D2H Register FIS */ - ahci_fill_cmd_slot(pp, 0, cmd_fis_len); - tf.ctl &= ~ATA_SRST; - ata_tf_to_fis(&tf, fis, 0); - fis[1] &= ~(1 << 7); /* turn off Command FIS bit */ - - writel(1, port_mmio + PORT_CMD_ISSUE); - readl(port_mmio + PORT_CMD_ISSUE); /* flush */ + ahci_exec_polled_cmd(ap, pmp, &tf, 0, 0, 0); /* spec mandates ">= 2ms" before checking status. * We wait 150ms, because that was the magic delay used for @@ -1002,16 +1129,26 @@ static int ahci_softreset(struct ata_port *ap, unsigned int *class, DPRINTK("EXIT, class=%u\n", *class); return 0; - fail_restart: - ahci_start_engine(ap); fail: - ata_port_printk(ap, KERN_ERR, "softreset failed (%s)\n", reason); + ata_link_printk(link, KERN_ERR, "softreset failed (%s)\n", reason); return rc; } -static int ahci_hardreset(struct ata_port *ap, unsigned int *class, +static int ahci_softreset(struct ata_link *link, unsigned int *class, + unsigned long 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, unsigned long deadline) { + struct ata_port *ap = link->ap; struct ahci_port_priv *pp = ap->private_data; u8 *d2h_fis = pp->rx_fis + RX_FIS_D2H_REG; struct ata_taskfile tf; @@ -1022,37 +1159,40 @@ static int ahci_hardreset(struct ata_port *ap, unsigned int *class, ahci_stop_engine(ap); /* clear D2H reception area to properly wait for D2H FIS */ - ata_tf_init(ap->device, &tf); + ata_tf_init(link->device, &tf); tf.command = 0x80; - ata_tf_to_fis(&tf, d2h_fis, 0); + ata_tf_to_fis(&tf, 0, 0, d2h_fis); - rc = sata_std_hardreset(ap, class, deadline); + rc = sata_std_hardreset(link, class, deadline); ahci_start_engine(ap); - if (rc == 0 && ata_port_online(ap)) + 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); return rc; } -static int ahci_vt8251_hardreset(struct ata_port *ap, unsigned int *class, +static int ahci_vt8251_hardreset(struct ata_link *link, unsigned int *class, unsigned long deadline) { + struct ata_port *ap = link->ap; + u32 serror; int rc; DPRINTK("ENTER\n"); ahci_stop_engine(ap); - rc = sata_port_hardreset(ap, sata_ehc_deb_timing(&ap->eh_context), + rc = sata_link_hardreset(link, sata_ehc_deb_timing(&link->eh_context), deadline); /* vt8251 needs SError cleared for the port to operate */ - ahci_scr_write(ap, SCR_ERROR, ahci_scr_read(ap, SCR_ERROR)); + ahci_scr_read(ap, SCR_ERROR, &serror); + ahci_scr_write(ap, SCR_ERROR, serror); ahci_start_engine(ap); @@ -1064,12 +1204,13 @@ static int ahci_vt8251_hardreset(struct ata_port *ap, unsigned int *class, return rc ?: -EAGAIN; } -static void ahci_postreset(struct ata_port *ap, unsigned int *class) +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(ap, class); + ata_std_postreset(link, class); /* Make sure port's ATAPI bit is set appropriately */ new_tmp = tmp = readl(port_mmio + PORT_CMD); @@ -1083,6 +1224,12 @@ static void ahci_postreset(struct ata_port *ap, 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; @@ -1141,7 +1288,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, cmd_tbl, 0); + 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); @@ -1154,7 +1301,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) @@ -1165,66 +1312,87 @@ static void ahci_qc_prep(struct ata_queued_cmd *qc) 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 *ehi = &ap->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 */ - serror = ahci_scr_read(ap, SCR_ERROR); + 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) + if (hpriv->flags & AHCI_HFLAG_IGN_IRQ_IF_ERR) irq_stat &= ~PORT_IRQ_IF_ERR; if (irq_stat & PORT_IRQ_TF_ERR) { - err_mask |= AC_ERR_DEV; - if (ap->flags & AHCI_FLAG_IGN_SERR_INTERNAL) - serror &= ~SERR_INTERNAL; + /* 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 = (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->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); @@ -1232,10 +1400,10 @@ static void ahci_error_intr(struct ata_port *ap, u32 irq_stat) ata_port_abort(ap); } -static void ahci_host_intr(struct ata_port *ap) +static void ahci_port_intr(struct ata_port *ap) { void __iomem *port_mmio = ap->ioaddr.cmd_addr; - struct ata_eh_info *ehi = &ap->eh_info; + struct ata_eh_info *ehi = &ap->link.eh_info; struct ahci_port_priv *pp = ap->private_data; u32 status, qc_active; int rc, known_irq = 0; @@ -1248,7 +1416,22 @@ static void ahci_host_intr(struct ata_port *ap) return; } - if (ap->sactive) + if (status & PORT_IRQ_SDB_FIS) { + /* 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]); + + if (f0 & (1 << 15)) + sata_async_notification(ap); + } + + /* 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); @@ -1268,7 +1451,7 @@ static void ahci_host_intr(struct ata_port *ap) /* if !NCQ, ignore. No modern ATA device has broken HSM * implementation for non-NCQ commands. */ - if (!ap->sactive) + if (!ap->link.sactive) return; if (status & PORT_IRQ_D2H_REG_FIS) { @@ -1321,7 +1504,7 @@ static void ahci_host_intr(struct ata_port *ap) if (!known_irq) ata_port_printk(ap, KERN_INFO, "spurious interrupt " "(irq_stat 0x%x active_tag 0x%x sactive 0x%x)\n", - status, ap->active_tag, ap->sactive); + status, ap->link.active_tag, ap->link.sactive); } static void ahci_irq_clear(struct ata_port *ap) @@ -1358,7 +1541,7 @@ static irqreturn_t ahci_interrupt(int irq, void *dev_instance) ap = host->ports[i]; if (ap) { - ahci_host_intr(ap); + ahci_port_intr(ap); VPRINTK("port %u\n", i); } else { VPRINTK("port %u (no irq)\n", i); @@ -1386,6 +1569,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); @@ -1408,14 +1598,18 @@ static void ahci_thaw(struct ata_port *ap) void __iomem *mmio = ap->host->iomap[AHCI_PCI_BAR]; 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(DEF_PORT_IRQ, 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) @@ -1427,8 +1621,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) @@ -1448,11 +1644,80 @@ static void ahci_post_internal_cmd(struct ata_queued_cmd *qc) { struct ata_port *ap = qc->ap; - if (qc->flags & ATA_QCFLAG_FAILED) { - /* make DMA engine forget about the failed command */ - ahci_stop_engine(ap); - ahci_start_engine(ap); + /* make DMA engine forget about the failed command */ + if (qc->flags & ATA_QCFLAG_FAILED) + 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; } #ifdef CONFIG_PM @@ -1466,20 +1731,12 @@ static int ahci_port_suspend(struct ata_port *ap, pm_message_t mesg) ahci_power_down(ap); else { ata_port_printk(ap, KERN_ERR, "%s (%d)\n", emsg, rc); - ahci_init_port(ap); + ahci_start_port(ap); } return rc; } -static int ahci_port_resume(struct ata_port *ap) -{ - ahci_power_up(ap); - ahci_init_port(ap); - - return 0; -} - static int ahci_pci_device_suspend(struct pci_dev *pdev, pm_message_t mesg) { struct ata_host *host = dev_get_drvdata(&pdev->dev); @@ -1571,15 +1828,16 @@ static int ahci_port_start(struct ata_port *ap) pp->cmd_tbl = mem; pp->cmd_tbl_dma = mem_dma; - ap->private_data = pp; - - /* power up port */ - ahci_power_up(ap); + /* + * Save off initial list of interrupts to be enabled. + * This could be changed later + */ + pp->intr_mask = DEF_PORT_IRQ; - /* initialize port */ - ahci_init_port(ap); + ap->private_data = pp; - return 0; + /* engage engines, captain */ + return ahci_port_resume(ap); } static void ahci_port_stop(struct ata_port *ap) @@ -1675,12 +1933,13 @@ static void ahci_print_info(struct ata_host *host) dev_printk(KERN_INFO, &pdev->dev, "flags: " - "%s%s%s%s%s%s" - "%s%s%s%s%s%s%s\n" + "%s%s%s%s%s%s%s" + "%s%s%s%s%s%s%s\n" , cap & (1 << 31) ? "64bit " : "", cap & (1 << 30) ? "ncq " : "", + cap & (1 << 29) ? "sntf " : "", cap & (1 << 28) ? "ilck " : "", cap & (1 << 27) ? "stag " : "", cap & (1 << 26) ? "pm " : "", @@ -1724,20 +1983,24 @@ static int ahci_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) if (rc) return rc; - if (pci_enable_msi(pdev)) - pci_intx(pdev, 1); - hpriv = devm_kzalloc(dev, sizeof(*hpriv), GFP_KERNEL); if (!hpriv) return -ENOMEM; + hpriv->flags |= (unsigned long)pi.private_data; + + if ((hpriv->flags & AHCI_HFLAG_NO_MSI) || pci_enable_msi(pdev)) + pci_intx(pdev, 1); /* save initial config */ - ahci_save_initial_config(pdev, &pi, hpriv); + ahci_save_initial_config(pdev, hpriv); /* prepare host */ - if (!(pi.flags & AHCI_FLAG_NO_NCQ) && (hpriv->cap & HOST_CAP_NCQ)) + 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; @@ -1745,14 +2008,20 @@ static int ahci_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) host->private_data = hpriv; for (i = 0; i < host->n_ports; i++) { - if (hpriv->port_map & (1 << i)) { - struct ata_port *ap = host->ports[i]; - void __iomem *port_mmio = ahci_port_base(ap); + 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; - ap->ioaddr.scr_addr = port_mmio + PORT_SCR; - } else - host->ports[i]->ops = &ata_dummy_port_ops; + + /* disabled/not-implemented port */ + else + ap->ops = &ata_dummy_port_ops; } /* initialize adapter */