@@ -81,6 +81,17 @@ config MMC_RICOH_MMC
If unsure, say Y.
+config MMC_SDHCI_INTEL_MID
+ tristate "SDHCI support on Intel MID platforms"
+ depends on MMC_SDHCI_PCI
+ help
+ This includes support for the SDHCI controllers found on Intel
+ MID platform systems.
+
+ If you have a controller with this interface, say Y or M here.
+
+ If unsure, say N.
+
config MMC_SDHCI_OF
tristate "SDHCI support on OpenFirmware platforms"
depends on MMC_SDHCI && PPC_OF
@@ -13,6 +13,7 @@ obj-$(CONFIG_MMC_MXC) += mxcmmc.o
obj-$(CONFIG_MMC_SDHCI) += sdhci.o
obj-$(CONFIG_MMC_SDHCI_MV) += sdhci-mv.o
obj-$(CONFIG_MMC_SDHCI_PCI) += sdhci-pci.o
+obj-$(CONFIG_MMC_SDHCI_MID) += sdhci-intel-mid.o
obj-$(CONFIG_MMC_SDHCI_S3C) += sdhci-s3c.o
obj-$(CONFIG_MMC_SDHCI_SPEAR) += sdhci-spear.o
obj-$(CONFIG_MMC_WBSD) += wbsd.o
new file mode 100644
@@ -0,0 +1,173 @@
+#include <linux/delay.h>
+#include <linux/highmem.h>
+#include <linux/io.h>
+#include <linux/dma-mapping.h>
+#include <linux/slab.h>
+#include <linux/scatterlist.h>
+#include <linux/regulator/consumer.h>
+
+#include <linux/leds.h>
+
+#include <linux/mmc/host.h>
+
+#include "sdhci.h"
+
+/*
+ * Support code for SDHCI on Intel MID platforms. We have lots
+ * of quirks specific to these platforms so wrap them up in one place
+ * that keeps them out of the main flow. We can't just make it a new
+ * driver as the shdci core code isn't really a library.
+ */
+
+static void sdhci_broken_reset(struct sdhci_host *host, u8 mask)
+{
+ unsigned long timeout;
+ u32 uninitialized_var(ier);
+
+ if (host->quirks & SDHCI_QUIRK_NO_CARD_NO_RESET) {
+ if (!(sdhci_readl(host, SDHCI_PRESENT_STATE) &
+ SDHCI_CARD_PRESENT))
+ return;
+ }
+
+ if (host->quirks & SDHCI_QUIRK_RESTORE_IRQS_AFTER_RESET)
+ ier = sdhci_readl(host, SDHCI_INT_ENABLE);
+
+ sdhci_writeb(host, mask, SDHCI_SOFTWARE_RESET);
+
+ if (mask & SDHCI_RESET_ALL)
+ host->clock = 0;
+
+ /* Wait max 100 ms */
+ timeout = 100;
+
+ /* hw clears the bit when it's done */
+ while (sdhci_readb(host, SDHCI_SOFTWARE_RESET) & mask) {
+ if (timeout == 0) {
+ printk(KERN_ERR "%s: Reset 0x%x never completed.\n",
+ mmc_hostname(host->mmc), (int)mask);
+ if (!(host->quirks & SDHCI_QUIRK_BROKEN_RESETALL)) {
+ printk(KERN_ERR "%s: Reset 0x%x never completed.\n",
+ mmc_hostname(host->mmc), (int)mask);
+ sdhci_dumpregs(host);
+ }
+ return;
+ }
+ timeout--;
+ mdelay(1);
+ }
+ if (host->quirks & SDHCI_QUIRK_RESTORE_IRQS_AFTER_RESET)
+ sdhci_clear_set_irqs(host, SDHCI_INT_ALL_MASK, ier);
+}
+
+static void sdhci_mid_broken_resetall(struct sdhci_host *host, int down)
+{
+ if (down)
+ sdhci_broken_reset(host, SDHCI_RESET_ALL);
+}
+
+static void sdhci_mid_init_no_reset(struct sdhci_host *host, int soft)
+{
+ u32 intmask;
+
+ intmask = sdhci_readl(host, SDHCI_INT_STATUS);
+ sdhci_writel(host,
+ intmask & (SDHCI_INT_CARD_INSERT | SDHCI_INT_CARD_REMOVE),
+ SDHCI_INT_STATUS);
+
+ /* Ensure any IRQ left over from pre boot time (eg from Kboot) does
+ not turn up and cause chaos */
+ sdhci_writel(host, 0, SDHCI_INT_ENABLE);
+ sdhci_writel(host, 0, SDHCI_SIGNAL_ENABLE);
+
+ sdhci_clear_set_irqs(host, SDHCI_INT_ALL_MASK,
+ SDHCI_INT_BUS_POWER | SDHCI_INT_DATA_END_BIT |
+ SDHCI_INT_DATA_CRC | SDHCI_INT_DATA_TIMEOUT | SDHCI_INT_INDEX |
+ SDHCI_INT_END_BIT | SDHCI_INT_CRC | SDHCI_INT_TIMEOUT |
+ SDHCI_INT_DATA_END | SDHCI_INT_RESPONSE);
+
+ if (soft) {
+ /* force clock reconfiguration */
+ host->clock = 0;
+ sdhci_set_ios(host->mmc, &host->mmc->ios);
+ }
+
+ /* disable wakeup signal during initialization */
+ sdhci_writeb(host, 0x0, SDHCI_WAKE_UP_CONTROL);
+}
+
+/*
+ * HW problem exists in LNW A3 so clock register has to be set
+ * for every command if both SDIO0 and SDIO1 are enabled.
+ */
+static void sdhci_clock_reset(struct sdhci_host *host)
+{
+ u16 clk;
+ unsigned long timeout;
+
+ clk = sdhci_readw(host, SDHCI_CLOCK_CONTROL);
+
+ clk |= SDHCI_CLOCK_CARD_EN;
+ sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL);
+
+ /* Wait max 10 ms */
+ timeout = 10;
+ while (!((clk = sdhci_readw(host, SDHCI_CLOCK_CONTROL))
+ & SDHCI_CLOCK_INT_STABLE)) {
+ if (timeout == 0) {
+ printk(KERN_ERR "%s: Internal clock never "
+ "stabilised.\n",
+ mmc_hostname(host->mmc));
+ sdhci_dumpregs(host);
+ return;
+ }
+ timeout--;
+ mdelay(1);
+ }
+}
+
+static void sdhci_clockreset_wcmd(struct sdhci_host *host,
+ struct mmc_command *cmd, int flags)
+{
+ sdhci_clock_reset(host);
+ sdhci_writew(host, SDHCI_MAKE_CMD(cmd->opcode, flags), SDHCI_COMMAND);
+}
+
+static void sdhci_lnw_a3_set_ios(struct sdhci_host *host,
+ struct mmc_ios *ios, u8 ctrl)
+{
+ /*
+ * For LNW A3, HISPD bit has to be cleared in order to
+ * enable the 50MHz clock
+ */
+ if (ios->timing == MMC_TIMING_SD_HS ||
+ ios->timing == MMC_TIMING_MMC_HS)
+ ctrl |= SDHCI_CTRL_HISPD;
+ else
+ ctrl &= ~SDHCI_CTRL_HISPD;
+ sdhci_writeb(host, ctrl, SDHCI_HOST_CONTROL);
+}
+
+/*
+ * HW problem exists in LNW A3 which leads to fake interrupt on SDIO1 if SDIO0
+ * and SDIO1 are both enabled.
+ */
+static void sdhci_lnw_a3_unexpected_cmd(struct sdhci_host *host, u32 intmask)
+{
+}
+
+static void sdhci_no_reset(struct sdhci_host *host, u8 mask)
+{
+}
+
+struct sdhci_ops sdhci_intel_mrst_hc = {
+ .reset = sdhci_broken_reset,
+ .init = sdhci_mid_init_no_reset,
+ .set_ios = sdhci_lnw_a3_set_ios,
+ .reset_all = sdhci_broken,reset_all,
+ .write_command = sdhci_clockreset_wcmd,
+ .unexpected_cmd_irq = sdhci_lnw_a3_unexpected_cmd,
+};
+EXPORT_SYMBOL_GPL(sdhci_intel_mrst_hc);
+
+
@@ -54,6 +54,8 @@ struct sdhci_pci_fixes {
int (*suspend)(struct sdhci_pci_chip*,
pm_message_t);
int (*resume)(struct sdhci_pci_chip*);
+
+ struct sdhci_ops *host_ops;
};
struct sdhci_pci_slot {
@@ -397,6 +399,39 @@ static const struct sdhci_pci_fixes sdhci_via = {
.probe = via_probe,
};
+#if defined(CONFIG_SDHCI_INTEL_MID) || defined(CONFIG_SDHCI_INTEL_MID_MODULE)
+/*
+ * ADMA operation is disabled for Moorestown platform due to
+ * hardware bugs.
+ */
+static const struct sdhci_pci_fixes sdhci_intel_mrst_hc0 = {
+ .quirks = SDHCI_QUIRK_BROKEN_ADMA |
+ SDHCI_QUIRK_BROKEN_MULTIPLE_SLOTS |
+ SDHCI_QUIRK_BROKEN_RESETALL |
+ SDHCI_QUIRK_FORCE_FULL_SPEED_MODE,
+ .probe = mrst_hc0_probe,
+ .host_ops = &sdhci_intel_mrst_hc,
+};
+
+static int mrst_hc0_probe(struct sdhci_pci_chip *chip)
+{
+ /*
+ * slots number is fixed here for MRST as SDIO3 is never used and has
+ * hardware bugs.
+ */
+ chip->num_slots = 1;
+}
+
+static const struct sdhci_pci_fixes sdhci_intel_mrst_hc1 = {
+ .quirks = SDHCI_QUIRK_BROKEN_ADMA |
+ SDHCI_QUIRK_BROKEN_RESETALL |
+ SDHCI_QUIRK_FORCE_FULL_SPEED_MODE,
+ .probe = mrst_hc1_probe,
+ .host_ops = &sdhci_intel_mrst_hc,
+};
+
+#endif
+
static const struct pci_device_id pci_ids[] __devinitdata = {
{
.vendor = PCI_VENDOR_ID_RICOH,
@@ -494,6 +529,23 @@ static const struct pci_device_id pci_ids[] __devinitdata = {
.driver_data = (kernel_ulong_t)&sdhci_via,
},
+#if defined(CONFIG_SDHCI_INTEL_MID) || defined(CONFIG_SDHCI_INTEL_MID_MODULE)
+ {
+ .vendor = PCI_VENDOR_ID_INTEL,
+ .device = PCI_DEVICE_ID_INTEL_MRST_SD0,
+ .subvendor = PCI_ANY_ID,
+ .subdevice = PCI_ANY_ID,
+ .driver_data = (kernel_ulong_t)&sdhci_intel_mrst_hc0,
+ },
+
+ {
+ .vendor = PCI_VENDOR_ID_INTEL,
+ .device = PCI_DEVICE_ID_INTEL_MRST_SD1,
+ .subvendor = PCI_ANY_ID,
+ .subdevice = PCI_ANY_ID,
+ .driver_data = (kernel_ulong_t)&sdhci_intel_mrst_hc1,
+ },
+#endif
{ /* Generic SD host controller */
PCI_DEVICE_CLASS((PCI_CLASS_SYSTEM_SDHCI << 8), 0xFFFF00)
},
@@ -509,7 +561,7 @@ MODULE_DEVICE_TABLE(pci, pci_ids);
* *
\*****************************************************************************/
-static int sdhci_pci_enable_dma(struct sdhci_host *host)
+int sdhci_pci_enable_dma(struct sdhci_host *host)
{
struct sdhci_pci_slot *slot;
struct pci_dev *pdev;
@@ -533,6 +585,7 @@ static int sdhci_pci_enable_dma(struct sdhci_host *host)
return 0;
}
+EXPORT_SYMBOL_GPL(sdhci_pci_enable_dma);
static struct sdhci_ops sdhci_pci_ops = {
.enable_dma = sdhci_pci_enable_dma,
@@ -687,7 +740,10 @@ static struct sdhci_pci_slot * __devinit sdhci_pci_probe_slot(
slot->pci_bar = bar;
host->hw_name = "PCI";
- host->ops = &sdhci_pci_ops;
+ if (chip->fixes->host_ops)
+ host->ops = chip->fixes->host_ops;
+ else
+ host->ops = &sdhci_pci_ops;
host->quirks = chip->quirks;
host->irq = pdev->irq;
@@ -94,7 +94,7 @@ static void sdhci_dumpregs(struct sdhci_host *host)
* *
\*****************************************************************************/
-static void sdhci_clear_set_irqs(struct sdhci_host *host, u32 clear, u32 set)
+void sdhci_clear_set_irqs(struct sdhci_host *host, u32 clear, u32 set)
{
u32 ier;
@@ -104,6 +104,7 @@ static void sdhci_clear_set_irqs(struct sdhci_host *host, u32 clear, u32 set)
sdhci_writel(host, ier, SDHCI_INT_ENABLE);
sdhci_writel(host, ier, SDHCI_SIGNAL_ENABLE);
}
+EXPORT_SYMBOL_GPL(sdhci_clear_set_irqs);
static void sdhci_unmask_irqs(struct sdhci_host *host, u32 irqs)
{
@@ -143,6 +144,11 @@ static void sdhci_reset(struct sdhci_host *host, u8 mask)
unsigned long timeout;
u32 uninitialized_var(ier);
+ if (host->ops->reset) {
+ host->ops->reset(host, mask);
+ return;
+ }
+
if (host->quirks & SDHCI_QUIRK_NO_CARD_NO_RESET) {
if (!(sdhci_readl(host, SDHCI_PRESENT_STATE) &
SDHCI_CARD_PRESENT))
@@ -176,6 +182,15 @@ static void sdhci_reset(struct sdhci_host *host, u8 mask)
sdhci_clear_set_irqs(host, SDHCI_INT_ALL_MASK, ier);
}
+/* Down is true if we are shutting down the port */
+static void sdhci_reset_all(struct sdhci_host *host, int down)
+{
+ if (host->ops->reset_all)
+ host->ops->reset_all(host, down);
+ else
+ sdhci_reset(host, SDHCI_RESET_ALL);
+}
+
static void sdhci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios);
static void sdhci_init(struct sdhci_host *host, int soft)
@@ -183,7 +198,7 @@ static void sdhci_init(struct sdhci_host *host, int soft)
if (soft)
sdhci_reset(host, SDHCI_RESET_CMD|SDHCI_RESET_DATA);
else
- sdhci_reset(host, SDHCI_RESET_ALL);
+ sdhci_reset_all(host, 0);
sdhci_clear_set_irqs(host, SDHCI_INT_ALL_MASK,
SDHCI_INT_BUS_POWER | SDHCI_INT_DATA_END_BIT |
@@ -945,7 +960,11 @@ static void sdhci_send_command(struct sdhci_host *host, struct mmc_command *cmd)
if (cmd->data)
flags |= SDHCI_CMD_DATA;
- sdhci_writew(host, SDHCI_MAKE_CMD(cmd->opcode, flags), SDHCI_COMMAND);
+ if (host->ops->write_command)
+ host->ops->write_command(host, cmd, flags);
+ else
+ sdhci_writew(host, SDHCI_MAKE_CMD(cmd->opcode, flags),
+ SDHCI_COMMAND);
}
static void sdhci_finish_command(struct sdhci_host *host)
@@ -1186,9 +1205,13 @@ static void sdhci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
else
ctrl &= ~SDHCI_CTRL_HISPD;
- sdhci_writeb(host, ctrl, SDHCI_HOST_CONTROL);
+ if (host->ops->set_ios)
+ host->ops->set_ios(host, ios, ctrl);
+ else
+ sdhci_writeb(host, ctrl, SDHCI_HOST_CONTROL);
- /*
+ /* FIXME: This can also be moved to ops->set_ios
+ *
* Some (ENE) controllers go apeshit on some ios operation,
* signalling timeout and CRC errors even on CMD0. Resetting
* it on each ios seems to solve the problem.
@@ -1383,10 +1406,13 @@ static void sdhci_cmd_irq(struct sdhci_host *host, u32 intmask)
BUG_ON(intmask == 0);
if (!host->cmd) {
- printk(KERN_ERR "%s: Got command interrupt 0x%08x even "
- "though no command operation was in progress.\n",
- mmc_hostname(host->mmc), (unsigned)intmask);
- sdhci_dumpregs(host);
+ if (host->ops->unexpected_cmd_irq)
+ host->ops->unexpected_cmd_irq(host, intmask);
+ else {
+ printk(KERN_ERR "%s: Got command interrupt 0x%08x even though no command operation was in progress.\n",
+ mmc_hostname(host->mmc), (unsigned)intmask);
+ sdhci_dumpregs(host);
+ }
return;
}
@@ -1703,7 +1729,7 @@ int sdhci_add_host(struct sdhci_host *host)
if (debug_quirks)
host->quirks = debug_quirks;
- sdhci_reset(host, SDHCI_RESET_ALL);
+ sdhci_reset_all(host, 0);
host->version = sdhci_readw(host, SDHCI_HOST_VERSION);
host->version = (host->version & SDHCI_SPEC_VER_MASK)
@@ -1829,6 +1855,9 @@ int sdhci_add_host(struct sdhci_host *host)
if (host->quirks & SDHCI_QUIRK_BROKEN_CARD_DETECTION)
mmc->caps |= MMC_CAP_NEEDS_POLL;
+ if (host->ops->set_caps)
+ host->ops->set_caps(host);
+
mmc->ocr_avail = 0;
if (caps & SDHCI_CAN_VDD_330)
mmc->ocr_avail |= MMC_VDD_32_33|MMC_VDD_33_34;
@@ -1881,7 +1910,7 @@ int sdhci_add_host(struct sdhci_host *host)
} else {
mmc->max_blk_size = (caps & SDHCI_MAX_BLOCK_MASK) >>
SDHCI_MAX_BLOCK_SHIFT;
- if (mmc->max_blk_size >= 3) {
+ if (mmc->max_blk_size > 3) {
printk(KERN_WARNING "%s: Invalid maximum block size, "
"assuming 512 bytes\n", mmc_hostname(mmc));
mmc->max_blk_size = 0;
@@ -1952,7 +1981,7 @@ int sdhci_add_host(struct sdhci_host *host)
#ifdef SDHCI_USE_LEDS_CLASS
reset:
- sdhci_reset(host, SDHCI_RESET_ALL);
+ sdhci_reset_all(host, 1);
free_irq(host->irq, host);
#endif
untasklet:
@@ -1993,7 +2022,7 @@ void sdhci_remove_host(struct sdhci_host *host, int dead)
#endif
if (!dead)
- sdhci_reset(host, SDHCI_RESET_ALL);
+ sdhci_reset_all(host, 1);
free_irq(host->irq, host);
@@ -323,6 +323,17 @@ struct sdhci_ops {
unsigned int (*get_max_clock)(struct sdhci_host *host);
unsigned int (*get_min_clock)(struct sdhci_host *host);
unsigned int (*get_timeout_clock)(struct sdhci_host *host);
+
+ /* Interface hooks to allow drivers to override standard behaviour */
+ void (*reset)(struct sdhci_host *host, u8 mask);
+ void (*reset_all)(struct sdhci_host *host, int down);
+ void (*init)(struct sdhci_host *host, int soft);
+ void (*write_command)(struct sdhci_host *host,
+ struct mmc_command *cmd, int flags);
+ void (*set_ios)(struct sdhci_host *host,
+ struct mmc_ios *ios, u8 ctrl);
+ void (*set_caps)(struct sdhci_host *host);
+ void (*unexpected_cmd_irq)(struct sdhci_host *host, u32 intmask);
};
#ifdef CONFIG_MMC_SDHCI_IO_ACCESSORS
@@ -427,4 +438,11 @@ extern int sdhci_suspend_host(struct sdhci_host *host, pm_message_t state);
extern int sdhci_resume_host(struct sdhci_host *host);
#endif
+extern void sdhci_clear_set_irqs(struct sdhci_host *host, u32 clear, u32 set);
+extern int sdhci_pci_enable_dma(struct sdhci_host *host);
+
+/* Host function sets from driver support files */
+extern struct sdhci_ops sdhci_intel_mrst_hc;
+
+
#endif /* __SDHCI_H */
@@ -2410,6 +2410,8 @@
#define PCI_DEVICE_ID_INTEL_82375 0x0482
#define PCI_DEVICE_ID_INTEL_82424 0x0483
#define PCI_DEVICE_ID_INTEL_82378 0x0484
+#define PCI_DEVICE_ID_INTEL_MRST_SD0 0x0807
+#define PCI_DEVICE_ID_INTEL_MRST_SD1 0x0808
#define PCI_DEVICE_ID_INTEL_I960 0x0960
#define PCI_DEVICE_ID_INTEL_I960RM 0x0962
#define PCI_DEVICE_ID_INTEL_8257X_SOL 0x1062