* [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