From 17bd9a2f4c35de29d2539a2ff6851d61be281e25 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Mon, 29 Oct 2007 21:21:37 +0100 Subject: [PATCH 1/6] libata and bogus LBA48 drives A colleague noticed recent versions of Ubuntu no longer detect his 80 GB ST380020ACE drive. This drive is special in that it advertises LBA48 support, but has the lba_capacity_2 field set to zero (cfr. http://lkml.org/lkml/2004/3/30/163). Upon closer look, libata indeed doesn't seem to handle this case yet. Below is an (untested) fix. Signed-off-by: Jeff Garzik --- include/linux/ata.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/include/linux/ata.h b/include/linux/ata.h index 61535e72834d..304825b1c977 100644 --- a/include/linux/ata.h +++ b/include/linux/ata.h @@ -425,6 +425,8 @@ static inline int ata_id_has_lba48(const u16 *id) { if ((id[83] & 0xC000) != 0x4000) return 0; + if (!ata_id_u64(id, 100)) + return 0; return id[83] & (1 << 10); } From f5456b63ec9fdad37b680fc9fe6fe8222d2c1839 Mon Sep 17 00:00:00 2001 From: Kristen Carlson Accardi Date: Fri, 2 Nov 2007 16:37:08 -0700 Subject: [PATCH 2/6] libata: Don't disable dipm with SET FEATURES LPM seems to get hung up while disabling DIPM, and after thinking about this a bit, I don't think we really need to manually disable it anyway. Signed-off-by: Kristen Carlson Accardi Signed-off-by: Jeff Garzik --- drivers/ata/libata-core.c | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/drivers/ata/libata-core.c b/drivers/ata/libata-core.c index 164c7d9514f9..3ed3cf2f5568 100644 --- a/drivers/ata/libata-core.c +++ b/drivers/ata/libata-core.c @@ -676,10 +676,11 @@ static int ata_dev_set_dipm(struct ata_device *dev, enum link_pm policy) if (rc) return rc; - /* disable DIPM */ - if (ata_dev_enabled(dev) && (dev->flags & ATA_DFLAG_DIPM)) - err_mask = ata_dev_set_feature(dev, - SETFEATURES_SATA_DISABLE, SATA_DIPM); + /* + * we don't have to disable DIPM since IPM flags + * disallow transitions to SLUMBER, which effectively + * disable DIPM if it does not support PARTIAL + */ break; case NOT_AVAILABLE: case MAX_PERFORMANCE: @@ -689,10 +690,11 @@ static int ata_dev_set_dipm(struct ata_device *dev, enum link_pm policy) if (rc) return rc; - /* disable DIPM */ - if (ata_dev_enabled(dev) && (dev->flags & ATA_DFLAG_DIPM)) - err_mask = ata_dev_set_feature(dev, - SETFEATURES_SATA_DISABLE, SATA_DIPM); + /* + * we don't have to disable DIPM since IPM flags + * disallow all transitions which effectively + * disable DIPM anyway. + */ break; } From 36beb82390235236c60eb97ca526b1cad97e2df3 Mon Sep 17 00:00:00 2001 From: Alan Cox Date: Mon, 5 Nov 2007 15:04:40 +0000 Subject: [PATCH 3/6] pata_serverworks: Fix problem with some drive combinations The driver used the channel not the device number for deciding where to load some timing parameters. Also change so that we clear the UDMA field as the old driver did. Not believed neccessary but does no harm. Signed-off-by: Alan Cox Signed-off-by: Jeff Garzik --- drivers/ata/pata_serverworks.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/drivers/ata/pata_serverworks.c b/drivers/ata/pata_serverworks.c index df68806df4be..8bed88873720 100644 --- a/drivers/ata/pata_serverworks.c +++ b/drivers/ata/pata_serverworks.c @@ -274,28 +274,27 @@ static void serverworks_set_dmamode(struct ata_port *ap, struct ata_device *adev { static const u8 dma_mode[] = { 0x77, 0x21, 0x20 }; int offset = 1 + 2 * ap->port_no - adev->devno; - int devbits = (2 * ap->port_no + adev->devno); + int devbits = 2 * ap->port_no + adev->devno; u8 ultra; u8 ultra_cfg; struct pci_dev *pdev = to_pci_dev(ap->host->dev); pci_read_config_byte(pdev, 0x54, &ultra_cfg); + pci_read_config_byte(pdev, 0x56 + ap->port_no, &ultra); + ultra &= ~(0x0F << (adev->devno * 4)); if (adev->dma_mode >= XFER_UDMA_0) { pci_write_config_byte(pdev, 0x44 + offset, 0x20); - pci_read_config_byte(pdev, 0x56 + ap->port_no, &ultra); - ultra &= ~(0x0F << (ap->port_no * 4)); ultra |= (adev->dma_mode - XFER_UDMA_0) - << (ap->port_no * 4); - pci_write_config_byte(pdev, 0x56 + ap->port_no, ultra); - + << (adev->devno * 4); ultra_cfg |= (1 << devbits); } else { pci_write_config_byte(pdev, 0x44 + offset, dma_mode[adev->dma_mode - XFER_MW_DMA_0]); ultra_cfg &= ~(1 << devbits); } + pci_write_config_byte(pdev, 0x56 + ap->port_no, ultra); pci_write_config_byte(pdev, 0x54, ultra_cfg); } From 7f2803d0266844adacacbc3dea7822d5347ccb50 Mon Sep 17 00:00:00 2001 From: Alan Cox Date: Mon, 5 Nov 2007 22:51:09 +0000 Subject: [PATCH 4/6] ata_piix: Add additional PCI identifier for 40 wire short cable Keeping the list in sync with the old IDE driver Signed-off-by: Alan Cox Signed-off-by: Jeff Garzik --- drivers/ata/ata_piix.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/ata/ata_piix.c b/drivers/ata/ata_piix.c index a4b2cb29f46c..f08cca21702c 100644 --- a/drivers/ata/ata_piix.c +++ b/drivers/ata/ata_piix.c @@ -621,6 +621,7 @@ struct ich_laptop { static const struct ich_laptop ich_laptop[] = { /* devid, subvendor, subdev */ { 0x27DF, 0x0005, 0x0280 }, /* ICH7 on Acer 5602WLMi */ + { 0x27DF, 0x1025, 0x0102 }, /* ICH7 on Acer 5602aWLMi */ { 0x27DF, 0x1025, 0x0110 }, /* ICH7 on Acer 3682WLMi */ { 0x27DF, 0x1043, 0x1267 }, /* ICH7 on Asus W5F */ { 0x27DF, 0x103C, 0x30A1 }, /* ICH7 on HP Compaq nc2400 */ From 73946f9fc5be1433f1e182d11303188390ff242f Mon Sep 17 00:00:00 2001 From: Alan Cox Date: Mon, 5 Nov 2007 22:53:38 +0000 Subject: [PATCH 5/6] pata_hpt37x: Fix outstanding bug reports on the HPT374 and 37x cable detect - Read frequency correctly - Correct cable detect handling - Fix wrong filter test Signed-off-by: Alan Cox Signed-off-by: Jeff Garzik --- drivers/ata/pata_hpt37x.c | 49 ++++++++++++++++++++++++++++----------- 1 file changed, 36 insertions(+), 13 deletions(-) diff --git a/drivers/ata/pata_hpt37x.c b/drivers/ata/pata_hpt37x.c index e61cb1fd57b2..3816b8605e0d 100644 --- a/drivers/ata/pata_hpt37x.c +++ b/drivers/ata/pata_hpt37x.c @@ -295,7 +295,7 @@ static unsigned long hpt370_filter(struct ata_device *adev, unsigned long mask) static unsigned long hpt370a_filter(struct ata_device *adev, unsigned long mask) { - if (adev->class != ATA_DEV_ATA) { + if (adev->class == ATA_DEV_ATA) { if (hpt_dma_blacklisted(adev, "UDMA100", bad_ata100_5)) mask &= ~ (0x1F << ATA_SHIFT_UDMA); } @@ -359,28 +359,25 @@ static int hpt374_pre_reset(struct ata_link *link, unsigned long deadline) { 0x50, 1, 0x04, 0x04 }, { 0x54, 1, 0x04, 0x04 } }; - u16 mcr3, mcr6; + u16 mcr3; u8 ata66; struct ata_port *ap = link->ap; struct pci_dev *pdev = to_pci_dev(ap->host->dev); + unsigned int mcrbase = 0x50 + 4 * ap->port_no; if (!pci_test_config_bits(pdev, &hpt37x_enable_bits[ap->port_no])) return -ENOENT; /* Do the extra channel work */ - pci_read_config_word(pdev, 0x52, &mcr3); - pci_read_config_word(pdev, 0x56, &mcr6); + pci_read_config_word(pdev, mcrbase + 2, &mcr3); /* Set bit 15 of 0x52 to enable TCBLID as input - Set bit 15 of 0x56 to enable FCBLID as input */ - pci_write_config_word(pdev, 0x52, mcr3 | 0x8000); - pci_write_config_word(pdev, 0x56, mcr6 | 0x8000); + pci_write_config_word(pdev, mcrbase + 2, mcr3 | 0x8000); pci_read_config_byte(pdev, 0x5A, &ata66); /* Reset TCBLID/FCBLID to output */ pci_write_config_word(pdev, 0x52, mcr3); - pci_write_config_word(pdev, 0x56, mcr6); - if (ata66 & (1 << ap->port_no)) + if (ata66 & (2 >> ap->port_no)) ap->cbl = ATA_CBL_PATA40; else ap->cbl = ATA_CBL_PATA80; @@ -844,6 +841,25 @@ static int hpt37x_calibrate_dpll(struct pci_dev *dev) /* Never went stable */ return 0; } + +static u32 hpt374_read_freq(struct pci_dev *pdev) +{ + u32 freq; + unsigned long io_base = pci_resource_start(pdev, 4); + if (PCI_FUNC(pdev->devfn) & 1) { + struct pci_dev *pdev_0 = pci_get_slot(pdev->bus, pdev->devfn - 1); + /* Someone hot plugged the controller on us ? */ + if (pdev_0 == NULL) + return 0; + io_base = pci_resource_start(pdev_0, 4); + freq = inl(io_base + 0x90); + pci_dev_put(pdev_0); + } + else + freq = inl(io_base + 0x90); + return freq; +} + /** * hpt37x_init_one - Initialise an HPT37X/302 * @dev: PCI device @@ -902,7 +918,7 @@ static int hpt37x_init_one(struct pci_dev *dev, const struct pci_device_id *id) .flags = ATA_FLAG_SLAVE_POSS, .pio_mask = 0x1f, .mwdma_mask = 0x07, - .udma_mask = 0x0f, + .udma_mask = ATA_UDMA5, .port_ops = &hpt370_port_ops }; /* HPT370A - UDMA100 */ @@ -911,7 +927,7 @@ static int hpt37x_init_one(struct pci_dev *dev, const struct pci_device_id *id) .flags = ATA_FLAG_SLAVE_POSS, .pio_mask = 0x1f, .mwdma_mask = 0x07, - .udma_mask = 0x0f, + .udma_mask = ATA_UDMA5, .port_ops = &hpt370a_port_ops }; /* HPT371, 372 and friends - UDMA133 */ @@ -1047,9 +1063,16 @@ static int hpt37x_init_one(struct pci_dev *dev, const struct pci_device_id *id) outb(0x0e, iobase + 0x9c); /* Some devices do not let this value be accessed via PCI space - according to the old driver */ + according to the old driver. In addition we must use the value + from FN 0 on the HPT374 */ + + if (chip_table == &hpt374) { + freq = hpt374_read_freq(dev); + if (freq == 0) + return -ENODEV; + } else + freq = inl(iobase + 0x90); - freq = inl(iobase + 0x90); if ((freq >> 12) != 0xABCDE) { int i; u8 sr; From 6bbfd53d47abd1fb20d7c93a9b19a75970b66f49 Mon Sep 17 00:00:00 2001 From: Alan Cox Date: Mon, 5 Nov 2007 22:58:58 +0000 Subject: [PATCH 6/6] libata: handle broken cable reporting One or two ancient drives predated the cable spec and didn't sent the valid bits for the field. I had hoped to leave this out of libata as a piece of historical annoyance but a recent CD drive shows the same bug so we have to import support for it. Same concept as Bartlomiej's changes old IDE except that as we have centralised blacklists we can avoid keeping another private table of stuff Signed-off-by: Alan Cox Signed-off-by: Jeff Garzik --- drivers/ata/libata-core.c | 21 ++++++++++++++++++++- include/linux/ata.h | 9 +++++++++ include/linux/libata.h | 1 + 3 files changed, 30 insertions(+), 1 deletion(-) diff --git a/drivers/ata/libata-core.c b/drivers/ata/libata-core.c index 3ed3cf2f5568..ec3ce120a517 100644 --- a/drivers/ata/libata-core.c +++ b/drivers/ata/libata-core.c @@ -4241,6 +4241,10 @@ static const struct ata_blacklist_entry ata_device_blacklist [] = { { "ST340823A", NULL, ATA_HORKAGE_HPA_SIZE, }, { "ST320413A", NULL, ATA_HORKAGE_HPA_SIZE, }, + /* Devices which get the IVB wrong */ + { "QUANTUM FIREBALLlct10 05", "A03.0900", ATA_HORKAGE_IVB, }, + { "TSSTcorp CDDVDW SH-S202J", "SB00", ATA_HORKAGE_IVB, }, + /* End Marker */ { } }; @@ -4301,6 +4305,21 @@ static int ata_dma_blacklisted(const struct ata_device *dev) return (dev->horkage & ATA_HORKAGE_NODMA) ? 1 : 0; } +/** + * ata_is_40wire - check drive side detection + * @dev: device + * + * Perform drive side detection decoding, allowing for device vendors + * who can't follow the documentation. + */ + +static int ata_is_40wire(struct ata_device *dev) +{ + if (dev->horkage & ATA_HORKAGE_IVB) + return ata_drive_40wire_relaxed(dev->id); + return ata_drive_40wire(dev->id); +} + /** * ata_dev_xfermask - Compute supported xfermask of the given device * @dev: Device to compute xfermask for @@ -4370,7 +4389,7 @@ static void ata_dev_xfermask(struct ata_device *dev) if (xfer_mask & (0xF8 << ATA_SHIFT_UDMA)) /* UDMA/44 or higher would be available */ if ((ap->cbl == ATA_CBL_PATA40) || - (ata_drive_40wire(dev->id) && + (ata_is_40wire(dev) && (ap->cbl == ATA_CBL_PATA_UNK || ap->cbl == ATA_CBL_PATA80))) { ata_dev_printk(dev, KERN_WARNING, diff --git a/include/linux/ata.h b/include/linux/ata.h index 304825b1c977..5c4e54a2a8d6 100644 --- a/include/linux/ata.h +++ b/include/linux/ata.h @@ -537,6 +537,15 @@ static inline int ata_drive_40wire(const u16 *dev_id) return 1; } +static inline int ata_drive_40wire_relaxed(const u16 *dev_id) +{ + if (ata_id_is_sata(dev_id)) + return 0; /* SATA */ + if ((dev_id[93] & 0x2000) == 0x2000) + return 0; /* 80 wire */ + return 1; +} + static inline int atapi_cdb_len(const u16 *dev_id) { u16 tmp = dev_id[0] & 0x3; diff --git a/include/linux/libata.h b/include/linux/libata.h index 1e277852ba42..56a5673aebad 100644 --- a/include/linux/libata.h +++ b/include/linux/libata.h @@ -339,6 +339,7 @@ enum { ATA_HORKAGE_SKIP_PM = (1 << 5), /* Skip PM operations */ ATA_HORKAGE_HPA_SIZE = (1 << 6), /* native size off by one */ ATA_HORKAGE_IPM = (1 << 7), /* Link PM problems */ + ATA_HORKAGE_IVB = (1 << 8), /* cbl det validity bit bugs */ /* DMA mask for user DMA control: User visible values; DO NOT renumber */