#include <linux/dma-mapping.h>
#include <linux/scatterlist.h>
+#include <linux/leds.h>
+
#include <linux/mmc/host.h>
#include "sdhci.h"
#define SDHCI_QUIRK_CLOCK_BEFORE_RESET (1<<0)
/* Controller has bad caps bits, but really supports DMA */
#define SDHCI_QUIRK_FORCE_DMA (1<<1)
-/* Controller doesn't like some resets when there is no card inserted. */
+/* Controller doesn't like to be reset when there is no card inserted. */
#define SDHCI_QUIRK_NO_CARD_NO_RESET (1<<2)
/* Controller doesn't like clearing the power reg before a change */
#define SDHCI_QUIRK_SINGLE_POWER_WRITE (1<<3)
#define SDHCI_QUIRK_32BIT_DMA_SIZE (1<<7)
/* Controller needs to be reset after each request to stay stable */
#define SDHCI_QUIRK_RESET_AFTER_REQUEST (1<<8)
+/* Controller needs voltage and power writes to happen separately */
+#define SDHCI_QUIRK_NO_SIMULT_VDD_AND_POWER (1<<9)
+/* Controller has an off-by-one issue with timeout value */
+#define SDHCI_QUIRK_INCR_TIMEOUT_CONTROL (1<<10)
static const struct pci_device_id pci_ids[] __devinitdata = {
{
{
.vendor = PCI_VENDOR_ID_RICOH,
.device = PCI_DEVICE_ID_RICOH_R5C822,
- .subvendor = PCI_ANY_ID,
+ .subvendor = PCI_VENDOR_ID_SAMSUNG,
.subdevice = PCI_ANY_ID,
.driver_data = SDHCI_QUIRK_FORCE_DMA |
SDHCI_QUIRK_NO_CARD_NO_RESET,
},
+ {
+ .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_SINGLE_POWER_WRITE |
- SDHCI_QUIRK_RESET_CMD_DATA_ON_IOS,
+ SDHCI_QUIRK_RESET_CMD_DATA_ON_IOS |
+ SDHCI_QUIRK_BROKEN_DMA,
},
{
.subvendor = PCI_ANY_ID,
.subdevice = PCI_ANY_ID,
.driver_data = SDHCI_QUIRK_SINGLE_POWER_WRITE |
- SDHCI_QUIRK_RESET_CMD_DATA_ON_IOS,
+ SDHCI_QUIRK_RESET_CMD_DATA_ON_IOS |
+ SDHCI_QUIRK_BROKEN_DMA,
+ },
+
+ {
+ .vendor = PCI_VENDOR_ID_MARVELL,
+ .device = PCI_DEVICE_ID_MARVELL_CAFE_SD,
+ .subvendor = PCI_ANY_ID,
+ .subdevice = PCI_ANY_ID,
+ .driver_data = SDHCI_QUIRK_NO_SIMULT_VDD_AND_POWER |
+ SDHCI_QUIRK_INCR_TIMEOUT_CONTROL,
},
{
writeb(ctrl, host->ioaddr + SDHCI_HOST_CONTROL);
}
+#ifdef CONFIG_LEDS_CLASS
+static void sdhci_led_control(struct led_classdev *led,
+ enum led_brightness brightness)
+{
+ struct sdhci_host *host = container_of(led, struct sdhci_host, led);
+ unsigned long flags;
+
+ spin_lock_irqsave(&host->lock, flags);
+
+ if (brightness == LED_OFF)
+ sdhci_deactivate_led(host);
+ else
+ sdhci_activate_led(host);
+
+ spin_unlock_irqrestore(&host->lock, flags);
+}
+#endif
+
/*****************************************************************************\
* *
* Core functions *
break;
}
+ /*
+ * Compensate for an off-by-one error in the CaFe hardware; otherwise,
+ * a too-small count gives us interrupt timeouts.
+ */
+ if ((host->chip->quirks & SDHCI_QUIRK_INCR_TIMEOUT_CONTROL))
+ count++;
+
if (count >= 0xF) {
printk(KERN_WARNING "%s: Too large timeout requested!\n",
mmc_hostname(host->mmc));
BUG();
}
+ /*
+ * At least the CaFe chip gets confused if we set the voltage
+ * and set turn on power at the same time, so set the voltage first.
+ */
+ if ((host->chip->quirks & SDHCI_QUIRK_NO_SIMULT_VDD_AND_POWER))
+ writeb(pwr & ~SDHCI_POWER_ON,
+ host->ioaddr + SDHCI_POWER_CONTROL);
+
writeb(pwr, host->ioaddr + SDHCI_POWER_CONTROL);
out:
WARN_ON(host->mrq != NULL);
+#ifndef CONFIG_LEDS_CLASS
sdhci_activate_led(host);
+#endif
host->mrq = mrq;
host->cmd = NULL;
host->data = NULL;
+#ifndef CONFIG_LEDS_CLASS
sdhci_deactivate_led(host);
+#endif
mmiowb();
spin_unlock_irqrestore(&host->lock, flags);
sdhci_dumpregs(host);
#endif
+#ifdef CONFIG_LEDS_CLASS
+ host->led.name = mmc_hostname(mmc);
+ host->led.brightness = LED_OFF;
+ host->led.default_trigger = mmc_hostname(mmc);
+ host->led.brightness_set = sdhci_led_control;
+
+ ret = led_classdev_register(&pdev->dev, &host->led);
+ if (ret)
+ goto reset;
+#endif
+
mmiowb();
mmc_add_host(mmc);
return 0;
+#ifdef CONFIG_LEDS_CLASS
+reset:
+ sdhci_reset(host, SDHCI_RESET_ALL);
+ free_irq(host->irq, host);
+#endif
untasklet:
tasklet_kill(&host->card_tasklet);
tasklet_kill(&host->finish_tasklet);
mmc_remove_host(mmc);
+#ifdef CONFIG_LEDS_CLASS
+ led_classdev_unregister(&host->led);
+#endif
+
sdhci_reset(host, SDHCI_RESET_ALL);
free_irq(host->irq, host);