Skip to contentSkip to conversation

SDHCI: 8-bit data transfer width support

PATCH In review Kyungmin Park 5 participants 6 messages Jun 12, 2010 – Jun 29, 2010

Subsystems (5)
lore
Share & tools
lore
#0 lore raw

PATCH 3/3 SDHCI: 8-bit data transfer width support

From: Kyungmin Park <[email protected]>

Some host constroller such as s5pc110 has WIDE8 support feature.

---
 drivers/mmc/host/sdhci.c |    5 +++++
 drivers/mmc/host/sdhci.h |    1 +
 2 files changed, 6 insertions(+), 0 deletions(-)
drivers/mmc/host/sdhci.c +5 -0
index 142419c..6cf018a 100644
@@ -1159,6 +1159,11 @@ static void sdhci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
 
 	ctrl = sdhci_readb(host, SDHCI_HOST_CONTROL);
 
+	if (ios->bus_width == MMC_BUS_WIDTH_8)
+		ctrl |= SDHCI_CTRL_8BITBUS;
+	else
+		ctrl &= ~SDHCI_CTRL_8BITBUS;
+
 	if (ios->bus_width == MMC_BUS_WIDTH_4)
 		ctrl |= SDHCI_CTRL_4BITBUS;
 	else
drivers/mmc/host/sdhci.h +1 -0
index c846813..eb5efe0 100644
@@ -72,6 +72,7 @@
 #define   SDHCI_CTRL_ADMA1	0x08
 #define   SDHCI_CTRL_ADMA32	0x10
 #define   SDHCI_CTRL_ADMA64	0x18
+#define  SDHCI_CTRL_8BITBUS	0x20
 
 #define SDHCI_POWER_CONTROL	0x29
 #define  SDHCI_POWER_ON		0x01
Signature · 1 line
1.5.3.3
#1 lore raw
Quoted 31 lines · On Sat, 12 Jun 2010 14:45:02 +0900 Kyungmin Park <[email protected]>…

From: Kyungmin Park <[email protected]>

Some host constroller such as s5pc110 has WIDE8 support feature.

---
 drivers/mmc/host/sdhci.c |    5 +++++
 drivers/mmc/host/sdhci.h |    1 +
 2 files changed, 6 insertions(+), 0 deletions(-)
drivers/mmc/host/sdhci.c +5 -0
index 142419c..6cf018a 100644
@@ -1159,6 +1159,11 @@ static void sdhci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
 
 	ctrl = sdhci_readb(host, SDHCI_HOST_CONTROL);
 
+	if (ios->bus_width == MMC_BUS_WIDTH_8)
+		ctrl |= SDHCI_CTRL_8BITBUS;
+	else
+		ctrl &= ~SDHCI_CTRL_8BITBUS;
+
 	if (ios->bus_width == MMC_BUS_WIDTH_4)
 		ctrl |= SDHCI_CTRL_4BITBUS;
 	else
drivers/mmc/host/sdhci.h +0 -0
index c846813..eb5efe0 100644

drivers/mmc/host/sdhci.h line 78

Quoted 7 lines
drivers/mmc/host/sdhci.h +1 -0
@@ -72,6 +72,7 @@
 #define   SDHCI_CTRL_ADMA1	0x08
 #define   SDHCI_CTRL_ADMA32	0x10
 #define   SDHCI_CTRL_ADMA64	0x18
+#define  SDHCI_CTRL_8BITBUS	0x20
 
 #define SDHCI_POWER_CONTROL	0x29
 #define  SDHCI_POWER_ON		0x01

This change (or something similar) also seems to have been lumped into the unchangelogged, unreviewed "mmc: sdhci: Initial Tegra sdhci driver" patch. So again, I'll drop your patch and if "mmc: sdhci: Initial Tegra sdhci driver" doesn't get merged, your patch will be lost.

I wonder what else it does. Here it is:

commit feed6702dc4bb130869171cbd8167637ea13c33c

Author:     Colin Cross <[email protected]>
AuthorDate: Wed Mar 10 20:42:35 2010 -0800
Commit:     Grant Likely <[email protected]>
CommitDate: Fri Jun 25 09:47:58 2010 -0600

mmc: sdhci: Initial Tegra sdhci driver

[Olof: Fixed up merge conflicts]
arch/arm/mach-tegra/include/mach/sdhci.h new file +33 -0
new file mode 100644
index 0000000..34e2686
@@ -0,0 +1,33 @@
+/*
+ * include/asm-arm/arch-tegra/sdhci.h
+ *
+ * Copyright (C) 2009 Palm, Inc.
+ * Author: Yvonne Yip <[email protected]>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+#ifndef __ASM_ARM_ARCH_TEGRA_SDHCI_H
+#define __ASM_ARM_ARCH_TEGRA_SDHCI_H
+
+#include <linux/mmc/host.h>
+
+struct tegra_sdhci_platform_data {
+	const char *clk_id;
+	int force_hs;
+	int cd_gpio;
+	int wp_gpio;
+	int power_gpio;
+
+	void (*board_probe)(int id, struct mmc_host *);
+	void (*board_remove)(int id, struct mmc_host *);
+};
+
+#endif
drivers/mmc/core/sdio.c +6 -4
index b9dee28..85c473e 100644
@@ -229,11 +229,13 @@ static int sdio_enable_hs(struct mmc_card *card)
 	int ret;
 	u8 speed;
 
-	if (!(card->host->caps & MMC_CAP_SD_HIGHSPEED))
-		return 0;
+	if (!(card->host->caps & MMC_CAP_FORCE_HS)) {
+		if (!(card->host->caps & MMC_CAP_SD_HIGHSPEED))
+			return 0;
 
-	if (!card->cccr.high_speed)
-		return 0;
+		if (!card->cccr.high_speed)
+			return 0;
+	}
 
 	ret = mmc_io_rw_direct(card, 0, 0, SDIO_CCCR_SPEED, 0, &speed);
 	if (ret)
drivers/mmc/host/Kconfig +6 -0
index f06d06e..357c294 100644
@@ -382,6 +382,12 @@ config MMC_TMIO
 	  This provides support for the SD/MMC cell found in TC6393XB,
 	  T7L66XB and also HTC ASIC3
 
+config MMC_SDHCI_TEGRA
+	tristate "Tegra SD/MMC Controller Support"
+	depends on ARCH_TEGRA && MMC_SDHCI
+	help
+	  This selects the Tegra SD/MMC controller.
+
 config MMC_CB710
 	tristate "ENE CB710 MMC/SD Interface support"
 	depends on PCI
drivers/mmc/host/Makefile +1 -0
index e30c2ee..fb04448 100644
@@ -15,6 +15,7 @@ obj-$(CONFIG_MMC_SDHCI_PCI)	+= sdhci-pci.o
 obj-$(CONFIG_MMC_SDHCI_PLTFM)	+= sdhci-pltfm.o
 obj-$(CONFIG_MMC_SDHCI_S3C)	+= sdhci-s3c.o
 obj-$(CONFIG_MMC_SDHCI_SPEAR)	+= sdhci-spear.o
+obj-$(CONFIG_MMC_SDHCI_TEGRA)	+= sdhci-tegra.o
 obj-$(CONFIG_MMC_WBSD)		+= wbsd.o
 obj-$(CONFIG_MMC_AU1X)		+= au1xmmc.o
 obj-$(CONFIG_MMC_OMAP)		+= omap.o
drivers/mmc/host/sdhci-tegra.c new file +217 -0

217 lines not on this page · raw message

drivers/mmc/host/sdhci.c +77 -31

191 lines not on this page · raw message

drivers/mmc/host/sdhci.h +20 -1
index c846813..2c8a83a 100644
@@ -66,6 +66,7 @@
 #define SDHCI_HOST_CONTROL 	0x28
 #define  SDHCI_CTRL_LED		0x01
 #define  SDHCI_CTRL_4BITBUS	0x02
+#define  SDHCI_CTRL_8BITBUS	0x20
 #define  SDHCI_CTRL_HISPD	0x04
 #define  SDHCI_CTRL_DMA_MASK	0x18
 #define   SDHCI_CTRL_SDMA	0x00
@@ -184,7 +185,7 @@ struct sdhci_host {
 	/* Data set by hardware interface driver */
 	const char		*hw_name;	/* Hardware bus name */
 
-	unsigned int		quirks;		/* Deviations from spec. */
+	u64			quirks;		/* Deviations from spec. */
 
 /* Controller doesn't honor resets unless we touch the clock register */
 #define SDHCI_QUIRK_CLOCK_BEFORE_RESET			(1<<0)
@@ -240,6 +241,22 @@ struct sdhci_host {
 #define SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN		(1<<25)
 /* Controller cannot support End Attribute in NOP ADMA descriptor */
 #define SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC		(1<<26)
+/* Controller write protect bit is broken. Assume no write protection */
+#define SDHCI_QUIRK_BROKEN_WRITE_PROTECT		(1<<27)
+/* Controller needs INTERRUPT_AT_BLOCK_GAP enabled to detect card interrupts */
+#define SDHCI_QUIRK_ENABLE_INTERRUPT_AT_BLOCK_GAP	(1<<28)
+/* Controller should not program HIGH_SPEED_EN after switching to high speed */
+#define SDHCI_QUIRK_BROKEN_CTRL_HISPD			(1<<29)
+/* Controller supports 8-bit data width */
+#define SDHCI_QUIRK_8_BIT_DATA				(1<<30)
+/* Controller has no version register */
+#define SDHCI_QUIRK_NO_VERSION_REG			(1<<31)
+/* Controller treats ADMA descriptors with length 0000h incorrectly */
+#define SDHCI_QUIRK_BROKEN_ADMA_ZEROLEN_DESC		(1LL<<32)
+/* Controller should not use SDIO IRQ */
+#define SDHCI_QUIRK_NO_SDIO_IRQ				(1LL<<33)
+/* Controller should only use high-speed mode */
+#define SDHCI_QUIRK_FORCE_HIGH_SPEED_MODE		(1LL<<34)
 
 	int			irq;		/* Device IRQ */
 	void __iomem *		ioaddr;		/* Mapped address */
@@ -309,6 +326,7 @@ struct sdhci_ops {
 	void	(*set_clock)(struct sdhci_host *host, unsigned int clock);
 
 	int		(*enable_dma)(struct sdhci_host *host);
+	int		(*get_ro)(struct sdhci_host *host);
 	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);
@@ -401,6 +419,7 @@ static inline u8 sdhci_readb(struct sdhci_host *host, int reg)
 extern struct sdhci_host *sdhci_alloc_host(struct device *dev,
 	size_t priv_size);
 extern void sdhci_free_host(struct sdhci_host *host);
+extern void sdhci_card_detect_callback(struct sdhci_host *host);
 
 static inline void *sdhci_priv(struct sdhci_host *host)
 {
include/linux/mmc/host.h +1 -0
index f65913c..9ab146a 100644
@@ -155,6 +155,7 @@ struct mmc_host {
 #define MMC_CAP_DISABLE		(1 << 7)	/* Can the host be disabled */
 #define MMC_CAP_NONREMOVABLE	(1 << 8)	/* Nonremovable e.g. eMMC */
 #define MMC_CAP_WAIT_WHILE_BUSY	(1 << 9)	/* Waits while card is busy */
+#define MMC_CAP_FORCE_HS	(1 << 10)	/* Must enable highspeed mode */
 
 	mmc_pm_flag_t		pm_caps;	/* supported pm features */
 
#2 lore raw

These patches are not coming from the tegra/for-next branch, they are coming from Grant's devicetree-next branch. Grant, why are these patches in your tree, and why is tegra/for-next in your tree? It's going to cause conflicts when we rebase our for-next branch. Please remove tegra and this sdhci patch from your tree.

As for the mmc patch, it seems to have been over-squashed to contain the changes to the common code and the tegra sdhci driver. Now that tegra is in for-next, we'll be cleaning up and sending subsystem drivers for review. Expect a patch within a week or two.

Quoted 655 lines · Andrew Morton
Quoted 31 lines · On Sat, 12 Jun 2010 14:45:02 +0900 Kyungmin Park <[email protected]>…

From: Kyungmin Park <[email protected]>

Some host constroller such as s5pc110 has WIDE8 support feature.

---
 drivers/mmc/host/sdhci.c |    5 +++++
 drivers/mmc/host/sdhci.h |    1 +

 2 files changed, 6 insertions(+), 0 deletions(-)
drivers/mmc/host/sdhci.c +5 -0
index 142419c..6cf018a 100644
@@ -1159,6 +1159,11 @@ static void sdhci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
 
       ctrl = sdhci_readb(host, SDHCI_HOST_CONTROL);
 
+     if (ios->bus_width == MMC_BUS_WIDTH_8)
+             ctrl |= SDHCI_CTRL_8BITBUS;
+     else
+             ctrl &= ~SDHCI_CTRL_8BITBUS;
+
       if (ios->bus_width == MMC_BUS_WIDTH_4)
               ctrl |= SDHCI_CTRL_4BITBUS;

      else

drivers/mmc/host/sdhci.h +0 -0
index c846813..eb5efe0 100644

drivers/mmc/host/sdhci.h line 78

Quoted 7 lines
drivers/mmc/host/sdhci.h +1 -0
@@ -72,6 +72,7 @@
  #define   SDHCI_CTRL_ADMA1   0x08
  #define   SDHCI_CTRL_ADMA32  0x10
  #define   SDHCI_CTRL_ADMA64  0x18
+#define  SDHCI_CTRL_8BITBUS  0x20
 
  #define SDHCI_POWER_CONTROL  0x29
  #define  SDHCI_POWER_ON              0x01

This change (or something similar) also seems to have been lumped into the unchangelogged, unreviewed "mmc: sdhci: Initial Tegra sdhci driver" patch.  So again, I'll drop your patch and if "mmc: sdhci: Initial Tegra sdhci driver" doesn't get merged, your patch will be lost.

I wonder what else it does.  Here it is:

commit feed6702dc4bb130869171cbd8167637ea13c33c
Author:     Colin Cross <[email protected]>
AuthorDate: Wed Mar 10 20:42:35 2010 -0800
Commit:     Grant Likely <[email protected]>
CommitDate: Fri Jun 25 09:47:58 2010 -0600

   mmc: sdhci: Initial Tegra sdhci driver

   [Olof: Fixed up merge conflicts]
arch/arm/mach-tegra/include/mach/sdhci.h new file +33 -0
new file mode 100644
index 0000000..34e2686
@@ -0,0 +1,33 @@
+/*
+ * include/asm-arm/arch-tegra/sdhci.h
+ *
+ * Copyright (C) 2009 Palm, Inc.
+ * Author: Yvonne Yip <[email protected]>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+#ifndef __ASM_ARM_ARCH_TEGRA_SDHCI_H
+#define __ASM_ARM_ARCH_TEGRA_SDHCI_H
+
+#include <linux/mmc/host.h>
+
+struct tegra_sdhci_platform_data {
+       const char *clk_id;
+       int force_hs;
+       int cd_gpio;
+       int wp_gpio;
+       int power_gpio;
+
+       void (*board_probe)(int id, struct mmc_host *);
+       void (*board_remove)(int id, struct mmc_host *);
+};
+
+#endif
drivers/mmc/core/sdio.c +6 -4
index b9dee28..85c473e 100644
@@ -229,11 +229,13 @@ static int sdio_enable_hs(struct mmc_card *card)
        int ret;
        u8 speed;
 
-       if (!(card->host->caps & MMC_CAP_SD_HIGHSPEED))
-               return 0;
+       if (!(card->host->caps & MMC_CAP_FORCE_HS)) {
+               if (!(card->host->caps & MMC_CAP_SD_HIGHSPEED))
+                       return 0;
 
-       if (!card->cccr.high_speed)
-               return 0;
+               if (!card->cccr.high_speed)
+                       return 0;
+       }
 
        ret = mmc_io_rw_direct(card, 0, 0, SDIO_CCCR_SPEED, 0, &speed);
        if (ret)
drivers/mmc/host/Kconfig +0 -0
index f06d06e..357c294 100644
@@ -382,6 +382,12 @@ config MMC_TMIO
          This provides support for the SD/MMC cell found in TC6393XB,

         T7L66XB and also HTC ASIC3

drivers/mmc/host/Kconfig +6 -0
+config MMC_SDHCI_TEGRA
+       tristate "Tegra SD/MMC Controller Support"
+       depends on ARCH_TEGRA && MMC_SDHCI
+       help
+         This selects the Tegra SD/MMC controller.
+

 config MMC_CB710
       tristate "ENE CB710 MMC/SD Interface support" depends on PCI

drivers/mmc/host/Makefile +0 -0
index e30c2ee..fb04448 100644
@@ -15,6 +15,7 @@ obj-$(CONFIG_MMC_SDHCI_PCI)   += sdhci-pci.o

 obj-$(CONFIG_MMC_SDHCI_PLTFM)  += sdhci-pltfm.o
 obj-$(CONFIG_MMC_SDHCI_S3C)    += sdhci-s3c.o
 obj-$(CONFIG_MMC_SDHCI_SPEAR)  += sdhci-spear.o
+obj-$(CONFIG_MMC_SDHCI_TEGRA)  += sdhci-tegra.o
 obj-$(CONFIG_MMC_WBSD)         += wbsd.o
 obj-$(CONFIG_MMC_AU1X)         += au1xmmc.o
 obj-$(CONFIG_MMC_OMAP)         += omap.o

drivers/mmc/host/sdhci-tegra.c new file +217 -0
new file mode 100644
index 0000000..5e2a1f1
@@ -0,0 +1,217 @@
+/*
+ * drivers/mmc/host/sdhci-tegra.c
+ *
+ * Copyright (C) 2009 Palm, Inc.
+ * Author: Yvonne Yip <[email protected]>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/err.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <linux/gpio.h>
+
+#include <mach/sdhci.h>
+
+#include "sdhci.h"
+
+#define DRIVER_NAME    "sdhci-tegra"
+
+struct tegra_sdhci_host {
+       struct sdhci_host *sdhci;
+       struct clk *clk;
+};
+
+static irqreturn_t carddetect_irq(int irq, void *data)
+{
+       struct sdhci_host *sdhost = (struct sdhci_host *)data;
+
+       sdhci_card_detect_callback(sdhost);
+       return IRQ_HANDLED;
+};
+
+static int tegra_sdhci_enable_dma(struct sdhci_host *host)
+{
+       return 0;
+}
+
+static void tegra_sdhci_set_clock(struct sdhci_host *host, unsigned int clock)
+{
+       struct tegra_sdhci_host *tegra_host = sdhci_priv(host);
+       host->max_clk = clk_get_rate(tegra_host->clk);
+}
+
+static struct sdhci_ops tegra_sdhci_ops = {
+       .enable_dma = tegra_sdhci_enable_dma,
+       .set_clock  = tegra_sdhci_set_clock,
+};
+
+static int __devinit tegra_sdhci_probe(struct platform_device *pdev)
+{
+       int rc;
+       struct tegra_sdhci_platform_data *plat;
+       struct sdhci_host *sdhci;
+       struct tegra_sdhci_host *host;
+       struct resource *res;
+       int irq;
+       void __iomem *ioaddr;
+
+       plat = pdev->dev.platform_data;
+       if (plat == NULL)
+               return -ENXIO;
+
+       res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+       if (res == NULL)
+               return -ENODEV;
+
+       irq = res->start;
+
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       if (res == NULL)
+               return -ENODEV;
+
+       ioaddr = ioremap(res->start, res->end - res->start);
+
+       sdhci = sdhci_alloc_host(&pdev->dev, sizeof(struct tegra_sdhci_host));
+       if (IS_ERR(sdhci)) {
+               rc = PTR_ERR(sdhci);
+               goto err_unmap;
+       }
+
+       host = sdhci_priv(sdhci);
+       host->sdhci = sdhci;
+
+       host->clk = clk_get(&pdev->dev, plat->clk_id);
+       if (IS_ERR(host->clk)) {
+               rc = PTR_ERR(host->clk);
+               goto err_free_host;
+       }
+
+       rc = clk_enable(host->clk);
+       if (rc != 0)
+               goto err_clkput;
+
+       sdhci->hw_name = "tegra";
+       sdhci->ops = &tegra_sdhci_ops;
+       sdhci->irq = irq;
+       sdhci->ioaddr = ioaddr;
+       sdhci->version = SDHCI_SPEC_200;
+       sdhci->quirks = SDHCI_QUIRK_BROKEN_TIMEOUT_VAL |
+                       SDHCI_QUIRK_SINGLE_POWER_WRITE |
+                       SDHCI_QUIRK_ENABLE_INTERRUPT_AT_BLOCK_GAP |
+                       SDHCI_QUIRK_BROKEN_WRITE_PROTECT |
+                       SDHCI_QUIRK_BROKEN_CTRL_HISPD |
+                       SDHCI_QUIRK_8_BIT_DATA |
+                       SDHCI_QUIRK_NO_VERSION_REG |
+                       SDHCI_QUIRK_BROKEN_ADMA_ZEROLEN_DESC |
+                       SDHCI_QUIRK_NO_SDIO_IRQ;
+
+       if (plat->force_hs != 0)
+               sdhci->quirks |= SDHCI_QUIRK_FORCE_HIGH_SPEED_MODE;
+
+       rc = sdhci_add_host(sdhci);
+       if (rc)
+               goto err_clk_disable;
+
+       platform_set_drvdata(pdev, host);
+
+       if (plat->cd_gpio) {
+               rc = request_irq(gpio_to_irq(plat->cd_gpio), carddetect_irq,
+                       IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING,
+                       mmc_hostname(sdhci->mmc), sdhci);
+
+               if (rc)
+                       goto err_remove_host;
+       }
+
+       if (plat->board_probe)
+               plat->board_probe(pdev->id, sdhci->mmc);
+
+       printk(KERN_INFO "sdhci%d: initialized irq %d ioaddr %p\n", pdev->id,
+                       sdhci->irq, sdhci->ioaddr);
+
+       return 0;
+
+err_remove_host:
+       sdhci_remove_host(sdhci, 1);
+err_clk_disable:
+       clk_disable(host->clk);
+err_clkput:
+       clk_put(host->clk);
+err_free_host:
+       if (sdhci)
+               sdhci_free_host(sdhci);
+err_unmap:
+       iounmap(sdhci->ioaddr);
+
+       return rc;
+}
+
+static int tegra_sdhci_remove(struct platform_device *pdev)
+{
+       struct tegra_sdhci_host *host = platform_get_drvdata(pdev);
+       if (host) {
+               struct tegra_sdhci_platform_data *plat;
+               plat = pdev->dev.platform_data;
+               if (plat && plat->board_probe)
+                       plat->board_probe(pdev->id, host->sdhci->mmc);
+
+               sdhci_remove_host(host->sdhci, 0);
+               sdhci_free_host(host->sdhci);
+       }
+       return 0;
+}
+
+#ifdef CONFIG_PM
+static int tegra_sdhci_suspend(struct platform_device *pdev, pm_message_t state)
+{
+       return -1;
+}
+
+static int tegra_sdhci_resume(struct platform_device *pdev)
+{
+       return -1;
+}
+#else
+#define tegra_sdhci_suspend    NULL
+#define tegra_sdhci_resume     NULL
+#endif
+
+static struct platform_driver tegra_sdhci_driver = {
+       .probe = tegra_sdhci_probe,
+       .remove = tegra_sdhci_remove,
+       .suspend = tegra_sdhci_suspend,
+       .resume = tegra_sdhci_resume,
+       .driver = {
+               .name = DRIVER_NAME,
+               .owner = THIS_MODULE,
+       },
+};
+
+static int __init tegra_sdhci_init(void)
+{
+       return platform_driver_register(&tegra_sdhci_driver);
+}
+
+static void __exit tegra_sdhci_exit(void)
+{
+       platform_driver_unregister(&tegra_sdhci_driver);
+}
+
+module_init(tegra_sdhci_init);
+module_exit(tegra_sdhci_exit);
+
+MODULE_DESCRIPTION("Tegra SDHCI controller driver");
+MODULE_LICENSE("GPL");
drivers/mmc/host/sdhci.c +0 -0
index c6d1bd8..4af24d6 100644
@@ -1024,6 +1024,7 @@ static void sdhci_set_clock(struct sdhci_host *host, unsigned int clock)
        sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL);
 

 out:
+
       host->clock = clock;
 }

drivers/mmc/host/sdhci.c +19 -10
@@ -1159,15 +1160,18 @@ static void sdhci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
 
        ctrl = sdhci_readb(host, SDHCI_HOST_CONTROL);
 
-       if (ios->bus_width == MMC_BUS_WIDTH_4)
+       ctrl &= ~(SDHCI_CTRL_8BITBUS|SDHCI_CTRL_4BITBUS);
+       if (ios->bus_width == MMC_BUS_WIDTH_8)
+               ctrl |= SDHCI_CTRL_8BITBUS;
+       else if (ios->bus_width == MMC_BUS_WIDTH_4)
                ctrl |= SDHCI_CTRL_4BITBUS;
-       else
-               ctrl &= ~SDHCI_CTRL_4BITBUS;
 
-       if (ios->timing == MMC_TIMING_SD_HS)
-               ctrl |= SDHCI_CTRL_HISPD;
-       else
-               ctrl &= ~SDHCI_CTRL_HISPD;
+       if (!(host->quirks & SDHCI_QUIRK_BROKEN_CTRL_HISPD)) {
+               if (ios->timing == MMC_TIMING_SD_HS)
+                       ctrl |= SDHCI_CTRL_HISPD;
+               else
+                       ctrl &= ~SDHCI_CTRL_HISPD;
+       }
 
        sdhci_writeb(host, ctrl, SDHCI_HOST_CONTROL);
 
@@ -1194,16 +1198,22 @@ static int sdhci_get_ro(struct mmc_host *mmc)
 
        spin_lock_irqsave(&host->lock, flags);
 
-       if (host->flags & SDHCI_DEVICE_DEAD)
+       if (host->flags & SDHCI_DEVICE_DEAD) {
                present = 0;
-       else
+       } else if (!(host->quirks & SDHCI_QUIRK_BROKEN_WRITE_PROTECT)) {
                present = sdhci_readl(host, SDHCI_PRESENT_STATE);
+               present = !(present & SDHCI_WRITE_PROTECT);
+       } else if (host->ops->get_ro) {
+               present = host->ops->get_ro(host);
+       } else {
+               present = 0;
+       }
 
        spin_unlock_irqrestore(&host->lock, flags);
 
        if (host->quirks & SDHCI_QUIRK_INVERTED_WRITE_PROTECT)
                return !!(present & SDHCI_WRITE_PROTECT);
-       return !(present & SDHCI_WRITE_PROTECT);
+       return present;
  }
 
  static void sdhci_enable_sdio_irq(struct mmc_host *mmc, int enable)
@@ -1222,6 +1232,16 @@ static void sdhci_enable_sdio_irq(struct mmc_host *mmc, int enable)
                sdhci_unmask_irqs(host, SDHCI_INT_CARD_INT);

       else
               sdhci_mask_irqs(host, SDHCI_INT_CARD_INT);

drivers/mmc/host/sdhci.c +10 -0
+
+       if (host->quirks & SDHCI_QUIRK_ENABLE_INTERRUPT_AT_BLOCK_GAP) {
+               u8 gap_ctrl = readb(host->ioaddr + SDHCI_BLOCK_GAP_CONTROL);
+               if (enable)
+                       gap_ctrl |= 0x8;
+               else
+                       gap_ctrl &= ~0x8;
+               writeb(gap_ctrl, host->ioaddr + SDHCI_BLOCK_GAP_CONTROL);
+       }
+

 out:
       mmiowb();

drivers/mmc/host/sdhci.c +25 -14
@@ -1235,19 +1255,10 @@ static const struct mmc_host_ops sdhci_ops = {
        .enable_sdio_irq = sdhci_enable_sdio_irq,
  };
 
-/*****************************************************************************\
- *                                                                           *
- * Tasklets                                                                  *
- *                                                                           *
-\*****************************************************************************/
-
-static void sdhci_tasklet_card(unsigned long param)
+void sdhci_card_detect_callback(struct sdhci_host *host)
  {
-       struct sdhci_host *host;
        unsigned long flags;
 
-       host = (struct sdhci_host*)param;
-
        spin_lock_irqsave(&host->lock, flags);
 
        if (!(sdhci_readl(host, SDHCI_PRESENT_STATE) & SDHCI_CARD_PRESENT)) {
@@ -1269,6 +1280,22 @@ static void sdhci_tasklet_card(unsigned long param)
 
        mmc_detect_change(host->mmc, msecs_to_jiffies(200));
  }
+EXPORT_SYMBOL_GPL(sdhci_card_detect_callback);
+
+/*****************************************************************************\
+ *                                                                           *
+ * Tasklets                                                                  *
+ *                                                                           *
+\*****************************************************************************/
+
+static void sdhci_tasklet_card(unsigned long param)
+{
+       struct sdhci_host *host;
+
+       host = (struct sdhci_host *)param;
+
+       sdhci_card_detect_callback(host);
+}
 
  static void sdhci_tasklet_finish(unsigned long param)
  {
@@ -1380,7 +1407,8 @@ static void sdhci_cmd_irq(struct sdhci_host *host, u32 intmask)
                host->cmd->error = -EILSEQ;
 
        if (host->cmd->error) {
-               tasklet_schedule(&host->finish_tasklet);
+               if (intmask & SDHCI_INT_RESPONSE)
+                       tasklet_schedule(&host->finish_tasklet);
                return;
        }
 
@@ -1678,9 +1706,12 @@ int sdhci_add_host(struct sdhci_host *host)
 
        sdhci_reset(host, SDHCI_RESET_ALL);
 
-       host->version = sdhci_readw(host, SDHCI_HOST_VERSION);
-       host->version = (host->version & SDHCI_SPEC_VER_MASK)
-                               >> SDHCI_SPEC_VER_SHIFT;
+       if (!(host->quirks & SDHCI_QUIRK_NO_VERSION_REG)) {
+               host->version = sdhci_readw(host, SDHCI_HOST_VERSION);
+               host->version = (host->version & SDHCI_SPEC_VER_MASK)
+                                       >> SDHCI_SPEC_VER_SHIFT;
+       }
+
        if (host->version > SDHCI_SPEC_200) {

               printk(KERN_ERR "%s: Unknown controller version (%d). " "You may experience problems.\n", mmc_hostname(mmc),

drivers/mmc/host/sdhci.c +0 -0
@@ -1791,13 +1822,24 @@ int sdhci_add_host(struct sdhci_host *host)

       else
               mmc->f_min = host->max_clk / 256;
       mmc->f_max = host->max_clk;
-       mmc->caps = MMC_CAP_SDIO_IRQ;
+       mmc->caps = 0;

-       if (!(host->quirks & SDHCI_QUIRK_FORCE_1_BIT_DATA))
+       if (host->quirks & SDHCI_QUIRK_8_BIT_DATA)
+               mmc->caps |= MMC_CAP_8_BIT_DATA;
+       else if (!(host->quirks & SDHCI_QUIRK_FORCE_1_BIT_DATA)) mmc->caps |= MMC_CAP_4_BIT_DATA;

-       if (caps & SDHCI_CAN_DO_HISPD)

drivers/mmc/host/sdhci.c +5 -0
+
+       if (!(host->quirks & SDHCI_QUIRK_NO_SDIO_IRQ))
+               mmc->caps |= MMC_CAP_SDIO_IRQ;
+
+       if (caps & SDHCI_CAN_DO_HISPD) {

               mmc->caps |= MMC_CAP_SD_HIGHSPEED;
+               mmc->caps |= MMC_CAP_MMC_HIGHSPEED;
+       }
+
+       if (host->quirks & SDHCI_QUIRK_FORCE_HIGH_SPEED_MODE)
+               mmc->caps |= MMC_CAP_FORCE_HS;

       if (host->quirks & SDHCI_QUIRK_BROKEN_CARD_DETECTION) mmc->caps |= MMC_CAP_NEEDS_POLL;

drivers/mmc/host/sdhci.c +8 -4
@@ -1841,10 +1883,14 @@ int sdhci_add_host(struct sdhci_host *host)
         * of bytes. When doing hardware scatter/gather, each entry cannot
         * be larger than 64 KiB though.
         */
-       if (host->flags & SDHCI_USE_ADMA)
-               mmc->max_seg_size = 65536;
-       else
+       if (host->flags & SDHCI_USE_ADMA) {
+               if (host->quirks & SDHCI_QUIRK_BROKEN_ADMA_ZEROLEN_DESC)
+                       mmc->max_seg_size = 0xffff;
+               else
+                       mmc->max_seg_size = 65536;
+       } else {
                mmc->max_seg_size = mmc->max_req_size;
+       }
 
        /*
         * Maximum block size. This varies from controller to controller and
@@ -1868,7 +1914,7 @@ int sdhci_add_host(struct sdhci_host *host)
         * Maximum block count.
         */
        mmc->max_blk_count = (host->quirks & SDHCI_QUIRK_NO_MULTIBLOCK) ? 1 : 65535;
-
+
        /*
         * Init tasklets.
         */
drivers/mmc/host/sdhci.h +1 -0
index c846813..2c8a83a 100644
@@ -66,6 +66,7 @@
  #define SDHCI_HOST_CONTROL     0x28
  #define  SDHCI_CTRL_LED                0x01
  #define  SDHCI_CTRL_4BITBUS    0x02
+#define  SDHCI_CTRL_8BITBUS    0x20
  #define  SDHCI_CTRL_HISPD      0x04
  #define  SDHCI_CTRL_DMA_MASK   0x18
  #define   SDHCI_CTRL_SDMA      0x00
@@ -184,7 +185,7 @@ struct sdhci_host {
        /* Data set by hardware interface driver */

       const char              *hw_name;       /* Hardware bus name */

-       unsigned int            quirks;         /* Deviations from spec. */
+       u64                     quirks;         /* Deviations from spec. */

 /* Controller doesn't honor resets unless we touch the clock register */ #define SDHCI_QUIRK_CLOCK_BEFORE_RESET                 (1<<0)

drivers/mmc/host/sdhci.h +16 -0
@@ -240,6 +241,22 @@ struct sdhci_host {
  #define SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN              (1<<25)
  /* Controller cannot support End Attribute in NOP ADMA descriptor */
  #define SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC              (1<<26)
+/* Controller write protect bit is broken. Assume no write protection */
+#define SDHCI_QUIRK_BROKEN_WRITE_PROTECT               (1<<27)
+/* Controller needs INTERRUPT_AT_BLOCK_GAP enabled to detect card interrupts */
+#define SDHCI_QUIRK_ENABLE_INTERRUPT_AT_BLOCK_GAP      (1<<28)
+/* Controller should not program HIGH_SPEED_EN after switching to high speed */
+#define SDHCI_QUIRK_BROKEN_CTRL_HISPD                  (1<<29)
+/* Controller supports 8-bit data width */
+#define SDHCI_QUIRK_8_BIT_DATA                         (1<<30)
+/* Controller has no version register */
+#define SDHCI_QUIRK_NO_VERSION_REG                     (1<<31)
+/* Controller treats ADMA descriptors with length 0000h incorrectly */
+#define SDHCI_QUIRK_BROKEN_ADMA_ZEROLEN_DESC           (1LL<<32)
+/* Controller should not use SDIO IRQ */
+#define SDHCI_QUIRK_NO_SDIO_IRQ                                (1LL<<33)
+/* Controller should only use high-speed mode */
+#define SDHCI_QUIRK_FORCE_HIGH_SPEED_MODE              (1LL<<34)
 

       int                     irq;            /* Device IRQ */ void __iomem *          ioaddr;         /* Mapped address */

drivers/mmc/host/sdhci.h +2 -0
@@ -309,6 +326,7 @@ struct sdhci_ops {
        void    (*set_clock)(struct sdhci_host *host, unsigned int clock);
 
        int             (*enable_dma)(struct sdhci_host *host);
+       int             (*get_ro)(struct sdhci_host *host);
        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);
@@ -401,6 +419,7 @@ static inline u8 sdhci_readb(struct sdhci_host *host, int reg)
  extern struct sdhci_host *sdhci_alloc_host(struct device *dev,
        size_t priv_size);
  extern void sdhci_free_host(struct sdhci_host *host);
+extern void sdhci_card_detect_callback(struct sdhci_host *host);
 
  static inline void *sdhci_priv(struct sdhci_host *host)
  {
include/linux/mmc/host.h +1 -0
index f65913c..9ab146a 100644
@@ -155,6 +155,7 @@ struct mmc_host {
  #define MMC_CAP_DISABLE                (1 << 7)        /* Can the host be disabled */
  #define MMC_CAP_NONREMOVABLE   (1 << 8)        /* Nonremovable e.g. eMMC */
  #define MMC_CAP_WAIT_WHILE_BUSY        (1 << 9)        /* Waits while card is busy */
+#define MMC_CAP_FORCE_HS       (1 << 10)       /* Must enable highspeed mode */
 

       mmc_pm_flag_t           pm_caps;        /* supported pm features */

#3 lore raw
Quoted 5 lines · Colin Cross

These patches are not coming from the tegra/for-next branch, they are coming from Grant's devicetree-next branch. Grant, why are these patches in your tree, and why is tegra/for-next in your tree? It's going to cause conflicts when we rebase our for-next branch. Please remove tegra and this sdhci patch from your tree.

Yeah, this seems to be a mixup by Grant. I gave him a few patches I had picked up locally to work with, and he seems to have published the work accidentally.

-Olof

#4 lore raw
Quoted 10 lines · Olof Johansson
Quoted 5 lines · Colin Cross

These patches are not coming from the tegra/for-next branch, they are coming from Grant's devicetree-next branch.  Grant, why are these patches in your tree, and why is tegra/for-next in your tree?  It's going to cause conflicts when we rebase our for-next branch.  Please remove tegra and this sdhci patch from your tree.

Yeah, this seems to be a mixup by Grant. I gave him a few patches I had picked up locally to work with, and he seems to have published the work accidentally.

Hi Colin,

I've fixed it now. Profuse apologies, sorry for the noise, and I owe you a beer or 3.

g.

#5 lore raw
Quoted 8 lines · On Mon, 28 Jun 2010 12:48:29 -0700 Grant Likely <[email protected]>…
Quoted 7 lines · Olof Johansson
Quoted 5 lines · Colin Cross

These patches are not coming from the tegra/for-next branch, they are coming from Grant's devicetree-next branch. __Grant, why are these patches in your tree, and why is tegra/for-next in your tree? __It's going to cause conflicts when we rebase our for-next branch. __Please remove tegra and this sdhci patch from your tree.

Quoted 8 lines
Quoted 3 lines

Yeah, this seems to be a mixup by Grant. I gave him a few patches I had picked up locally to work with, and he seems to have published the work accidentally.

Hi Colin,

I've fixed it now. Profuse apologies, sorry for the noise, and I owe you a beer or 3.

Thanks, guys - I resurrected sdhci-8-bit-data-transfer-width-support.patch and sdhci-dont-assign-mmc-caps-at-sdhci-directly.patch