Commit 0a053e8c authored by Linus Torvalds's avatar Linus Torvalds

Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/drzeus/mmc

* 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/drzeus/mmc: (42 commits)
  atmel-mci: fix sdc_reg typo
  tmio_mmc: add maintainer
  mmc: Add OpenFirmware bindings for SDHCI driver
  sdhci: Add quirk for forcing maximum block size to 2048 bytes
  sdhci: Add quirk for controllers that need IRQ re-init after reset
  sdhci: Add quirk for controllers that need small delays for PIO
  sdhci: Add set_clock callback and a quirk for nonstandard clocks
  sdhci: Add get_{max,timeout}_clock callbacks
  sdhci: Add support for hosts reporting inverted write-protect state
  sdhci: Add support for card-detection polling
  sdhci: Enable only relevant (DMA/PIO) interrupts during transfers
  sdhci: Split card-detection IRQs management from sdhci_init()
  sdhci: Add support for bus-specific IO memory accessors
  mmc_spi: adjust for delayed data token response
  omap_hsmmc: Wait for SDBP
  omap_hsmmc: Fix MMC3 dma
  omap_hsmmc: Disable SDBP at suspend
  omap_hsmmc: Do not prefix slot name
  omap_hsmmc: Allow cover switch to cause rescan
  omap_hsmmc: Add 8-bit bus width mode support
  ...
parents 601cc11d 32ab83a5
...@@ -2924,6 +2924,12 @@ M: buytenh@marvell.com ...@@ -2924,6 +2924,12 @@ M: buytenh@marvell.com
L: netdev@vger.kernel.org L: netdev@vger.kernel.org
S: Supported S: Supported
MARVELL SOC MMC/SD/SDIO CONTROLLER DRIVER
P: Nicolas Pitre
M: nico@cam.org
L: linux-kernel@vger.kernel.org
S: Maintained
MARVELL YUKON / SYSKONNECT DRIVER MARVELL YUKON / SYSKONNECT DRIVER
P: Mirko Lindner P: Mirko Lindner
M: mlindner@syskonnect.de M: mlindner@syskonnect.de
...@@ -3916,7 +3922,14 @@ S: Maintained ...@@ -3916,7 +3922,14 @@ S: Maintained
SECURE DIGITAL HOST CONTROLLER INTERFACE (SDHCI) DRIVER SECURE DIGITAL HOST CONTROLLER INTERFACE (SDHCI) DRIVER
P: Pierre Ossman P: Pierre Ossman
M: drzeus-sdhci@drzeus.cx M: drzeus-sdhci@drzeus.cx
L: sdhci-devel@list.drzeus.cx L: sdhci-devel@lists.ossman.eu
S: Maintained
SECURE DIGITAL HOST CONTROLLER INTERFACE, OPEN FIRMWARE BINDINGS (SDHCI-OF)
P: Anton Vorontsov
M: avorontsov@ru.mvista.com
L: linuxppc-dev@ozlabs.org
L: sdhci-devel@lists.ossman.eu
S: Maintained S: Maintained
SECURITY SUBSYSTEM SECURITY SUBSYSTEM
...@@ -4394,6 +4407,11 @@ L: tlinux-users@tce.toshiba-dme.co.jp ...@@ -4394,6 +4407,11 @@ L: tlinux-users@tce.toshiba-dme.co.jp
W: http://www.buzzard.org.uk/toshiba/ W: http://www.buzzard.org.uk/toshiba/
S: Maintained S: Maintained
TMIO MMC DRIVER
P: Ian Molton
M: ian@mnementh.co.uk
S: Maintained
TPM DEVICE DRIVER TPM DEVICE DRIVER
P: Debora Velarde P: Debora Velarde
M: debora@linux.vnet.ibm.com M: debora@linux.vnet.ibm.com
......
...@@ -41,6 +41,8 @@ ...@@ -41,6 +41,8 @@
#include "queue.h" #include "queue.h"
MODULE_ALIAS("mmc:block");
/* /*
* max 8 partitions per card * max 8 partitions per card
*/ */
......
...@@ -84,6 +84,14 @@ mmc_bus_uevent(struct device *dev, struct kobj_uevent_env *env) ...@@ -84,6 +84,14 @@ mmc_bus_uevent(struct device *dev, struct kobj_uevent_env *env)
} }
retval = add_uevent_var(env, "MMC_NAME=%s", mmc_card_name(card)); retval = add_uevent_var(env, "MMC_NAME=%s", mmc_card_name(card));
if (retval)
return retval;
/*
* Request the mmc_block device. Note: that this is a direct request
* for the module it carries no information as to what is inserted.
*/
retval = add_uevent_var(env, "MODALIAS=mmc:block");
return retval; return retval;
} }
......
...@@ -298,6 +298,21 @@ void mmc_set_data_timeout(struct mmc_data *data, const struct mmc_card *card) ...@@ -298,6 +298,21 @@ void mmc_set_data_timeout(struct mmc_data *data, const struct mmc_card *card)
data->timeout_clks = 0; data->timeout_clks = 0;
} }
} }
/*
* Some cards need very high timeouts if driven in SPI mode.
* The worst observed timeout was 900ms after writing a
* continuous stream of data until the internal logic
* overflowed.
*/
if (mmc_host_is_spi(card->host)) {
if (data->flags & MMC_DATA_WRITE) {
if (data->timeout_ns < 1000000000)
data->timeout_ns = 1000000000; /* 1s */
} else {
if (data->timeout_ns < 100000000)
data->timeout_ns = 100000000; /* 100ms */
}
}
} }
EXPORT_SYMBOL(mmc_set_data_timeout); EXPORT_SYMBOL(mmc_set_data_timeout);
...@@ -915,6 +930,7 @@ void mmc_stop_host(struct mmc_host *host) ...@@ -915,6 +930,7 @@ void mmc_stop_host(struct mmc_host *host)
spin_unlock_irqrestore(&host->lock, flags); spin_unlock_irqrestore(&host->lock, flags);
#endif #endif
cancel_delayed_work(&host->detect);
mmc_flush_scheduled_work(); mmc_flush_scheduled_work();
mmc_bus_get(host); mmc_bus_get(host);
...@@ -942,6 +958,7 @@ void mmc_stop_host(struct mmc_host *host) ...@@ -942,6 +958,7 @@ void mmc_stop_host(struct mmc_host *host)
*/ */
int mmc_suspend_host(struct mmc_host *host, pm_message_t state) int mmc_suspend_host(struct mmc_host *host, pm_message_t state)
{ {
cancel_delayed_work(&host->detect);
mmc_flush_scheduled_work(); mmc_flush_scheduled_work();
mmc_bus_get(host); mmc_bus_get(host);
...@@ -975,6 +992,7 @@ int mmc_resume_host(struct mmc_host *host) ...@@ -975,6 +992,7 @@ int mmc_resume_host(struct mmc_host *host)
mmc_bus_get(host); mmc_bus_get(host);
if (host->bus_ops && !host->bus_dead) { if (host->bus_ops && !host->bus_dead) {
mmc_power_up(host); mmc_power_up(host);
mmc_select_voltage(host, host->ocr);
BUG_ON(!host->bus_ops->resume); BUG_ON(!host->bus_ops->resume);
host->bus_ops->resume(host); host->bus_ops->resume(host);
} }
......
...@@ -184,6 +184,68 @@ static int mmc_dbg_card_status_get(void *data, u64 *val) ...@@ -184,6 +184,68 @@ static int mmc_dbg_card_status_get(void *data, u64 *val)
DEFINE_SIMPLE_ATTRIBUTE(mmc_dbg_card_status_fops, mmc_dbg_card_status_get, DEFINE_SIMPLE_ATTRIBUTE(mmc_dbg_card_status_fops, mmc_dbg_card_status_get,
NULL, "%08llx\n"); NULL, "%08llx\n");
#define EXT_CSD_STR_LEN 1025
static int mmc_ext_csd_open(struct inode *inode, struct file *filp)
{
struct mmc_card *card = inode->i_private;
char *buf;
ssize_t n = 0;
u8 *ext_csd;
int err, i;
buf = kmalloc(EXT_CSD_STR_LEN + 1, GFP_KERNEL);
if (!buf)
return -ENOMEM;
ext_csd = kmalloc(512, GFP_KERNEL);
if (!ext_csd) {
err = -ENOMEM;
goto out_free;
}
mmc_claim_host(card->host);
err = mmc_send_ext_csd(card, ext_csd);
mmc_release_host(card->host);
if (err)
goto out_free;
for (i = 511; i >= 0; i--)
n += sprintf(buf + n, "%02x", ext_csd[i]);
n += sprintf(buf + n, "\n");
BUG_ON(n != EXT_CSD_STR_LEN);
filp->private_data = buf;
kfree(ext_csd);
return 0;
out_free:
kfree(buf);
kfree(ext_csd);
return err;
}
static ssize_t mmc_ext_csd_read(struct file *filp, char __user *ubuf,
size_t cnt, loff_t *ppos)
{
char *buf = filp->private_data;
return simple_read_from_buffer(ubuf, cnt, ppos,
buf, EXT_CSD_STR_LEN);
}
static int mmc_ext_csd_release(struct inode *inode, struct file *file)
{
kfree(file->private_data);
return 0;
}
static struct file_operations mmc_dbg_ext_csd_fops = {
.open = mmc_ext_csd_open,
.read = mmc_ext_csd_read,
.release = mmc_ext_csd_release,
};
void mmc_add_card_debugfs(struct mmc_card *card) void mmc_add_card_debugfs(struct mmc_card *card)
{ {
struct mmc_host *host = card->host; struct mmc_host *host = card->host;
...@@ -211,6 +273,11 @@ void mmc_add_card_debugfs(struct mmc_card *card) ...@@ -211,6 +273,11 @@ void mmc_add_card_debugfs(struct mmc_card *card)
&mmc_dbg_card_status_fops)) &mmc_dbg_card_status_fops))
goto err; goto err;
if (mmc_card_mmc(card))
if (!debugfs_create_file("ext_csd", S_IRUSR, root, card,
&mmc_dbg_ext_csd_fops))
goto err;
return; return;
err: err:
......
...@@ -223,10 +223,18 @@ static int sdio_read_cis(struct mmc_card *card, struct sdio_func *func) ...@@ -223,10 +223,18 @@ static int sdio_read_cis(struct mmc_card *card, struct sdio_func *func)
if (tpl_code == 0xff) if (tpl_code == 0xff)
break; break;
/* null entries have no link field or data */
if (tpl_code == 0x00)
continue;
ret = mmc_io_rw_direct(card, 0, 0, ptr++, 0, &tpl_link); ret = mmc_io_rw_direct(card, 0, 0, ptr++, 0, &tpl_link);
if (ret) if (ret)
break; break;
/* a size of 0xff also means we're done */
if (tpl_link == 0xff)
break;
this = kmalloc(sizeof(*this) + tpl_link, GFP_KERNEL); this = kmalloc(sizeof(*this) + tpl_link, GFP_KERNEL);
if (!this) if (!this)
return -ENOMEM; return -ENOMEM;
......
...@@ -76,6 +76,10 @@ int mmc_io_rw_direct(struct mmc_card *card, int write, unsigned fn, ...@@ -76,6 +76,10 @@ int mmc_io_rw_direct(struct mmc_card *card, int write, unsigned fn,
BUG_ON(!card); BUG_ON(!card);
BUG_ON(fn > 7); BUG_ON(fn > 7);
/* sanity check */
if (addr & ~0x1FFFF)
return -EINVAL;
memset(&cmd, 0, sizeof(struct mmc_command)); memset(&cmd, 0, sizeof(struct mmc_command));
cmd.opcode = SD_IO_RW_DIRECT; cmd.opcode = SD_IO_RW_DIRECT;
...@@ -125,6 +129,10 @@ int mmc_io_rw_extended(struct mmc_card *card, int write, unsigned fn, ...@@ -125,6 +129,10 @@ int mmc_io_rw_extended(struct mmc_card *card, int write, unsigned fn,
WARN_ON(blocks == 0); WARN_ON(blocks == 0);
WARN_ON(blksz == 0); WARN_ON(blksz == 0);
/* sanity check */
if (addr & ~0x1FFFF)
return -EINVAL;
memset(&mrq, 0, sizeof(struct mmc_request)); memset(&mrq, 0, sizeof(struct mmc_request));
memset(&cmd, 0, sizeof(struct mmc_command)); memset(&cmd, 0, sizeof(struct mmc_command));
memset(&data, 0, sizeof(struct mmc_data)); memset(&data, 0, sizeof(struct mmc_data));
......
...@@ -37,6 +37,13 @@ config MMC_SDHCI ...@@ -37,6 +37,13 @@ config MMC_SDHCI
If unsure, say N. If unsure, say N.
config MMC_SDHCI_IO_ACCESSORS
bool
depends on MMC_SDHCI
help
This is silent Kconfig symbol that is selected by the drivers that
need to overwrite SDHCI IO memory accessors.
config MMC_SDHCI_PCI config MMC_SDHCI_PCI
tristate "SDHCI support on PCI bus" tristate "SDHCI support on PCI bus"
depends on MMC_SDHCI && PCI depends on MMC_SDHCI && PCI
...@@ -65,6 +72,17 @@ config MMC_RICOH_MMC ...@@ -65,6 +72,17 @@ config MMC_RICOH_MMC
If unsure, say Y. If unsure, say Y.
config MMC_SDHCI_OF
tristate "SDHCI support on OpenFirmware platforms"
depends on MMC_SDHCI && PPC_OF
select MMC_SDHCI_IO_ACCESSORS
help
This selects the OF support for Secure Digital Host Controller
Interfaces. So far, only the Freescale eSDHC controller is known
to exist on OF platforms.
If unsure, say N.
config MMC_OMAP config MMC_OMAP
tristate "TI OMAP Multimedia Card Interface support" tristate "TI OMAP Multimedia Card Interface support"
depends on ARCH_OMAP depends on ARCH_OMAP
...@@ -171,6 +189,17 @@ config MMC_TIFM_SD ...@@ -171,6 +189,17 @@ config MMC_TIFM_SD
To compile this driver as a module, choose M here: the To compile this driver as a module, choose M here: the
module will be called tifm_sd. module will be called tifm_sd.
config MMC_MVSDIO
tristate "Marvell MMC/SD/SDIO host driver"
depends on PLAT_ORION
---help---
This selects the Marvell SDIO host driver.
SDIO may currently be found on the Kirkwood 88F6281 and 88F6192
SoC controllers.
To compile this driver as a module, choose M here: the
module will be called mvsdio.
config MMC_SPI config MMC_SPI
tristate "MMC/SD/SDIO over SPI" tristate "MMC/SD/SDIO over SPI"
depends on SPI_MASTER && !HIGHMEM && HAS_DMA depends on SPI_MASTER && !HIGHMEM && HAS_DMA
......
...@@ -13,6 +13,7 @@ obj-$(CONFIG_MMC_MXC) += mxcmmc.o ...@@ -13,6 +13,7 @@ obj-$(CONFIG_MMC_MXC) += mxcmmc.o
obj-$(CONFIG_MMC_SDHCI) += sdhci.o obj-$(CONFIG_MMC_SDHCI) += sdhci.o
obj-$(CONFIG_MMC_SDHCI_PCI) += sdhci-pci.o obj-$(CONFIG_MMC_SDHCI_PCI) += sdhci-pci.o
obj-$(CONFIG_MMC_RICOH_MMC) += ricoh_mmc.o obj-$(CONFIG_MMC_RICOH_MMC) += ricoh_mmc.o
obj-$(CONFIG_MMC_SDHCI_OF) += sdhci-of.o
obj-$(CONFIG_MMC_WBSD) += wbsd.o obj-$(CONFIG_MMC_WBSD) += wbsd.o
obj-$(CONFIG_MMC_AU1X) += au1xmmc.o obj-$(CONFIG_MMC_AU1X) += au1xmmc.o
obj-$(CONFIG_MMC_OMAP) += omap.o obj-$(CONFIG_MMC_OMAP) += omap.o
...@@ -20,6 +21,7 @@ obj-$(CONFIG_MMC_OMAP_HS) += omap_hsmmc.o ...@@ -20,6 +21,7 @@ obj-$(CONFIG_MMC_OMAP_HS) += omap_hsmmc.o
obj-$(CONFIG_MMC_AT91) += at91_mci.o obj-$(CONFIG_MMC_AT91) += at91_mci.o
obj-$(CONFIG_MMC_ATMELMCI) += atmel-mci.o obj-$(CONFIG_MMC_ATMELMCI) += atmel-mci.o
obj-$(CONFIG_MMC_TIFM_SD) += tifm_sd.o obj-$(CONFIG_MMC_TIFM_SD) += tifm_sd.o
obj-$(CONFIG_MMC_MVSDIO) += mvsdio.o
obj-$(CONFIG_MMC_SPI) += mmc_spi.o obj-$(CONFIG_MMC_SPI) += mmc_spi.o
ifeq ($(CONFIG_OF),y) ifeq ($(CONFIG_OF),y)
obj-$(CONFIG_MMC_SPI) += of_mmc_spi.o obj-$(CONFIG_MMC_SPI) += of_mmc_spi.o
......
...@@ -812,7 +812,7 @@ static void atmci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) ...@@ -812,7 +812,7 @@ static void atmci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
slot->sdc_reg |= MCI_SDCBUS_1BIT; slot->sdc_reg |= MCI_SDCBUS_1BIT;
break; break;
case MMC_BUS_WIDTH_4: case MMC_BUS_WIDTH_4:
slot->sdc_reg = MCI_SDCBUS_4BIT; slot->sdc_reg |= MCI_SDCBUS_4BIT;
break; break;
} }
......
...@@ -279,8 +279,11 @@ static int mmc_spi_response_get(struct mmc_spi_host *host, ...@@ -279,8 +279,11 @@ static int mmc_spi_response_get(struct mmc_spi_host *host,
* so it can always DMA directly into the target buffer. * so it can always DMA directly into the target buffer.
* It'd probably be better to memcpy() the first chunk and * It'd probably be better to memcpy() the first chunk and
* avoid extra i/o calls... * avoid extra i/o calls...
*
* Note we check for more than 8 bytes, because in practice,
* some SD cards are slow...
*/ */
for (i = 2; i < 9; i++) { for (i = 2; i < 16; i++) {
value = mmc_spi_readbytes(host, 1); value = mmc_spi_readbytes(host, 1);
if (value < 0) if (value < 0)
goto done; goto done;
...@@ -609,6 +612,7 @@ mmc_spi_writeblock(struct mmc_spi_host *host, struct spi_transfer *t, ...@@ -609,6 +612,7 @@ mmc_spi_writeblock(struct mmc_spi_host *host, struct spi_transfer *t,
struct spi_device *spi = host->spi; struct spi_device *spi = host->spi;
int status, i; int status, i;
struct scratch *scratch = host->data; struct scratch *scratch = host->data;
u32 pattern;
if (host->mmc->use_spi_crc) if (host->mmc->use_spi_crc)
scratch->crc_val = cpu_to_be16( scratch->crc_val = cpu_to_be16(
...@@ -636,8 +640,27 @@ mmc_spi_writeblock(struct mmc_spi_host *host, struct spi_transfer *t, ...@@ -636,8 +640,27 @@ mmc_spi_writeblock(struct mmc_spi_host *host, struct spi_transfer *t,
* doesn't necessarily tell whether the write operation succeeded; * doesn't necessarily tell whether the write operation succeeded;
* it just says if the transmission was ok and whether *earlier* * it just says if the transmission was ok and whether *earlier*
* writes succeeded; see the standard. * writes succeeded; see the standard.
*
* In practice, there are (even modern SDHC-)cards which are late
* in sending the response, and miss the time frame by a few bits,
* so we have to cope with this situation and check the response
* bit-by-bit. Arggh!!!
*/ */
switch (SPI_MMC_RESPONSE_CODE(scratch->status[0])) { pattern = scratch->status[0] << 24;
pattern |= scratch->status[1] << 16;
pattern |= scratch->status[2] << 8;
pattern |= scratch->status[3];
/* First 3 bit of pattern are undefined */
pattern |= 0xE0000000;
/* left-adjust to leading 0 bit */
while (pattern & 0x80000000)
pattern <<= 1;
/* right-adjust for pattern matching. Code is in bit 4..0 now. */
pattern >>= 27;
switch (pattern) {
case SPI_RESPONSE_ACCEPTED: case SPI_RESPONSE_ACCEPTED:
status = 0; status = 0;
break; break;
...@@ -668,8 +691,9 @@ mmc_spi_writeblock(struct mmc_spi_host *host, struct spi_transfer *t, ...@@ -668,8 +691,9 @@ mmc_spi_writeblock(struct mmc_spi_host *host, struct spi_transfer *t,
/* Return when not busy. If we didn't collect that status yet, /* Return when not busy. If we didn't collect that status yet,
* we'll need some more I/O. * we'll need some more I/O.
*/ */
for (i = 1; i < sizeof(scratch->status); i++) { for (i = 4; i < sizeof(scratch->status); i++) {
if (scratch->status[i] != 0) /* card is non-busy if the most recent bit is 1 */
if (scratch->status[i] & 0x01)
return 0; return 0;
} }
return mmc_spi_wait_unbusy(host, timeout); return mmc_spi_wait_unbusy(host, timeout);
...@@ -1204,9 +1228,11 @@ static int mmc_spi_probe(struct spi_device *spi) ...@@ -1204,9 +1228,11 @@ static int mmc_spi_probe(struct spi_device *spi)
/* MMC and SD specs only seem to care that sampling is on the /* MMC and SD specs only seem to care that sampling is on the
* rising edge ... meaning SPI modes 0 or 3. So either SPI mode * rising edge ... meaning SPI modes 0 or 3. So either SPI mode
* should be legit. We'll use mode 0 since it seems to be a * should be legit. We'll use mode 0 since the steady state is 0,
* bit less troublesome on some hardware ... unclear why. * which is appropriate for hotplugging, unless the platform data
* specify mode 3 (if hardware is not compatible to mode 0).
*/ */
if (spi->mode != SPI_MODE_3)
spi->mode = SPI_MODE_0; spi->mode = SPI_MODE_0;
spi->bits_per_word = 8; spi->bits_per_word = 8;
......
This diff is collapsed.
/*
* Copyright (C) 2008 Marvell Semiconductors, All Rights Reserved.
*
* 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 __MVSDIO_H
#define __MVSDIO_H
/*
* Clock rates
*/
#define MVSD_CLOCKRATE_MAX 50000000
#define MVSD_BASE_DIV_MAX 0x7ff
/*
* Register offsets
*/
#define MVSD_SYS_ADDR_LOW 0x000
#define MVSD_SYS_ADDR_HI 0x004
#define MVSD_BLK_SIZE 0x008
#define MVSD_BLK_COUNT 0x00c
#define MVSD_ARG_LOW 0x010
#define MVSD_ARG_HI 0x014
#define MVSD_XFER_MODE 0x018
#define MVSD_CMD 0x01c
#define MVSD_RSP(i) (0x020 + ((i)<<2))
#define MVSD_RSP0 0x020
#define MVSD_RSP1 0x024
#define MVSD_RSP2 0x028
#define MVSD_RSP3 0x02c
#define MVSD_RSP4 0x030
#define MVSD_RSP5 0x034
#define MVSD_RSP6 0x038
#define MVSD_RSP7 0x03c
#define MVSD_FIFO 0x040
#define MVSD_RSP_CRC7 0x044
#define MVSD_HW_STATE 0x048
#define MVSD_HOST_CTRL 0x050
#define MVSD_BLK_GAP_CTRL 0x054
#define MVSD_CLK_CTRL 0x058
#define MVSD_SW_RESET 0x05c
#define MVSD_NOR_INTR_STATUS 0x060
#define MVSD_ERR_INTR_STATUS 0x064
#define MVSD_NOR_STATUS_EN 0x068
#define MVSD_ERR_STATUS_EN 0x06c
#define MVSD_NOR_INTR_EN 0x070
#define MVSD_ERR_INTR_EN 0x074
#define MVSD_AUTOCMD12_ERR_STATUS 0x078
#define MVSD_CURR_BYTE_LEFT 0x07c
#define MVSD_CURR_BLK_LEFT 0x080
#define MVSD_AUTOCMD12_ARG_LOW 0x084
#define MVSD_AUTOCMD12_ARG_HI 0x088
#define MVSD_AUTOCMD12_CMD 0x08c
#define MVSD_AUTO_RSP(i) (0x090 + ((i)<<2))
#define MVSD_AUTO_RSP0 0x090
#define MVSD_AUTO_RSP1 0x094
#define MVSD_AUTO_RSP2 0x098
#define MVSD_CLK_DIV 0x128
#define MVSD_WINDOW_CTRL(i) (0x108 + ((i) << 3))
#define MVSD_WINDOW_BASE(i) (0x10c + ((i) << 3))
/*
* MVSD_CMD
*/
#define MVSD_CMD_RSP_NONE (0 << 0)
#define MVSD_CMD_RSP_136 (1 << 0)
#define MVSD_CMD_RSP_48 (2 << 0)
#define MVSD_CMD_RSP_48BUSY (3 << 0)
#define MVSD_CMD_CHECK_DATACRC16 (1 << 2)
#define MVSD_CMD_CHECK_CMDCRC (1 << 3)
#define MVSD_CMD_INDX_CHECK (1 << 4)
#define MVSD_CMD_DATA_PRESENT (1 << 5)
#define MVSD_UNEXPECTED_RESP (1 << 7)
#define MVSD_CMD_INDEX(x) ((x) << 8)
/*
* MVSD_AUTOCMD12_CMD
*/
#define MVSD_AUTOCMD12_BUSY (1 << 0)
#define MVSD_AUTOCMD12_INDX_CHECK (1 << 1)
#define MVSD_AUTOCMD12_INDEX(x) ((x) << 8)
/*
* MVSD_XFER_MODE
*/
#define MVSD_XFER_MODE_WR_DATA_START (1 << 0)
#define MVSD_XFER_MODE_HW_WR_DATA_EN (1 << 1)
#define MVSD_XFER_MODE_AUTO_CMD12 (1 << 2)
#define MVSD_XFER_MODE_INT_CHK_EN (1 << 3)
#define MVSD_XFER_MODE_TO_HOST (1 << 4)
#define MVSD_XFER_MODE_STOP_CLK (1 << 5)
#define MVSD_XFER_MODE_PIO (1 << 6)
/*
* MVSD_HOST_CTRL
*/
#define MVSD_HOST_CTRL_PUSH_PULL_EN (1 << 0)
#define MVSD_HOST_CTRL_CARD_TYPE_MEM_ONLY (0 << 1)
#define MVSD_HOST_CTRL_CARD_TYPE_IO_ONLY (1 << 1)
#define MVSD_HOST_CTRL_CARD_TYPE_IO_MEM_COMBO (2 << 1)
#define MVSD_HOST_CTRL_CARD_TYPE_IO_MMC (3 << 1)
#define MVSD_HOST_CTRL_CARD_TYPE_MASK (3 << 1)
#define MVSD_HOST_CTRL_BIG_ENDIAN (1 << 3)
#define MVSD_HOST_CTRL_LSB_FIRST (1 << 4)
#define MVSD_HOST_CTRL_DATA_WIDTH_4_BITS (1 << 9)
#define MVSD_HOST_CTRL_HI_SPEED_EN (1 << 10)
#define MVSD_HOST_CTRL_TMOUT_MAX 0xf
#define MVSD_HOST_CTRL_TMOUT_MASK (0xf << 11)
#define MVSD_HOST_CTRL_TMOUT(x) ((x) << 11)
#define MVSD_HOST_CTRL_TMOUT_EN (1 << 15)
/*
* MVSD_SW_RESET
*/
#define MVSD_SW_RESET_NOW (1 << 8)
/*
* Normal interrupt status bits
*/
#define MVSD_NOR_CMD_DONE (1 << 0)
#define MVSD_NOR_XFER_DONE (1 << 1)
#define MVSD_NOR_BLK_GAP_EVT (1 << 2)
#define MVSD_NOR_DMA_DONE (1 << 3)
#define MVSD_NOR_TX_AVAIL (1 << 4)
#define MVSD_NOR_RX_READY (1 << 5)
#define MVSD_NOR_CARD_INT (1 << 8)
#define MVSD_NOR_READ_WAIT_ON (1 << 9)
#define MVSD_NOR_RX_FIFO_8W (1 << 10)
#define MVSD_NOR_TX_FIFO_8W (1 << 11)
#define MVSD_NOR_SUSPEND_ON (1 << 12)
#define MVSD_NOR_AUTOCMD12_DONE (1 << 13)
#define MVSD_NOR_UNEXP_RSP (1 << 14)
#define MVSD_NOR_ERROR (1 << 15)
/*
* Error status bits
*/
#define MVSD_ERR_CMD_TIMEOUT (1 << 0)
#define MVSD_ERR_CMD_CRC (1 << 1)
#define MVSD_ERR_CMD_ENDBIT (1 << 2)
#define MVSD_ERR_CMD_INDEX (1 << 3)
#define MVSD_ERR_DATA_TIMEOUT (1 << 4)
#define MVSD_ERR_DATA_CRC (1 << 5)
#define MVSD_ERR_DATA_ENDBIT (1 << 6)
#define MVSD_ERR_AUTOCMD12 (1 << 8)
#define MVSD_ERR_CMD_STARTBIT (1 << 9)
#define MVSD_ERR_XFER_SIZE (1 << 10)
#define MVSD_ERR_RESP_T_BIT (1 << 11)
#define MVSD_ERR_CRC_ENDBIT (1 << 12)
#define MVSD_ERR_CRC_STARTBIT (1 << 13)
#define MVSD_ERR_CRC_STATUS (1 << 14)
/*
* CMD12 error status bits
*/
#define MVSD_AUTOCMD12_ERR_NOTEXE (1 << 0)
#define MVSD_AUTOCMD12_ERR_TIMEOUT (1 << 1)
#define MVSD_AUTOCMD12_ERR_CRC (1 << 2)
#define MVSD_AUTOCMD12_ERR_ENDBIT (1 << 3)
#define MVSD_AUTOCMD12_ERR_INDEX (1 << 4)
#define MVSD_AUTOCMD12_ERR_RESP_T_BIT (1 << 5)
#define MVSD_AUTOCMD12_ERR_RESP_STARTBIT (1 << 6)
#endif
This diff is collapsed.
/*
* OpenFirmware bindings for Secure Digital Host Controller Interface.
*
* Copyright (c) 2007 Freescale Semiconductor, Inc.
* Copyright (c) 2009 MontaVista Software, Inc.
*
* Authors: Xiaobo Xie <X.Xie@freescale.com>
* Anton Vorontsov <avorontsov@ru.mvista.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or (at
* your option) any later version.
*/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/io.h>
#include <linux/interrupt.h>
#include <linux/delay.h>
#include <linux/of.h>
#include <linux/of_platform.h>
#include <linux/mmc/host.h>
#include "sdhci.h"
struct sdhci_of_data {
unsigned int quirks;
struct sdhci_ops ops;
};
struct sdhci_of_host {
unsigned int clock;
u16 xfer_mode_shadow;
};
/*
* Ops and quirks for the Freescale eSDHC controller.
*/
#define ESDHC_DMA_SYSCTL 0x40c
#define ESDHC_DMA_SNOOP 0x00000040
#define ESDHC_SYSTEM_CONTROL 0x2c
#define ESDHC_CLOCK_MASK 0x0000fff0
#define ESDHC_PREDIV_SHIFT 8
#define ESDHC_DIVIDER_SHIFT 4
#define ESDHC_CLOCK_PEREN 0x00000004
#define ESDHC_CLOCK_HCKEN 0x00000002
#define ESDHC_CLOCK_IPGEN 0x00000001
static u32 esdhc_readl(struct sdhci_host *host, int reg)
{
return in_be32(host->ioaddr + reg);
}
static u16 esdhc_readw(struct sdhci_host *host, int reg)
{
return in_be16(host->ioaddr + (reg ^ 0x2));
}
static u8 esdhc_readb(struct sdhci_host *host, int reg)
{
return in_8(host->ioaddr + (reg ^ 0x3));
}
static void esdhc_writel(struct sdhci_host *host, u32 val, int reg)
{
out_be32(host->ioaddr + reg, val);
}
static void esdhc_writew(struct sdhci_host *host, u16 val, int reg)
{
struct sdhci_of_host *of_host = sdhci_priv(host);
int base = reg & ~0x3;
int shift = (reg & 0x2) * 8;
switch (reg) {
case SDHCI_TRANSFER_MODE:
/*
* Postpone this write, we must do it together with a
* command write that is down below.
*/
of_host->xfer_mode_shadow = val;
return;
case SDHCI_COMMAND:
esdhc_writel(host, val << 16 | of_host->xfer_mode_shadow,
SDHCI_TRANSFER_MODE);
return;
case SDHCI_BLOCK_SIZE:
/*
* Two last DMA bits are reserved, and first one is used for
* non-standard blksz of 4096 bytes that we don't support
* yet. So clear the DMA boundary bits.
*/
val &= ~SDHCI_MAKE_BLKSZ(0x7, 0);
/* fall through */
}
clrsetbits_be32(host->ioaddr + base, 0xffff << shift, val << shift);
}
static void esdhc_writeb(struct sdhci_host *host, u8 val, int reg)
{
int base = reg & ~0x3;
int shift = (reg & 0x3) * 8;
clrsetbits_be32(host->ioaddr + base , 0xff << shift, val << shift);
}
static void esdhc_set_clock(struct sdhci_host *host, unsigned int clock)
{
int div;
int pre_div = 2;
clrbits32(host->ioaddr + ESDHC_SYSTEM_CONTROL, ESDHC_CLOCK_IPGEN |
ESDHC_CLOCK_HCKEN | ESDHC_CLOCK_PEREN | ESDHC_CLOCK_MASK);
if (clock == 0)
goto out;
if (host->max_clk / 16 > clock) {
for (; pre_div < 256; pre_div *= 2) {
if (host->max_clk / pre_div < clock * 16)
break;
}
}
for (div = 1; div <= 16; div++) {
if (host->max_clk / (div * pre_div) <= clock)
break;
}
pre_div >>= 1;
setbits32(host->ioaddr + ESDHC_SYSTEM_CONTROL, ESDHC_CLOCK_IPGEN |
ESDHC_CLOCK_HCKEN | ESDHC_CLOCK_PEREN |
div << ESDHC_DIVIDER_SHIFT | pre_div << ESDHC_PREDIV_SHIFT);
mdelay(100);
out:
host->clock = clock;
}
static int esdhc_enable_dma(struct sdhci_host *host)
{
setbits32(host->ioaddr + ESDHC_DMA_SYSCTL, ESDHC_DMA_SNOOP);
return 0;
}
static unsigned int esdhc_get_max_clock(struct sdhci_host *host)
{
struct sdhci_of_host *of_host = sdhci_priv(host);
return of_host->clock;
}
static unsigned int esdhc_get_timeout_clock(struct sdhci_host *host)
{
struct sdhci_of_host *of_host = sdhci_priv(host);
return of_host->clock / 1000;
}
static struct sdhci_of_data sdhci_esdhc = {
.quirks = SDHCI_QUIRK_FORCE_BLK_SZ_2048 |
SDHCI_QUIRK_BROKEN_CARD_DETECTION |
SDHCI_QUIRK_INVERTED_WRITE_PROTECT |
SDHCI_QUIRK_NO_BUSY_IRQ |
SDHCI_QUIRK_NONSTANDARD_CLOCK |
SDHCI_QUIRK_PIO_NEEDS_DELAY |
SDHCI_QUIRK_RESTORE_IRQS_AFTER_RESET |
SDHCI_QUIRK_NO_CARD_NO_RESET,
.ops = {
.readl = esdhc_readl,
.readw = esdhc_readw,
.readb = esdhc_readb,
.writel = esdhc_writel,
.writew = esdhc_writew,
.writeb = esdhc_writeb,
.set_clock = esdhc_set_clock,
.enable_dma = esdhc_enable_dma,
.get_max_clock = esdhc_get_max_clock,
.get_timeout_clock = esdhc_get_timeout_clock,
},
};
#ifdef CONFIG_PM
static int sdhci_of_suspend(struct of_device *ofdev, pm_message_t state)
{
struct sdhci_host *host = dev_get_drvdata(&ofdev->dev);
return mmc_suspend_host(host->mmc, state);
}
static int sdhci_of_resume(struct of_device *ofdev)
{
struct sdhci_host *host = dev_get_drvdata(&ofdev->dev);
return mmc_resume_host(host->mmc);
}
#else
#define sdhci_of_suspend NULL
#define sdhci_of_resume NULL
#endif
static int __devinit sdhci_of_probe(struct of_device *ofdev,
const struct of_device_id *match)
{
struct device_node *np = ofdev->node;
struct sdhci_of_data *sdhci_of_data = match->data;
struct sdhci_host *host;
struct sdhci_of_host *of_host;
const u32 *clk;
int size;
int ret;
if (!of_device_is_available(np))
return -ENODEV;
host = sdhci_alloc_host(&ofdev->dev, sizeof(*of_host));
if (!host)
return -ENOMEM;
of_host = sdhci_priv(host);
dev_set_drvdata(&ofdev->dev, host);
host->ioaddr = of_iomap(np, 0);
if (!host->ioaddr) {
ret = -ENOMEM;
goto err_addr_map;
}
host->irq = irq_of_parse_and_map(np, 0);
if (!host->irq) {
ret = -EINVAL;
goto err_no_irq;
}
host->hw_name = dev_name(&ofdev->dev);
if (sdhci_of_data) {
host->quirks = sdhci_of_data->quirks;
host->ops = &sdhci_of_data->ops;
}
clk = of_get_property(np, "clock-frequency", &size);
if (clk && size == sizeof(*clk) && *clk)
of_host->clock = *clk;
ret = sdhci_add_host(host);
if (ret)
goto err_add_host;
return 0;
err_add_host:
irq_dispose_mapping(host->irq);
err_no_irq:
iounmap(host->ioaddr);
err_addr_map:
sdhci_free_host(host);
return ret;
}
static int __devexit sdhci_of_remove(struct of_device *ofdev)
{
struct sdhci_host *host = dev_get_drvdata(&ofdev->dev);
sdhci_remove_host(host, 0);
sdhci_free_host(host);
irq_dispose_mapping(host->irq);
iounmap(host->ioaddr);
return 0;
}
static const struct of_device_id sdhci_of_match[] = {
{ .compatible = "fsl,mpc8379-esdhc", .data = &sdhci_esdhc, },
{ .compatible = "fsl,mpc8536-esdhc", .data = &sdhci_esdhc, },
{ .compatible = "generic-sdhci", },
{},
};
MODULE_DEVICE_TABLE(of, sdhci_of_match);
static struct of_platform_driver sdhci_of_driver = {
.driver.name = "sdhci-of",
.match_table = sdhci_of_match,
.probe = sdhci_of_probe,
.remove = __devexit_p(sdhci_of_remove),
.suspend = sdhci_of_suspend,
.resume = sdhci_of_resume,
};
static int __init sdhci_of_init(void)
{
return of_register_platform_driver(&sdhci_of_driver);
}
module_init(sdhci_of_init);
static void __exit sdhci_of_exit(void)
{
of_unregister_platform_driver(&sdhci_of_driver);
}
module_exit(sdhci_of_exit);
MODULE_DESCRIPTION("Secure Digital Host Controller Interface OF driver");
MODULE_AUTHOR("Xiaobo Xie <X.Xie@freescale.com>, "
"Anton Vorontsov <avorontsov@ru.mvista.com>");
MODULE_LICENSE("GPL");
This diff is collapsed.
...@@ -10,6 +10,9 @@ ...@@ -10,6 +10,9 @@
*/ */
#include <linux/scatterlist.h> #include <linux/scatterlist.h>
#include <linux/compiler.h>
#include <linux/types.h>
#include <linux/io.h>
/* /*
* Controller registers * Controller registers
...@@ -123,6 +126,7 @@ ...@@ -123,6 +126,7 @@
SDHCI_INT_DATA_AVAIL | SDHCI_INT_SPACE_AVAIL | \ SDHCI_INT_DATA_AVAIL | SDHCI_INT_SPACE_AVAIL | \
SDHCI_INT_DATA_TIMEOUT | SDHCI_INT_DATA_CRC | \ SDHCI_INT_DATA_TIMEOUT | SDHCI_INT_DATA_CRC | \
SDHCI_INT_DATA_END_BIT) SDHCI_INT_DATA_END_BIT)
#define SDHCI_INT_ALL_MASK ((unsigned int)-1)
#define SDHCI_ACMD12_ERR 0x3C #define SDHCI_ACMD12_ERR 0x3C
...@@ -210,6 +214,18 @@ struct sdhci_host { ...@@ -210,6 +214,18 @@ struct sdhci_host {
#define SDHCI_QUIRK_BROKEN_SMALL_PIO (1<<13) #define SDHCI_QUIRK_BROKEN_SMALL_PIO (1<<13)
/* Controller does not provide transfer-complete interrupt when not busy */ /* Controller does not provide transfer-complete interrupt when not busy */
#define SDHCI_QUIRK_NO_BUSY_IRQ (1<<14) #define SDHCI_QUIRK_NO_BUSY_IRQ (1<<14)
/* Controller has unreliable card detection */
#define SDHCI_QUIRK_BROKEN_CARD_DETECTION (1<<15)
/* Controller reports inverted write-protect state */
#define SDHCI_QUIRK_INVERTED_WRITE_PROTECT (1<<16)
/* Controller has nonstandard clock management */
#define SDHCI_QUIRK_NONSTANDARD_CLOCK (1<<17)
/* Controller does not like fast PIO transfers */
#define SDHCI_QUIRK_PIO_NEEDS_DELAY (1<<18)
/* Controller losing signal/interrupt enable states after reset */
#define SDHCI_QUIRK_RESTORE_IRQS_AFTER_RESET (1<<19)
/* Controller has to be forced to use block size of 2048 bytes */
#define SDHCI_QUIRK_FORCE_BLK_SZ_2048 (1<<20)
int irq; /* Device IRQ */ int irq; /* Device IRQ */
void __iomem * ioaddr; /* Mapped address */ void __iomem * ioaddr; /* Mapped address */
...@@ -267,9 +283,105 @@ struct sdhci_host { ...@@ -267,9 +283,105 @@ struct sdhci_host {
struct sdhci_ops { struct sdhci_ops {
#ifdef CONFIG_MMC_SDHCI_IO_ACCESSORS
u32 (*readl)(struct sdhci_host *host, int reg);
u16 (*readw)(struct sdhci_host *host, int reg);
u8 (*readb)(struct sdhci_host *host, int reg);
void (*writel)(struct sdhci_host *host, u32 val, int reg);
void (*writew)(struct sdhci_host *host, u16 val, int reg);
void (*writeb)(struct sdhci_host *host, u8 val, int reg);
#endif
void (*set_clock)(struct sdhci_host *host, unsigned int clock);
int (*enable_dma)(struct sdhci_host *host); int (*enable_dma)(struct sdhci_host *host);
unsigned int (*get_max_clock)(struct sdhci_host *host);
unsigned int (*get_timeout_clock)(struct sdhci_host *host);
}; };
#ifdef CONFIG_MMC_SDHCI_IO_ACCESSORS
static inline void sdhci_writel(struct sdhci_host *host, u32 val, int reg)
{
if (unlikely(host->ops->writel))
host->ops->writel(host, val, reg);
else
writel(val, host->ioaddr + reg);
}
static inline void sdhci_writew(struct sdhci_host *host, u16 val, int reg)
{
if (unlikely(host->ops->writew))
host->ops->writew(host, val, reg);
else
writew(val, host->ioaddr + reg);
}
static inline void sdhci_writeb(struct sdhci_host *host, u8 val, int reg)
{
if (unlikely(host->ops->writeb))
host->ops->writeb(host, val, reg);
else
writeb(val, host->ioaddr + reg);
}
static inline u32 sdhci_readl(struct sdhci_host *host, int reg)
{
if (unlikely(host->ops->readl))
return host->ops->readl(host, reg);
else
return readl(host->ioaddr + reg);
}
static inline u16 sdhci_readw(struct sdhci_host *host, int reg)
{
if (unlikely(host->ops->readw))
return host->ops->readw(host, reg);
else
return readw(host->ioaddr + reg);
}
static inline u8 sdhci_readb(struct sdhci_host *host, int reg)
{
if (unlikely(host->ops->readb))
return host->ops->readb(host, reg);
else
return readb(host->ioaddr + reg);
}
#else
static inline void sdhci_writel(struct sdhci_host *host, u32 val, int reg)
{
writel(val, host->ioaddr + reg);
}
static inline void sdhci_writew(struct sdhci_host *host, u16 val, int reg)
{
writew(val, host->ioaddr + reg);
}
static inline void sdhci_writeb(struct sdhci_host *host, u8 val, int reg)
{
writeb(val, host->ioaddr + reg);
}
static inline u32 sdhci_readl(struct sdhci_host *host, int reg)
{
return readl(host->ioaddr + reg);
}
static inline u16 sdhci_readw(struct sdhci_host *host, int reg)
{
return readw(host->ioaddr + reg);
}
static inline u8 sdhci_readb(struct sdhci_host *host, int reg)
{
return readb(host->ioaddr + reg);
}
#endif /* CONFIG_MMC_SDHCI_IO_ACCESSORS */
extern struct sdhci_host *sdhci_alloc_host(struct device *dev, extern struct sdhci_host *sdhci_alloc_host(struct device *dev,
size_t priv_size); size_t priv_size);
......
...@@ -568,11 +568,11 @@ static int __devinit tmio_mmc_probe(struct platform_device *dev) ...@@ -568,11 +568,11 @@ static int __devinit tmio_mmc_probe(struct platform_device *dev)
host->mmc = mmc; host->mmc = mmc;
platform_set_drvdata(dev, mmc); platform_set_drvdata(dev, mmc);
host->ctl = ioremap(res_ctl->start, res_ctl->end - res_ctl->start); host->ctl = ioremap(res_ctl->start, resource_size(res_ctl));
if (!host->ctl) if (!host->ctl)
goto host_free; goto host_free;
host->cnf = ioremap(res_cnf->start, res_cnf->end - res_cnf->start); host->cnf = ioremap(res_cnf->start, resource_size(res_cnf));
if (!host->cnf) if (!host->cnf)
goto unmap_ctl; goto unmap_ctl;
...@@ -650,10 +650,10 @@ static int __devexit tmio_mmc_remove(struct platform_device *dev) ...@@ -650,10 +650,10 @@ static int __devexit tmio_mmc_remove(struct platform_device *dev)
if (mmc) { if (mmc) {
struct tmio_mmc_host *host = mmc_priv(mmc); struct tmio_mmc_host *host = mmc_priv(mmc);
mmc_remove_host(mmc); mmc_remove_host(mmc);
mmc_free_host(mmc);
free_irq(host->irq, host); free_irq(host->irq, host);
iounmap(host->ctl); iounmap(host->ctl);
iounmap(host->cnf); iounmap(host->cnf);
mmc_free_host(mmc);
} }
return 0; return 0;
......
...@@ -8,6 +8,9 @@ ...@@ -8,6 +8,9 @@
* published by the Free Software Foundation. * published by the Free Software Foundation.
* *
*/ */
#include <linux/highmem.h>
#define CNF_CMD 0x04 #define CNF_CMD 0x04
#define CNF_CTL_BASE 0x10 #define CNF_CTL_BASE 0x10
#define CNF_INT_PIN 0x3d #define CNF_INT_PIN 0x3d
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment