diff options
Diffstat (limited to 'drivers/ata/ata_piix.c')
-rw-r--r-- | drivers/ata/ata_piix.c | 85 |
1 files changed, 69 insertions, 16 deletions
diff --git a/drivers/ata/ata_piix.c b/drivers/ata/ata_piix.c index 5fdf1678d0cc..54961c0b2c73 100644 --- a/drivers/ata/ata_piix.c +++ b/drivers/ata/ata_piix.c @@ -154,11 +154,13 @@ struct piix_map_db { struct piix_host_priv { const int *map; + u32 saved_iocfg; void __iomem *sidpr; }; static int piix_init_one(struct pci_dev *pdev, const struct pci_device_id *ent); +static void piix_remove_one(struct pci_dev *pdev); static int piix_pata_prereset(struct ata_link *link, unsigned long deadline); static void piix_set_piomode(struct ata_port *ap, struct ata_device *adev); static void piix_set_dmamode(struct ata_port *ap, struct ata_device *adev); @@ -296,7 +298,7 @@ static struct pci_driver piix_pci_driver = { .name = DRV_NAME, .id_table = piix_pci_tbl, .probe = piix_init_one, - .remove = ata_pci_remove_one, + .remove = piix_remove_one, #ifdef CONFIG_PM .suspend = piix_pci_device_suspend, .resume = piix_pci_device_resume, @@ -308,7 +310,7 @@ static struct scsi_host_template piix_sht = { }; static struct ata_port_operations piix_pata_ops = { - .inherits = &ata_bmdma_port_ops, + .inherits = &ata_bmdma32_port_ops, .cable_detect = ata_cable_40wire, .set_piomode = piix_set_piomode, .set_dmamode = piix_set_dmamode, @@ -610,8 +612,9 @@ static const struct ich_laptop ich_laptop[] = { static int ich_pata_cable_detect(struct ata_port *ap) { struct pci_dev *pdev = to_pci_dev(ap->host->dev); + struct piix_host_priv *hpriv = ap->host->private_data; const struct ich_laptop *lap = &ich_laptop[0]; - u8 tmp, mask; + u8 mask; /* Check for specials - Acer Aspire 5602WLMi */ while (lap->device) { @@ -625,8 +628,7 @@ static int ich_pata_cable_detect(struct ata_port *ap) /* check BIOS cable detect results */ mask = ap->port_no == 0 ? PIIX_80C_PRI : PIIX_80C_SEC; - pci_read_config_byte(pdev, PIIX_IOCFG, &tmp); - if ((tmp & mask) == 0) + if ((hpriv->saved_iocfg & mask) == 0) return ATA_CBL_PATA40; return ATA_CBL_PATA80; } @@ -1350,7 +1352,7 @@ static int __devinit piix_init_sidpr(struct ata_host *host) return 0; } -static void piix_iocfg_bit18_quirk(struct pci_dev *pdev) +static void piix_iocfg_bit18_quirk(struct ata_host *host) { static const struct dmi_system_id sysids[] = { { @@ -1367,7 +1369,8 @@ static void piix_iocfg_bit18_quirk(struct pci_dev *pdev) { } /* terminate list */ }; - u32 iocfg; + struct pci_dev *pdev = to_pci_dev(host->dev); + struct piix_host_priv *hpriv = host->private_data; if (!dmi_check_system(sysids)) return; @@ -1376,13 +1379,38 @@ static void piix_iocfg_bit18_quirk(struct pci_dev *pdev) * seem to use it to disable a channel. Clear the bit on the * affected systems. */ - pci_read_config_dword(pdev, PIIX_IOCFG, &iocfg); - if (iocfg & (1 << 18)) { + if (hpriv->saved_iocfg & (1 << 18)) { dev_printk(KERN_INFO, &pdev->dev, "applying IOCFG bit18 quirk\n"); - iocfg &= ~(1 << 18); - pci_write_config_dword(pdev, PIIX_IOCFG, iocfg); + pci_write_config_dword(pdev, PIIX_IOCFG, + hpriv->saved_iocfg & ~(1 << 18)); + } +} + +static bool piix_broken_system_poweroff(struct pci_dev *pdev) +{ + static const struct dmi_system_id broken_systems[] = { + { + .ident = "HP Compaq 2510p", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"), + DMI_MATCH(DMI_PRODUCT_NAME, "HP Compaq 2510p"), + }, + /* PCI slot number of the controller */ + .driver_data = (void *)0x1FUL, + }, + + { } /* terminate list */ + }; + const struct dmi_system_id *dmi = dmi_first_match(broken_systems); + + if (dmi) { + unsigned long slot = (unsigned long)dmi->driver_data; + /* apply the quirk only to on-board controllers */ + return slot == PCI_SLOT(pdev->devfn); } + + return false; } /** @@ -1420,6 +1448,14 @@ static int __devinit piix_init_one(struct pci_dev *pdev, if (!in_module_init) return -ENODEV; + if (piix_broken_system_poweroff(pdev)) { + piix_port_info[ent->driver_data].flags |= + ATA_FLAG_NO_POWEROFF_SPINDOWN | + ATA_FLAG_NO_HIBERNATE_SPINDOWN; + dev_info(&pdev->dev, "quirky BIOS, skipping spindown " + "on poweroff and hibernation\n"); + } + port_info[0] = piix_port_info[ent->driver_data]; port_info[1] = piix_port_info[ent->driver_data]; @@ -1430,6 +1466,17 @@ static int __devinit piix_init_one(struct pci_dev *pdev, if (rc) return rc; + hpriv = devm_kzalloc(dev, sizeof(*hpriv), GFP_KERNEL); + if (!hpriv) + return -ENOMEM; + + /* Save IOCFG, this will be used for cable detection, quirk + * detection and restoration on detach. This is necessary + * because some ACPI implementations mess up cable related + * bits on _STM. Reported on kernel bz#11879. + */ + pci_read_config_dword(pdev, PIIX_IOCFG, &hpriv->saved_iocfg); + /* ICH6R may be driven by either ata_piix or ahci driver * regardless of BIOS configuration. Make sure AHCI mode is * off. @@ -1441,10 +1488,6 @@ static int __devinit piix_init_one(struct pci_dev *pdev, } /* SATA map init can change port_info, do it before prepping host */ - hpriv = devm_kzalloc(dev, sizeof(*hpriv), GFP_KERNEL); - if (!hpriv) - return -ENOMEM; - if (port_flags & ATA_FLAG_SATA) hpriv->map = piix_init_sata_map(pdev, port_info, piix_map_db_table[ent->driver_data]); @@ -1463,7 +1506,7 @@ static int __devinit piix_init_one(struct pci_dev *pdev, } /* apply IOCFG bit18 quirk */ - piix_iocfg_bit18_quirk(pdev); + piix_iocfg_bit18_quirk(host); /* On ICH5, some BIOSen disable the interrupt using the * PCI_COMMAND_INTX_DISABLE bit added in PCI 2.3. @@ -1488,6 +1531,16 @@ static int __devinit piix_init_one(struct pci_dev *pdev, return ata_pci_sff_activate_host(host, ata_sff_interrupt, &piix_sht); } +static void piix_remove_one(struct pci_dev *pdev) +{ + struct ata_host *host = dev_get_drvdata(&pdev->dev); + struct piix_host_priv *hpriv = host->private_data; + + pci_write_config_dword(pdev, PIIX_IOCFG, hpriv->saved_iocfg); + + ata_pci_remove_one(pdev); +} + static int __init piix_init(void) { int rc; |