From 94d89efb2c347a82a08a61dbac8565b1087c3259 Mon Sep 17 00:00:00 2001 From: Jorg Schummer Date: Tue, 31 Mar 2009 17:51:21 +0300 Subject: mmc: mmc_rescan detects card change in one run With this patch, mmc_rescan can detect the removal of an mmc card and the insertion of (possibly another) card in the same run. This means that a card change can be detected without having to call mmc_detect_change multiple times. This change generalises the core such that it can be easily used by hosts which provide a mechanism to detect only the presence of a card reader cover, which has to be taken off in order to insert a card. Other hosts ("card detect" or "MMC_CAP_NEEDS_POLL") each receive an event when a card is removed and when a card is inserted, so it is sufficient for them if mmc_rescan handles only one event at a time. "Cover detect" hosts, however, only receive events about the cover status. This means that between 2 subsequent events, both a card removal and a card insertion can occur. In this case, the pre-patch version of mmc_rescan would only detect the removal of the previous card but not the insertion of the new card. Signed-off-by: Jorg Schummer Signed-off-by: Pierre Ossman --- drivers/mmc/core/core.c | 99 +++++++++++++++++++++++++++---------------------- 1 file changed, 55 insertions(+), 44 deletions(-) (limited to 'drivers/mmc') diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c index 264911732756..23fb2c39d9ed 100644 --- a/drivers/mmc/core/core.c +++ b/drivers/mmc/core/core.c @@ -855,61 +855,72 @@ void mmc_rescan(struct work_struct *work) mmc_bus_get(host); - if (host->bus_ops == NULL) { - /* - * Only we can add a new handler, so it's safe to - * release the lock here. - */ + /* if there is a card registered, check whether it is still present */ + if ((host->bus_ops != NULL) && host->bus_ops->detect && !host->bus_dead) + host->bus_ops->detect(host); + + mmc_bus_put(host); + + + mmc_bus_get(host); + + /* if there still is a card present, stop here */ + if (host->bus_ops != NULL) { mmc_bus_put(host); + goto out; + } - if (host->ops->get_cd && host->ops->get_cd(host) == 0) - goto out; + /* detect a newly inserted card */ - mmc_claim_host(host); + /* + * Only we can add a new handler, so it's safe to + * release the lock here. + */ + mmc_bus_put(host); - mmc_power_up(host); - mmc_go_idle(host); + if (host->ops->get_cd && host->ops->get_cd(host) == 0) + goto out; - mmc_send_if_cond(host, host->ocr_avail); + mmc_claim_host(host); - /* - * First we search for SDIO... - */ - err = mmc_send_io_op_cond(host, 0, &ocr); - if (!err) { - if (mmc_attach_sdio(host, ocr)) - mmc_power_off(host); - goto out; - } + mmc_power_up(host); + mmc_go_idle(host); - /* - * ...then normal SD... - */ - err = mmc_send_app_op_cond(host, 0, &ocr); - if (!err) { - if (mmc_attach_sd(host, ocr)) - mmc_power_off(host); - goto out; - } + mmc_send_if_cond(host, host->ocr_avail); - /* - * ...and finally MMC. - */ - err = mmc_send_op_cond(host, 0, &ocr); - if (!err) { - if (mmc_attach_mmc(host, ocr)) - mmc_power_off(host); - goto out; - } + /* + * First we search for SDIO... + */ + err = mmc_send_io_op_cond(host, 0, &ocr); + if (!err) { + if (mmc_attach_sdio(host, ocr)) + mmc_power_off(host); + goto out; + } - mmc_release_host(host); - mmc_power_off(host); - } else { - if (host->bus_ops->detect && !host->bus_dead) - host->bus_ops->detect(host); + /* + * ...then normal SD... + */ + err = mmc_send_app_op_cond(host, 0, &ocr); + if (!err) { + if (mmc_attach_sd(host, ocr)) + mmc_power_off(host); + goto out; + } - mmc_bus_put(host); + /* + * ...and finally MMC. + */ + err = mmc_send_op_cond(host, 0, &ocr); + if (!err) { + if (mmc_attach_mmc(host, ocr)) + mmc_power_off(host); + goto out; } + + mmc_release_host(host); + mmc_power_off(host); + out: if (host->caps & MMC_CAP_NEEDS_POLL) mmc_schedule_delayed_work(&host->detect, HZ); -- cgit v1.2.3 From 5cf20aa557e8f9dd5af302b8f33972082479753a Mon Sep 17 00:00:00 2001 From: Wolfgang Muees Date: Wed, 8 Apr 2009 10:14:07 +0100 Subject: mmc_spi: speedup for slow cards, less wear-out Speedup for slow cards by transfering more data at once. This patch also reduces the amount of wear-out of the flash blocks because fewer partial blocks are written. Signed-off-by: Wolfgang Muees Signed-off-by: Pierre Ossman --- drivers/mmc/host/mmc_spi.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) (limited to 'drivers/mmc') diff --git a/drivers/mmc/host/mmc_spi.c b/drivers/mmc/host/mmc_spi.c index f48349d18c92..a789db8eed28 100644 --- a/drivers/mmc/host/mmc_spi.c +++ b/drivers/mmc/host/mmc_spi.c @@ -97,6 +97,14 @@ */ #define r1b_timeout (HZ * 3) +/* One of the critical speed parameters is the amount of data which may + * be transfered in one command. If this value is too low, the SD card + * controller has to do multiple partial block writes (argggh!). With + * today (2008) SD cards there is little speed gain if we transfer more + * than 64 KBytes at a time. So use this value until there is any indication + * that we should do more here. + */ +#define MMC_SPI_BLOCKSATONCE 128 /****************************************************************************/ @@ -1366,6 +1374,10 @@ static int mmc_spi_probe(struct spi_device *spi) mmc->ops = &mmc_spi_ops; mmc->max_blk_size = MMC_SPI_BLOCKSIZE; + mmc->max_hw_segs = MMC_SPI_BLOCKSATONCE; + mmc->max_phys_segs = MMC_SPI_BLOCKSATONCE; + mmc->max_req_size = MMC_SPI_BLOCKSATONCE * MMC_SPI_BLOCKSIZE; + mmc->max_blk_count = MMC_SPI_BLOCKSATONCE; mmc->caps = MMC_CAP_SPI; -- cgit v1.2.3 From 7ceeb6a40a4dcc9b9cded6127ad5cdddb79b40ad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Thu, 2 Apr 2009 19:47:41 +0200 Subject: mmc/omap: make mmci-omap using platform_driver_probe MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A pointer to mmc_omap_probe which lives in .init.text is passed to the core via platform_driver_register and so the kernel might oops if probe is called after the init code is discarded. As requested by David Brownell platform_driver_probe is used instead of moving the probe function to .devinit.text. This saves some memory, but might have the downside that a device being registered after the call to mmc_omap_init but before the init sections are discarded will not be bound anymore to the driver. Signed-off-by: Uwe Kleine-König Acked-by: David Brownell Signed-off-by: Pierre Ossman --- drivers/mmc/host/omap.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'drivers/mmc') diff --git a/drivers/mmc/host/omap.c b/drivers/mmc/host/omap.c index dceb5ee3bda0..e7a331de5733 100644 --- a/drivers/mmc/host/omap.c +++ b/drivers/mmc/host/omap.c @@ -1593,7 +1593,6 @@ static int mmc_omap_resume(struct platform_device *pdev) #endif static struct platform_driver mmc_omap_driver = { - .probe = mmc_omap_probe, .remove = mmc_omap_remove, .suspend = mmc_omap_suspend, .resume = mmc_omap_resume, @@ -1605,7 +1604,7 @@ static struct platform_driver mmc_omap_driver = { static int __init mmc_omap_init(void) { - return platform_driver_register(&mmc_omap_driver); + return platform_driver_probe(&mmc_omap_driver, mmc_omap_probe); } static void __exit mmc_omap_exit(void) -- cgit v1.2.3 From ae628903ab6cc9a04fdf8d4ff3f0c5b2ffa6f939 Mon Sep 17 00:00:00 2001 From: Pierre Ossman Date: Sun, 3 May 2009 20:45:03 +0200 Subject: sdhci: avoid changing voltage needlessly Because of granularity issues, sometimes we told the hardware to change to the voltage we were already at. Rework the logic so this doesn't happen. Signed-off-by: Pierre Ossman --- drivers/mmc/host/sdhci.c | 53 +++++++++++++++++++++++++----------------------- drivers/mmc/host/sdhci.h | 2 +- 2 files changed, 29 insertions(+), 26 deletions(-) (limited to 'drivers/mmc') diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index 9234be2226e7..1432a35690dd 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -1005,12 +1005,34 @@ static void sdhci_set_power(struct sdhci_host *host, unsigned short power) { u8 pwr; - if (host->power == power) + if (power == (unsigned short)-1) + pwr = 0; + else { + switch (1 << power) { + case MMC_VDD_165_195: + pwr = SDHCI_POWER_180; + break; + case MMC_VDD_29_30: + case MMC_VDD_30_31: + pwr = SDHCI_POWER_300; + break; + case MMC_VDD_32_33: + case MMC_VDD_33_34: + pwr = SDHCI_POWER_330; + break; + default: + BUG(); + } + } + + if (host->pwr == pwr) return; - if (power == (unsigned short)-1) { + host->pwr = pwr; + + if (pwr == 0) { sdhci_writeb(host, 0, SDHCI_POWER_CONTROL); - goto out; + return; } /* @@ -1020,35 +1042,16 @@ static void sdhci_set_power(struct sdhci_host *host, unsigned short power) if (!(host->quirks & SDHCI_QUIRK_SINGLE_POWER_WRITE)) sdhci_writeb(host, 0, SDHCI_POWER_CONTROL); - pwr = SDHCI_POWER_ON; - - switch (1 << power) { - case MMC_VDD_165_195: - pwr |= SDHCI_POWER_180; - break; - case MMC_VDD_29_30: - case MMC_VDD_30_31: - pwr |= SDHCI_POWER_300; - break; - case MMC_VDD_32_33: - case MMC_VDD_33_34: - pwr |= SDHCI_POWER_330; - break; - default: - BUG(); - } - /* * At least the Marvell CaFe chip gets confused if we set the voltage * and set turn on power at the same time, so set the voltage first. */ if ((host->quirks & SDHCI_QUIRK_NO_SIMULT_VDD_AND_POWER)) - sdhci_writeb(host, pwr & ~SDHCI_POWER_ON, SDHCI_POWER_CONTROL); + sdhci_writeb(host, pwr, SDHCI_POWER_CONTROL); - sdhci_writeb(host, pwr, SDHCI_POWER_CONTROL); + pwr |= SDHCI_POWER_ON; -out: - host->power = power; + sdhci_writeb(host, pwr, SDHCI_POWER_CONTROL); } /*****************************************************************************\ diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h index 65c6f996bbd3..2de08349c3ca 100644 --- a/drivers/mmc/host/sdhci.h +++ b/drivers/mmc/host/sdhci.h @@ -255,7 +255,7 @@ struct sdhci_host { unsigned int timeout_clk; /* Timeout freq (KHz) */ unsigned int clock; /* Current clock (MHz) */ - unsigned short power; /* Current voltage */ + u8 pwr; /* Current voltage */ struct mmc_request *mrq; /* Current request */ struct mmc_command *cmd; /* Current command */ -- cgit v1.2.3 From 8dfd0374be84793360db7fff2e635d2cd3bbcb21 Mon Sep 17 00:00:00 2001 From: Sascha Hauer Date: Thu, 9 Apr 2009 08:32:02 +0200 Subject: MMC core: limit minimum initialization frequency to 400kHz Some controllers allow a much lower frequency than 400kHz. Keep the minimum frequency within sensible limits. Signed-off-by: Sascha Hauer --- drivers/mmc/core/core.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) (limited to 'drivers/mmc') diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c index 23fb2c39d9ed..d84c880fac84 100644 --- a/drivers/mmc/core/core.c +++ b/drivers/mmc/core/core.c @@ -708,7 +708,13 @@ static void mmc_power_up(struct mmc_host *host) */ mmc_delay(10); - host->ios.clock = host->f_min; + if (host->f_min > 400000) { + pr_warning("%s: Minimum clock frequency too high for " + "identification mode\n", mmc_hostname(host)); + host->ios.clock = host->f_min; + } else + host->ios.clock = 400000; + host->ios.power_mode = MMC_POWER_ON; mmc_set_ios(host); -- cgit v1.2.3 From 8385f9cb7f12ef6a5261fa76f1a1b612280c94f7 Mon Sep 17 00:00:00 2001 From: Daniel Ribeiro Date: Thu, 21 May 2009 08:54:18 -0300 Subject: pxamci: add regulator support. Changes pxamci.c to use the regulator subsystem. Uses the regulator case CONFIG_REGULATOR is defined and a matching is regulator is provided, or falls back to pdata->setpower otherwise. A warning is displayed case both a valid regulator and pdata is set, and the regulator is used. Signed-off-by: Daniel Ribeiro Acked-by: Eric Miao Signed-off-by: Pierre Ossman --- drivers/mmc/host/pxamci.c | 46 +++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 41 insertions(+), 5 deletions(-) (limited to 'drivers/mmc') diff --git a/drivers/mmc/host/pxamci.c b/drivers/mmc/host/pxamci.c index 430095725f9f..d7d7109ef47e 100644 --- a/drivers/mmc/host/pxamci.c +++ b/drivers/mmc/host/pxamci.c @@ -27,6 +27,7 @@ #include #include #include +#include #include @@ -67,8 +68,42 @@ struct pxamci_host { unsigned int dma_dir; unsigned int dma_drcmrrx; unsigned int dma_drcmrtx; + + struct regulator *vcc; }; +static inline void pxamci_init_ocr(struct pxamci_host *host) +{ +#ifdef CONFIG_REGULATOR + host->vcc = regulator_get(mmc_dev(host->mmc), "vmmc"); + + if (IS_ERR(host->vcc)) + host->vcc = NULL; + else { + host->mmc->ocr_avail = mmc_regulator_get_ocrmask(host->vcc); + if (host->pdata && host->pdata->ocr_mask) + dev_warn(mmc_dev(host->mmc), + "ocr_mask/setpower will not be used\n"); + } +#endif + if (host->vcc == NULL) { + /* fall-back to platform data */ + host->mmc->ocr_avail = host->pdata ? + host->pdata->ocr_mask : + MMC_VDD_32_33 | MMC_VDD_33_34; + } +} + +static inline void pxamci_set_power(struct pxamci_host *host, unsigned int vdd) +{ +#ifdef CONFIG_REGULATOR + if (host->vcc) + mmc_regulator_set_ocr(host->vcc, vdd); +#endif + if (!host->vcc && host->pdata && host->pdata->setpower) + host->pdata->setpower(mmc_dev(host->mmc), vdd); +} + static void pxamci_stop_clock(struct pxamci_host *host) { if (readl(host->base + MMC_STAT) & STAT_CLK_EN) { @@ -438,8 +473,7 @@ static void pxamci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) if (host->power_mode != ios->power_mode) { host->power_mode = ios->power_mode; - if (host->pdata && host->pdata->setpower) - host->pdata->setpower(mmc_dev(mmc), ios->vdd); + pxamci_set_power(host, ios->vdd); if (ios->power_mode == MMC_POWER_ON) host->cmdat |= CMDAT_INIT; @@ -562,9 +596,8 @@ static int pxamci_probe(struct platform_device *pdev) mmc->f_max = (cpu_is_pxa300() || cpu_is_pxa310()) ? 26000000 : host->clkrate; - mmc->ocr_avail = host->pdata ? - host->pdata->ocr_mask : - MMC_VDD_32_33|MMC_VDD_33_34; + pxamci_init_ocr(host); + mmc->caps = 0; host->cmdat = 0; if (!cpu_is_pxa25x()) { @@ -661,6 +694,9 @@ static int pxamci_remove(struct platform_device *pdev) if (mmc) { struct pxamci_host *host = mmc_priv(mmc); + if (host->vcc) + regulator_put(host->vcc); + if (host->pdata && host->pdata->exit) host->pdata->exit(&pdev->dev, mmc); -- cgit v1.2.3 From 5f5bac8272be791b67c7b7b411e7c8c5847e598a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Miros=C5=82aw?= Date: Fri, 22 May 2009 20:33:59 +0200 Subject: mmc: Driver for CB710/720 memory card reader (MMC part) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The code is divided in two parts. There is a virtual 'bus' driver that handles PCI device and registers three new devices one per card reader type. The other driver handles SD/MMC part of the reader. Signed-off-by: Michał Mirosław Signed-off-by: Pierre Ossman --- drivers/mmc/host/Kconfig | 13 + drivers/mmc/host/Makefile | 1 + drivers/mmc/host/cb710-mmc.c | 804 +++++++++++++++++++++++++++++++++++++++++++ drivers/mmc/host/cb710-mmc.h | 104 ++++++ 4 files changed, 922 insertions(+) create mode 100644 drivers/mmc/host/cb710-mmc.c create mode 100644 drivers/mmc/host/cb710-mmc.h (limited to 'drivers/mmc') diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig index b4cf691f3f64..75337e442da9 100644 --- a/drivers/mmc/host/Kconfig +++ b/drivers/mmc/host/Kconfig @@ -241,3 +241,16 @@ config MMC_TMIO help This provides support for the SD/MMC cell found in TC6393XB, T7L66XB and also ipaq ASIC3 + +config MMC_CB710 + tristate "ENE CB710 MMC/SD Interface support" + depends on PCI + select CB710_CORE + help + This option enables support for MMC/SD part of ENE CB710/720 Flash + memory card reader found in some laptops (ie. some versions of + HP Compaq nx9500). + + This driver can also be built as a module. If so, the module + will be called cb710-mmc. + diff --git a/drivers/mmc/host/Makefile b/drivers/mmc/host/Makefile index 970a997620e1..ce9c8b6f0f4b 100644 --- a/drivers/mmc/host/Makefile +++ b/drivers/mmc/host/Makefile @@ -29,4 +29,5 @@ endif obj-$(CONFIG_MMC_S3C) += s3cmci.o obj-$(CONFIG_MMC_SDRICOH_CS) += sdricoh_cs.o obj-$(CONFIG_MMC_TMIO) += tmio_mmc.o +obj-$(CONFIG_MMC_CB710) += cb710-mmc.o diff --git a/drivers/mmc/host/cb710-mmc.c b/drivers/mmc/host/cb710-mmc.c new file mode 100644 index 000000000000..bc048aae7207 --- /dev/null +++ b/drivers/mmc/host/cb710-mmc.c @@ -0,0 +1,804 @@ +/* + * cb710/mmc.c + * + * Copyright by Michał Mirosław, 2008-2009 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#include +#include +#include +#include +#include +#include "cb710-mmc.h" + +static const u8 cb710_clock_divider_log2[8] = { +/* 1, 2, 4, 8, 16, 32, 128, 512 */ + 0, 1, 2, 3, 4, 5, 7, 9 +}; +#define CB710_MAX_DIVIDER_IDX \ + (ARRAY_SIZE(cb710_clock_divider_log2) - 1) + +static const u8 cb710_src_freq_mhz[16] = { + 33, 10, 20, 25, 30, 35, 40, 45, + 50, 55, 60, 65, 70, 75, 80, 85 +}; + +static void cb710_mmc_set_clock(struct mmc_host *mmc, int hz) +{ + struct cb710_slot *slot = cb710_mmc_to_slot(mmc); + struct pci_dev *pdev = cb710_slot_to_chip(slot)->pdev; + u32 src_freq_idx; + u32 divider_idx; + int src_hz; + + /* this is magic, unverifiable for me, unless I get + * MMC card with cables connected to bus signals */ + pci_read_config_dword(pdev, 0x48, &src_freq_idx); + src_freq_idx = (src_freq_idx >> 16) & 0xF; + src_hz = cb710_src_freq_mhz[src_freq_idx] * 1000000; + + for (divider_idx = 0; divider_idx < CB710_MAX_DIVIDER_IDX; ++divider_idx) { + if (hz >= src_hz >> cb710_clock_divider_log2[divider_idx]) + break; + } + + if (src_freq_idx) + divider_idx |= 0x8; + + cb710_pci_update_config_reg(pdev, 0x40, ~0xF0000000, divider_idx << 28); + + dev_dbg(cb710_slot_dev(slot), + "clock set to %d Hz, wanted %d Hz; flag = %d\n", + src_hz >> cb710_clock_divider_log2[divider_idx & 7], + hz, (divider_idx & 8) != 0); +} + +static void __cb710_mmc_enable_irq(struct cb710_slot *slot, + unsigned short enable, unsigned short mask) +{ + /* clear global IE + * - it gets set later if any interrupt sources are enabled */ + mask |= CB710_MMC_IE_IRQ_ENABLE; + + /* look like interrupt is fired whenever + * WORD[0x0C] & WORD[0x10] != 0; + * -> bit 15 port 0x0C seems to be global interrupt enable + */ + + enable = (cb710_read_port_16(slot, CB710_MMC_IRQ_ENABLE_PORT) + & ~mask) | enable; + + if (enable) + enable |= CB710_MMC_IE_IRQ_ENABLE; + + cb710_write_port_16(slot, CB710_MMC_IRQ_ENABLE_PORT, enable); +} + +static void cb710_mmc_enable_irq(struct cb710_slot *slot, + unsigned short enable, unsigned short mask) +{ + struct cb710_mmc_reader *reader = mmc_priv(cb710_slot_to_mmc(slot)); + unsigned long flags; + + spin_lock_irqsave(&reader->irq_lock, flags); + /* this is the only thing irq_lock protects */ + __cb710_mmc_enable_irq(slot, enable, mask); + spin_unlock_irqrestore(&reader->irq_lock, flags); +} + +static void cb710_mmc_reset_events(struct cb710_slot *slot) +{ + cb710_write_port_8(slot, CB710_MMC_STATUS0_PORT, 0xFF); + cb710_write_port_8(slot, CB710_MMC_STATUS1_PORT, 0xFF); + cb710_write_port_8(slot, CB710_MMC_STATUS2_PORT, 0xFF); +} + +static int cb710_mmc_is_card_inserted(struct cb710_slot *slot) +{ + return cb710_read_port_8(slot, CB710_MMC_STATUS3_PORT) + & CB710_MMC_S3_CARD_DETECTED; +} + +static void cb710_mmc_enable_4bit_data(struct cb710_slot *slot, int enable) +{ + dev_dbg(cb710_slot_dev(slot), "configuring %d-data-line%s mode\n", + enable ? 4 : 1, enable ? "s" : ""); + if (enable) + cb710_modify_port_8(slot, CB710_MMC_CONFIG1_PORT, + CB710_MMC_C1_4BIT_DATA_BUS, 0); + else + cb710_modify_port_8(slot, CB710_MMC_CONFIG1_PORT, + 0, CB710_MMC_C1_4BIT_DATA_BUS); +} + +static int cb710_check_event(struct cb710_slot *slot, u8 what) +{ + u16 status; + + status = cb710_read_port_16(slot, CB710_MMC_STATUS_PORT); + + if (status & CB710_MMC_S0_FIFO_UNDERFLOW) { + /* it is just a guess, so log it */ + dev_dbg(cb710_slot_dev(slot), + "CHECK : ignoring bit 6 in status %04X\n", status); + cb710_write_port_8(slot, CB710_MMC_STATUS0_PORT, + CB710_MMC_S0_FIFO_UNDERFLOW); + status &= ~CB710_MMC_S0_FIFO_UNDERFLOW; + } + + if (status & CB710_MMC_STATUS_ERROR_EVENTS) { + dev_dbg(cb710_slot_dev(slot), + "CHECK : returning EIO on status %04X\n", status); + cb710_write_port_8(slot, CB710_MMC_STATUS0_PORT, status & 0xFF); + cb710_write_port_8(slot, CB710_MMC_STATUS1_PORT, + CB710_MMC_S1_RESET); + return -EIO; + } + + /* 'what' is a bit in MMC_STATUS1 */ + if ((status >> 8) & what) { + cb710_write_port_8(slot, CB710_MMC_STATUS1_PORT, what); + return 1; + } + + return 0; +} + +static int cb710_wait_for_event(struct cb710_slot *slot, u8 what) +{ + int err = 0; + unsigned limit = 2000000; /* FIXME: real timeout */ + +#ifdef CONFIG_CB710_DEBUG + u32 e, x; + e = cb710_read_port_32(slot, CB710_MMC_STATUS_PORT); +#endif + + while (!(err = cb710_check_event(slot, what))) { + if (!--limit) { + cb710_dump_regs(cb710_slot_to_chip(slot), + CB710_DUMP_REGS_MMC); + err = -ETIMEDOUT; + break; + } + udelay(1); + } + +#ifdef CONFIG_CB710_DEBUG + x = cb710_read_port_32(slot, CB710_MMC_STATUS_PORT); + + limit = 2000000 - limit; + if (limit > 100) + dev_dbg(cb710_slot_dev(slot), + "WAIT10: waited %d loops, what %d, entry val %08X, exit val %08X\n", + limit, what, e, x); +#endif + return err < 0 ? err : 0; +} + + +static int cb710_wait_while_busy(struct cb710_slot *slot, uint8_t mask) +{ + unsigned limit = 500000; /* FIXME: real timeout */ + int err = 0; + +#ifdef CONFIG_CB710_DEBUG + u32 e, x; + e = cb710_read_port_32(slot, CB710_MMC_STATUS_PORT); +#endif + + while (cb710_read_port_8(slot, CB710_MMC_STATUS2_PORT) & mask) { + if (!--limit) { + cb710_dump_regs(cb710_slot_to_chip(slot), + CB710_DUMP_REGS_MMC); + err = -ETIMEDOUT; + break; + } + udelay(1); + } + +#ifdef CONFIG_CB710_DEBUG + x = cb710_read_port_32(slot, CB710_MMC_STATUS_PORT); + + limit = 500000 - limit; + if (limit > 100) + dev_dbg(cb710_slot_dev(slot), + "WAIT12: waited %d loops, mask %02X, entry val %08X, exit val %08X\n", + limit, mask, e, x); +#endif + return 0; +} + +static void cb710_mmc_set_transfer_size(struct cb710_slot *slot, + size_t count, size_t blocksize) +{ + cb710_wait_while_busy(slot, CB710_MMC_S2_BUSY_20); + cb710_write_port_32(slot, CB710_MMC_TRANSFER_SIZE_PORT, + ((count - 1) << 16)|(blocksize - 1)); + + dev_vdbg(cb710_slot_dev(slot), "set up for %d block%s of %d bytes\n", + count, count == 1 ? "" : "s", blocksize); +} + +static void cb710_mmc_fifo_hack(struct cb710_slot *slot) +{ + /* without this, received data is prepended with 8-bytes of zeroes */ + u32 r1, r2; + int ok = 0; + + r1 = cb710_read_port_32(slot, CB710_MMC_DATA_PORT); + r2 = cb710_read_port_32(slot, CB710_MMC_DATA_PORT); + if (cb710_read_port_8(slot, CB710_MMC_STATUS0_PORT) + & CB710_MMC_S0_FIFO_UNDERFLOW) { + cb710_write_port_8(slot, CB710_MMC_STATUS0_PORT, + CB710_MMC_S0_FIFO_UNDERFLOW); + ok = 1; + } + + dev_dbg(cb710_slot_dev(slot), + "FIFO-read-hack: expected STATUS0 bit was %s\n", + ok ? "set." : "NOT SET!"); + dev_dbg(cb710_slot_dev(slot), + "FIFO-read-hack: dwords ignored: %08X %08X - %s\n", + r1, r2, (r1|r2) ? "BAD (NOT ZERO)!" : "ok"); +} + +static int cb710_mmc_receive_pio(struct cb710_slot *slot, + struct sg_mapping_iter *miter, size_t dw_count) +{ + if (!(cb710_read_port_8(slot, CB710_MMC_STATUS2_PORT) & CB710_MMC_S2_FIFO_READY)) { + int err = cb710_wait_for_event(slot, + CB710_MMC_S1_PIO_TRANSFER_DONE); + if (err) + return err; + } + + cb710_sg_dwiter_write_from_io(miter, + slot->iobase + CB710_MMC_DATA_PORT, dw_count); + + return 0; +} + +static bool cb710_is_transfer_size_supported(struct mmc_data *data) +{ + return !(data->blksz & 15 && (data->blocks != 1 || data->blksz != 8)); +} + +static int cb710_mmc_receive(struct cb710_slot *slot, struct mmc_data *data) +{ + struct sg_mapping_iter miter; + size_t len, blocks = data->blocks; + int err = 0; + + /* TODO: I don't know how/if the hardware handles non-16B-boundary blocks + * except single 8B block */ + if (unlikely(data->blksz & 15 && (data->blocks != 1 || data->blksz != 8))) + return -EINVAL; + + sg_miter_start(&miter, data->sg, data->sg_len, 0); + + cb710_modify_port_8(slot, CB710_MMC_CONFIG2_PORT, + 15, CB710_MMC_C2_READ_PIO_SIZE_MASK); + + cb710_mmc_fifo_hack(slot); + + while (blocks-- > 0) { + len = data->blksz; + + while (len >= 16) { + err = cb710_mmc_receive_pio(slot, &miter, 4); + if (err) + goto out; + len -= 16; + } + + if (!len) + continue; + + cb710_modify_port_8(slot, CB710_MMC_CONFIG2_PORT, + len - 1, CB710_MMC_C2_READ_PIO_SIZE_MASK); + + len = (len >= 8) ? 4 : 2; + err = cb710_mmc_receive_pio(slot, &miter, len); + if (err) + goto out; + } +out: + cb710_sg_miter_stop_writing(&miter); + return err; +} + +static int cb710_mmc_send(struct cb710_slot *slot, struct mmc_data *data) +{ + struct sg_mapping_iter miter; + size_t len, blocks = data->blocks; + int err = 0; + + /* TODO: I don't know how/if the hardware handles multiple + * non-16B-boundary blocks */ + if (unlikely(data->blocks > 1 && data->blksz & 15)) + return -EINVAL; + + sg_miter_start(&miter, data->sg, data->sg_len, 0); + + cb710_modify_port_8(slot, CB710_MMC_CONFIG2_PORT, + 0, CB710_MMC_C2_READ_PIO_SIZE_MASK); + + while (blocks-- > 0) { + len = (data->blksz + 15) >> 4; + do { + if (!(cb710_read_port_8(slot, CB710_MMC_STATUS2_PORT) + & CB710_MMC_S2_FIFO_EMPTY)) { + err = cb710_wait_for_event(slot, + CB710_MMC_S1_PIO_TRANSFER_DONE); + if (err) + goto out; + } + cb710_sg_dwiter_read_to_io(&miter, + slot->iobase + CB710_MMC_DATA_PORT, 4); + } while (--len); + } +out: + sg_miter_stop(&miter); + return err; +} + +static u16 cb710_encode_cmd_flags(struct cb710_mmc_reader *reader, + struct mmc_command *cmd) +{ + unsigned int flags = cmd->flags; + u16 cb_flags = 0; + + /* Windows driver returned 0 for commands for which no response + * is expected. It happened that there were only two such commands + * used: MMC_GO_IDLE_STATE and MMC_GO_INACTIVE_STATE so it might + * as well be a bug in that driver. + * + * Original driver set bit 14 for MMC/SD application + * commands. There's no difference 'on the wire' and + * it apparently works without it anyway. + */ + + switch (flags & MMC_CMD_MASK) { + case MMC_CMD_AC: cb_flags = CB710_MMC_CMD_AC; break; + case MMC_CMD_ADTC: cb_flags = CB710_MMC_CMD_ADTC; break; + case MMC_CMD_BC: cb_flags = CB710_MMC_CMD_BC; break; + case MMC_CMD_BCR: cb_flags = CB710_MMC_CMD_BCR; break; + } + + if (flags & MMC_RSP_BUSY) + cb_flags |= CB710_MMC_RSP_BUSY; + + cb_flags |= cmd->opcode << CB710_MMC_CMD_CODE_SHIFT; + + if (cmd->data && (cmd->data->flags & MMC_DATA_READ)) + cb_flags |= CB710_MMC_DATA_READ; + + if (flags & MMC_RSP_PRESENT) { + /* Windows driver set 01 at bits 4,3 except for + * MMC_SET_BLOCKLEN where it set 10. Maybe the + * hardware can do something special about this + * command? The original driver looks buggy/incomplete + * anyway so we ignore this for now. + * + * I assume that 00 here means no response is expected. + */ + cb_flags |= CB710_MMC_RSP_PRESENT; + + if (flags & MMC_RSP_136) + cb_flags |= CB710_MMC_RSP_136; + if (!(flags & MMC_RSP_CRC)) + cb_flags |= CB710_MMC_RSP_NO_CRC; + } + + return cb_flags; +} + +static void cb710_receive_response(struct cb710_slot *slot, + struct mmc_command *cmd) +{ + unsigned rsp_opcode, wanted_opcode; + + /* Looks like final byte with CRC is always stripped (same as SDHCI) */ + if (cmd->flags & MMC_RSP_136) { + u32 resp[4]; + + resp[0] = cb710_read_port_32(slot, CB710_MMC_RESPONSE3_PORT); + resp[1] = cb710_read_port_32(slot, CB710_MMC_RESPONSE2_PORT); + resp[2] = cb710_read_port_32(slot, CB710_MMC_RESPONSE1_PORT); + resp[3] = cb710_read_port_32(slot, CB710_MMC_RESPONSE0_PORT); + rsp_opcode = resp[0] >> 24; + + cmd->resp[0] = (resp[0] << 8)|(resp[1] >> 24); + cmd->resp[1] = (resp[1] << 8)|(resp[2] >> 24); + cmd->resp[2] = (resp[2] << 8)|(resp[3] >> 24); + cmd->resp[3] = (resp[3] << 8); + } else { + rsp_opcode = cb710_read_port_32(slot, CB710_MMC_RESPONSE1_PORT) & 0x3F; + cmd->resp[0] = cb710_read_port_32(slot, CB710_MMC_RESPONSE0_PORT); + } + + wanted_opcode = (cmd->flags & MMC_RSP_OPCODE) ? cmd->opcode : 0x3F; + if (rsp_opcode != wanted_opcode) + cmd->error = -EILSEQ; +} + +static int cb710_mmc_transfer_data(struct cb710_slot *slot, + struct mmc_data *data) +{ + int error, to; + + if (data->flags & MMC_DATA_READ) + error = cb710_mmc_receive(slot, data); + else + error = cb710_mmc_send(slot, data); + + to = cb710_wait_for_event(slot, CB710_MMC_S1_DATA_TRANSFER_DONE); + if (!error) + error = to; + + if (!error) + data->bytes_xfered = data->blksz * data->blocks; + return error; +} + +static int cb710_mmc_command(struct mmc_host *mmc, struct mmc_command *cmd) +{ + struct cb710_slot *slot = cb710_mmc_to_slot(mmc); + struct cb710_mmc_reader *reader = mmc_priv(mmc); + struct mmc_data *data = cmd->data; + + u16 cb_cmd = cb710_encode_cmd_flags(reader, cmd); + dev_dbg(cb710_slot_dev(slot), "cmd request: 0x%04X\n", cb_cmd); + + if (data) { + if (!cb710_is_transfer_size_supported(data)) { + data->error = -EINVAL; + return -1; + } + cb710_mmc_set_transfer_size(slot, data->blocks, data->blksz); + } + + cb710_wait_while_busy(slot, CB710_MMC_S2_BUSY_20|CB710_MMC_S2_BUSY_10); + cb710_write_port_16(slot, CB710_MMC_CMD_TYPE_PORT, cb_cmd); + cb710_wait_while_busy(slot, CB710_MMC_S2_BUSY_20); + cb710_write_port_32(slot, CB710_MMC_CMD_PARAM_PORT, cmd->arg); + cb710_mmc_reset_events(slot); + cb710_wait_while_busy(slot, CB710_MMC_S2_BUSY_20); + cb710_modify_port_8(slot, CB710_MMC_CONFIG0_PORT, 0x01, 0); + + cmd->error = cb710_wait_for_event(slot, CB710_MMC_S1_COMMAND_SENT); + if (cmd->error) + return -1; + + if (cmd->flags & MMC_RSP_PRESENT) { + cb710_receive_response(slot, cmd); + if (cmd->error) + return -1; + } + + if (data) + data->error = cb710_mmc_transfer_data(slot, data); + return 0; +} + +static void cb710_mmc_request(struct mmc_host *mmc, struct mmc_request *mrq) +{ + struct cb710_slot *slot = cb710_mmc_to_slot(mmc); + struct cb710_mmc_reader *reader = mmc_priv(mmc); + + WARN_ON(reader->mrq != NULL); + + reader->mrq = mrq; + cb710_mmc_enable_irq(slot, CB710_MMC_IE_TEST_MASK, 0); + + if (cb710_mmc_is_card_inserted(slot)) { + if (!cb710_mmc_command(mmc, mrq->cmd) && mrq->stop) + cb710_mmc_command(mmc, mrq->stop); + mdelay(1); + } else { + mrq->cmd->error = -ENOMEDIUM; + } + + tasklet_schedule(&reader->finish_req_tasklet); +} + +static int cb710_mmc_powerup(struct cb710_slot *slot) +{ +#ifdef CONFIG_CB710_DEBUG + struct cb710_chip *chip = cb710_slot_to_chip(slot); +#endif + int err; + + /* a lot of magic; see comment in cb710_mmc_set_clock() */ + dev_dbg(cb710_slot_dev(slot), "bus powerup\n"); + cb710_dump_regs(chip, CB710_DUMP_REGS_MMC); + err = cb710_wait_while_busy(slot, CB710_MMC_S2_BUSY_20); + if (unlikely(err)) + return err; + cb710_modify_port_8(slot, CB710_MMC_CONFIG1_PORT, 0x80, 0); + cb710_modify_port_8(slot, CB710_MMC_CONFIG3_PORT, 0x80, 0); + cb710_dump_regs(chip, CB710_DUMP_REGS_MMC); + mdelay(1); + dev_dbg(cb710_slot_dev(slot), "after delay 1\n"); + cb710_dump_regs(chip, CB710_DUMP_REGS_MMC); + err = cb710_wait_while_busy(slot, CB710_MMC_S2_BUSY_20); + if (unlikely(err)) + return err; + cb710_modify_port_8(slot, CB710_MMC_CONFIG1_PORT, 0x09, 0); + cb710_dump_regs(chip, CB710_DUMP_REGS_MMC); + mdelay(1); + dev_dbg(cb710_slot_dev(slot), "after delay 2\n"); + cb710_dump_regs(chip, CB710_DUMP_REGS_MMC); + err = cb710_wait_while_busy(slot, CB710_MMC_S2_BUSY_20); + if (unlikely(err)) + return err; + cb710_modify_port_8(slot, CB710_MMC_CONFIG1_PORT, 0, 0x08); + cb710_dump_regs(chip, CB710_DUMP_REGS_MMC); + mdelay(2); + dev_dbg(cb710_slot_dev(slot), "after delay 3\n"); + cb710_dump_regs(chip, CB710_DUMP_REGS_MMC); + cb710_modify_port_8(slot, CB710_MMC_CONFIG0_PORT, 0x06, 0); + cb710_modify_port_8(slot, CB710_MMC_CONFIG1_PORT, 0x70, 0); + cb710_modify_port_8(slot, CB710_MMC_CONFIG2_PORT, 0x80, 0); + cb710_modify_port_8(slot, CB710_MMC_CONFIG3_PORT, 0x03, 0); + cb710_dump_regs(chip, CB710_DUMP_REGS_MMC); + err = cb710_wait_while_busy(slot, CB710_MMC_S2_BUSY_20); + if (unlikely(err)) + return err; + /* This port behaves weird: quick byte reads of 0x08,0x09 return + * 0xFF,0x00 after writing 0xFFFF to 0x08; it works correctly when + * read/written from userspace... What am I missing here? + * (it doesn't depend on write-to-read delay) */ + cb710_write_port_16(slot, CB710_MMC_CONFIGB_PORT, 0xFFFF); + cb710_modify_port_8(slot, CB710_MMC_CONFIG0_PORT, 0x06, 0); + cb710_dump_regs(chip, CB710_DUMP_REGS_MMC); + dev_dbg(cb710_slot_dev(slot), "bus powerup finished\n"); + + return cb710_check_event(slot, 0); +} + +static void cb710_mmc_powerdown(struct cb710_slot *slot) +{ + cb710_modify_port_8(slot, CB710_MMC_CONFIG1_PORT, 0, 0x81); + cb710_modify_port_8(slot, CB710_MMC_CONFIG3_PORT, 0, 0x80); +} + +static void cb710_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) +{ + struct cb710_slot *slot = cb710_mmc_to_slot(mmc); + struct cb710_mmc_reader *reader = mmc_priv(mmc); + int err; + + cb710_mmc_set_clock(mmc, ios->clock); + + if (!cb710_mmc_is_card_inserted(slot)) { + dev_dbg(cb710_slot_dev(slot), + "no card inserted - ignoring bus powerup request\n"); + ios->power_mode = MMC_POWER_OFF; + } + + if (ios->power_mode != reader->last_power_mode) + switch (ios->power_mode) { + case MMC_POWER_ON: + err = cb710_mmc_powerup(slot); + if (err) { + dev_warn(cb710_slot_dev(slot), + "powerup failed (%d)- retrying\n", err); + cb710_mmc_powerdown(slot); + udelay(1); + err = cb710_mmc_powerup(slot); + if (err) + dev_warn(cb710_slot_dev(slot), + "powerup retry failed (%d) - expect errors\n", + err); + } + reader->last_power_mode = MMC_POWER_ON; + break; + case MMC_POWER_OFF: + cb710_mmc_powerdown(slot); + reader->last_power_mode = MMC_POWER_OFF; + break; + case MMC_POWER_UP: + default: + /* ignore */; + } + + cb710_mmc_enable_4bit_data(slot, ios->bus_width != MMC_BUS_WIDTH_1); + + cb710_mmc_enable_irq(slot, CB710_MMC_IE_TEST_MASK, 0); +} + +static int cb710_mmc_get_ro(struct mmc_host *mmc) +{ + struct cb710_slot *slot = cb710_mmc_to_slot(mmc); + + return cb710_read_port_8(slot, CB710_MMC_STATUS3_PORT) + & CB710_MMC_S3_WRITE_PROTECTED; +} + +static int cb710_mmc_irq_handler(struct cb710_slot *slot) +{ + struct mmc_host *mmc = cb710_slot_to_mmc(slot); + struct cb710_mmc_reader *reader = mmc_priv(mmc); + u32 status, config1, config2, irqen; + + status = cb710_read_port_32(slot, CB710_MMC_STATUS_PORT); + irqen = cb710_read_port_32(slot, CB710_MMC_IRQ_ENABLE_PORT); + config2 = cb710_read_port_32(slot, CB710_MMC_CONFIGB_PORT); + config1 = cb710_read_port_32(slot, CB710_MMC_CONFIG_PORT); + + dev_dbg(cb710_slot_dev(slot), "interrupt; status: %08X, " + "ie: %08X, c2: %08X, c1: %08X\n", + status, irqen, config2, config1); + + if (status & (CB710_MMC_S1_CARD_CHANGED << 8)) { + /* ack the event */ + cb710_write_port_8(slot, CB710_MMC_STATUS1_PORT, + CB710_MMC_S1_CARD_CHANGED); + if ((irqen & CB710_MMC_IE_CISTATUS_MASK) + == CB710_MMC_IE_CISTATUS_MASK) + mmc_detect_change(mmc, HZ/5); + } else { + dev_dbg(cb710_slot_dev(slot), "unknown interrupt (test)\n"); + spin_lock(&reader->irq_lock); + __cb710_mmc_enable_irq(slot, 0, CB710_MMC_IE_TEST_MASK); + spin_unlock(&reader->irq_lock); + } + + return 1; +} + +static void cb710_mmc_finish_request_tasklet(unsigned long data) +{ + struct mmc_host *mmc = (void *)data; + struct cb710_mmc_reader *reader = mmc_priv(mmc); + struct mmc_request *mrq = reader->mrq; + + reader->mrq = NULL; + mmc_request_done(mmc, mrq); +} + +static const struct mmc_host_ops cb710_mmc_host = { + .request = cb710_mmc_request, + .set_ios = cb710_mmc_set_ios, + .get_ro = cb710_mmc_get_ro +}; + +#ifdef CONFIG_PM + +static int cb710_mmc_suspend(struct platform_device *pdev, pm_message_t state) +{ + struct cb710_slot *slot = cb710_pdev_to_slot(pdev); + struct mmc_host *mmc = cb710_slot_to_mmc(slot); + int err; + + err = mmc_suspend_host(mmc, state); + if (err) + return err; + + cb710_mmc_enable_irq(slot, 0, ~0); + return 0; +} + +static int cb710_mmc_resume(struct platform_device *pdev) +{ + struct cb710_slot *slot = cb710_pdev_to_slot(pdev); + struct mmc_host *mmc = cb710_slot_to_mmc(slot); + + cb710_mmc_enable_irq(slot, 0, ~0); + + return mmc_resume_host(mmc); +} + +#endif /* CONFIG_PM */ + +static int __devinit cb710_mmc_init(struct platform_device *pdev) +{ + struct cb710_slot *slot = cb710_pdev_to_slot(pdev); + struct cb710_chip *chip = cb710_slot_to_chip(slot); + struct mmc_host *mmc; + struct cb710_mmc_reader *reader; + int err; + u32 val; + + mmc = mmc_alloc_host(sizeof(*reader), cb710_slot_dev(slot)); + if (!mmc) + return -ENOMEM; + + dev_set_drvdata(&pdev->dev, mmc); + + /* harmless (maybe) magic */ + pci_read_config_dword(chip->pdev, 0x48, &val); + val = cb710_src_freq_mhz[(val >> 16) & 0xF]; + dev_dbg(cb710_slot_dev(slot), "source frequency: %dMHz\n", val); + val *= 1000000; + + mmc->ops = &cb710_mmc_host; + mmc->f_max = val; + mmc->f_min = val >> cb710_clock_divider_log2[CB710_MAX_DIVIDER_IDX]; + mmc->ocr_avail = MMC_VDD_32_33|MMC_VDD_33_34; + mmc->caps = MMC_CAP_4_BIT_DATA; + + reader = mmc_priv(mmc); + + tasklet_init(&reader->finish_req_tasklet, + cb710_mmc_finish_request_tasklet, (unsigned long)mmc); + spin_lock_init(&reader->irq_lock); + cb710_dump_regs(chip, CB710_DUMP_REGS_MMC); + + cb710_mmc_enable_irq(slot, 0, ~0); + cb710_set_irq_handler(slot, cb710_mmc_irq_handler); + + err = mmc_add_host(mmc); + if (unlikely(err)) + goto err_free_mmc; + + dev_dbg(cb710_slot_dev(slot), "mmc_hostname is %s\n", + mmc_hostname(mmc)); + + cb710_mmc_enable_irq(slot, CB710_MMC_IE_CARD_INSERTION_STATUS, 0); + + return 0; + +err_free_mmc: + dev_dbg(cb710_slot_dev(slot), "mmc_add_host() failed: %d\n", err); + + mmc_free_host(mmc); + return err; +} + +static int __devexit cb710_mmc_exit(struct platform_device *pdev) +{ + struct cb710_slot *slot = cb710_pdev_to_slot(pdev); + struct mmc_host *mmc = cb710_slot_to_mmc(slot); + struct cb710_mmc_reader *reader = mmc_priv(mmc); + + cb710_mmc_enable_irq(slot, 0, CB710_MMC_IE_CARD_INSERTION_STATUS); + + mmc_remove_host(mmc); + + /* IRQs should be disabled now, but let's stay on the safe side */ + cb710_mmc_enable_irq(slot, 0, ~0); + cb710_set_irq_handler(slot, NULL); + + /* clear config ports - just in case */ + cb710_write_port_32(slot, CB710_MMC_CONFIG_PORT, 0); + cb710_write_port_16(slot, CB710_MMC_CONFIGB_PORT, 0); + + tasklet_kill(&reader->finish_req_tasklet); + + mmc_free_host(mmc); + return 0; +} + +static struct platform_driver cb710_mmc_driver = { + .driver.name = "cb710-mmc", + .probe = cb710_mmc_init, + .remove = __devexit_p(cb710_mmc_exit), +#ifdef CONFIG_PM + .suspend = cb710_mmc_suspend, + .resume = cb710_mmc_resume, +#endif +}; + +static int __init cb710_mmc_init_module(void) +{ + return platform_driver_register(&cb710_mmc_driver); +} + +static void __exit cb710_mmc_cleanup_module(void) +{ + platform_driver_unregister(&cb710_mmc_driver); +} + +module_init(cb710_mmc_init_module); +module_exit(cb710_mmc_cleanup_module); + +MODULE_AUTHOR("Michał Mirosław "); +MODULE_DESCRIPTION("ENE CB710 memory card reader driver - MMC/SD part"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:cb710-mmc"); diff --git a/drivers/mmc/host/cb710-mmc.h b/drivers/mmc/host/cb710-mmc.h new file mode 100644 index 000000000000..e845c776bdd7 --- /dev/null +++ b/drivers/mmc/host/cb710-mmc.h @@ -0,0 +1,104 @@ +/* + * cb710/cb710-mmc.h + * + * Copyright by Michał Mirosław, 2008-2009 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#ifndef LINUX_CB710_MMC_H +#define LINUX_CB710_MMC_H + +#include + +/* per-MMC-reader structure */ +struct cb710_mmc_reader { + struct tasklet_struct finish_req_tasklet; + struct mmc_request *mrq; + spinlock_t irq_lock; + unsigned char last_power_mode; +}; + +/* some device struct walking */ + +static inline struct mmc_host *cb710_slot_to_mmc(struct cb710_slot *slot) +{ + return dev_get_drvdata(&slot->pdev.dev); +} + +static inline struct cb710_slot *cb710_mmc_to_slot(struct mmc_host *mmc) +{ + struct platform_device *pdev = container_of(mmc_dev(mmc), + struct platform_device, dev); + return cb710_pdev_to_slot(pdev); +} + +/* registers (this might be all wrong ;) */ + +#define CB710_MMC_DATA_PORT 0x00 + +#define CB710_MMC_CONFIG_PORT 0x04 +#define CB710_MMC_CONFIG0_PORT 0x04 +#define CB710_MMC_CONFIG1_PORT 0x05 +#define CB710_MMC_C1_4BIT_DATA_BUS 0x40 +#define CB710_MMC_CONFIG2_PORT 0x06 +#define CB710_MMC_C2_READ_PIO_SIZE_MASK 0x0F /* N-1 */ +#define CB710_MMC_CONFIG3_PORT 0x07 + +#define CB710_MMC_CONFIGB_PORT 0x08 + +#define CB710_MMC_IRQ_ENABLE_PORT 0x0C +#define CB710_MMC_IE_TEST_MASK 0x00BF +#define CB710_MMC_IE_CARD_INSERTION_STATUS 0x1000 +#define CB710_MMC_IE_IRQ_ENABLE 0x8000 +#define CB710_MMC_IE_CISTATUS_MASK \ + (CB710_MMC_IE_CARD_INSERTION_STATUS|CB710_MMC_IE_IRQ_ENABLE) + +#define CB710_MMC_STATUS_PORT 0x10 +#define CB710_MMC_STATUS_ERROR_EVENTS 0x60FF +#define CB710_MMC_STATUS0_PORT 0x10 +#define CB710_MMC_S0_FIFO_UNDERFLOW 0x40 +#define CB710_MMC_STATUS1_PORT 0x11 +#define CB710_MMC_S1_COMMAND_SENT 0x01 +#define CB710_MMC_S1_DATA_TRANSFER_DONE 0x02 +#define CB710_MMC_S1_PIO_TRANSFER_DONE 0x04 +#define CB710_MMC_S1_CARD_CHANGED 0x10 +#define CB710_MMC_S1_RESET 0x20 +#define CB710_MMC_STATUS2_PORT 0x12 +#define CB710_MMC_S2_FIFO_READY 0x01 +#define CB710_MMC_S2_FIFO_EMPTY 0x02 +#define CB710_MMC_S2_BUSY_10 0x10 +#define CB710_MMC_S2_BUSY_20 0x20 +#define CB710_MMC_STATUS3_PORT 0x13 +#define CB710_MMC_S3_CARD_DETECTED 0x02 +#define CB710_MMC_S3_WRITE_PROTECTED 0x04 + +#define CB710_MMC_CMD_TYPE_PORT 0x14 +#define CB710_MMC_RSP_TYPE_MASK 0x0007 +#define CB710_MMC_RSP_R1 (0) +#define CB710_MMC_RSP_136 (5) +#define CB710_MMC_RSP_NO_CRC (2) +#define CB710_MMC_RSP_PRESENT_MASK 0x0018 +#define CB710_MMC_RSP_NONE (0 << 3) +#define CB710_MMC_RSP_PRESENT (1 << 3) +#define CB710_MMC_RSP_PRESENT_X (2 << 3) +#define CB710_MMC_CMD_TYPE_MASK 0x0060 +#define CB710_MMC_CMD_BC (0 << 5) +#define CB710_MMC_CMD_BCR (1 << 5) +#define CB710_MMC_CMD_AC (2 << 5) +#define CB710_MMC_CMD_ADTC (3 << 5) +#define CB710_MMC_DATA_READ 0x0080 +#define CB710_MMC_CMD_CODE_MASK 0x3F00 +#define CB710_MMC_CMD_CODE_SHIFT 8 +#define CB710_MMC_IS_APP_CMD 0x4000 +#define CB710_MMC_RSP_BUSY 0x8000 + +#define CB710_MMC_CMD_PARAM_PORT 0x18 +#define CB710_MMC_TRANSFER_SIZE_PORT 0x1C +#define CB710_MMC_RESPONSE0_PORT 0x20 +#define CB710_MMC_RESPONSE1_PORT 0x24 +#define CB710_MMC_RESPONSE2_PORT 0x28 +#define CB710_MMC_RESPONSE3_PORT 0x2C + +#endif /* LINUX_CB710_MMC_H */ -- cgit v1.2.3 From 09adfe454c87e3ec5dffbc42567cd7b4b4948522 Mon Sep 17 00:00:00 2001 From: Pierre Ossman Date: Thu, 4 Jun 2009 07:53:38 +0200 Subject: cb710: fix printk format string Signed-off-by: Pierre Ossman --- drivers/mmc/host/cb710-mmc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/mmc') diff --git a/drivers/mmc/host/cb710-mmc.c b/drivers/mmc/host/cb710-mmc.c index bc048aae7207..11efefb1af51 100644 --- a/drivers/mmc/host/cb710-mmc.c +++ b/drivers/mmc/host/cb710-mmc.c @@ -219,7 +219,7 @@ static void cb710_mmc_set_transfer_size(struct cb710_slot *slot, cb710_write_port_32(slot, CB710_MMC_TRANSFER_SIZE_PORT, ((count - 1) << 16)|(blocksize - 1)); - dev_vdbg(cb710_slot_dev(slot), "set up for %d block%s of %d bytes\n", + dev_vdbg(cb710_slot_dev(slot), "set up for %zu block%s of %zu bytes\n", count, count == 1 ? "" : "s", blocksize); } -- cgit v1.2.3 From 99d9260c30f0705c95a5839d9184f2e949d2051e Mon Sep 17 00:00:00 2001 From: Pierre Ossman Date: Tue, 9 Jun 2009 20:17:25 +0200 Subject: mxcmmc: remove frequency workaround The MMC core has now been fixed to not send silly frequencies to the drivers which means we can remove this workaround. Signed-off-by: Pierre Ossman --- drivers/mmc/host/mxcmmc.c | 2 -- 1 file changed, 2 deletions(-) (limited to 'drivers/mmc') diff --git a/drivers/mmc/host/mxcmmc.c b/drivers/mmc/host/mxcmmc.c index f4cbe473670e..bc14bb1b0579 100644 --- a/drivers/mmc/host/mxcmmc.c +++ b/drivers/mmc/host/mxcmmc.c @@ -746,8 +746,6 @@ static int mxcmci_probe(struct platform_device *pdev) } mmc->f_min = clk_get_rate(host->clk) >> 16; - if (mmc->f_min < 400000) - mmc->f_min = 400000; mmc->f_max = clk_get_rate(host->clk) >> 1; /* recommended in data sheet */ -- cgit v1.2.3 From a3456a2da14fb1d8246df63bb229623d58d09ce1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Richard=20R=C3=B6jfors?= Date: Thu, 4 Jun 2009 13:57:29 +0200 Subject: sdhci: platform driver for SDHCI MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Added a platform driver which uses the SDHCI core. Signed-off-by: Richard Röjfors Signed-off-by: Pierre Ossman --- drivers/mmc/host/Kconfig | 11 +++ drivers/mmc/host/Makefile | 1 + drivers/mmc/host/sdhci-pltfm.c | 168 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 180 insertions(+) create mode 100644 drivers/mmc/host/sdhci-pltfm.c (limited to 'drivers/mmc') diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig index 75337e442da9..b1cf79083fdd 100644 --- a/drivers/mmc/host/Kconfig +++ b/drivers/mmc/host/Kconfig @@ -83,6 +83,17 @@ config MMC_SDHCI_OF If unsure, say N. +config MMC_SDHCI_PLTFM + tristate "SDHCI support on the platform specific bus" + depends on MMC_SDHCI + help + This selects the platform specific bus support for Secure Digital Host + Controller Interface. + + If you have a controller with this interface, say Y or M here. + + If unsure, say N. + config MMC_OMAP tristate "TI OMAP Multimedia Card Interface support" depends on ARCH_OMAP diff --git a/drivers/mmc/host/Makefile b/drivers/mmc/host/Makefile index ce9c8b6f0f4b..254e441291d0 100644 --- a/drivers/mmc/host/Makefile +++ b/drivers/mmc/host/Makefile @@ -14,6 +14,7 @@ obj-$(CONFIG_MMC_SDHCI) += sdhci.o obj-$(CONFIG_MMC_SDHCI_PCI) += sdhci-pci.o obj-$(CONFIG_MMC_RICOH_MMC) += ricoh_mmc.o obj-$(CONFIG_MMC_SDHCI_OF) += sdhci-of.o +obj-$(CONFIG_MMC_SDHCI_PLTFM) += sdhci-pltfm.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-pltfm.c b/drivers/mmc/host/sdhci-pltfm.c new file mode 100644 index 000000000000..297f40ae6ad5 --- /dev/null +++ b/drivers/mmc/host/sdhci-pltfm.c @@ -0,0 +1,168 @@ +/* + * sdhci-pltfm.c Support for SDHCI platform devices + * Copyright (c) 2009 Intel Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +/* Supports: + * SDHCI platform devices + * + * Inspired by sdhci-pci.c, by Pierre Ossman + */ + +#include +#include +#include + +#include + +#include + +#include "sdhci.h" + +/*****************************************************************************\ + * * + * SDHCI core callbacks * + * * +\*****************************************************************************/ + +static struct sdhci_ops sdhci_pltfm_ops = { +}; + +/*****************************************************************************\ + * * + * Device probing/removal * + * * +\*****************************************************************************/ + +static int __devinit sdhci_pltfm_probe(struct platform_device *pdev) +{ + struct sdhci_host *host; + struct resource *iomem; + int ret; + + BUG_ON(pdev == NULL); + + iomem = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!iomem) { + ret = -ENOMEM; + goto err; + } + + if (resource_size(iomem) != 0x100) + dev_err(&pdev->dev, "Invalid iomem size. You may " + "experience problems.\n"); + + if (pdev->dev.parent) + host = sdhci_alloc_host(pdev->dev.parent, 0); + else + host = sdhci_alloc_host(&pdev->dev, 0); + + if (IS_ERR(host)) { + ret = PTR_ERR(host); + goto err; + } + + host->hw_name = "platform"; + host->ops = &sdhci_pltfm_ops; + host->irq = platform_get_irq(pdev, 0); + + if (!request_mem_region(iomem->start, resource_size(iomem), + mmc_hostname(host->mmc))) { + dev_err(&pdev->dev, "cannot request region\n"); + ret = -EBUSY; + goto err_request; + } + + host->ioaddr = ioremap(iomem->start, resource_size(iomem)); + if (!host->ioaddr) { + dev_err(&pdev->dev, "failed to remap registers\n"); + ret = -ENOMEM; + goto err_remap; + } + + ret = sdhci_add_host(host); + if (ret) + goto err_add_host; + + platform_set_drvdata(pdev, host); + + return 0; + +err_add_host: + iounmap(host->ioaddr); +err_remap: + release_mem_region(iomem->start, resource_size(iomem)); +err_request: + sdhci_free_host(host); +err: + printk(KERN_ERR"Probing of sdhci-pltfm failed: %d\n", ret); + return ret; +} + +static int __devexit sdhci_pltfm_remove(struct platform_device *pdev) +{ + struct sdhci_host *host = platform_get_drvdata(pdev); + struct resource *iomem = platform_get_resource(pdev, IORESOURCE_MEM, 0); + int dead; + u32 scratch; + + dead = 0; + scratch = readl(host->ioaddr + SDHCI_INT_STATUS); + if (scratch == (u32)-1) + dead = 1; + + sdhci_remove_host(host, dead); + iounmap(host->ioaddr); + release_mem_region(iomem->start, resource_size(iomem)); + sdhci_free_host(host); + platform_set_drvdata(pdev, NULL); + + return 0; +} + +static struct platform_driver sdhci_pltfm_driver = { + .driver = { + .name = "sdhci", + .owner = THIS_MODULE, + }, + .probe = sdhci_pltfm_probe, + .remove = __devexit_p(sdhci_pltfm_remove), +}; + +/*****************************************************************************\ + * * + * Driver init/exit * + * * +\*****************************************************************************/ + +static int __init sdhci_drv_init(void) +{ + return platform_driver_register(&sdhci_pltfm_driver); +} + +static void __exit sdhci_drv_exit(void) +{ + platform_driver_unregister(&sdhci_pltfm_driver); +} + +module_init(sdhci_drv_init); +module_exit(sdhci_drv_exit); + +MODULE_DESCRIPTION("Secure Digital Host Controller Interface platform driver"); +MODULE_AUTHOR("Mocean Laboratories "); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:sdhci"); + -- cgit v1.2.3 From c54f6bc67a4398243682f7438a2129906e127d21 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Miros=C5=82aw?= Date: Sat, 13 Jun 2009 12:37:59 +0200 Subject: cb710: more cleanup for the DEBUG case. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Michał Mirosław Signed-off-by: Pierre Ossman --- drivers/mmc/host/Makefile | 3 +++ 1 file changed, 3 insertions(+) (limited to 'drivers/mmc') diff --git a/drivers/mmc/host/Makefile b/drivers/mmc/host/Makefile index 254e441291d0..79da397c5fea 100644 --- a/drivers/mmc/host/Makefile +++ b/drivers/mmc/host/Makefile @@ -32,3 +32,6 @@ obj-$(CONFIG_MMC_SDRICOH_CS) += sdricoh_cs.o obj-$(CONFIG_MMC_TMIO) += tmio_mmc.o obj-$(CONFIG_MMC_CB710) += cb710-mmc.o +ifeq ($(CONFIG_CB710_DEBUG),y) + CFLAGS-cb710-mmc += -DDEBUG +endif -- cgit v1.2.3 From fdd858db7113ca64132de390188d7ca00701013d Mon Sep 17 00:00:00 2001 From: Wolfgang Muees Date: Tue, 26 May 2009 08:56:19 +0100 Subject: mmc_spi: don't use EINVAL for possible transmission errors This patch changes the reported error code for the responses to a command from EINVAL to EFAULT/ENOSYS, as EINVAL is reserved for non-recoverable host errors, and the responses from the SD/MMC card may be because of recoverable transmission errors in the command or in the response. Response codes in SPI mode are NOT protected by a checksum, so don't trust them. Signed-off-by: Wolfgang Muees Acked-by: Matt Fleming Signed-off-by: Pierre Ossman --- drivers/mmc/host/mmc_spi.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) (limited to 'drivers/mmc') diff --git a/drivers/mmc/host/mmc_spi.c b/drivers/mmc/host/mmc_spi.c index a789db8eed28..240608cc7ae9 100644 --- a/drivers/mmc/host/mmc_spi.c +++ b/drivers/mmc/host/mmc_spi.c @@ -335,15 +335,16 @@ checkstatus: /* Status byte: the entire seven-bit R1 response. */ if (cmd->resp[0] != 0) { - if ((R1_SPI_PARAMETER | R1_SPI_ADDRESS - | R1_SPI_ILLEGAL_COMMAND) + if ((R1_SPI_PARAMETER | R1_SPI_ADDRESS) & cmd->resp[0]) - value = -EINVAL; + value = -EFAULT; /* Bad address */ + else if (R1_SPI_ILLEGAL_COMMAND & cmd->resp[0]) + value = -ENOSYS; /* Function not implemented */ else if (R1_SPI_COM_CRC & cmd->resp[0]) - value = -EILSEQ; + value = -EILSEQ; /* Illegal byte sequence */ else if ((R1_SPI_ERASE_SEQ | R1_SPI_ERASE_RESET) & cmd->resp[0]) - value = -EIO; + value = -EIO; /* I/O error */ /* else R1_SPI_IDLE, "it's resetting" */ } -- cgit v1.2.3 From f0e46cc4971f6be96010d9248e0fc076b229d989 Mon Sep 17 00:00:00 2001 From: Philipp Zabel Date: Thu, 4 Jun 2009 20:12:31 +0200 Subject: MFD,mmc: tmio_mmc: make HCLK configurable The Toshiba parts all have a 24 MHz HCLK, but HTC ASIC3 has a 24.576 MHz HCLK and AMD Imageon w228x's HCLK is 80 MHz. With this patch, the MFD driver provides the HCLK frequency to tmio_mmc via mfd_cell->driver_data. Signed-off-by: Philipp Zabel Acked-by: Ian Molton Acked-by: Samuel Ortiz Signed-off-by: Pierre Ossman --- drivers/mmc/host/tmio_mmc.c | 24 +++++++++++------------- 1 file changed, 11 insertions(+), 13 deletions(-) (limited to 'drivers/mmc') diff --git a/drivers/mmc/host/tmio_mmc.c b/drivers/mmc/host/tmio_mmc.c index 63fbd5b7d312..49df71e6be17 100644 --- a/drivers/mmc/host/tmio_mmc.c +++ b/drivers/mmc/host/tmio_mmc.c @@ -35,23 +35,14 @@ #include "tmio_mmc.h" -/* - * Fixme - documentation conflicts on what the clock values are for the - * various dividers. - * One document I have says that its a divisor of a 24MHz clock, another 33. - * This probably depends on HCLK for a given platform, so we may need to - * require HCLK be passed to us from the MFD core. - * - */ - static void tmio_mmc_set_clock(struct tmio_mmc_host *host, int new_clock) { void __iomem *cnf = host->cnf; void __iomem *ctl = host->ctl; - u32 clk = 0, clock; + u32 clk = 0, clock, f_min = host->mmc->f_min; if (new_clock) { - for (clock = 46875, clk = 0x100; new_clock >= (clock<<1); ) { + for (clock = f_min, clk = 0x100; new_clock >= (clock<<1); ) { clock <<= 1; clk >>= 1; } @@ -545,6 +536,7 @@ out: static int __devinit tmio_mmc_probe(struct platform_device *dev) { struct mfd_cell *cell = (struct mfd_cell *)dev->dev.platform_data; + struct tmio_mmc_data *pdata; struct resource *res_ctl, *res_cnf; struct tmio_mmc_host *host; struct mmc_host *mmc; @@ -560,6 +552,12 @@ static int __devinit tmio_mmc_probe(struct platform_device *dev) goto out; } + pdata = cell->driver_data; + if (!pdata || !pdata->hclk) { + ret = -EINVAL; + goto out; + } + mmc = mmc_alloc_host(sizeof(struct tmio_mmc_host), &dev->dev); if (!mmc) goto out; @@ -578,8 +576,8 @@ static int __devinit tmio_mmc_probe(struct platform_device *dev) mmc->ops = &tmio_mmc_ops; mmc->caps = MMC_CAP_4_BIT_DATA; - mmc->f_min = 46875; /* 24000000 / 512 */ - mmc->f_max = 24000000; + mmc->f_max = pdata->hclk; + mmc->f_min = mmc->f_max / 512; mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34; /* Enable the MMC/SD Control registers */ -- cgit v1.2.3 From 5e74672c0925335bb00772530634ac70179e8a19 Mon Sep 17 00:00:00 2001 From: Philipp Zabel Date: Thu, 4 Jun 2009 20:12:32 +0200 Subject: tmio_mmc: add bus_shift support Some ASIC3 devices in the wild are connected with the address bus shifted by one line, so that its 16-bit registers appear 32-bit aligned in host memory space. Signed-off-by: Philipp Zabel Acked-by: Ian Molton Signed-off-by: Pierre Ossman --- drivers/mmc/host/tmio_mmc.c | 129 ++++++++++++++++++++------------------------ drivers/mmc/host/tmio_mmc.h | 77 ++++++++++++++++++++++---- 2 files changed, 125 insertions(+), 81 deletions(-) (limited to 'drivers/mmc') diff --git a/drivers/mmc/host/tmio_mmc.c b/drivers/mmc/host/tmio_mmc.c index 49df71e6be17..01add9bd6289 100644 --- a/drivers/mmc/host/tmio_mmc.c +++ b/drivers/mmc/host/tmio_mmc.c @@ -37,8 +37,6 @@ static void tmio_mmc_set_clock(struct tmio_mmc_host *host, int new_clock) { - void __iomem *cnf = host->cnf; - void __iomem *ctl = host->ctl; u32 clk = 0, clock, f_min = host->mmc->f_min; if (new_clock) { @@ -50,45 +48,39 @@ static void tmio_mmc_set_clock(struct tmio_mmc_host *host, int new_clock) clk = 0x20000; clk >>= 2; - tmio_iowrite8((clk & 0x8000) ? 0 : 1, cnf + CNF_SD_CLK_MODE); + sd_config_write8(host, CNF_SD_CLK_MODE, (clk & 0x8000) ? 0 : 1); clk |= 0x100; } - tmio_iowrite16(clk, ctl + CTL_SD_CARD_CLK_CTL); + sd_ctrl_write16(host, CTL_SD_CARD_CLK_CTL, clk); } static void tmio_mmc_clk_stop(struct tmio_mmc_host *host) { - void __iomem *ctl = host->ctl; - - tmio_iowrite16(0x0000, ctl + CTL_CLK_AND_WAIT_CTL); + sd_ctrl_write16(host, CTL_CLK_AND_WAIT_CTL, 0x0000); msleep(10); - tmio_iowrite16(tmio_ioread16(ctl + CTL_SD_CARD_CLK_CTL) & ~0x0100, - ctl + CTL_SD_CARD_CLK_CTL); + sd_ctrl_write16(host, CTL_SD_CARD_CLK_CTL, ~0x0100 & + sd_ctrl_read16(host, CTL_SD_CARD_CLK_CTL)); msleep(10); } static void tmio_mmc_clk_start(struct tmio_mmc_host *host) { - void __iomem *ctl = host->ctl; - - tmio_iowrite16(tmio_ioread16(ctl + CTL_SD_CARD_CLK_CTL) | 0x0100, - ctl + CTL_SD_CARD_CLK_CTL); + sd_ctrl_write16(host, CTL_SD_CARD_CLK_CTL, 0x0100 | + sd_ctrl_read16(host, CTL_SD_CARD_CLK_CTL)); msleep(10); - tmio_iowrite16(0x0100, ctl + CTL_CLK_AND_WAIT_CTL); + sd_ctrl_write16(host, CTL_CLK_AND_WAIT_CTL, 0x0100); msleep(10); } static void reset(struct tmio_mmc_host *host) { - void __iomem *ctl = host->ctl; - /* FIXME - should we set stop clock reg here */ - tmio_iowrite16(0x0000, ctl + CTL_RESET_SD); - tmio_iowrite16(0x0000, ctl + CTL_RESET_SDIO); + sd_ctrl_write16(host, CTL_RESET_SD, 0x0000); + sd_ctrl_write16(host, CTL_RESET_SDIO, 0x0000); msleep(10); - tmio_iowrite16(0x0001, ctl + CTL_RESET_SD); - tmio_iowrite16(0x0001, ctl + CTL_RESET_SDIO); + sd_ctrl_write16(host, CTL_RESET_SD, 0x0001); + sd_ctrl_write16(host, CTL_RESET_SDIO, 0x0001); msleep(10); } @@ -120,13 +112,12 @@ tmio_mmc_finish_request(struct tmio_mmc_host *host) static int tmio_mmc_start_command(struct tmio_mmc_host *host, struct mmc_command *cmd) { - void __iomem *ctl = host->ctl; struct mmc_data *data = host->data; int c = cmd->opcode; /* Command 12 is handled by hardware */ if (cmd->opcode == 12 && !cmd->arg) { - tmio_iowrite16(0x001, ctl + CTL_STOP_INTERNAL_ACTION); + sd_ctrl_write16(host, CTL_STOP_INTERNAL_ACTION, 0x001); return 0; } @@ -151,18 +142,18 @@ tmio_mmc_start_command(struct tmio_mmc_host *host, struct mmc_command *cmd) if (data) { c |= DATA_PRESENT; if (data->blocks > 1) { - tmio_iowrite16(0x100, ctl + CTL_STOP_INTERNAL_ACTION); + sd_ctrl_write16(host, CTL_STOP_INTERNAL_ACTION, 0x100); c |= TRANSFER_MULTI; } if (data->flags & MMC_DATA_READ) c |= TRANSFER_READ; } - enable_mmc_irqs(ctl, TMIO_MASK_CMD); + enable_mmc_irqs(host, TMIO_MASK_CMD); /* Fire off the command */ - tmio_iowrite32(cmd->arg, ctl + CTL_ARG_REG); - tmio_iowrite16(c, ctl + CTL_SD_CMD); + sd_ctrl_write32(host, CTL_ARG_REG, cmd->arg); + sd_ctrl_write16(host, CTL_SD_CMD, c); return 0; } @@ -174,7 +165,6 @@ tmio_mmc_start_command(struct tmio_mmc_host *host, struct mmc_command *cmd) */ static inline void tmio_mmc_pio_irq(struct tmio_mmc_host *host) { - void __iomem *ctl = host->ctl; struct mmc_data *data = host->data; unsigned short *buf; unsigned int count; @@ -197,9 +187,9 @@ static inline void tmio_mmc_pio_irq(struct tmio_mmc_host *host) /* Transfer the data */ if (data->flags & MMC_DATA_READ) - tmio_ioread16_rep(ctl + CTL_SD_DATA_PORT, buf, count >> 1); + sd_ctrl_read16_rep(host, CTL_SD_DATA_PORT, buf, count >> 1); else - tmio_iowrite16_rep(ctl + CTL_SD_DATA_PORT, buf, count >> 1); + sd_ctrl_write16_rep(host, CTL_SD_DATA_PORT, buf, count >> 1); host->sg_off += count; @@ -213,7 +203,6 @@ static inline void tmio_mmc_pio_irq(struct tmio_mmc_host *host) static inline void tmio_mmc_data_irq(struct tmio_mmc_host *host) { - void __iomem *ctl = host->ctl; struct mmc_data *data = host->data; struct mmc_command *stop; @@ -242,13 +231,13 @@ static inline void tmio_mmc_data_irq(struct tmio_mmc_host *host) */ if (data->flags & MMC_DATA_READ) - disable_mmc_irqs(ctl, TMIO_MASK_READOP); + disable_mmc_irqs(host, TMIO_MASK_READOP); else - disable_mmc_irqs(ctl, TMIO_MASK_WRITEOP); + disable_mmc_irqs(host, TMIO_MASK_WRITEOP); if (stop) { if (stop->opcode == 12 && !stop->arg) - tmio_iowrite16(0x000, ctl + CTL_STOP_INTERNAL_ACTION); + sd_ctrl_write16(host, CTL_STOP_INTERNAL_ACTION, 0x000); else BUG(); } @@ -259,9 +248,8 @@ static inline void tmio_mmc_data_irq(struct tmio_mmc_host *host) static inline void tmio_mmc_cmd_irq(struct tmio_mmc_host *host, unsigned int stat) { - void __iomem *ctl = host->ctl, *addr; struct mmc_command *cmd = host->cmd; - int i; + int i, addr; if (!host->cmd) { pr_debug("Spurious CMD irq\n"); @@ -275,8 +263,8 @@ static inline void tmio_mmc_cmd_irq(struct tmio_mmc_host *host, * modify the order of the response for short response command types. */ - for (i = 3, addr = ctl + CTL_RESPONSE ; i >= 0 ; i--, addr += 4) - cmd->resp[i] = tmio_ioread32(addr); + for (i = 3, addr = CTL_RESPONSE ; i >= 0 ; i--, addr += 4) + cmd->resp[i] = sd_ctrl_read32(host, addr); if (cmd->flags & MMC_RSP_136) { cmd->resp[0] = (cmd->resp[0] << 8) | (cmd->resp[1] >> 24); @@ -298,9 +286,9 @@ static inline void tmio_mmc_cmd_irq(struct tmio_mmc_host *host, */ if (host->data && !cmd->error) { if (host->data->flags & MMC_DATA_READ) - enable_mmc_irqs(ctl, TMIO_MASK_READOP); + enable_mmc_irqs(host, TMIO_MASK_READOP); else - enable_mmc_irqs(ctl, TMIO_MASK_WRITEOP); + enable_mmc_irqs(host, TMIO_MASK_WRITEOP); } else { tmio_mmc_finish_request(host); } @@ -312,20 +300,19 @@ static inline void tmio_mmc_cmd_irq(struct tmio_mmc_host *host, static irqreturn_t tmio_mmc_irq(int irq, void *devid) { struct tmio_mmc_host *host = devid; - void __iomem *ctl = host->ctl; unsigned int ireg, irq_mask, status; pr_debug("MMC IRQ begin\n"); - status = tmio_ioread32(ctl + CTL_STATUS); - irq_mask = tmio_ioread32(ctl + CTL_IRQ_MASK); + status = sd_ctrl_read32(host, CTL_STATUS); + irq_mask = sd_ctrl_read32(host, CTL_IRQ_MASK); ireg = status & TMIO_MASK_IRQ & ~irq_mask; pr_debug_status(status); pr_debug_status(ireg); if (!ireg) { - disable_mmc_irqs(ctl, status & ~irq_mask); + disable_mmc_irqs(host, status & ~irq_mask); pr_debug("tmio_mmc: Spurious irq, disabling! " "0x%08x 0x%08x 0x%08x\n", status, irq_mask, ireg); @@ -337,7 +324,7 @@ static irqreturn_t tmio_mmc_irq(int irq, void *devid) while (ireg) { /* Card insert / remove attempts */ if (ireg & (TMIO_STAT_CARD_INSERT | TMIO_STAT_CARD_REMOVE)) { - ack_mmc_irqs(ctl, TMIO_STAT_CARD_INSERT | + ack_mmc_irqs(host, TMIO_STAT_CARD_INSERT | TMIO_STAT_CARD_REMOVE); mmc_detect_change(host->mmc, 0); } @@ -349,25 +336,25 @@ static irqreturn_t tmio_mmc_irq(int irq, void *devid) /* Command completion */ if (ireg & TMIO_MASK_CMD) { - ack_mmc_irqs(ctl, TMIO_MASK_CMD); + ack_mmc_irqs(host, TMIO_MASK_CMD); tmio_mmc_cmd_irq(host, status); } /* Data transfer */ if (ireg & (TMIO_STAT_RXRDY | TMIO_STAT_TXRQ)) { - ack_mmc_irqs(ctl, TMIO_STAT_RXRDY | TMIO_STAT_TXRQ); + ack_mmc_irqs(host, TMIO_STAT_RXRDY | TMIO_STAT_TXRQ); tmio_mmc_pio_irq(host); } /* Data transfer completion */ if (ireg & TMIO_STAT_DATAEND) { - ack_mmc_irqs(ctl, TMIO_STAT_DATAEND); + ack_mmc_irqs(host, TMIO_STAT_DATAEND); tmio_mmc_data_irq(host); } /* Check status - keep going until we've handled it all */ - status = tmio_ioread32(ctl + CTL_STATUS); - irq_mask = tmio_ioread32(ctl + CTL_IRQ_MASK); + status = sd_ctrl_read32(host, CTL_STATUS); + irq_mask = sd_ctrl_read32(host, CTL_IRQ_MASK); ireg = status & TMIO_MASK_IRQ & ~irq_mask; pr_debug("Status at end of loop: %08x\n", status); @@ -382,8 +369,6 @@ out: static int tmio_mmc_start_data(struct tmio_mmc_host *host, struct mmc_data *data) { - void __iomem *ctl = host->ctl; - pr_debug("setup data transfer: blocksize %08x nr_blocks %d\n", data->blksz, data->blocks); @@ -398,8 +383,8 @@ static int tmio_mmc_start_data(struct tmio_mmc_host *host, host->data = data; /* Set transfer length / blocksize */ - tmio_iowrite16(data->blksz, ctl + CTL_SD_XFER_LEN); - tmio_iowrite16(data->blocks, ctl + CTL_XFER_BLK_COUNT); + sd_ctrl_write16(host, CTL_SD_XFER_LEN, data->blksz); + sd_ctrl_write16(host, CTL_XFER_BLK_COUNT, data->blocks); return 0; } @@ -440,8 +425,6 @@ fail: static void tmio_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) { struct tmio_mmc_host *host = mmc_priv(mmc); - void __iomem *cnf = host->cnf; - void __iomem *ctl = host->ctl; if (ios->clock) tmio_mmc_set_clock(host, ios->clock); @@ -449,12 +432,12 @@ static void tmio_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) /* Power sequence - OFF -> ON -> UP */ switch (ios->power_mode) { case MMC_POWER_OFF: /* power down SD bus */ - tmio_iowrite8(0x00, cnf + CNF_PWR_CTL_2); + sd_config_write8(host, CNF_PWR_CTL_2, 0x00); tmio_mmc_clk_stop(host); break; case MMC_POWER_ON: /* power up SD bus */ - tmio_iowrite8(0x02, cnf + CNF_PWR_CTL_2); + sd_config_write8(host, CNF_PWR_CTL_2, 0x02); break; case MMC_POWER_UP: /* start bus clock */ tmio_mmc_clk_start(host); @@ -463,10 +446,10 @@ static void tmio_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) switch (ios->bus_width) { case MMC_BUS_WIDTH_1: - tmio_iowrite16(0x80e0, ctl + CTL_SD_MEM_CARD_OPT); + sd_ctrl_write16(host, CTL_SD_MEM_CARD_OPT, 0x80e0); break; case MMC_BUS_WIDTH_4: - tmio_iowrite16(0x00e0, ctl + CTL_SD_MEM_CARD_OPT); + sd_ctrl_write16(host, CTL_SD_MEM_CARD_OPT, 0x00e0); break; } @@ -477,9 +460,8 @@ static void tmio_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) static int tmio_mmc_get_ro(struct mmc_host *mmc) { struct tmio_mmc_host *host = mmc_priv(mmc); - void __iomem *ctl = host->ctl; - return (tmio_ioread16(ctl + CTL_STATUS) & TMIO_STAT_WRPROTECT) ? 0 : 1; + return (sd_ctrl_read16(host, CTL_STATUS) & TMIO_STAT_WRPROTECT) ? 0 : 1; } static struct mmc_host_ops tmio_mmc_ops = { @@ -509,12 +491,12 @@ static int tmio_mmc_resume(struct platform_device *dev) struct mfd_cell *cell = (struct mfd_cell *)dev->dev.platform_data; struct mmc_host *mmc = platform_get_drvdata(dev); struct tmio_mmc_host *host = mmc_priv(mmc); - void __iomem *cnf = host->cnf; int ret = 0; /* Enable the MMC/SD Control registers */ - tmio_iowrite16(SDCREN, cnf + CNF_CMD); - tmio_iowrite32(dev->resource[0].start & 0xfffe, cnf + CNF_CTL_BASE); + sd_config_write16(host, CNF_CMD, SDCREN); + sd_config_write32(host, CNF_CTL_BASE, + (dev->resource[0].start >> host->bus_shift) & 0xfffe); /* Tell the MFD core we are ready to be enabled */ if (cell->enable) { @@ -566,6 +548,9 @@ static int __devinit tmio_mmc_probe(struct platform_device *dev) host->mmc = mmc; platform_set_drvdata(dev, mmc); + /* SD control register space size is 0x200, 0x400 for bus_shift=1 */ + host->bus_shift = resource_size(res_ctl) >> 10; + host->ctl = ioremap(res_ctl->start, resource_size(res_ctl)); if (!host->ctl) goto host_free; @@ -581,9 +566,9 @@ static int __devinit tmio_mmc_probe(struct platform_device *dev) mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34; /* Enable the MMC/SD Control registers */ - tmio_iowrite16(SDCREN, host->cnf + CNF_CMD); - tmio_iowrite32(dev->resource[0].start & 0xfffe, - host->cnf + CNF_CTL_BASE); + sd_config_write16(host, CNF_CMD, SDCREN); + sd_config_write32(host, CNF_CTL_BASE, + (dev->resource[0].start >> host->bus_shift) & 0xfffe); /* Tell the MFD core we are ready to be enabled */ if (cell->enable) { @@ -593,13 +578,13 @@ static int __devinit tmio_mmc_probe(struct platform_device *dev) } /* Disable SD power during suspend */ - tmio_iowrite8(0x01, host->cnf + CNF_PWR_CTL_3); + sd_config_write8(host, CNF_PWR_CTL_3, 0x01); /* The below is required but why? FIXME */ - tmio_iowrite8(0x1f, host->cnf + CNF_STOP_CLK_CTL); + sd_config_write8(host, CNF_STOP_CLK_CTL, 0x1f); /* Power down SD bus*/ - tmio_iowrite8(0x0, host->cnf + CNF_PWR_CTL_2); + sd_config_write8(host, CNF_PWR_CTL_2, 0x00); tmio_mmc_clk_stop(host); reset(host); @@ -610,7 +595,7 @@ static int __devinit tmio_mmc_probe(struct platform_device *dev) else goto unmap_cnf; - disable_mmc_irqs(host->ctl, TMIO_MASK_ALL); + disable_mmc_irqs(host, TMIO_MASK_ALL); ret = request_irq(host->irq, tmio_mmc_irq, IRQF_DISABLED, "tmio-mmc", host); @@ -625,7 +610,7 @@ static int __devinit tmio_mmc_probe(struct platform_device *dev) (unsigned long)host->ctl, host->irq); /* Unmask the IRQs we want to know about */ - enable_mmc_irqs(host->ctl, TMIO_MASK_IRQ); + enable_mmc_irqs(host, TMIO_MASK_IRQ); return 0; diff --git a/drivers/mmc/host/tmio_mmc.h b/drivers/mmc/host/tmio_mmc.h index 9c831ab2ece6..9fa998594974 100644 --- a/drivers/mmc/host/tmio_mmc.h +++ b/drivers/mmc/host/tmio_mmc.h @@ -83,34 +83,36 @@ TMIO_STAT_CARD_REMOVE | TMIO_STAT_CARD_INSERT) #define TMIO_MASK_IRQ (TMIO_MASK_READOP | TMIO_MASK_WRITEOP | TMIO_MASK_CMD) -#define enable_mmc_irqs(ctl, i) \ + +#define enable_mmc_irqs(host, i) \ do { \ u32 mask;\ - mask = tmio_ioread32((ctl) + CTL_IRQ_MASK); \ + mask = sd_ctrl_read32((host), CTL_IRQ_MASK); \ mask &= ~((i) & TMIO_MASK_IRQ); \ - tmio_iowrite32(mask, (ctl) + CTL_IRQ_MASK); \ + sd_ctrl_write32((host), CTL_IRQ_MASK, mask); \ } while (0) -#define disable_mmc_irqs(ctl, i) \ +#define disable_mmc_irqs(host, i) \ do { \ u32 mask;\ - mask = tmio_ioread32((ctl) + CTL_IRQ_MASK); \ + mask = sd_ctrl_read32((host), CTL_IRQ_MASK); \ mask |= ((i) & TMIO_MASK_IRQ); \ - tmio_iowrite32(mask, (ctl) + CTL_IRQ_MASK); \ + sd_ctrl_write32((host), CTL_IRQ_MASK, mask); \ } while (0) -#define ack_mmc_irqs(ctl, i) \ +#define ack_mmc_irqs(host, i) \ do { \ u32 mask;\ - mask = tmio_ioread32((ctl) + CTL_STATUS); \ + mask = sd_ctrl_read32((host), CTL_STATUS); \ mask &= ~((i) & TMIO_MASK_IRQ); \ - tmio_iowrite32(mask, (ctl) + CTL_STATUS); \ + sd_ctrl_write32((host), CTL_STATUS, mask); \ } while (0) struct tmio_mmc_host { void __iomem *cnf; void __iomem *ctl; + unsigned long bus_shift; struct mmc_command *cmd; struct mmc_request *mrq; struct mmc_data *data; @@ -123,6 +125,63 @@ struct tmio_mmc_host { unsigned int sg_off; }; +#include + +static inline u16 sd_ctrl_read16(struct tmio_mmc_host *host, int addr) +{ + return readw(host->ctl + (addr << host->bus_shift)); +} + +static inline void sd_ctrl_read16_rep(struct tmio_mmc_host *host, int addr, + u16 *buf, int count) +{ + readsw(host->ctl + (addr << host->bus_shift), buf, count); +} + +static inline u32 sd_ctrl_read32(struct tmio_mmc_host *host, int addr) +{ + return readw(host->ctl + (addr << host->bus_shift)) | + readw(host->ctl + ((addr + 2) << host->bus_shift)) << 16; +} + +static inline void sd_ctrl_write16(struct tmio_mmc_host *host, int addr, + u16 val) +{ + writew(val, host->ctl + (addr << host->bus_shift)); +} + +static inline void sd_ctrl_write16_rep(struct tmio_mmc_host *host, int addr, + u16 *buf, int count) +{ + writesw(host->ctl + (addr << host->bus_shift), buf, count); +} + +static inline void sd_ctrl_write32(struct tmio_mmc_host *host, int addr, + u32 val) +{ + writew(val, host->ctl + (addr << host->bus_shift)); + writew(val >> 16, host->ctl + ((addr + 2) << host->bus_shift)); +} + +static inline void sd_config_write8(struct tmio_mmc_host *host, int addr, + u8 val) +{ + writeb(val, host->cnf + (addr << host->bus_shift)); +} + +static inline void sd_config_write16(struct tmio_mmc_host *host, int addr, + u16 val) +{ + writew(val, host->cnf + (addr << host->bus_shift)); +} + +static inline void sd_config_write32(struct tmio_mmc_host *host, int addr, + u32 val) +{ + writew(val, host->cnf + (addr << host->bus_shift)); + writew(val >> 16, host->cnf + ((addr + 2) << host->bus_shift)); +} + #include #include -- cgit v1.2.3 From 6c413cc76b893310b3b258b7de47fb74dcc50203 Mon Sep 17 00:00:00 2001 From: Philipp Zabel Date: Thu, 4 Jun 2009 20:12:33 +0200 Subject: tmio_mmc: don't use set_irq_type Use an IRQF_TRIGGER_ flag in request_irq instead. Signed-off-by: Philipp Zabel Acked-by: Ian Molton Signed-off-by: Pierre Ossman --- drivers/mmc/host/tmio_mmc.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) (limited to 'drivers/mmc') diff --git a/drivers/mmc/host/tmio_mmc.c b/drivers/mmc/host/tmio_mmc.c index 01add9bd6289..bbcbd72a46a4 100644 --- a/drivers/mmc/host/tmio_mmc.c +++ b/drivers/mmc/host/tmio_mmc.c @@ -597,13 +597,11 @@ static int __devinit tmio_mmc_probe(struct platform_device *dev) disable_mmc_irqs(host, TMIO_MASK_ALL); - ret = request_irq(host->irq, tmio_mmc_irq, IRQF_DISABLED, "tmio-mmc", - host); + ret = request_irq(host->irq, tmio_mmc_irq, IRQF_DISABLED | + IRQF_TRIGGER_FALLING, "tmio-mmc", host); if (ret) goto unmap_cnf; - set_irq_type(host->irq, IRQ_TYPE_EDGE_FALLING); - mmc_add_host(mmc); printk(KERN_INFO "%s at 0x%08lx irq %d\n", mmc_hostname(host->mmc), -- cgit v1.2.3 From d6c9b5ed37c26503795d241474a17db1d306e7ea Mon Sep 17 00:00:00 2001 From: Philipp Zabel Date: Thu, 4 Jun 2009 20:12:34 +0200 Subject: tmio_mmc: correct probe return value for num_resources != 3 Signed-off-by: Philipp Zabel Acked-by: Ian Molton Signed-off-by: Pierre Ossman --- drivers/mmc/host/tmio_mmc.c | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) (limited to 'drivers/mmc') diff --git a/drivers/mmc/host/tmio_mmc.c b/drivers/mmc/host/tmio_mmc.c index bbcbd72a46a4..b57664070485 100644 --- a/drivers/mmc/host/tmio_mmc.c +++ b/drivers/mmc/host/tmio_mmc.c @@ -522,23 +522,21 @@ static int __devinit tmio_mmc_probe(struct platform_device *dev) struct resource *res_ctl, *res_cnf; struct tmio_mmc_host *host; struct mmc_host *mmc; - int ret = -ENOMEM; + int ret = -EINVAL; if (dev->num_resources != 3) goto out; res_ctl = platform_get_resource(dev, IORESOURCE_MEM, 0); res_cnf = platform_get_resource(dev, IORESOURCE_MEM, 1); - if (!res_ctl || !res_cnf) { - ret = -EINVAL; + if (!res_ctl || !res_cnf) goto out; - } pdata = cell->driver_data; - if (!pdata || !pdata->hclk) { - ret = -EINVAL; + if (!pdata || !pdata->hclk) goto out; - } + + ret = -ENOMEM; mmc = mmc_alloc_host(sizeof(struct tmio_mmc_host), &dev->dev); if (!mmc) -- cgit v1.2.3 From 544f277bb849da0ba86cfc4203a4c9139e2cd927 Mon Sep 17 00:00:00 2001 From: Philipp Zabel Date: Thu, 4 Jun 2009 20:12:35 +0200 Subject: tmio_mmc: map SD control registers after enabling the MFD cell ASIC3 can disable the memory, so we need to wait for mfd_cell->enable to enable the memory before we can map the SD control registers. Signed-off-by: Philipp Zabel Acked-by: Ian Molton Signed-off-by: Pierre Ossman --- drivers/mmc/host/tmio_mmc.c | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) (limited to 'drivers/mmc') diff --git a/drivers/mmc/host/tmio_mmc.c b/drivers/mmc/host/tmio_mmc.c index b57664070485..fe6d2b6c0161 100644 --- a/drivers/mmc/host/tmio_mmc.c +++ b/drivers/mmc/host/tmio_mmc.c @@ -493,11 +493,6 @@ static int tmio_mmc_resume(struct platform_device *dev) struct tmio_mmc_host *host = mmc_priv(mmc); int ret = 0; - /* Enable the MMC/SD Control registers */ - sd_config_write16(host, CNF_CMD, SDCREN); - sd_config_write32(host, CNF_CTL_BASE, - (dev->resource[0].start >> host->bus_shift) & 0xfffe); - /* Tell the MFD core we are ready to be enabled */ if (cell->enable) { ret = cell->enable(dev); @@ -505,6 +500,11 @@ static int tmio_mmc_resume(struct platform_device *dev) goto out; } + /* Enable the MMC/SD Control registers */ + sd_config_write16(host, CNF_CMD, SDCREN); + sd_config_write32(host, CNF_CTL_BASE, + (dev->resource[0].start >> host->bus_shift) & 0xfffe); + mmc_resume_host(mmc); out: @@ -563,11 +563,6 @@ static int __devinit tmio_mmc_probe(struct platform_device *dev) mmc->f_min = mmc->f_max / 512; mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34; - /* Enable the MMC/SD Control registers */ - sd_config_write16(host, CNF_CMD, SDCREN); - sd_config_write32(host, CNF_CTL_BASE, - (dev->resource[0].start >> host->bus_shift) & 0xfffe); - /* Tell the MFD core we are ready to be enabled */ if (cell->enable) { ret = cell->enable(dev); @@ -575,6 +570,11 @@ static int __devinit tmio_mmc_probe(struct platform_device *dev) goto unmap_cnf; } + /* Enable the MMC/SD Control registers */ + sd_config_write16(host, CNF_CMD, SDCREN); + sd_config_write32(host, CNF_CTL_BASE, + (dev->resource[0].start >> host->bus_shift) & 0xfffe); + /* Disable SD power during suspend */ sd_config_write8(host, CNF_PWR_CTL_3, 0x01); -- cgit v1.2.3 From da46a0bd42c81a473618e94871500fb792c98727 Mon Sep 17 00:00:00 2001 From: Ian Molton Date: Fri, 12 Jun 2009 21:53:05 +0100 Subject: tmio_mmc: fix clock setup This patch fixes the clock setup in tmio_mmc. * Incorrect divider setting * Cruft written to the clock registers (seemingly harmless but Not Good (tm)) It also eliminates some unnecessary ifs and tidies the loop syntax. Thanks to Philipp Zabel who discovered the divider issue, commenting "Except for the SDCLK = HCLK (divider bypassed) case, the clock setting resulted in double the requested frequency. The smallest possible frequency (f_max/512) is configured with a divider setting 0x80, not 0x40." Signed-off-by: Ian Molton Signed-off-by: Pierre Ossman --- drivers/mmc/host/tmio_mmc.c | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) (limited to 'drivers/mmc') diff --git a/drivers/mmc/host/tmio_mmc.c b/drivers/mmc/host/tmio_mmc.c index fe6d2b6c0161..10951b7131d6 100644 --- a/drivers/mmc/host/tmio_mmc.c +++ b/drivers/mmc/host/tmio_mmc.c @@ -37,22 +37,17 @@ static void tmio_mmc_set_clock(struct tmio_mmc_host *host, int new_clock) { - u32 clk = 0, clock, f_min = host->mmc->f_min; + u32 clk = 0, clock; if (new_clock) { - for (clock = f_min, clk = 0x100; new_clock >= (clock<<1); ) { + for (clock = host->mmc->f_min, clk = 0x80000080; + new_clock >= (clock<<1); clk >>= 1) clock <<= 1; - clk >>= 1; - } - if (clk & 0x1) - clk = 0x20000; - - clk >>= 2; - sd_config_write8(host, CNF_SD_CLK_MODE, (clk & 0x8000) ? 0 : 1); clk |= 0x100; } - sd_ctrl_write16(host, CTL_SD_CARD_CLK_CTL, clk); + sd_config_write8(host, CNF_SD_CLK_MODE, clk >> 22); + sd_ctrl_write16(host, CTL_SD_CARD_CLK_CTL, clk & 0x1ff); } static void tmio_mmc_clk_stop(struct tmio_mmc_host *host) -- cgit v1.2.3 From be3f4ae0c0c56aab903aceaceed4b9d8418e180e Mon Sep 17 00:00:00 2001 From: Ben Dooks Date: Mon, 8 Jun 2009 23:33:52 +0100 Subject: sdhci: Print ADMA status and pointer on debug If using ADMA, then we should print the ADMA error and current pointer in sdhci_dumpregs() when any debug is requested. Signed-off-by: Ben Dooks Signed-off-by: Pierre Ossman --- drivers/mmc/host/sdhci.c | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'drivers/mmc') diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index 1432a35690dd..35789c6edc19 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -78,6 +78,11 @@ static void sdhci_dumpregs(struct sdhci_host *host) sdhci_readl(host, SDHCI_CAPABILITIES), sdhci_readl(host, SDHCI_MAX_CURRENT)); + if (host->flags & SDHCI_USE_ADMA) + printk(KERN_DEBUG DRIVER_NAME ": ADMA Err: 0x%08x | ADMA Ptr: 0x%08x\n", + readl(host->ioaddr + SDHCI_ADMA_ERROR), + readl(host->ioaddr + SDHCI_ADMA_ADDRESS)); + printk(KERN_DEBUG DRIVER_NAME ": ===========================================\n"); } -- cgit v1.2.3 From 051913dada046ac948eb6f48c0717fc25de2a917 Mon Sep 17 00:00:00 2001 From: Ben Dooks Date: Mon, 8 Jun 2009 23:33:57 +0100 Subject: mmc_block: do not DMA to stack In the write recovery routine, the data to get from the card is allocated from the stack. The DMA mapping documentation says explicitly stack memory is not mappable by any of the DMA calls. Change to using kmalloc() to allocate the memory for the result from the card and then free it once we've finished with the transaction. [ Changed to GFP_KERNEL allocation - Pierre Ossman ] Signed-off-by: Ben Dooks Signed-off-by: Pierre Ossman --- drivers/mmc/card/block.c | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) (limited to 'drivers/mmc') diff --git a/drivers/mmc/card/block.c b/drivers/mmc/card/block.c index 98ffc41eaf2c..adc205c49fbf 100644 --- a/drivers/mmc/card/block.c +++ b/drivers/mmc/card/block.c @@ -147,7 +147,8 @@ struct mmc_blk_request { static u32 mmc_sd_num_wr_blocks(struct mmc_card *card) { int err; - __be32 blocks; + u32 result; + __be32 *blocks; struct mmc_request mrq; struct mmc_command cmd; @@ -199,14 +200,21 @@ static u32 mmc_sd_num_wr_blocks(struct mmc_card *card) mrq.cmd = &cmd; mrq.data = &data; - sg_init_one(&sg, &blocks, 4); + blocks = kmalloc(4, GFP_KERNEL); + if (!blocks) + return (u32)-1; + + sg_init_one(&sg, blocks, 4); mmc_wait_for_req(card->host, &mrq); + result = ntohl(*blocks); + kfree(blocks); + if (cmd.error || data.error) - return (u32)-1; + result = (u32)-1; - return ntohl(blocks); + return result; } static u32 get_card_status(struct mmc_card *card, struct request *req) -- cgit v1.2.3 From e6f2c7adc1318e233d31d113e6896607c54073a4 Mon Sep 17 00:00:00 2001 From: Philipp Zabel Date: Thu, 4 Jun 2009 20:12:37 +0200 Subject: tmio_mmc: allow compilation for ASIC3 Now tmio_mmc is able to drive the MMC/SD cell in ASIC3. Signed-off-by: Philipp Zabel Acked-by: Ian Molton Signed-off-by: Pierre Ossman --- drivers/mmc/host/Kconfig | 4 ++-- drivers/mmc/host/tmio_mmc.c | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers/mmc') diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig index b1cf79083fdd..660bda0c5a1e 100644 --- a/drivers/mmc/host/Kconfig +++ b/drivers/mmc/host/Kconfig @@ -248,10 +248,10 @@ config MMC_SDRICOH_CS config MMC_TMIO tristate "Toshiba Mobile IO Controller (TMIO) MMC/SD function support" - depends on MFD_TMIO + depends on MFD_TMIO || MFD_ASIC3 help This provides support for the SD/MMC cell found in TC6393XB, - T7L66XB and also ipaq ASIC3 + T7L66XB and also HTC ASIC3 config MMC_CB710 tristate "ENE CB710 MMC/SD Interface support" diff --git a/drivers/mmc/host/tmio_mmc.c b/drivers/mmc/host/tmio_mmc.c index 10951b7131d6..91991b460c45 100644 --- a/drivers/mmc/host/tmio_mmc.c +++ b/drivers/mmc/host/tmio_mmc.c @@ -10,7 +10,7 @@ * * Driver for the MMC / SD / SDIO cell found in: * - * TC6393XB TC6391XB TC6387XB T7L66XB + * TC6393XB TC6391XB TC6387XB T7L66XB ASIC3 * * This driver draws mainly on scattered spec sheets, Reverse engineering * of the toshiba e800 SD driver and some parts of the 2.4 ASIC3 driver (4 bit -- cgit v1.2.3 From 199118959e9b31cd6cd6492a323669966061033c Mon Sep 17 00:00:00 2001 From: Nicolas Ferre Date: Fri, 12 Jun 2009 17:58:29 +0200 Subject: atmel-mci: Integrate AT91 specific definition in header file The MCI IP is shared among AVR32 and AT91 SOCs. AT91 has specific bit definitions in the user interface of MCI SD/MMC IP. Signed-off-by: Nicolas Ferre Acked-by: Haavard Skinnemoen Signed-off-by: Pierre Ossman --- drivers/mmc/host/atmel-mci-regs.h | 11 +++++++++++ 1 file changed, 11 insertions(+) (limited to 'drivers/mmc') diff --git a/drivers/mmc/host/atmel-mci-regs.h b/drivers/mmc/host/atmel-mci-regs.h index b58364ed6bba..adfb885db60f 100644 --- a/drivers/mmc/host/atmel-mci-regs.h +++ b/drivers/mmc/host/atmel-mci-regs.h @@ -7,6 +7,11 @@ * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ + +/* + * Superset of MCI IP registers integrated in Atmel AVR32 and AT91 Processors + */ + #ifndef __DRIVERS_MMC_ATMEL_MCI_H__ #define __DRIVERS_MMC_ATMEL_MCI_H__ @@ -14,11 +19,17 @@ #define MCI_CR 0x0000 /* Control */ # define MCI_CR_MCIEN ( 1 << 0) /* MCI Enable */ # define MCI_CR_MCIDIS ( 1 << 1) /* MCI Disable */ +# define MCI_CR_PWSEN ( 1 << 2) /* Power Save Enable */ +# define MCI_CR_PWSDIS ( 1 << 3) /* Power Save Disable */ # define MCI_CR_SWRST ( 1 << 7) /* Software Reset */ #define MCI_MR 0x0004 /* Mode */ # define MCI_MR_CLKDIV(x) ((x) << 0) /* Clock Divider */ +# define MCI_MR_PWSDIV(x) ((x) << 8) /* Power Saving Divider */ # define MCI_MR_RDPROOF ( 1 << 11) /* Read Proof */ # define MCI_MR_WRPROOF ( 1 << 12) /* Write Proof */ +# define MCI_MR_PDCFBYTE ( 1 << 13) /* Force Byte Transfer */ +# define MCI_MR_PDCPADV ( 1 << 14) /* Padding Value */ +# define MCI_MR_PDCMODE ( 1 << 15) /* PDC-oriented Mode */ #define MCI_DTOR 0x0008 /* Data Timeout */ # define MCI_DTOCYC(x) ((x) << 0) /* Data Timeout Cycles */ # define MCI_DTOMUL(x) ((x) << 4) /* Data Timeout Multiplier */ -- cgit v1.2.3 From 7f72134c32eb64c77d1fb35123ba8bf815bf797c Mon Sep 17 00:00:00 2001 From: Nicolas Ferre Date: Fri, 12 Jun 2009 17:58:30 +0200 Subject: atmel-mci: add MCI2 register definitions New revision of Atmel MCI interface adds new features. This is a update of register definition in header file. This new MCI IP is called MCI2. Signed-off-by: Nicolas Ferre Acked-by: Haavard Skinnemoen Signed-off-by: Pierre Ossman --- drivers/mmc/host/atmel-mci-regs.h | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) (limited to 'drivers/mmc') diff --git a/drivers/mmc/host/atmel-mci-regs.h b/drivers/mmc/host/atmel-mci-regs.h index adfb885db60f..fc8a0fe7c5c5 100644 --- a/drivers/mmc/host/atmel-mci-regs.h +++ b/drivers/mmc/host/atmel-mci-regs.h @@ -10,6 +10,7 @@ /* * Superset of MCI IP registers integrated in Atmel AVR32 and AT91 Processors + * Registers and bitfields marked with [2] are only available in MCI2 */ #ifndef __DRIVERS_MMC_ATMEL_MCI_H__ @@ -39,6 +40,7 @@ # define MCI_SDCSEL_MASK ( 3 << 0) # define MCI_SDCBUS_1BIT ( 0 << 6) /* 1-bit data bus */ # define MCI_SDCBUS_4BIT ( 2 << 6) /* 4-bit data bus */ +# define MCI_SDCBUS_8BIT ( 3 << 6) /* 8-bit data bus[2] */ # define MCI_SDCBUS_MASK ( 3 << 6) #define MCI_ARGR 0x0010 /* Command Argument */ #define MCI_CMDR 0x0014 /* Command */ @@ -67,6 +69,9 @@ #define MCI_BLKR 0x0018 /* Block */ # define MCI_BCNT(x) ((x) << 0) /* Data Block Count */ # define MCI_BLKLEN(x) ((x) << 16) /* Data Block Length */ +#define MCI_CSTOR 0x001c /* Completion Signal Timeout[2] */ +# define MCI_CSTOCYC(x) ((x) << 0) /* CST cycles */ +# define MCI_CSTOMUL(x) ((x) << 4) /* CST multiplier */ #define MCI_RSPR 0x0020 /* Response 0 */ #define MCI_RSPR1 0x0024 /* Response 1 */ #define MCI_RSPR2 0x0028 /* Response 2 */ @@ -94,7 +99,24 @@ # define MCI_DTOE ( 1 << 22) /* Data Time-Out Error */ # define MCI_OVRE ( 1 << 30) /* RX Overrun Error */ # define MCI_UNRE ( 1 << 31) /* TX Underrun Error */ +#define MCI_DMA 0x0050 /* DMA Configuration[2] */ +# define MCI_DMA_OFFSET(x) ((x) << 0) /* DMA Write Buffer Offset */ +# define MCI_DMA_CHKSIZE(x) ((x) << 4) /* DMA Channel Read and Write Chunk Size */ +# define MCI_DMAEN ( 1 << 8) /* DMA Hardware Handshaking Enable */ +#define MCI_CFG 0x0054 /* Configuration[2] */ +# define MCI_CFG_FIFOMODE_1DATA ( 1 << 0) /* MCI Internal FIFO control mode */ +# define MCI_CFG_FERRCTRL_COR ( 1 << 4) /* Flow Error flag reset control mode */ +# define MCI_CFG_HSMODE ( 1 << 8) /* High Speed Mode */ +# define MCI_CFG_LSYNC ( 1 << 12) /* Synchronize on the last block */ +#define MCI_WPMR 0x00e4 /* Write Protection Mode[2] */ +# define MCI_WP_EN ( 1 << 0) /* WP Enable */ +# define MCI_WP_KEY (0x4d4349 << 8) /* WP Key */ +#define MCI_WPSR 0x00e8 /* Write Protection Status[2] */ +# define MCI_GET_WP_VS(x) ((x) & 0x0f) +# define MCI_GET_WP_VSRC(x) (((x) >> 8) & 0xffff) +#define MCI_FIFO_APERTURE 0x0200 /* FIFO Aperture[2] */ +/* This is not including the FIFO Aperture on MCI2 */ #define MCI_REGS_SIZE 0x100 /* Register access macros */ -- cgit v1.2.3