mmc: sdhci: Disable ADMA on some O2Micro SD/MMC parts.
authorJennifer Li <Jennifer.li@o2micro.com>
Thu, 18 Nov 2010 04:01:59 +0000 (23:01 -0500)
committerChris Ball <cjb@laptop.org>
Sun, 9 Jan 2011 03:48:03 +0000 (22:48 -0500)
This patch disables the broken ADMA on selected O2Micro devices.

Signed-off-by: Jennifer Li <Jennifer.li@o2micro.com>
Reviewed-by: Chris Ball <cjb@laptop.org>
Signed-off-by: Chris Ball <cjb@laptop.org>
drivers/mmc/host/sdhci-pci.c
include/linux/pci_ids.h

index 3d9c246..831cf91 100644 (file)
@@ -176,6 +176,74 @@ static const struct sdhci_pci_fixes sdhci_intel_mfd_emmc_sdio = {
        .quirks         = SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC,
 };
 
+/* O2Micro extra registers */
+#define O2_SD_LOCK_WP          0xD3
+#define O2_SD_MULTI_VCC3V      0xEE
+#define O2_SD_CLKREQ           0xEC
+#define O2_SD_CAPS             0xE0
+#define O2_SD_ADMA1            0xE2
+#define O2_SD_ADMA2            0xE7
+#define O2_SD_INF_MOD          0xF1
+
+static int o2_probe(struct sdhci_pci_chip *chip)
+{
+       int ret;
+       u8 scratch;
+
+       switch (chip->pdev->device) {
+       case PCI_DEVICE_ID_O2_8220:
+       case PCI_DEVICE_ID_O2_8221:
+       case PCI_DEVICE_ID_O2_8320:
+       case PCI_DEVICE_ID_O2_8321:
+               /* This extra setup is required due to broken ADMA. */
+               ret = pci_read_config_byte(chip->pdev, O2_SD_LOCK_WP, &scratch);
+               if (ret)
+                       return ret;
+               scratch &= 0x7f;
+               pci_write_config_byte(chip->pdev, O2_SD_LOCK_WP, scratch);
+
+               /* Set Multi 3 to VCC3V# */
+               pci_write_config_byte(chip->pdev, O2_SD_MULTI_VCC3V, 0x08);
+
+               /* Disable CLK_REQ# support after media DET */
+               ret = pci_read_config_byte(chip->pdev, O2_SD_CLKREQ, &scratch);
+               if (ret)
+                       return ret;
+               scratch |= 0x20;
+               pci_write_config_byte(chip->pdev, O2_SD_CLKREQ, scratch);
+
+               /* Choose capabilities, enable SDMA.  We have to write 0x01
+                * to the capabilities register first to unlock it.
+                */
+               ret = pci_read_config_byte(chip->pdev, O2_SD_CAPS, &scratch);
+               if (ret)
+                       return ret;
+               scratch |= 0x01;
+               pci_write_config_byte(chip->pdev, O2_SD_CAPS, scratch);
+               pci_write_config_byte(chip->pdev, O2_SD_CAPS, 0x73);
+
+               /* Disable ADMA1/2 */
+               pci_write_config_byte(chip->pdev, O2_SD_ADMA1, 0x39);
+               pci_write_config_byte(chip->pdev, O2_SD_ADMA2, 0x08);
+
+               /* Disable the infinite transfer mode */
+               ret = pci_read_config_byte(chip->pdev, O2_SD_INF_MOD, &scratch);
+               if (ret)
+                       return ret;
+               scratch |= 0x08;
+               pci_write_config_byte(chip->pdev, O2_SD_INF_MOD, scratch);
+
+               /* Lock WP */
+               ret = pci_read_config_byte(chip->pdev, O2_SD_LOCK_WP, &scratch);
+               if (ret)
+                       return ret;
+               scratch |= 0x80;
+               pci_write_config_byte(chip->pdev, O2_SD_LOCK_WP, scratch);
+       }
+
+       return 0;
+}
+
 static int jmicron_pmos(struct sdhci_pci_chip *chip, int on)
 {
        u8 scratch;
@@ -339,6 +407,10 @@ static int jmicron_resume(struct sdhci_pci_chip *chip)
        return 0;
 }
 
+static const struct sdhci_pci_fixes sdhci_o2 = {
+       .probe          = o2_probe,
+};
+
 static const struct sdhci_pci_fixes sdhci_jmicron = {
        .probe          = jmicron_probe,
 
@@ -589,6 +661,46 @@ static const struct pci_device_id pci_ids[] __devinitdata = {
                .driver_data    = (kernel_ulong_t)&sdhci_intel_mfd_emmc_sdio,
        },
 
+       {
+               .vendor         = PCI_VENDOR_ID_O2,
+               .device         = PCI_DEVICE_ID_O2_8120,
+               .subvendor      = PCI_ANY_ID,
+               .subdevice      = PCI_ANY_ID,
+               .driver_data    = (kernel_ulong_t)&sdhci_o2,
+       },
+
+       {
+               .vendor         = PCI_VENDOR_ID_O2,
+               .device         = PCI_DEVICE_ID_O2_8220,
+               .subvendor      = PCI_ANY_ID,
+               .subdevice      = PCI_ANY_ID,
+               .driver_data    = (kernel_ulong_t)&sdhci_o2,
+       },
+
+       {
+               .vendor         = PCI_VENDOR_ID_O2,
+               .device         = PCI_DEVICE_ID_O2_8221,
+               .subvendor      = PCI_ANY_ID,
+               .subdevice      = PCI_ANY_ID,
+               .driver_data    = (kernel_ulong_t)&sdhci_o2,
+       },
+
+       {
+               .vendor         = PCI_VENDOR_ID_O2,
+               .device         = PCI_DEVICE_ID_O2_8320,
+               .subvendor      = PCI_ANY_ID,
+               .subdevice      = PCI_ANY_ID,
+               .driver_data    = (kernel_ulong_t)&sdhci_o2,
+       },
+
+       {
+               .vendor         = PCI_VENDOR_ID_O2,
+               .device         = PCI_DEVICE_ID_O2_8321,
+               .subvendor      = PCI_ANY_ID,
+               .subdevice      = PCI_ANY_ID,
+               .driver_data    = (kernel_ulong_t)&sdhci_o2,
+       },
+
        {       /* Generic SD host controller */
                PCI_DEVICE_CLASS((PCI_CLASS_SYSTEM_SDHCI << 8), 0xFFFF00)
        },
index cb845c1..7a58875 100644 (file)
 #define PCI_DEVICE_ID_O2_6836          0x6836
 #define PCI_DEVICE_ID_O2_6812          0x6872
 #define PCI_DEVICE_ID_O2_6933          0x6933
+#define PCI_DEVICE_ID_O2_8120          0x8120
+#define PCI_DEVICE_ID_O2_8220          0x8220
+#define PCI_DEVICE_ID_O2_8221          0x8221
+#define PCI_DEVICE_ID_O2_8320          0x8320
+#define PCI_DEVICE_ID_O2_8321          0x8321
 
 #define PCI_VENDOR_ID_3DFX             0x121a
 #define PCI_DEVICE_ID_3DFX_VOODOO      0x0001