[PATCH] libata: add 40pin "short" cable support, honour drive side speed detection
Alan Cox [Tue, 10 Oct 2006 21:28:11 +0000 (14:28 -0700)]
[deweerdt@free.fr: build fix]
Signed-off-by: Alan Cox <alan@redhat.com>
Signed-off-by: Frederik Deweerdt <deweerdt@free.fr>
Cc: Tejun Heo <htejun@gmail.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Jeff Garzik <jeff@garzik.org>

drivers/ata/ata_piix.c
drivers/ata/libata-core.c
drivers/ata/pata_ali.c
include/linux/ata.h

index 720174d..14b726b 100644 (file)
@@ -93,7 +93,7 @@
 #include <linux/libata.h>
 
 #define DRV_NAME       "ata_piix"
-#define DRV_VERSION    "2.00ac6"
+#define DRV_VERSION    "2.00ac7"
 
 enum {
        PIIX_IOCFG              = 0x54, /* IDE I/O configuration register */
@@ -571,6 +571,23 @@ module_param(force_pcs, int, 0444);
 MODULE_PARM_DESC(force_pcs, "force honoring or ignoring PCS to work around "
                 "device mis-detection (0=default, 1=ignore PCS, 2=honor PCS)");
 
+struct ich_laptop {
+       u16 device;
+       u16 subvendor;
+       u16 subdevice;
+};
+
+/*
+ *     List of laptops that use short cables rather than 80 wire
+ */
+
+static const struct ich_laptop ich_laptop[] = {
+       /* devid, subvendor, subdev */
+       { 0x27DF, 0x0005, 0x0280 },     /* ICH7 on Acer 5602WLMi */
+       /* end marker */
+       { 0, }
+};
+
 /**
  *     piix_pata_cbl_detect - Probe host controller cable detect info
  *     @ap: Port for which cable detect info is desired
@@ -585,12 +602,24 @@ MODULE_PARM_DESC(force_pcs, "force honoring or ignoring PCS to work around "
 static void ich_pata_cbl_detect(struct ata_port *ap)
 {
        struct pci_dev *pdev = to_pci_dev(ap->host->dev);
+       const struct ich_laptop *lap = &ich_laptop[0];
        u8 tmp, mask;
 
        /* no 80c support in host controller? */
        if ((ap->udma_mask & ~ATA_UDMA_MASK_40C) == 0)
                goto cbl40;
 
+       /* Check for specials - Acer Aspire 5602WLMi */
+       while (lap->device) {
+               if (lap->device == pdev->device &&
+                   lap->subvendor == pdev->subsystem_vendor &&
+                   lap->subdevice == pdev->subsystem_device) {
+                       ap->cbl = ATA_CBL_PATA40_SHORT;
+                       return;
+               }
+               lap++;
+       }
+
        /* check BIOS cable detect results */
        mask = ap->port_no == 0 ? PIIX_80C_PRI : PIIX_80C_SEC;
        pci_read_config_byte(pdev, PIIX_IOCFG, &tmp);
index 915a55a..b896119 100644 (file)
@@ -3119,6 +3119,13 @@ static void ata_dev_xfermask(struct ata_device *dev)
         */
        if (ap->cbl == ATA_CBL_PATA40)
                xfer_mask &= ~(0xF8 << ATA_SHIFT_UDMA);
+       /* Apply drive side cable rule. Unknown or 80 pin cables reported
+        * host side are checked drive side as well. Cases where we know a
+        * 40wire cable is used safely for 80 are not checked here.
+        */
+        if (ata_drive_40wire(dev->id) && (ap->cbl == ATA_CBL_PATA_UNK || ap->cbl == ATA_CBL_PATA80))
+               xfer_mask &= ~(0xF8 << ATA_SHIFT_UDMA);
+
 
        xfer_mask &= ata_pack_xfermask(dev->pio_mask,
                                       dev->mwdma_mask, dev->udma_mask);
index 64eed99..e6d2b84 100644 (file)
@@ -78,7 +78,7 @@ static int ali_c2_cable_detect(struct ata_port *ap)
           implement the detect logic */
 
        if (ali_cable_override(pdev))
-               return ATA_CBL_PATA80;
+               return ATA_CBL_PATA40_SHORT;
 
        /* Host view cable detect 0x4A bit 0 primary bit 1 secondary
           Bit set for 40 pin */
index d894419..1df9416 100644 (file)
@@ -200,8 +200,9 @@ enum {
        ATA_CBL_NONE            = 0,
        ATA_CBL_PATA40          = 1,
        ATA_CBL_PATA80          = 2,
-       ATA_CBL_PATA_UNK        = 3,
-       ATA_CBL_SATA            = 4,
+       ATA_CBL_PATA40_SHORT    = 3,            /* 40 wire cable to high UDMA spec */
+       ATA_CBL_PATA_UNK        = 4,
+       ATA_CBL_SATA            = 5,
 
        /* SATA Status and Control Registers */
        SCR_STATUS              = 0,
@@ -342,6 +343,15 @@ static inline int ata_id_is_cfa(const u16 *id)
        return 0;
 }
 
+static inline int ata_drive_40wire(const u16 *dev_id)
+{
+       if (ata_id_major_version(dev_id) >= 5 && ata_id_is_sata(dev_id))
+               return 0;       /* SATA */
+       if (dev_id[93] & 0x4000)
+               return 0;       /* 80 wire */
+       return 1;
+}
+
 static inline int atapi_cdb_len(const u16 *dev_id)
 {
        u16 tmp = dev_id[0] & 0x3;