Merge tag 'r8169-20060920-00' of git://electric-eye.fr.zoreil.com/home/romieu/linux...
[deliverable/linux.git] / drivers / mmc / sdhci.c
index 302dd5bde751cb84ac73113d64cb9b23a75b7566..4e21b3b9d330e4abf9834c22951cb18f8062828d 100644 (file)
 #include "sdhci.h"
 
 #define DRIVER_NAME "sdhci"
-#define DRIVER_VERSION "0.11"
+#define DRIVER_VERSION "0.12"
 
 #define BUGMAIL "<sdhci-devel@list.drzeus.cx>"
 
 #define DBG(f, x...) \
        pr_debug(DRIVER_NAME " [%s()]: " f, __func__,## x)
 
+static unsigned int debug_nodma = 0;
+static unsigned int debug_forcedma = 0;
+static unsigned int debug_quirks = 0;
+
+#define SDHCI_QUIRK_CLOCK_BEFORE_RESET                 (1<<0)
+#define SDHCI_QUIRK_FORCE_DMA                          (1<<1)
+
 static const struct pci_device_id pci_ids[] __devinitdata = {
-       /* handle any SD host controller */
-       {PCI_DEVICE_CLASS((PCI_CLASS_SYSTEM_SDHCI << 8), 0xFFFF00)},
+       {
+               .vendor         = PCI_VENDOR_ID_RICOH,
+               .device         = PCI_DEVICE_ID_RICOH_R5C822,
+               .subvendor      = PCI_VENDOR_ID_IBM,
+               .subdevice      = PCI_ANY_ID,
+               .driver_data    = SDHCI_QUIRK_CLOCK_BEFORE_RESET |
+                                 SDHCI_QUIRK_FORCE_DMA,
+       },
+
+       {
+               .vendor         = PCI_VENDOR_ID_RICOH,
+               .device         = PCI_DEVICE_ID_RICOH_R5C822,
+               .subvendor      = PCI_ANY_ID,
+               .subdevice      = PCI_ANY_ID,
+               .driver_data    = SDHCI_QUIRK_FORCE_DMA,
+       },
+
+       {
+               .vendor         = PCI_VENDOR_ID_TI,
+               .device         = PCI_DEVICE_ID_TI_XX21_XX11_SD,
+               .subvendor      = PCI_ANY_ID,
+               .subdevice      = PCI_ANY_ID,
+               .driver_data    = SDHCI_QUIRK_FORCE_DMA,
+       },
+
+       {       /* Generic SD host controller */
+               PCI_DEVICE_CLASS((PCI_CLASS_SYSTEM_SDHCI << 8), 0xFFFF00)
+       },
+
        { /* end: all zeroes */ },
 };
 
@@ -436,9 +470,7 @@ static void sdhci_finish_data(struct sdhci_host *host)
                        "though there were blocks left. Please report this "
                        "to " BUGMAIL ".\n", mmc_hostname(host->mmc));
                data->error = MMC_ERR_FAILED;
-       }
-
-       if (host->size != 0) {
+       } else if (host->size != 0) {
                printk(KERN_ERR "%s: %d bytes were left untransferred. "
                        "Please report this to " BUGMAIL ".\n",
                        mmc_hostname(host->mmc), host->size);
@@ -465,6 +497,7 @@ static void sdhci_finish_data(struct sdhci_host *host)
 static void sdhci_send_command(struct sdhci_host *host, struct mmc_command *cmd)
 {
        int flags;
+       u32 mask;
        unsigned long timeout;
 
        WARN_ON(host->cmd);
@@ -473,11 +506,20 @@ static void sdhci_send_command(struct sdhci_host *host, struct mmc_command *cmd)
 
        /* Wait max 10 ms */
        timeout = 10;
-       while (readl(host->ioaddr + SDHCI_PRESENT_STATE) &
-               (SDHCI_CMD_INHIBIT | SDHCI_DATA_INHIBIT)) {
+
+       mask = SDHCI_CMD_INHIBIT;
+       if ((cmd->data != NULL) || (cmd->flags & MMC_RSP_BUSY))
+               mask |= SDHCI_DATA_INHIBIT;
+
+       /* We shouldn't wait for data inihibit for stop commands, even
+          though they might use busy signaling */
+       if (host->mrq->data && (cmd == host->mrq->data->stop))
+               mask &= ~SDHCI_DATA_INHIBIT;
+
+       while (readl(host->ioaddr + SDHCI_PRESENT_STATE) & mask) {
                if (timeout == 0) {
                        printk(KERN_ERR "%s: Controller never released "
-                               "inhibit bits. Please report this to "
+                               "inhibit bit(s). Please report this to "
                                BUGMAIL ".\n", mmc_hostname(host->mmc));
                        sdhci_dumpregs(host);
                        cmd->error = MMC_ERR_FAILED;
@@ -523,7 +565,7 @@ static void sdhci_send_command(struct sdhci_host *host, struct mmc_command *cmd)
        if (cmd->data)
                flags |= SDHCI_CMD_DATA;
 
-       writel(SDHCI_MAKE_CMD(cmd->opcode, flags),
+       writew(SDHCI_MAKE_CMD(cmd->opcode, flags),
                host->ioaddr + SDHCI_COMMAND);
 }
 
@@ -794,6 +836,19 @@ static void sdhci_tasklet_finish(unsigned long param)
        if ((mrq->cmd->error != MMC_ERR_NONE) ||
                (mrq->data && ((mrq->data->error != MMC_ERR_NONE) ||
                (mrq->data->stop && (mrq->data->stop->error != MMC_ERR_NONE))))) {
+
+               /* Some controllers need this kick or reset won't work here */
+               if (host->chip->quirks & SDHCI_QUIRK_CLOCK_BEFORE_RESET) {
+                       unsigned int clock;
+
+                       /* This is to force an update */
+                       clock = host->clock;
+                       host->clock = 0;
+                       sdhci_set_clock(host, clock);
+               }
+
+               /* Spec says we should do both at the same time, but Ricoh
+                  controllers do not like that. */
                sdhci_reset(host, SDHCI_RESET_CMD);
                sdhci_reset(host, SDHCI_RESET_DATA);
        }
@@ -1063,6 +1118,7 @@ static int sdhci_resume (struct pci_dev *pdev)
 static int __devinit sdhci_probe_slot(struct pci_dev *pdev, int slot)
 {
        int ret;
+       unsigned int version;
        struct sdhci_chip *chip;
        struct mmc_host *mmc;
        struct sdhci_host *host;
@@ -1094,6 +1150,16 @@ static int __devinit sdhci_probe_slot(struct pci_dev *pdev, int slot)
                return -ENODEV;
        }
 
+       if ((pdev->class & 0x0000FF) == PCI_SDHCI_IFVENDOR) {
+               printk(KERN_ERR DRIVER_NAME ": Vendor specific interface. Aborting.\n");
+               return -ENODEV;
+       }
+
+       if ((pdev->class & 0x0000FF) > PCI_SDHCI_IFVENDOR) {
+               printk(KERN_ERR DRIVER_NAME ": Unknown interface. Aborting.\n");
+               return -ENODEV;
+       }
+
        mmc = mmc_alloc_host(sizeof(struct sdhci_host), &pdev->dev);
        if (!mmc)
                return -ENOMEM;
@@ -1121,9 +1187,30 @@ static int __devinit sdhci_probe_slot(struct pci_dev *pdev, int slot)
                goto release;
        }
 
+       sdhci_reset(host, SDHCI_RESET_ALL);
+
+       version = readw(host->ioaddr + SDHCI_HOST_VERSION);
+       version = (version & SDHCI_SPEC_VER_MASK) >> SDHCI_SPEC_VER_SHIFT;
+       if (version != 0) {
+               printk(KERN_ERR "%s: Unknown controller version (%d). "
+                       "You may experience problems.\n", host->slot_descr,
+                       version);
+       }
+
        caps = readl(host->ioaddr + SDHCI_CAPABILITIES);
 
-       if ((caps & SDHCI_CAN_DO_DMA) && ((pdev->class & 0x0000FF) == 0x01))
+       if (debug_nodma)
+               DBG("DMA forced off\n");
+       else if (debug_forcedma) {
+               DBG("DMA forced on\n");
+               host->flags |= SDHCI_USE_DMA;
+       } else if (chip->quirks & SDHCI_QUIRK_FORCE_DMA)
+               host->flags |= SDHCI_USE_DMA;
+       else if ((pdev->class & 0x0000FF) != PCI_SDHCI_IFDMA)
+               DBG("Controller doesn't have DMA interface\n");
+       else if (!(caps & SDHCI_CAN_DO_DMA))
+               DBG("Controller doesn't have DMA capability\n");
+       else
                host->flags |= SDHCI_USE_DMA;
 
        if (host->flags & SDHCI_USE_DMA) {
@@ -1225,7 +1312,7 @@ static int __devinit sdhci_probe_slot(struct pci_dev *pdev, int slot)
 
        setup_timer(&host->timer, sdhci_timeout_timer, (long)host);
 
-       ret = request_irq(host->irq, sdhci_irq, SA_SHIRQ,
+       ret = request_irq(host->irq, sdhci_irq, IRQF_SHARED,
                host->slot_descr, host);
        if (ret)
                goto untasklet;
@@ -1328,6 +1415,10 @@ static int __devinit sdhci_probe(struct pci_dev *pdev,
        }
 
        chip->pdev = pdev;
+       chip->quirks = ent->driver_data;
+
+       if (debug_quirks)
+               chip->quirks = debug_quirks;
 
        chip->num_slots = slots;
        pci_set_drvdata(pdev, chip);
@@ -1406,7 +1497,15 @@ static void __exit sdhci_drv_exit(void)
 module_init(sdhci_drv_init);
 module_exit(sdhci_drv_exit);
 
+module_param(debug_nodma, uint, 0444);
+module_param(debug_forcedma, uint, 0444);
+module_param(debug_quirks, uint, 0444);
+
 MODULE_AUTHOR("Pierre Ossman <drzeus@drzeus.cx>");
 MODULE_DESCRIPTION("Secure Digital Host Controller Interface driver");
 MODULE_VERSION(DRIVER_VERSION);
 MODULE_LICENSE("GPL");
+
+MODULE_PARM_DESC(debug_nodma, "Forcefully disable DMA transfers. (default 0)");
+MODULE_PARM_DESC(debug_forcedma, "Forcefully enable DMA transfers. (default 0)");
+MODULE_PARM_DESC(debug_quirks, "Force certain quirks.");
This page took 0.034457 seconds and 5 git commands to generate.