#define MAX_SLOTS 8
struct sdhci_pci_chip;
+struct sdhci_pci_slot;
struct sdhci_pci_fixes {
unsigned int quirks;
int (*probe)(struct sdhci_pci_chip*);
+ int (*probe_slot)(struct sdhci_pci_slot*);
+ void (*remove_slot)(struct sdhci_pci_slot*, int);
+
+ int (*suspend)(struct sdhci_pci_chip*,
+ pm_message_t);
int (*resume)(struct sdhci_pci_chip*);
};
static const struct sdhci_pci_fixes sdhci_ricoh = {
.probe = ricoh_probe,
+ .quirks = SDHCI_QUIRK_32BIT_DMA_ADDR,
};
static const struct sdhci_pci_fixes sdhci_ene_712 = {
static const struct sdhci_pci_fixes sdhci_cafe = {
.quirks = SDHCI_QUIRK_NO_SIMULT_VDD_AND_POWER |
+ SDHCI_QUIRK_NO_BUSY_IRQ |
SDHCI_QUIRK_BROKEN_TIMEOUT_VAL,
};
{
int ret;
+ if (chip->pdev->revision == 0) {
+ chip->quirks |= SDHCI_QUIRK_32BIT_DMA_ADDR |
+ SDHCI_QUIRK_32BIT_DMA_SIZE |
+ SDHCI_QUIRK_32BIT_ADMA_SIZE |
+ SDHCI_QUIRK_RESET_AFTER_REQUEST |
+ SDHCI_QUIRK_BROKEN_SMALL_PIO;
+ }
+
+ /*
+ * JMicron chips can have two interfaces to the same hardware
+ * in order to work around limitations in Microsoft's driver.
+ * We need to make sure we only bind to one of them.
+ *
+ * This code assumes two things:
+ *
+ * 1. The PCI code adds subfunctions in order.
+ *
+ * 2. The MMC interface has a lower subfunction number
+ * than the SD interface.
+ */
+ if (chip->pdev->device == PCI_DEVICE_ID_JMICRON_JMB38X_SD) {
+ struct pci_dev *sd_dev;
+
+ sd_dev = NULL;
+ while ((sd_dev = pci_get_device(PCI_VENDOR_ID_JMICRON,
+ PCI_DEVICE_ID_JMICRON_JMB38X_MMC, sd_dev)) != NULL) {
+ if ((PCI_SLOT(chip->pdev->devfn) ==
+ PCI_SLOT(sd_dev->devfn)) &&
+ (chip->pdev->bus == sd_dev->bus))
+ break;
+ }
+
+ if (sd_dev) {
+ pci_dev_put(sd_dev);
+ dev_info(&chip->pdev->dev, "Refusing to bind to "
+ "secondary interface.\n");
+ return -ENODEV;
+ }
+ }
+
/*
* JMicron chips need a bit of a nudge to enable the power
* output pins.
return 0;
}
+static void jmicron_enable_mmc(struct sdhci_host *host, int on)
+{
+ u8 scratch;
+
+ scratch = readb(host->ioaddr + 0xC0);
+
+ if (on)
+ scratch |= 0x01;
+ else
+ scratch &= ~0x01;
+
+ writeb(scratch, host->ioaddr + 0xC0);
+}
+
+static int jmicron_probe_slot(struct sdhci_pci_slot *slot)
+{
+ if (slot->chip->pdev->revision == 0) {
+ u16 version;
+
+ version = readl(slot->host->ioaddr + SDHCI_HOST_VERSION);
+ version = (version & SDHCI_VENDOR_VER_MASK) >>
+ SDHCI_VENDOR_VER_SHIFT;
+
+ /*
+ * Older versions of the chip have lots of nasty glitches
+ * in the ADMA engine. It's best just to avoid it
+ * completely.
+ */
+ if (version < 0xAC)
+ slot->host->quirks |= SDHCI_QUIRK_BROKEN_ADMA;
+ }
+
+ /*
+ * The secondary interface requires a bit set to get the
+ * interrupts.
+ */
+ if (slot->chip->pdev->device == PCI_DEVICE_ID_JMICRON_JMB38X_MMC)
+ jmicron_enable_mmc(slot->host, 1);
+
+ return 0;
+}
+
+static void jmicron_remove_slot(struct sdhci_pci_slot *slot, int dead)
+{
+ if (dead)
+ return;
+
+ if (slot->chip->pdev->device == PCI_DEVICE_ID_JMICRON_JMB38X_MMC)
+ jmicron_enable_mmc(slot->host, 0);
+}
+
+static int jmicron_suspend(struct sdhci_pci_chip *chip, pm_message_t state)
+{
+ int i;
+
+ if (chip->pdev->device == PCI_DEVICE_ID_JMICRON_JMB38X_MMC) {
+ for (i = 0;i < chip->num_slots;i++)
+ jmicron_enable_mmc(chip->slots[i]->host, 0);
+ }
+
+ return 0;
+}
+
static int jmicron_resume(struct sdhci_pci_chip *chip)
{
- int ret;
+ int ret, i;
+
+ if (chip->pdev->device == PCI_DEVICE_ID_JMICRON_JMB38X_MMC) {
+ for (i = 0;i < chip->num_slots;i++)
+ jmicron_enable_mmc(chip->slots[i]->host, 1);
+ }
ret = jmicron_pmos(chip, 1);
if (ret) {
}
static const struct sdhci_pci_fixes sdhci_jmicron = {
- .quirks = SDHCI_QUIRK_32BIT_DMA_ADDR |
- SDHCI_QUIRK_32BIT_DMA_SIZE |
- SDHCI_QUIRK_RESET_AFTER_REQUEST,
-
.probe = jmicron_probe,
+ .probe_slot = jmicron_probe_slot,
+ .remove_slot = jmicron_remove_slot,
+
+ .suspend = jmicron_suspend,
.resume = jmicron_resume,
};
{
.vendor = PCI_VENDOR_ID_MARVELL,
- .device = PCI_DEVICE_ID_MARVELL_CAFE_SD,
+ .device = PCI_DEVICE_ID_MARVELL_88ALP01_SD,
.subvendor = PCI_ANY_ID,
.subdevice = PCI_ANY_ID,
.driver_data = (kernel_ulong_t)&sdhci_cafe,
.driver_data = (kernel_ulong_t)&sdhci_jmicron,
},
+ {
+ .vendor = PCI_VENDOR_ID_JMICRON,
+ .device = PCI_DEVICE_ID_JMICRON_JMB38X_MMC,
+ .subvendor = PCI_ANY_ID,
+ .subdevice = PCI_ANY_ID,
+ .driver_data = (kernel_ulong_t)&sdhci_jmicron,
+ },
+
{ /* Generic SD host controller */
PCI_DEVICE_CLASS((PCI_CLASS_SYSTEM_SDHCI << 8), 0xFFFF00)
},
}
}
+ if (chip->fixes && chip->fixes->suspend) {
+ ret = chip->fixes->suspend(chip, state);
+ if (ret) {
+ for (i = chip->num_slots - 1;i >= 0;i--)
+ sdhci_resume_host(chip->slots[i]->host);
+ return ret;
+ }
+ }
+
pci_save_state(pdev);
pci_enable_wake(pdev, pci_choose_state(pdev, state), 0);
pci_disable_device(pdev);
}
addr = pci_resource_start(pdev, bar);
- host->ioaddr = ioremap_nocache(addr, pci_resource_len(pdev, bar));
+ host->ioaddr = pci_ioremap_bar(pdev, bar);
if (!host->ioaddr) {
dev_err(&pdev->dev, "failed to remap registers\n");
goto release;
}
+ if (chip->fixes && chip->fixes->probe_slot) {
+ ret = chip->fixes->probe_slot(slot);
+ if (ret)
+ goto unmap;
+ }
+
ret = sdhci_add_host(host);
if (ret)
- goto unmap;
+ goto remove;
return slot;
+remove:
+ if (chip->fixes && chip->fixes->remove_slot)
+ chip->fixes->remove_slot(slot, 0);
+
unmap:
iounmap(host->ioaddr);
static void sdhci_pci_remove_slot(struct sdhci_pci_slot *slot)
{
- sdhci_remove_host(slot->host);
+ int dead;
+ u32 scratch;
+
+ dead = 0;
+ scratch = readl(slot->host->ioaddr + SDHCI_INT_STATUS);
+ if (scratch == (u32)-1)
+ dead = 1;
+
+ sdhci_remove_host(slot->host, dead);
+
+ if (slot->chip->fixes && slot->chip->fixes->remove_slot)
+ slot->chip->fixes->remove_slot(slot, dead);
+
pci_release_region(slot->chip->pdev, slot->pci_bar);
+
sdhci_free_host(slot->host);
}