diff options
Diffstat (limited to 'drivers/ata/ata_piix.c')
-rw-r--r-- | drivers/ata/ata_piix.c | 393 |
1 files changed, 317 insertions, 76 deletions
diff --git a/drivers/ata/ata_piix.c b/drivers/ata/ata_piix.c index b406b39b878..a65c8ae5c46 100644 --- a/drivers/ata/ata_piix.c +++ b/drivers/ata/ata_piix.c @@ -101,39 +101,21 @@ enum { ICH5_PMR = 0x90, /* port mapping register */ ICH5_PCS = 0x92, /* port control and status */ PIIX_SCC = 0x0A, /* sub-class code register */ + PIIX_SIDPR_BAR = 5, + PIIX_SIDPR_LEN = 16, + PIIX_SIDPR_IDX = 0, + PIIX_SIDPR_DATA = 4, - PIIX_FLAG_SCR = (1 << 26), /* SCR available */ PIIX_FLAG_AHCI = (1 << 27), /* AHCI possible */ PIIX_FLAG_CHECKINTR = (1 << 28), /* make sure PCI INTx enabled */ + PIIX_FLAG_SIDPR = (1 << 29), /* SATA idx/data pair regs */ PIIX_PATA_FLAGS = ATA_FLAG_SLAVE_POSS, PIIX_SATA_FLAGS = ATA_FLAG_SATA | PIIX_FLAG_CHECKINTR, - /* combined mode. if set, PATA is channel 0. - * if clear, PATA is channel 1. - */ - PIIX_PORT_ENABLED = (1 << 0), - PIIX_PORT_PRESENT = (1 << 4), - PIIX_80C_PRI = (1 << 5) | (1 << 4), PIIX_80C_SEC = (1 << 7) | (1 << 6), - /* controller IDs */ - piix_pata_mwdma = 0, /* PIIX3 MWDMA only */ - piix_pata_33, /* PIIX4 at 33Mhz */ - ich_pata_33, /* ICH up to UDMA 33 only */ - ich_pata_66, /* ICH up to 66 Mhz */ - ich_pata_100, /* ICH up to UDMA 100 */ - ich5_sata, - ich6_sata, - ich6_sata_ahci, - ich6m_sata_ahci, - ich8_sata_ahci, - ich8_2port_sata, - ich8m_apple_sata_ahci, /* locks up on second port enable */ - tolapai_sata_ahci, - piix_pata_vmw, /* PIIX4 for VMware, spurious DMA_ERR */ - /* constants for mapping table */ P0 = 0, /* port 0 */ P1 = 1, /* port 1 */ @@ -149,6 +131,24 @@ enum { PIIX_HOST_BROKEN_SUSPEND = (1 << 24), }; +enum piix_controller_ids { + /* controller IDs */ + piix_pata_mwdma, /* PIIX3 MWDMA only */ + piix_pata_33, /* PIIX4 at 33Mhz */ + ich_pata_33, /* ICH up to UDMA 33 only */ + ich_pata_66, /* ICH up to 66 Mhz */ + ich_pata_100, /* ICH up to UDMA 100 */ + ich5_sata, + ich6_sata, + ich6_sata_ahci, + ich6m_sata_ahci, + ich8_sata_ahci, + ich8_2port_sata, + ich8m_apple_sata_ahci, /* locks up on second port enable */ + tolapai_sata_ahci, + piix_pata_vmw, /* PIIX4 for VMware, spurious DMA_ERR */ +}; + struct piix_map_db { const u32 mask; const u16 port_enable; @@ -157,6 +157,7 @@ struct piix_map_db { struct piix_host_priv { const int *map; + void __iomem *sidpr; }; static int piix_init_one(struct pci_dev *pdev, @@ -167,6 +168,9 @@ static void piix_set_dmamode(struct ata_port *ap, struct ata_device *adev); static void ich_set_dmamode(struct ata_port *ap, struct ata_device *adev); static int ich_pata_cable_detect(struct ata_port *ap); static u8 piix_vmw_bmdma_status(struct ata_port *ap); +static int piix_sidpr_scr_read(struct ata_port *ap, unsigned int reg, u32 *val); +static int piix_sidpr_scr_write(struct ata_port *ap, unsigned int reg, u32 val); +static void piix_sidpr_error_handler(struct ata_port *ap); #ifdef CONFIG_PM static int piix_pci_device_suspend(struct pci_dev *pdev, pm_message_t mesg); static int piix_pci_device_resume(struct pci_dev *pdev); @@ -321,7 +325,6 @@ static const struct ata_port_operations piix_pata_ops = { .post_internal_cmd = ata_bmdma_post_internal_cmd, .cable_detect = ata_cable_40wire, - .irq_handler = ata_interrupt, .irq_clear = ata_bmdma_irq_clear, .irq_on = ata_irq_on, @@ -353,7 +356,6 @@ static const struct ata_port_operations ich_pata_ops = { .post_internal_cmd = ata_bmdma_post_internal_cmd, .cable_detect = ich_pata_cable_detect, - .irq_handler = ata_interrupt, .irq_clear = ata_bmdma_irq_clear, .irq_on = ata_irq_on, @@ -380,7 +382,6 @@ static const struct ata_port_operations piix_sata_ops = { .error_handler = ata_bmdma_error_handler, .post_internal_cmd = ata_bmdma_post_internal_cmd, - .irq_handler = ata_interrupt, .irq_clear = ata_bmdma_irq_clear, .irq_on = ata_irq_on, @@ -419,6 +420,35 @@ static const struct ata_port_operations piix_vmw_ops = { .port_start = ata_port_start, }; +static const struct ata_port_operations piix_sidpr_sata_ops = { + .tf_load = ata_tf_load, + .tf_read = ata_tf_read, + .check_status = ata_check_status, + .exec_command = ata_exec_command, + .dev_select = ata_std_dev_select, + + .bmdma_setup = ata_bmdma_setup, + .bmdma_start = ata_bmdma_start, + .bmdma_stop = ata_bmdma_stop, + .bmdma_status = ata_bmdma_status, + .qc_prep = ata_qc_prep, + .qc_issue = ata_qc_issue_prot, + .data_xfer = ata_data_xfer, + + .scr_read = piix_sidpr_scr_read, + .scr_write = piix_sidpr_scr_write, + + .freeze = ata_bmdma_freeze, + .thaw = ata_bmdma_thaw, + .error_handler = piix_sidpr_error_handler, + .post_internal_cmd = ata_bmdma_post_internal_cmd, + + .irq_clear = ata_bmdma_irq_clear, + .irq_on = ata_irq_on, + + .port_start = ata_port_start, +}; + static const struct piix_map_db ich5_map_db = { .mask = 0x7, .port_enable = 0x3, @@ -526,7 +556,6 @@ static const struct piix_map_db *piix_map_db_table[] = { static struct ata_port_info piix_port_info[] = { [piix_pata_mwdma] = /* PIIX3 MWDMA only */ { - .sht = &piix_sht, .flags = PIIX_PATA_FLAGS, .pio_mask = 0x1f, /* pio0-4 */ .mwdma_mask = 0x06, /* mwdma1-2 ?? CHECK 0 should be ok but slow */ @@ -535,7 +564,6 @@ static struct ata_port_info piix_port_info[] = { [piix_pata_33] = /* PIIX4 at 33MHz */ { - .sht = &piix_sht, .flags = PIIX_PATA_FLAGS, .pio_mask = 0x1f, /* pio0-4 */ .mwdma_mask = 0x06, /* mwdma1-2 ?? CHECK 0 should be ok but slow */ @@ -545,7 +573,6 @@ static struct ata_port_info piix_port_info[] = { [ich_pata_33] = /* ICH0 - ICH at 33Mhz*/ { - .sht = &piix_sht, .flags = PIIX_PATA_FLAGS, .pio_mask = 0x1f, /* pio 0-4 */ .mwdma_mask = 0x06, /* Check: maybe 0x07 */ @@ -555,7 +582,6 @@ static struct ata_port_info piix_port_info[] = { [ich_pata_66] = /* ICH controllers up to 66MHz */ { - .sht = &piix_sht, .flags = PIIX_PATA_FLAGS, .pio_mask = 0x1f, /* pio 0-4 */ .mwdma_mask = 0x06, /* MWDMA0 is broken on chip */ @@ -565,7 +591,6 @@ static struct ata_port_info piix_port_info[] = { [ich_pata_100] = { - .sht = &piix_sht, .flags = PIIX_PATA_FLAGS | PIIX_FLAG_CHECKINTR, .pio_mask = 0x1f, /* pio0-4 */ .mwdma_mask = 0x06, /* mwdma1-2 */ @@ -575,7 +600,6 @@ static struct ata_port_info piix_port_info[] = { [ich5_sata] = { - .sht = &piix_sht, .flags = PIIX_SATA_FLAGS, .pio_mask = 0x1f, /* pio0-4 */ .mwdma_mask = 0x07, /* mwdma0-2 */ @@ -585,8 +609,7 @@ static struct ata_port_info piix_port_info[] = { [ich6_sata] = { - .sht = &piix_sht, - .flags = PIIX_SATA_FLAGS | PIIX_FLAG_SCR, + .flags = PIIX_SATA_FLAGS, .pio_mask = 0x1f, /* pio0-4 */ .mwdma_mask = 0x07, /* mwdma0-2 */ .udma_mask = ATA_UDMA6, @@ -595,9 +618,7 @@ static struct ata_port_info piix_port_info[] = { [ich6_sata_ahci] = { - .sht = &piix_sht, - .flags = PIIX_SATA_FLAGS | PIIX_FLAG_SCR | - PIIX_FLAG_AHCI, + .flags = PIIX_SATA_FLAGS | PIIX_FLAG_AHCI, .pio_mask = 0x1f, /* pio0-4 */ .mwdma_mask = 0x07, /* mwdma0-2 */ .udma_mask = ATA_UDMA6, @@ -606,9 +627,7 @@ static struct ata_port_info piix_port_info[] = { [ich6m_sata_ahci] = { - .sht = &piix_sht, - .flags = PIIX_SATA_FLAGS | PIIX_FLAG_SCR | - PIIX_FLAG_AHCI, + .flags = PIIX_SATA_FLAGS | PIIX_FLAG_AHCI, .pio_mask = 0x1f, /* pio0-4 */ .mwdma_mask = 0x07, /* mwdma0-2 */ .udma_mask = ATA_UDMA6, @@ -617,9 +636,8 @@ static struct ata_port_info piix_port_info[] = { [ich8_sata_ahci] = { - .sht = &piix_sht, - .flags = PIIX_SATA_FLAGS | PIIX_FLAG_SCR | - PIIX_FLAG_AHCI, + .flags = PIIX_SATA_FLAGS | PIIX_FLAG_AHCI | + PIIX_FLAG_SIDPR, .pio_mask = 0x1f, /* pio0-4 */ .mwdma_mask = 0x07, /* mwdma0-2 */ .udma_mask = ATA_UDMA6, @@ -628,9 +646,8 @@ static struct ata_port_info piix_port_info[] = { [ich8_2port_sata] = { - .sht = &piix_sht, - .flags = PIIX_SATA_FLAGS | PIIX_FLAG_SCR | - PIIX_FLAG_AHCI, + .flags = PIIX_SATA_FLAGS | PIIX_FLAG_AHCI | + PIIX_FLAG_SIDPR, .pio_mask = 0x1f, /* pio0-4 */ .mwdma_mask = 0x07, /* mwdma0-2 */ .udma_mask = ATA_UDMA6, @@ -639,9 +656,7 @@ static struct ata_port_info piix_port_info[] = { [tolapai_sata_ahci] = { - .sht = &piix_sht, - .flags = PIIX_SATA_FLAGS | PIIX_FLAG_SCR | - PIIX_FLAG_AHCI, + .flags = PIIX_SATA_FLAGS | PIIX_FLAG_AHCI, .pio_mask = 0x1f, /* pio0-4 */ .mwdma_mask = 0x07, /* mwdma0-2 */ .udma_mask = ATA_UDMA6, @@ -650,9 +665,8 @@ static struct ata_port_info piix_port_info[] = { [ich8m_apple_sata_ahci] = { - .sht = &piix_sht, - .flags = PIIX_SATA_FLAGS | PIIX_FLAG_SCR | - PIIX_FLAG_AHCI, + .flags = PIIX_SATA_FLAGS | PIIX_FLAG_AHCI | + PIIX_FLAG_SIDPR, .pio_mask = 0x1f, /* pio0-4 */ .mwdma_mask = 0x07, /* mwdma0-2 */ .udma_mask = ATA_UDMA6, @@ -1001,6 +1015,180 @@ static void ich_set_dmamode(struct ata_port *ap, struct ata_device *adev) do_pata_set_dmamode(ap, adev, 1); } +/* + * Serial ATA Index/Data Pair Superset Registers access + * + * Beginning from ICH8, there's a sane way to access SCRs using index + * and data register pair located at BAR5. This creates an + * interesting problem of mapping two SCRs to one port. + * + * Although they have separate SCRs, the master and slave aren't + * independent enough to be treated as separate links - e.g. softreset + * resets both. Also, there's no protocol defined for hard resetting + * singled device sharing the virtual port (no defined way to acquire + * device signature). This is worked around by merging the SCR values + * into one sensible value and requesting follow-up SRST after + * hardreset. + * + * SCR merging is perfomed in nibbles which is the unit contents in + * SCRs are organized. If two values are equal, the value is used. + * When they differ, merge table which lists precedence of possible + * values is consulted and the first match or the last entry when + * nothing matches is used. When there's no merge table for the + * specific nibble, value from the first port is used. + */ +static const int piix_sidx_map[] = { + [SCR_STATUS] = 0, + [SCR_ERROR] = 2, + [SCR_CONTROL] = 1, +}; + +static void piix_sidpr_sel(struct ata_device *dev, unsigned int reg) +{ + struct ata_port *ap = dev->link->ap; + struct piix_host_priv *hpriv = ap->host->private_data; + + iowrite32(((ap->port_no * 2 + dev->devno) << 8) | piix_sidx_map[reg], + hpriv->sidpr + PIIX_SIDPR_IDX); +} + +static int piix_sidpr_read(struct ata_device *dev, unsigned int reg) +{ + struct piix_host_priv *hpriv = dev->link->ap->host->private_data; + + piix_sidpr_sel(dev, reg); + return ioread32(hpriv->sidpr + PIIX_SIDPR_DATA); +} + +static void piix_sidpr_write(struct ata_device *dev, unsigned int reg, u32 val) +{ + struct piix_host_priv *hpriv = dev->link->ap->host->private_data; + + piix_sidpr_sel(dev, reg); + iowrite32(val, hpriv->sidpr + PIIX_SIDPR_DATA); +} + +u32 piix_merge_scr(u32 val0, u32 val1, const int * const *merge_tbl) +{ + u32 val = 0; + int i, mi; + + for (i = 0, mi = 0; i < 32 / 4; i++) { + u8 c0 = (val0 >> (i * 4)) & 0xf; + u8 c1 = (val1 >> (i * 4)) & 0xf; + u8 merged = c0; + const int *cur; + + /* if no merge preference, assume the first value */ + cur = merge_tbl[mi]; + if (!cur) + goto done; + mi++; + + /* if two values equal, use it */ + if (c0 == c1) + goto done; + + /* choose the first match or the last from the merge table */ + while (*cur != -1) { + if (c0 == *cur || c1 == *cur) + break; + cur++; + } + if (*cur == -1) + cur--; + merged = *cur; + done: + val |= merged << (i * 4); + } + + return val; +} + +static int piix_sidpr_scr_read(struct ata_port *ap, unsigned int reg, u32 *val) +{ + const int * const sstatus_merge_tbl[] = { + /* DET */ (const int []){ 1, 3, 0, 4, 3, -1 }, + /* SPD */ (const int []){ 2, 1, 0, -1 }, + /* IPM */ (const int []){ 6, 2, 1, 0, -1 }, + NULL, + }; + const int * const scontrol_merge_tbl[] = { + /* DET */ (const int []){ 1, 0, 4, 0, -1 }, + /* SPD */ (const int []){ 0, 2, 1, 0, -1 }, + /* IPM */ (const int []){ 0, 1, 2, 3, 0, -1 }, + NULL, + }; + u32 v0, v1; + + if (reg >= ARRAY_SIZE(piix_sidx_map)) + return -EINVAL; + + if (!(ap->flags & ATA_FLAG_SLAVE_POSS)) { + *val = piix_sidpr_read(&ap->link.device[0], reg); + return 0; + } + + v0 = piix_sidpr_read(&ap->link.device[0], reg); + v1 = piix_sidpr_read(&ap->link.device[1], reg); + + switch (reg) { + case SCR_STATUS: + *val = piix_merge_scr(v0, v1, sstatus_merge_tbl); + break; + case SCR_ERROR: + *val = v0 | v1; + break; + case SCR_CONTROL: + *val = piix_merge_scr(v0, v1, scontrol_merge_tbl); + break; + } + + return 0; +} + +static int piix_sidpr_scr_write(struct ata_port *ap, unsigned int reg, u32 val) +{ + if (reg >= ARRAY_SIZE(piix_sidx_map)) + return -EINVAL; + + piix_sidpr_write(&ap->link.device[0], reg, val); + + if (ap->flags & ATA_FLAG_SLAVE_POSS) + piix_sidpr_write(&ap->link.device[1], reg, val); + + return 0; +} + +static int piix_sidpr_hardreset(struct ata_link *link, unsigned int *class, + unsigned long deadline) +{ + const unsigned long *timing = sata_ehc_deb_timing(&link->eh_context); + int rc; + + /* do hardreset */ + rc = sata_link_hardreset(link, timing, deadline); + if (rc) { + ata_link_printk(link, KERN_ERR, + "COMRESET failed (errno=%d)\n", rc); + return rc; + } + + /* TODO: phy layer with polling, timeouts, etc. */ + if (ata_link_offline(link)) { + *class = ATA_DEV_NONE; + return 0; + } + + return -EAGAIN; +} + +static void piix_sidpr_error_handler(struct ata_port *ap) +{ + ata_bmdma_drive_eh(ap, ata_std_prereset, ata_std_softreset, + piix_sidpr_hardreset, ata_std_postreset); +} + #ifdef CONFIG_PM static int piix_broken_suspend(void) { @@ -1034,6 +1222,13 @@ static int piix_broken_suspend(void) }, }, { + .ident = "TECRA M6", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "TOSHIBA"), + DMI_MATCH(DMI_PRODUCT_NAME, "TECRA M6"), + }, + }, + { .ident = "TECRA M7", .matches = { DMI_MATCH(DMI_SYS_VENDOR, "TOSHIBA"), @@ -1048,6 +1243,13 @@ static int piix_broken_suspend(void) }, }, { + .ident = "Satellite R20", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "TOSHIBA"), + DMI_MATCH(DMI_PRODUCT_NAME, "Satellite R20"), + }, + }, + { .ident = "Satellite R25", .matches = { DMI_MATCH(DMI_SYS_VENDOR, "TOSHIBA"), @@ -1253,10 +1455,10 @@ static int __devinit piix_check_450nx_errata(struct pci_dev *ata_dev) return no_piix_dma; } -static void __devinit piix_init_pcs(struct pci_dev *pdev, - struct ata_port_info *pinfo, +static void __devinit piix_init_pcs(struct ata_host *host, const struct piix_map_db *map_db) { + struct pci_dev *pdev = to_pci_dev(host->dev); u16 pcs, new_pcs; pci_read_config_word(pdev, ICH5_PCS, &pcs); @@ -1270,11 +1472,10 @@ static void __devinit piix_init_pcs(struct pci_dev *pdev, } } -static void __devinit piix_init_sata_map(struct pci_dev *pdev, - struct ata_port_info *pinfo, - const struct piix_map_db *map_db) +static const int *__devinit piix_init_sata_map(struct pci_dev *pdev, + struct ata_port_info *pinfo, + const struct piix_map_db *map_db) { - struct piix_host_priv *hpriv = pinfo[0].private_data; const int *map; int i, invalid_map = 0; u8 map_value; @@ -1298,7 +1499,6 @@ static void __devinit piix_init_sata_map(struct pci_dev *pdev, case IDE: WARN_ON((i & 1) || map[i + 1] != IDE); pinfo[i / 2] = piix_port_info[ich_pata_100]; - pinfo[i / 2].private_data = hpriv; i++; printk(" IDE IDE"); break; @@ -1316,7 +1516,33 @@ static void __devinit piix_init_sata_map(struct pci_dev *pdev, dev_printk(KERN_ERR, &pdev->dev, "invalid MAP value %u\n", map_value); - hpriv->map = map; + return map; +} + +static void __devinit piix_init_sidpr(struct ata_host *host) +{ + struct pci_dev *pdev = to_pci_dev(host->dev); + struct piix_host_priv *hpriv = host->private_data; + int i; + + /* check for availability */ + for (i = 0; i < 4; i++) + if (hpriv->map[i] == IDE) + return; + + if (!(host->ports[0]->flags & PIIX_FLAG_SIDPR)) + return; + + if (pci_resource_start(pdev, PIIX_SIDPR_BAR) == 0 || + pci_resource_len(pdev, PIIX_SIDPR_BAR) != PIIX_SIDPR_LEN) + return; + + if (pcim_iomap_regions(pdev, 1 << PIIX_SIDPR_BAR, DRV_NAME)) + return; + + hpriv->sidpr = pcim_iomap_table(pdev)[PIIX_SIDPR_BAR]; + host->ports[0]->ops = &piix_sidpr_sata_ops; + host->ports[1]->ops = &piix_sidpr_sata_ops; } static void piix_iocfg_bit18_quirk(struct pci_dev *pdev) @@ -1375,8 +1601,10 @@ static int piix_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) struct device *dev = &pdev->dev; struct ata_port_info port_info[2]; const struct ata_port_info *ppi[] = { &port_info[0], &port_info[1] }; - struct piix_host_priv *hpriv; unsigned long port_flags; + struct ata_host *host; + struct piix_host_priv *hpriv; + int rc; if (!printed_version++) dev_printk(KERN_DEBUG, &pdev->dev, @@ -1386,17 +1614,31 @@ static int piix_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) if (!in_module_init) return -ENODEV; + port_info[0] = piix_port_info[ent->driver_data]; + port_info[1] = piix_port_info[ent->driver_data]; + + port_flags = port_info[0].flags; + + /* enable device and prepare host */ + rc = pcim_enable_device(pdev); + if (rc) + return rc; + + /* SATA map init can change port_info, do it before prepping host */ hpriv = devm_kzalloc(dev, sizeof(*hpriv), GFP_KERNEL); if (!hpriv) return -ENOMEM; - port_info[0] = piix_port_info[ent->driver_data]; - port_info[1] = piix_port_info[ent->driver_data]; - port_info[0].private_data = hpriv; - port_info[1].private_data = hpriv; + if (port_flags & ATA_FLAG_SATA) + hpriv->map = piix_init_sata_map(pdev, port_info, + piix_map_db_table[ent->driver_data]); - port_flags = port_info[0].flags; + rc = ata_pci_prepare_sff_host(pdev, ppi, &host); + if (rc) + return rc; + host->private_data = hpriv; + /* initialize controller */ if (port_flags & PIIX_FLAG_AHCI) { u8 tmp; pci_read_config_byte(pdev, PIIX_SCC, &tmp); @@ -1407,12 +1649,9 @@ static int piix_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) } } - /* Initialize SATA map */ if (port_flags & ATA_FLAG_SATA) { - piix_init_sata_map(pdev, port_info, - piix_map_db_table[ent->driver_data]); - piix_init_pcs(pdev, port_info, - piix_map_db_table[ent->driver_data]); + piix_init_pcs(host, piix_map_db_table[ent->driver_data]); + piix_init_sidpr(host); } /* apply IOCFG bit18 quirk */ @@ -1431,12 +1670,14 @@ static int piix_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) /* This writes into the master table but it does not really matter for this errata as we will apply it to all the PIIX devices on the board */ - port_info[0].mwdma_mask = 0; - port_info[0].udma_mask = 0; - port_info[1].mwdma_mask = 0; - port_info[1].udma_mask = 0; + host->ports[0]->mwdma_mask = 0; + host->ports[0]->udma_mask = 0; + host->ports[1]->mwdma_mask = 0; + host->ports[1]->udma_mask = 0; } - return ata_pci_init_one(pdev, ppi); + + pci_set_master(pdev); + return ata_pci_activate_sff_host(host, ata_interrupt, &piix_sht); } static int __init piix_init(void) |