diff options
Diffstat (limited to 'drivers/ata/ahci.c')
-rw-r--r-- | drivers/ata/ahci.c | 260 |
1 files changed, 202 insertions, 58 deletions
diff --git a/drivers/ata/ahci.c b/drivers/ata/ahci.c index f510e1196dc..b517d249355 100644 --- a/drivers/ata/ahci.c +++ b/drivers/ata/ahci.c @@ -53,6 +53,7 @@ enum { AHCI_PCI_BAR = 5, + AHCI_MAX_PORTS = 32, AHCI_MAX_SG = 168, /* hardware max is 64K */ AHCI_DMA_BOUNDARY = 0xffffffff, AHCI_USE_CLUSTERING = 0, @@ -77,7 +78,9 @@ enum { RX_FIS_UNK = 0x60, /* offset of Unknown FIS data */ board_ahci = 0, - board_ahci_vt8251 = 1, + board_ahci_pi = 1, + board_ahci_vt8251 = 2, + board_ahci_ign_iferr = 3, /* global controller registers */ HOST_CAP = 0x00, /* host capabilities */ @@ -166,8 +169,9 @@ enum { AHCI_FLAG_MSI = (1 << 0), /* ap->flags bits */ - AHCI_FLAG_RESET_NEEDS_CLO = (1 << 24), - AHCI_FLAG_NO_NCQ = (1 << 25), + 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 */ }; struct ahci_cmd_hdr { @@ -214,6 +218,7 @@ 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_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_suspend(struct ata_port *ap, pm_message_t mesg); static int ahci_port_resume(struct ata_port *ap); @@ -273,6 +278,37 @@ static const struct ata_port_operations ahci_ops = { .port_stop = ahci_port_stop, }; +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_prep = ahci_qc_prep, + .qc_issue = ahci_qc_issue, + + .irq_handler = ahci_interrupt, + .irq_clear = ahci_irq_clear, + + .scr_read = ahci_scr_read, + .scr_write = ahci_scr_write, + + .freeze = ahci_freeze, + .thaw = ahci_thaw, + + .error_handler = ahci_vt8251_error_handler, + .post_internal_cmd = ahci_post_internal_cmd, + + .port_suspend = ahci_port_suspend, + .port_resume = ahci_port_resume, + + .port_start = ahci_port_start, + .port_stop = ahci_port_stop, +}; + static const struct ata_port_info ahci_port_info[] = { /* board_ahci */ { @@ -284,13 +320,34 @@ static const struct ata_port_info ahci_port_info[] = { .udma_mask = 0x7f, /* udma0-6 ; FIXME */ .port_ops = &ahci_ops, }, + /* board_ahci_pi */ + { + .sht = &ahci_sht, + .flags = ATA_FLAG_SATA | ATA_FLAG_NO_LEGACY | + ATA_FLAG_MMIO | ATA_FLAG_PIO_DMA | + ATA_FLAG_SKIP_D2H_BSY | AHCI_FLAG_HONOR_PI, + .pio_mask = 0x1f, /* pio0-4 */ + .udma_mask = 0x7f, /* udma0-6 ; FIXME */ + .port_ops = &ahci_ops, + }, /* board_ahci_vt8251 */ { .sht = &ahci_sht, .flags = ATA_FLAG_SATA | ATA_FLAG_NO_LEGACY | ATA_FLAG_MMIO | ATA_FLAG_PIO_DMA | ATA_FLAG_SKIP_D2H_BSY | - AHCI_FLAG_RESET_NEEDS_CLO | AHCI_FLAG_NO_NCQ, + ATA_FLAG_HRST_TO_RESUME | AHCI_FLAG_NO_NCQ, + .pio_mask = 0x1f, /* pio0-4 */ + .udma_mask = 0x7f, /* udma0-6 ; FIXME */ + .port_ops = &ahci_vt8251_ops, + }, + /* board_ahci_ign_iferr */ + { + .sht = &ahci_sht, + .flags = ATA_FLAG_SATA | ATA_FLAG_NO_LEGACY | + ATA_FLAG_MMIO | ATA_FLAG_PIO_DMA | + ATA_FLAG_SKIP_D2H_BSY | + AHCI_FLAG_IGN_IRQ_IF_ERR, .pio_mask = 0x1f, /* pio0-4 */ .udma_mask = 0x7f, /* udma0-6 ; FIXME */ .port_ops = &ahci_ops, @@ -309,29 +366,29 @@ 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 }, /* 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, 0x292f), board_ahci }, /* ICH9M */ - { PCI_VDEVICE(INTEL, 0x294d), board_ahci }, /* ICH9 */ - { PCI_VDEVICE(INTEL, 0x294e), board_ahci }, /* ICH9M */ + { 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, 0x292f), board_ahci_pi }, /* ICH9M */ + { PCI_VDEVICE(INTEL, 0x294d), board_ahci_pi }, /* ICH9 */ + { PCI_VDEVICE(INTEL, 0x294e), board_ahci_pi }, /* ICH9M */ /* JMicron */ - { PCI_VDEVICE(JMICRON, 0x2360), board_ahci }, /* JMicron JMB360 */ - { PCI_VDEVICE(JMICRON, 0x2361), board_ahci }, /* JMicron JMB361 */ - { PCI_VDEVICE(JMICRON, 0x2363), board_ahci }, /* JMicron JMB363 */ - { PCI_VDEVICE(JMICRON, 0x2365), board_ahci }, /* JMicron JMB365 */ - { PCI_VDEVICE(JMICRON, 0x2366), board_ahci }, /* JMicron JMB366 */ + { PCI_VDEVICE(JMICRON, 0x2360), board_ahci_ign_iferr }, /* JMB360 */ + { PCI_VDEVICE(JMICRON, 0x2361), board_ahci_ign_iferr }, /* JMB361 */ + { PCI_VDEVICE(JMICRON, 0x2363), board_ahci_ign_iferr }, /* JMB363 */ + { PCI_VDEVICE(JMICRON, 0x2365), board_ahci_ign_iferr }, /* JMB365 */ + { PCI_VDEVICE(JMICRON, 0x2366), board_ahci_ign_iferr }, /* JMB366 */ /* ATI */ { PCI_VDEVICE(ATI, 0x4380), board_ahci }, /* ATI SB600 non-raid */ @@ -345,6 +402,14 @@ static const struct pci_device_id ahci_pci_tbl[] = { { PCI_VDEVICE(NVIDIA, 0x044d), board_ahci }, /* MCP65 */ { PCI_VDEVICE(NVIDIA, 0x044e), board_ahci }, /* MCP65 */ { PCI_VDEVICE(NVIDIA, 0x044f), board_ahci }, /* MCP65 */ + { PCI_VDEVICE(NVIDIA, 0x045c), board_ahci }, /* MCP65 */ + { PCI_VDEVICE(NVIDIA, 0x045d), board_ahci }, /* MCP65 */ + { PCI_VDEVICE(NVIDIA, 0x045e), board_ahci }, /* MCP65 */ + { PCI_VDEVICE(NVIDIA, 0x045f), board_ahci }, /* MCP65 */ + { PCI_VDEVICE(NVIDIA, 0x0550), board_ahci }, /* MCP67 */ + { PCI_VDEVICE(NVIDIA, 0x0551), board_ahci }, /* MCP67 */ + { PCI_VDEVICE(NVIDIA, 0x0552), board_ahci }, /* MCP67 */ + { PCI_VDEVICE(NVIDIA, 0x0553), board_ahci }, /* MCP67 */ { PCI_VDEVICE(NVIDIA, 0x0554), board_ahci }, /* MCP67 */ { PCI_VDEVICE(NVIDIA, 0x0555), board_ahci }, /* MCP67 */ { PCI_VDEVICE(NVIDIA, 0x0556), board_ahci }, /* MCP67 */ @@ -359,6 +424,10 @@ 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 */ + /* Generic, PCI class code for AHCI */ + { PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID, + 0x010601, 0xffffff, board_ahci }, + { } /* terminate list */ }; @@ -373,6 +442,11 @@ static struct pci_driver ahci_pci_driver = { }; +static inline int ahci_nr_ports(u32 cap) +{ + return (cap & 0x1f) + 1; +} + static inline unsigned long ahci_port_base_ul (unsigned long base, unsigned int port) { return base + 0x100 + (port * 0x80); @@ -546,9 +620,6 @@ static void ahci_power_down(void __iomem *port_mmio, u32 cap) static void ahci_init_port(void __iomem *port_mmio, u32 cap, dma_addr_t cmd_slot_dma, dma_addr_t rx_fis_dma) { - /* power up */ - ahci_power_up(port_mmio, cap); - /* enable FIS reception */ ahci_start_fis_rx(port_mmio, cap, cmd_slot_dma, rx_fis_dma); @@ -574,19 +645,15 @@ static int ahci_deinit_port(void __iomem *port_mmio, u32 cap, const char **emsg) return rc; } - /* put device into slumber mode */ - ahci_power_down(port_mmio, cap); - return 0; } static int ahci_reset_controller(void __iomem *mmio, struct pci_dev *pdev) { - u32 cap_save, tmp; + u32 cap_save, impl_save, tmp; cap_save = readl(mmio + HOST_CAP); - cap_save &= ( (1<<28) | (1<<17) ); - cap_save |= (1 << 27); + impl_save = readl(mmio + HOST_PORTS_IMPL); /* global controller reset */ tmp = readl(mmio + HOST_CTL); @@ -607,10 +674,21 @@ static int ahci_reset_controller(void __iomem *mmio, struct pci_dev *pdev) return -EIO; } + /* turn on AHCI mode */ writel(HOST_AHCI_EN, mmio + HOST_CTL); (void) readl(mmio + HOST_CTL); /* flush */ + + /* These write-once registers are normally cleared on reset. + * Restore BIOS values... which we HOPE were present before + * reset. + */ + if (!impl_save) { + impl_save = (1 << ahci_nr_ports(cap_save)) - 1; + dev_printk(KERN_WARNING, &pdev->dev, + "PORTS_IMPL is zero, forcing 0x%x\n", impl_save); + } writel(cap_save, mmio + HOST_CAP); - writel(0xf, mmio + HOST_PORTS_IMPL); + writel(impl_save, mmio + HOST_PORTS_IMPL); (void) readl(mmio + HOST_PORTS_IMPL); /* flush */ if (pdev->vendor == PCI_VENDOR_ID_INTEL) { @@ -626,7 +704,8 @@ static int ahci_reset_controller(void __iomem *mmio, struct pci_dev *pdev) } static void ahci_init_controller(void __iomem *mmio, struct pci_dev *pdev, - int n_ports, u32 cap) + int n_ports, unsigned int port_flags, + struct ahci_host_priv *hpriv) { int i, rc; u32 tmp; @@ -635,13 +714,12 @@ static void ahci_init_controller(void __iomem *mmio, struct pci_dev *pdev, void __iomem *port_mmio = ahci_port_base(mmio, i); const char *emsg = NULL; -#if 0 /* BIOSen initialize this incorrectly */ - if (!(hpriv->port_map & (1 << i))) + if ((port_flags & AHCI_FLAG_HONOR_PI) && + !(hpriv->port_map & (1 << i))) continue; -#endif /* make sure port is not active */ - rc = ahci_deinit_port(port_mmio, cap, &emsg); + rc = ahci_deinit_port(port_mmio, hpriv->cap, &emsg); if (rc) dev_printk(KERN_WARNING, &pdev->dev, "%s (%d)\n", emsg, rc); @@ -716,17 +794,6 @@ static int ahci_clo(struct ata_port *ap) return 0; } -static int ahci_prereset(struct ata_port *ap) -{ - if ((ap->flags & AHCI_FLAG_RESET_NEEDS_CLO) && - (ata_busy_wait(ap, ATA_BUSY, 1000) & ATA_BUSY)) { - /* ATA_BUSY hasn't cleared, so send a CLO */ - ahci_clo(ap); - } - - return ata_std_prereset(ap); -} - static int ahci_softreset(struct ata_port *ap, unsigned int *class) { struct ahci_port_priv *pp = ap->private_data; @@ -864,6 +931,31 @@ static int ahci_hardreset(struct ata_port *ap, unsigned int *class) return rc; } +static int ahci_vt8251_hardreset(struct ata_port *ap, unsigned int *class) +{ + void __iomem *mmio = ap->host->mmio_base; + void __iomem *port_mmio = ahci_port_base(mmio, ap->port_no); + int rc; + + DPRINTK("ENTER\n"); + + ahci_stop_engine(port_mmio); + + rc = sata_port_hardreset(ap, sata_ehc_deb_timing(&ap->eh_context)); + + /* vt8251 needs SError cleared for the port to operate */ + ahci_scr_write(ap, SCR_ERROR, ahci_scr_read(ap, SCR_ERROR)); + + ahci_start_engine(port_mmio); + + DPRINTK("EXIT, rc=%d, class=%u\n", rc, *class); + + /* vt8251 doesn't clear BSY on signature FIS reception, + * request follow-up softreset. + */ + return rc ?: -EAGAIN; +} + static void ahci_postreset(struct ata_port *ap, unsigned int *class) { void __iomem *port_mmio = (void __iomem *) ap->ioaddr.cmd_addr; @@ -980,6 +1072,10 @@ static void ahci_error_intr(struct ata_port *ap, u32 irq_stat) /* analyze @irq_stat */ ata_ehi_push_desc(ehi, "irq_stat 0x%08x", irq_stat); + /* 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; @@ -1179,7 +1275,23 @@ static void ahci_error_handler(struct ata_port *ap) } /* perform recovery */ - ata_do_eh(ap, ahci_prereset, ahci_softreset, ahci_hardreset, + ata_do_eh(ap, ata_std_prereset, ahci_softreset, ahci_hardreset, + ahci_postreset); +} + +static void ahci_vt8251_error_handler(struct ata_port *ap) +{ + void __iomem *mmio = ap->host->mmio_base; + void __iomem *port_mmio = ahci_port_base(mmio, ap->port_no); + + if (!(ap->pflags & ATA_PFLAG_FROZEN)) { + /* restart engine */ + ahci_stop_engine(port_mmio); + ahci_start_engine(port_mmio); + } + + /* perform recovery */ + ata_do_eh(ap, ata_std_prereset, ahci_softreset, ahci_vt8251_hardreset, ahci_postreset); } @@ -1209,7 +1321,9 @@ static int ahci_port_suspend(struct ata_port *ap, pm_message_t mesg) int rc; rc = ahci_deinit_port(port_mmio, hpriv->cap, &emsg); - if (rc) { + if (rc == 0) + ahci_power_down(port_mmio, hpriv->cap); + else { ata_port_printk(ap, KERN_ERR, "%s (%d)\n", emsg, rc); ahci_init_port(port_mmio, hpriv->cap, pp->cmd_slot_dma, pp->rx_fis_dma); @@ -1225,6 +1339,7 @@ static int ahci_port_resume(struct ata_port *ap) void __iomem *mmio = ap->host->mmio_base; void __iomem *port_mmio = ahci_port_base(mmio, ap->port_no); + ahci_power_up(port_mmio, hpriv->cap); ahci_init_port(port_mmio, hpriv->cap, pp->cmd_slot_dma, pp->rx_fis_dma); return 0; @@ -1264,7 +1379,8 @@ static int ahci_pci_device_resume(struct pci_dev *pdev) if (rc) return rc; - ahci_init_controller(mmio, pdev, host->n_ports, hpriv->cap); + ahci_init_controller(mmio, pdev, host->n_ports, + host->ports[0]->flags, hpriv); } ata_host_resume(host); @@ -1330,6 +1446,9 @@ static int ahci_port_start(struct ata_port *ap) ap->private_data = pp; + /* power up port */ + ahci_power_up(port_mmio, hpriv->cap); + /* initialize port */ ahci_init_port(port_mmio, hpriv->cap, pp->cmd_slot_dma, pp->rx_fis_dma); @@ -1376,7 +1495,7 @@ static int ahci_host_init(struct ata_probe_ent *probe_ent) struct ahci_host_priv *hpriv = probe_ent->private_data; struct pci_dev *pdev = to_pci_dev(probe_ent->dev); void __iomem *mmio = probe_ent->mmio_base; - unsigned int i, using_dac; + unsigned int i, cap_n_ports, using_dac; int rc; rc = ahci_reset_controller(mmio, pdev); @@ -1385,10 +1504,34 @@ static int ahci_host_init(struct ata_probe_ent *probe_ent) hpriv->cap = readl(mmio + HOST_CAP); hpriv->port_map = readl(mmio + HOST_PORTS_IMPL); - probe_ent->n_ports = (hpriv->cap & 0x1f) + 1; + cap_n_ports = ahci_nr_ports(hpriv->cap); VPRINTK("cap 0x%x port_map 0x%x n_ports %d\n", - hpriv->cap, hpriv->port_map, probe_ent->n_ports); + hpriv->cap, hpriv->port_map, cap_n_ports); + + if (probe_ent->port_flags & AHCI_FLAG_HONOR_PI) { + unsigned int n_ports = cap_n_ports; + u32 port_map = hpriv->port_map; + int max_port = 0; + + for (i = 0; i < AHCI_MAX_PORTS && n_ports; i++) { + if (port_map & (1 << i)) { + n_ports--; + port_map &= ~(1 << i); + max_port = i; + } else + probe_ent->dummy_port_mask |= 1 << i; + } + + if (n_ports || port_map) + dev_printk(KERN_WARNING, &pdev->dev, + "nr_ports (%u) and implemented port map " + "(0x%x) don't match\n", + cap_n_ports, hpriv->port_map); + + probe_ent->n_ports = max_port + 1; + } else + probe_ent->n_ports = cap_n_ports; using_dac = hpriv->cap & HOST_CAP_64; if (using_dac && @@ -1420,7 +1563,8 @@ static int ahci_host_init(struct ata_probe_ent *probe_ent) for (i = 0; i < probe_ent->n_ports; i++) ahci_setup_port(&probe_ent->port[i], (unsigned long) mmio, i); - ahci_init_controller(mmio, pdev, probe_ent->n_ports, hpriv->cap); + ahci_init_controller(mmio, pdev, probe_ent->n_ports, + probe_ent->port_flags, hpriv); pci_set_master(pdev); |