* [PATCH 3/3] SDHCI: 8-bit data transfer width support
@ 2010-06-12 5:45 Kyungmin Park
2010-06-28 18:39 ` Andrew Morton
0 siblings, 1 reply; 6+ messages in thread
From: Kyungmin Park @ 2010-06-12 5:45 UTC (permalink / raw)
To: linux-mmc, akpm
From: Kyungmin Park <kyungmin.park@samsung.com>
Some host constroller such as s5pc110 has WIDE8 support feature.
Signed-off-by: Kyungmin Park <kyungmin.park@samsung.com>
---
drivers/mmc/host/sdhci.c | 5 +++++
drivers/mmc/host/sdhci.h | 1 +
2 files changed, 6 insertions(+), 0 deletions(-)
diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c
index 142419c..6cf018a 100644
--- a/drivers/mmc/host/sdhci.c
+++ b/drivers/mmc/host/sdhci.c
@@ -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
diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h
index c846813..eb5efe0 100644
--- a/drivers/mmc/host/sdhci.h
+++ b/drivers/mmc/host/sdhci.h
@@ -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
--
1.5.3.3
^ permalink raw reply related [flat|nested] 6+ messages in thread
* Re: [PATCH 3/3] SDHCI: 8-bit data transfer width support
2010-06-12 5:45 [PATCH 3/3] SDHCI: 8-bit data transfer width support Kyungmin Park
@ 2010-06-28 18:39 ` Andrew Morton
2010-06-28 18:56 ` Colin Cross
0 siblings, 1 reply; 6+ messages in thread
From: Andrew Morton @ 2010-06-28 18:39 UTC (permalink / raw)
To: Kyungmin Park; +Cc: linux-mmc, Colin Cross, Olof Johansson, Grant Likely
On Sat, 12 Jun 2010 14:45:02 +0900
Kyungmin Park <kyungmin.park@samsung.com> wrote:
> From: Kyungmin Park <kyungmin.park@samsung.com>
>
> Some host constroller such as s5pc110 has WIDE8 support feature.
>
> Signed-off-by: Kyungmin Park <kyungmin.park@samsung.com>
> ---
> drivers/mmc/host/sdhci.c | 5 +++++
> drivers/mmc/host/sdhci.h | 1 +
> 2 files changed, 6 insertions(+), 0 deletions(-)
>
> diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c
> index 142419c..6cf018a 100644
> --- a/drivers/mmc/host/sdhci.c
> +++ b/drivers/mmc/host/sdhci.c
> @@ -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
> diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h
> index c846813..eb5efe0 100644
> --- a/drivers/mmc/host/sdhci.h
> +++ b/drivers/mmc/host/sdhci.h
> @@ -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 <ccross@android.com>
AuthorDate: Wed Mar 10 20:42:35 2010 -0800
Commit: Grant Likely <grant.likely@secretlab.ca>
CommitDate: Fri Jun 25 09:47:58 2010 -0600
mmc: sdhci: Initial Tegra sdhci driver
Signed-off-by: Colin Cross <ccross@android.com>
[Olof: Fixed up merge conflicts]
Signed-off-by: Olof Johansson <olof@lixom.net>
Signed-off-by: Grant Likely <grant.likely@secretlab.ca>
diff --git a/arch/arm/mach-tegra/include/mach/sdhci.h b/arch/arm/mach-tegra/include/mach/sdhci.h
new file mode 100644
index 0000000..34e2686
--- /dev/null
+++ b/arch/arm/mach-tegra/include/mach/sdhci.h
@@ -0,0 +1,33 @@
+/*
+ * include/asm-arm/arch-tegra/sdhci.h
+ *
+ * Copyright (C) 2009 Palm, Inc.
+ * Author: Yvonne Yip <y@palm.com>
+ *
+ * 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
diff --git a/drivers/mmc/core/sdio.c b/drivers/mmc/core/sdio.c
index b9dee28..85c473e 100644
--- a/drivers/mmc/core/sdio.c
+++ b/drivers/mmc/core/sdio.c
@@ -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)
diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig
index f06d06e..357c294 100644
--- a/drivers/mmc/host/Kconfig
+++ b/drivers/mmc/host/Kconfig
@@ -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
diff --git a/drivers/mmc/host/Makefile b/drivers/mmc/host/Makefile
index e30c2ee..fb04448 100644
--- a/drivers/mmc/host/Makefile
+++ b/drivers/mmc/host/Makefile
@@ -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
diff --git a/drivers/mmc/host/sdhci-tegra.c b/drivers/mmc/host/sdhci-tegra.c
new file mode 100644
index 0000000..5e2a1f1
--- /dev/null
+++ b/drivers/mmc/host/sdhci-tegra.c
@@ -0,0 +1,217 @@
+/*
+ * drivers/mmc/host/sdhci-tegra.c
+ *
+ * Copyright (C) 2009 Palm, Inc.
+ * Author: Yvonne Yip <y@palm.com>
+ *
+ * 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");
diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c
index c6d1bd8..4af24d6 100644
--- a/drivers/mmc/host/sdhci.c
+++ b/drivers/mmc/host/sdhci.c
@@ -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;
}
@@ -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);
+
+ 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();
@@ -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),
@@ -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)
+
+ 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;
@@ -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.
*/
diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h
index c846813..2c8a83a 100644
--- a/drivers/mmc/host/sdhci.h
+++ b/drivers/mmc/host/sdhci.h
@@ -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)
{
diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h
index f65913c..9ab146a 100644
--- a/include/linux/mmc/host.h
+++ b/include/linux/mmc/host.h
@@ -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 */
^ permalink raw reply related [flat|nested] 6+ messages in thread
* Re: [PATCH 3/3] SDHCI: 8-bit data transfer width support
2010-06-28 18:39 ` Andrew Morton
@ 2010-06-28 18:56 ` Colin Cross
2010-06-28 19:33 ` Olof Johansson
0 siblings, 1 reply; 6+ messages in thread
From: Colin Cross @ 2010-06-28 18:56 UTC (permalink / raw)
To: Andrew Morton; +Cc: Kyungmin Park, linux-mmc, Olof Johansson, Grant Likely
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.
On Mon, Jun 28, 2010 at 11:39 AM, Andrew Morton
<akpm@linux-foundation.org> wrote:
> On Sat, 12 Jun 2010 14:45:02 +0900
> Kyungmin Park <kyungmin.park@samsung.com> wrote:
>
>> From: Kyungmin Park <kyungmin.park@samsung.com>
>>
>> Some host constroller such as s5pc110 has WIDE8 support feature.
>>
>> Signed-off-by: Kyungmin Park <kyungmin.park@samsung.com>
>> ---
>> drivers/mmc/host/sdhci.c | 5 +++++
>> drivers/mmc/host/sdhci.h | 1 +
>> 2 files changed, 6 insertions(+), 0 deletions(-)
>>
>> diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c
>> index 142419c..6cf018a 100644
>> --- a/drivers/mmc/host/sdhci.c
>> +++ b/drivers/mmc/host/sdhci.c
>> @@ -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
>> diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h
>> index c846813..eb5efe0 100644
>> --- a/drivers/mmc/host/sdhci.h
>> +++ b/drivers/mmc/host/sdhci.h
>> @@ -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 <ccross@android.com>
> AuthorDate: Wed Mar 10 20:42:35 2010 -0800
> Commit: Grant Likely <grant.likely@secretlab.ca>
> CommitDate: Fri Jun 25 09:47:58 2010 -0600
>
> mmc: sdhci: Initial Tegra sdhci driver
>
> Signed-off-by: Colin Cross <ccross@android.com>
> [Olof: Fixed up merge conflicts]
> Signed-off-by: Olof Johansson <olof@lixom.net>
> Signed-off-by: Grant Likely <grant.likely@secretlab.ca>
>
> diff --git a/arch/arm/mach-tegra/include/mach/sdhci.h b/arch/arm/mach-tegra/include/mach/sdhci.h
> new file mode 100644
> index 0000000..34e2686
> --- /dev/null
> +++ b/arch/arm/mach-tegra/include/mach/sdhci.h
> @@ -0,0 +1,33 @@
> +/*
> + * include/asm-arm/arch-tegra/sdhci.h
> + *
> + * Copyright (C) 2009 Palm, Inc.
> + * Author: Yvonne Yip <y@palm.com>
> + *
> + * 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
> diff --git a/drivers/mmc/core/sdio.c b/drivers/mmc/core/sdio.c
> index b9dee28..85c473e 100644
> --- a/drivers/mmc/core/sdio.c
> +++ b/drivers/mmc/core/sdio.c
> @@ -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)
> diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig
> index f06d06e..357c294 100644
> --- a/drivers/mmc/host/Kconfig
> +++ b/drivers/mmc/host/Kconfig
> @@ -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
> diff --git a/drivers/mmc/host/Makefile b/drivers/mmc/host/Makefile
> index e30c2ee..fb04448 100644
> --- a/drivers/mmc/host/Makefile
> +++ b/drivers/mmc/host/Makefile
> @@ -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
> diff --git a/drivers/mmc/host/sdhci-tegra.c b/drivers/mmc/host/sdhci-tegra.c
> new file mode 100644
> index 0000000..5e2a1f1
> --- /dev/null
> +++ b/drivers/mmc/host/sdhci-tegra.c
> @@ -0,0 +1,217 @@
> +/*
> + * drivers/mmc/host/sdhci-tegra.c
> + *
> + * Copyright (C) 2009 Palm, Inc.
> + * Author: Yvonne Yip <y@palm.com>
> + *
> + * 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");
> diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c
> index c6d1bd8..4af24d6 100644
> --- a/drivers/mmc/host/sdhci.c
> +++ b/drivers/mmc/host/sdhci.c
> @@ -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;
> }
>
> @@ -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);
> +
> + 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();
>
> @@ -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),
> @@ -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)
> +
> + 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;
> @@ -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.
> */
> diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h
> index c846813..2c8a83a 100644
> --- a/drivers/mmc/host/sdhci.h
> +++ b/drivers/mmc/host/sdhci.h
> @@ -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)
> {
> diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h
> index f65913c..9ab146a 100644
> --- a/include/linux/mmc/host.h
> +++ b/include/linux/mmc/host.h
> @@ -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 */
>
>
>
^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [PATCH 3/3] SDHCI: 8-bit data transfer width support
2010-06-28 18:56 ` Colin Cross
@ 2010-06-28 19:33 ` Olof Johansson
2010-06-28 19:48 ` Grant Likely
0 siblings, 1 reply; 6+ messages in thread
From: Olof Johansson @ 2010-06-28 19:33 UTC (permalink / raw)
To: Colin Cross; +Cc: Andrew Morton, Kyungmin Park, linux-mmc, Grant Likely
On Mon, Jun 28, 2010 at 11:56:26AM -0700, Colin Cross wrote:
> 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
^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [PATCH 3/3] SDHCI: 8-bit data transfer width support
2010-06-28 19:33 ` Olof Johansson
@ 2010-06-28 19:48 ` Grant Likely
2010-06-29 18:37 ` Andrew Morton
0 siblings, 1 reply; 6+ messages in thread
From: Grant Likely @ 2010-06-28 19:48 UTC (permalink / raw)
To: Olof Johansson; +Cc: Colin Cross, Andrew Morton, Kyungmin Park, linux-mmc
On Mon, Jun 28, 2010 at 12:33 PM, Olof Johansson <olof@lixom.net> wrote:
> On Mon, Jun 28, 2010 at 11:56:26AM -0700, Colin Cross wrote:
>> 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.
^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [PATCH 3/3] SDHCI: 8-bit data transfer width support
2010-06-28 19:48 ` Grant Likely
@ 2010-06-29 18:37 ` Andrew Morton
0 siblings, 0 replies; 6+ messages in thread
From: Andrew Morton @ 2010-06-29 18:37 UTC (permalink / raw)
To: Grant Likely; +Cc: Olof Johansson, Colin Cross, Kyungmin Park, linux-mmc
On Mon, 28 Jun 2010 12:48:29 -0700
Grant Likely <grant.likely@secretlab.ca> wrote:
> On Mon, Jun 28, 2010 at 12:33 PM, Olof Johansson <olof@lixom.net> wrote:
> > On Mon, Jun 28, 2010 at 11:56:26AM -0700, Colin Cross wrote:
> >> 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.
Thanks, guys - I resurrected
sdhci-8-bit-data-transfer-width-support.patch and
sdhci-dont-assign-mmc-caps-at-sdhci-directly.patch
^ permalink raw reply [flat|nested] 6+ messages in thread
end of thread, other threads:[~2010-06-29 18:38 UTC | newest]
Thread overview: 6+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2010-06-12 5:45 [PATCH 3/3] SDHCI: 8-bit data transfer width support Kyungmin Park
2010-06-28 18:39 ` Andrew Morton
2010-06-28 18:56 ` Colin Cross
2010-06-28 19:33 ` Olof Johansson
2010-06-28 19:48 ` Grant Likely
2010-06-29 18:37 ` Andrew Morton
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox