提交 0a053e8c 编写于 作者: L 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
  ...
......@@ -2924,6 +2924,12 @@ M: buytenh@marvell.com
L: netdev@vger.kernel.org
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
P: Mirko Lindner
M: mlindner@syskonnect.de
......@@ -3916,7 +3922,14 @@ S: Maintained
SECURE DIGITAL HOST CONTROLLER INTERFACE (SDHCI) DRIVER
P: Pierre Ossman
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
SECURITY SUBSYSTEM
......@@ -4394,6 +4407,11 @@ L: tlinux-users@tce.toshiba-dme.co.jp
W: http://www.buzzard.org.uk/toshiba/
S: Maintained
TMIO MMC DRIVER
P: Ian Molton
M: ian@mnementh.co.uk
S: Maintained
TPM DEVICE DRIVER
P: Debora Velarde
M: debora@linux.vnet.ibm.com
......
......@@ -41,6 +41,8 @@
#include "queue.h"
MODULE_ALIAS("mmc:block");
/*
* max 8 partitions per card
*/
......
......@@ -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));
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;
}
......
......@@ -298,6 +298,21 @@ void mmc_set_data_timeout(struct mmc_data *data, const struct mmc_card *card)
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);
......@@ -915,6 +930,7 @@ void mmc_stop_host(struct mmc_host *host)
spin_unlock_irqrestore(&host->lock, flags);
#endif
cancel_delayed_work(&host->detect);
mmc_flush_scheduled_work();
mmc_bus_get(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)
{
cancel_delayed_work(&host->detect);
mmc_flush_scheduled_work();
mmc_bus_get(host);
......@@ -975,6 +992,7 @@ int mmc_resume_host(struct mmc_host *host)
mmc_bus_get(host);
if (host->bus_ops && !host->bus_dead) {
mmc_power_up(host);
mmc_select_voltage(host, host->ocr);
BUG_ON(!host->bus_ops->resume);
host->bus_ops->resume(host);
}
......
......@@ -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,
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)
{
struct mmc_host *host = card->host;
......@@ -211,6 +273,11 @@ void mmc_add_card_debugfs(struct mmc_card *card)
&mmc_dbg_card_status_fops))
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;
err:
......
......@@ -223,10 +223,18 @@ static int sdio_read_cis(struct mmc_card *card, struct sdio_func *func)
if (tpl_code == 0xff)
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);
if (ret)
break;
/* a size of 0xff also means we're done */
if (tpl_link == 0xff)
break;
this = kmalloc(sizeof(*this) + tpl_link, GFP_KERNEL);
if (!this)
return -ENOMEM;
......
......@@ -76,6 +76,10 @@ int mmc_io_rw_direct(struct mmc_card *card, int write, unsigned fn,
BUG_ON(!card);
BUG_ON(fn > 7);
/* sanity check */
if (addr & ~0x1FFFF)
return -EINVAL;
memset(&cmd, 0, sizeof(struct mmc_command));
cmd.opcode = SD_IO_RW_DIRECT;
......@@ -125,6 +129,10 @@ int mmc_io_rw_extended(struct mmc_card *card, int write, unsigned fn,
WARN_ON(blocks == 0);
WARN_ON(blksz == 0);
/* sanity check */
if (addr & ~0x1FFFF)
return -EINVAL;
memset(&mrq, 0, sizeof(struct mmc_request));
memset(&cmd, 0, sizeof(struct mmc_command));
memset(&data, 0, sizeof(struct mmc_data));
......
......@@ -37,6 +37,13 @@ config MMC_SDHCI
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
tristate "SDHCI support on PCI bus"
depends on MMC_SDHCI && PCI
......@@ -65,6 +72,17 @@ config MMC_RICOH_MMC
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
tristate "TI OMAP Multimedia Card Interface support"
depends on ARCH_OMAP
......@@ -171,6 +189,17 @@ config MMC_TIFM_SD
To compile this driver as a module, choose M here: the
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
tristate "MMC/SD/SDIO over SPI"
depends on SPI_MASTER && !HIGHMEM && HAS_DMA
......
......@@ -13,6 +13,7 @@ obj-$(CONFIG_MMC_MXC) += mxcmmc.o
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_WBSD) += wbsd.o
obj-$(CONFIG_MMC_AU1X) += au1xmmc.o
obj-$(CONFIG_MMC_OMAP) += omap.o
......@@ -20,6 +21,7 @@ obj-$(CONFIG_MMC_OMAP_HS) += omap_hsmmc.o
obj-$(CONFIG_MMC_AT91) += at91_mci.o
obj-$(CONFIG_MMC_ATMELMCI) += atmel-mci.o
obj-$(CONFIG_MMC_TIFM_SD) += tifm_sd.o
obj-$(CONFIG_MMC_MVSDIO) += mvsdio.o
obj-$(CONFIG_MMC_SPI) += mmc_spi.o
ifeq ($(CONFIG_OF),y)
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)
slot->sdc_reg |= MCI_SDCBUS_1BIT;
break;
case MMC_BUS_WIDTH_4:
slot->sdc_reg = MCI_SDCBUS_4BIT;
slot->sdc_reg |= MCI_SDCBUS_4BIT;
break;
}
......
......@@ -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.
* It'd probably be better to memcpy() the first chunk and
* 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);
if (value < 0)
goto done;
......@@ -609,6 +612,7 @@ mmc_spi_writeblock(struct mmc_spi_host *host, struct spi_transfer *t,
struct spi_device *spi = host->spi;
int status, i;
struct scratch *scratch = host->data;
u32 pattern;
if (host->mmc->use_spi_crc)
scratch->crc_val = cpu_to_be16(
......@@ -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;
* it just says if the transmission was ok and whether *earlier*
* 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:
status = 0;
break;
......@@ -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,
* we'll need some more I/O.
*/
for (i = 1; i < sizeof(scratch->status); i++) {
if (scratch->status[i] != 0)
for (i = 4; i < sizeof(scratch->status); i++) {
/* card is non-busy if the most recent bit is 1 */
if (scratch->status[i] & 0x01)
return 0;
}
return mmc_spi_wait_unbusy(host, timeout);
......@@ -1204,10 +1228,12 @@ static int mmc_spi_probe(struct spi_device *spi)
/* 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
* should be legit. We'll use mode 0 since it seems to be a
* bit less troublesome on some hardware ... unclear why.
* should be legit. We'll use mode 0 since the steady state is 0,
* which is appropriate for hotplugging, unless the platform data
* specify mode 3 (if hardware is not compatible to mode 0).
*/
spi->mode = SPI_MODE_0;
if (spi->mode != SPI_MODE_3)
spi->mode = SPI_MODE_0;
spi->bits_per_word = 8;
status = spi_setup(spi);
......
此差异已折叠。
/*
* 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
......@@ -56,6 +56,7 @@
#define SDVS18 (0x5 << 9)
#define SDVS30 (0x6 << 9)
#define SDVS33 (0x7 << 9)
#define SDVS_MASK 0x00000E00
#define SDVSCLR 0xFFFFF1FF
#define SDVSDET 0x00000400
#define AUTOIDLE 0x1
......@@ -76,6 +77,7 @@
#define MSBS (1 << 5)
#define BCE (1 << 1)
#define FOUR_BIT (1 << 1)
#define DW8 (1 << 5)
#define CC 0x1
#define TC 0x02
#define OD 0x1
......@@ -98,10 +100,8 @@
*/
#define OMAP_MMC1_DEVID 0
#define OMAP_MMC2_DEVID 1
#define OMAP_MMC3_DEVID 2
#define OMAP_MMC_DATADIR_NONE 0
#define OMAP_MMC_DATADIR_READ 1
#define OMAP_MMC_DATADIR_WRITE 2
#define MMC_TIMEOUT_MS 20
#define OMAP_MMC_MASTER_CLOCK 96000000
#define DRIVER_NAME "mmci-omap-hs"
......@@ -137,18 +137,18 @@ struct mmc_omap_host {
resource_size_t mapbase;
unsigned int id;
unsigned int dma_len;
unsigned int dma_dir;
unsigned int dma_sg_idx;
unsigned char bus_mode;
unsigned char datadir;
u32 *buffer;
u32 bytesleft;
int suspended;
int irq;
int carddetect;
int use_dma, dma_ch;
int initstr;
int dma_line_tx, dma_line_rx;
int slot_id;
int dbclk_enabled;
int response_busy;
struct omap_mmc_platform_data *pdata;
};
......@@ -218,7 +218,7 @@ mmc_omap_show_slot_name(struct device *dev, struct device_attribute *attr,
struct mmc_omap_host *host = mmc_priv(mmc);
struct omap_mmc_slot_data slot = host->pdata->slots[host->slot_id];
return sprintf(buf, "slot:%s\n", slot.name);
return sprintf(buf, "%s\n", slot.name);
}
static DEVICE_ATTR(slot_name, S_IRUGO, mmc_omap_show_slot_name, NULL);
......@@ -243,10 +243,14 @@ mmc_omap_start_command(struct mmc_omap_host *host, struct mmc_command *cmd,
OMAP_HSMMC_WRITE(host->base, ISE, INT_EN_MASK);
OMAP_HSMMC_WRITE(host->base, IE, INT_EN_MASK);
host->response_busy = 0;
if (cmd->flags & MMC_RSP_PRESENT) {
if (cmd->flags & MMC_RSP_136)
resptype = 1;
else
else if (cmd->flags & MMC_RSP_BUSY) {
resptype = 3;
host->response_busy = 1;
} else
resptype = 2;
}
......@@ -275,19 +279,35 @@ mmc_omap_start_command(struct mmc_omap_host *host, struct mmc_command *cmd,
OMAP_HSMMC_WRITE(host->base, CMD, cmdreg);
}
static int
mmc_omap_get_dma_dir(struct mmc_omap_host *host, struct mmc_data *data)
{
if (data->flags & MMC_DATA_WRITE)
return DMA_TO_DEVICE;
else
return DMA_FROM_DEVICE;
}
/*
* Notify the transfer complete to MMC core
*/
static void
mmc_omap_xfer_done(struct mmc_omap_host *host, struct mmc_data *data)
{
if (!data) {
struct mmc_request *mrq = host->mrq;
host->mrq = NULL;
mmc_omap_fclk_lazy_disable(host);
mmc_request_done(host->mmc, mrq);
return;
}
host->data = NULL;
if (host->use_dma && host->dma_ch != -1)
dma_unmap_sg(mmc_dev(host->mmc), data->sg, host->dma_len,
host->dma_dir);
host->datadir = OMAP_MMC_DATADIR_NONE;
mmc_omap_get_dma_dir(host, data));
if (!data->error)
data->bytes_xfered += data->blocks * (data->blksz);
......@@ -322,7 +342,7 @@ mmc_omap_cmd_done(struct mmc_omap_host *host, struct mmc_command *cmd)
cmd->resp[0] = OMAP_HSMMC_READ(host->base, RSP10);
}
}
if (host->data == NULL || cmd->error) {
if ((host->data == NULL && !host->response_busy) || cmd->error) {
host->mrq = NULL;
mmc_request_done(host->mmc, cmd->mrq);
}
......@@ -331,19 +351,18 @@ mmc_omap_cmd_done(struct mmc_omap_host *host, struct mmc_command *cmd)
/*
* DMA clean up for command errors
*/
static void mmc_dma_cleanup(struct mmc_omap_host *host)
static void mmc_dma_cleanup(struct mmc_omap_host *host, int errno)
{
host->data->error = -ETIMEDOUT;
host->data->error = errno;
if (host->use_dma && host->dma_ch != -1) {
dma_unmap_sg(mmc_dev(host->mmc), host->data->sg, host->dma_len,
host->dma_dir);
mmc_omap_get_dma_dir(host, host->data));
omap_free_dma(host->dma_ch);
host->dma_ch = -1;
up(&host->sem);
}
host->data = NULL;
host->datadir = OMAP_MMC_DATADIR_NONE;
}
/*
......@@ -412,7 +431,7 @@ static irqreturn_t mmc_omap_irq(int irq, void *dev_id)
struct mmc_data *data;
int end_cmd = 0, end_trans = 0, status;
if (host->cmd == NULL && host->data == NULL) {
if (host->mrq == NULL) {
OMAP_HSMMC_WRITE(host->base, STAT,
OMAP_HSMMC_READ(host->base, STAT));
return IRQ_HANDLED;
......@@ -437,18 +456,24 @@ static irqreturn_t mmc_omap_irq(int irq, void *dev_id)
}
end_cmd = 1;
}
if (host->data) {
mmc_dma_cleanup(host);
if (host->data || host->response_busy) {
if (host->data)
mmc_dma_cleanup(host, -ETIMEDOUT);
host->response_busy = 0;
mmc_omap_reset_controller_fsm(host, SRD);
}
}
if ((status & DATA_TIMEOUT) ||
(status & DATA_CRC)) {
if (host->data) {
if (status & DATA_TIMEOUT)
mmc_dma_cleanup(host);
if (host->data || host->response_busy) {
int err = (status & DATA_TIMEOUT) ?
-ETIMEDOUT : -EILSEQ;
if (host->data)
mmc_dma_cleanup(host, err);
else
host->data->error = -EILSEQ;
host->mrq->cmd->error = err;
host->response_busy = 0;
mmc_omap_reset_controller_fsm(host, SRD);
end_trans = 1;
}
......@@ -473,6 +498,19 @@ static irqreturn_t mmc_omap_irq(int irq, void *dev_id)
return IRQ_HANDLED;
}
static void set_sd_bus_power(struct mmc_omap_host *host)
{
unsigned long i;
OMAP_HSMMC_WRITE(host->base, HCTL,
OMAP_HSMMC_READ(host->base, HCTL) | SDBP);
for (i = 0; i < loops_per_jiffy; i++) {
if (OMAP_HSMMC_READ(host->base, HCTL) & SDBP)
break;
cpu_relax();
}
}
/*
* Switch MMC interface voltage ... only relevant for MMC1.
*
......@@ -485,9 +523,6 @@ static int omap_mmc_switch_opcond(struct mmc_omap_host *host, int vdd)
u32 reg_val = 0;
int ret;
if (host->id != OMAP_MMC1_DEVID)
return 0;
/* Disable the clocks */
clk_disable(host->fclk);
clk_disable(host->iclk);
......@@ -532,9 +567,7 @@ static int omap_mmc_switch_opcond(struct mmc_omap_host *host, int vdd)
reg_val |= SDVS30;
OMAP_HSMMC_WRITE(host->base, HCTL, reg_val);
OMAP_HSMMC_WRITE(host->base, HCTL,
OMAP_HSMMC_READ(host->base, HCTL) | SDBP);
set_sd_bus_power(host);
return 0;
err:
......@@ -551,7 +584,10 @@ static void mmc_omap_detect(struct work_struct *work)
mmc_carddetect_work);
struct omap_mmc_slot_data *slot = &mmc_slot(host);
host->carddetect = slot->card_detect(slot->card_detect_irq);
if (mmc_slot(host).card_detect)
host->carddetect = slot->card_detect(slot->card_detect_irq);
else
host->carddetect = -ENOSYS;
sysfs_notify(&host->mmc->class_dev.kobj, NULL, "cover_switch");
if (host->carddetect) {
......@@ -574,6 +610,48 @@ static irqreturn_t omap_mmc_cd_handler(int irq, void *dev_id)
return IRQ_HANDLED;
}
static int mmc_omap_get_dma_sync_dev(struct mmc_omap_host *host,
struct mmc_data *data)
{
int sync_dev;
if (data->flags & MMC_DATA_WRITE)
sync_dev = host->dma_line_tx;
else
sync_dev = host->dma_line_rx;
return sync_dev;
}
static void mmc_omap_config_dma_params(struct mmc_omap_host *host,
struct mmc_data *data,
struct scatterlist *sgl)
{
int blksz, nblk, dma_ch;
dma_ch = host->dma_ch;
if (data->flags & MMC_DATA_WRITE) {
omap_set_dma_dest_params(dma_ch, 0, OMAP_DMA_AMODE_CONSTANT,
(host->mapbase + OMAP_HSMMC_DATA), 0, 0);
omap_set_dma_src_params(dma_ch, 0, OMAP_DMA_AMODE_POST_INC,
sg_dma_address(sgl), 0, 0);
} else {
omap_set_dma_src_params(dma_ch, 0, OMAP_DMA_AMODE_CONSTANT,
(host->mapbase + OMAP_HSMMC_DATA), 0, 0);
omap_set_dma_dest_params(dma_ch, 0, OMAP_DMA_AMODE_POST_INC,
sg_dma_address(sgl), 0, 0);
}
blksz = host->data->blksz;
nblk = sg_dma_len(sgl) / blksz;
omap_set_dma_transfer_params(dma_ch, OMAP_DMA_DATA_TYPE_S32,
blksz / 4, nblk, OMAP_DMA_SYNC_FRAME,
mmc_omap_get_dma_sync_dev(host, data),
!(data->flags & MMC_DATA_WRITE));
omap_start_dma(dma_ch);
}
/*
* DMA call back function
*/
......@@ -587,6 +665,14 @@ static void mmc_omap_dma_cb(int lch, u16 ch_status, void *data)
if (host->dma_ch < 0)
return;
host->dma_sg_idx++;
if (host->dma_sg_idx < host->dma_len) {
/* Fire up the next transfer. */
mmc_omap_config_dma_params(host, host->data,
host->data->sg + host->dma_sg_idx);
return;
}
omap_free_dma(host->dma_ch);
host->dma_ch = -1;
/*
......@@ -596,39 +682,29 @@ static void mmc_omap_dma_cb(int lch, u16 ch_status, void *data)
up(&host->sem);
}
/*
* Configure dma src and destination parameters
*/
static int mmc_omap_config_dma_param(int sync_dir, struct mmc_omap_host *host,
struct mmc_data *data)
{
if (sync_dir == 0) {
omap_set_dma_dest_params(host->dma_ch, 0,
OMAP_DMA_AMODE_CONSTANT,
(host->mapbase + OMAP_HSMMC_DATA), 0, 0);
omap_set_dma_src_params(host->dma_ch, 0,
OMAP_DMA_AMODE_POST_INC,
sg_dma_address(&data->sg[0]), 0, 0);
} else {
omap_set_dma_src_params(host->dma_ch, 0,
OMAP_DMA_AMODE_CONSTANT,
(host->mapbase + OMAP_HSMMC_DATA), 0, 0);
omap_set_dma_dest_params(host->dma_ch, 0,
OMAP_DMA_AMODE_POST_INC,
sg_dma_address(&data->sg[0]), 0, 0);
}
return 0;
}
/*
* Routine to configure and start DMA for the MMC card
*/
static int
mmc_omap_start_dma_transfer(struct mmc_omap_host *host, struct mmc_request *req)
{
int sync_dev, sync_dir = 0;
int dma_ch = 0, ret = 0, err = 1;
int dma_ch = 0, ret = 0, err = 1, i;
struct mmc_data *data = req->data;
/* Sanity check: all the SG entries must be aligned by block size. */
for (i = 0; i < host->dma_len; i++) {
struct scatterlist *sgl;
sgl = data->sg + i;
if (sgl->length % data->blksz)
return -EINVAL;
}
if ((data->blksz % 4) != 0)
/* REVISIT: The MMC buffer increments only when MSB is written.
* Return error for blksz which is non multiple of four.
*/
return -EINVAL;
/*
* If for some reason the DMA transfer is still active,
* we wait for timeout period and free the dma
......@@ -647,49 +723,22 @@ mmc_omap_start_dma_transfer(struct mmc_omap_host *host, struct mmc_request *req)
return err;
}
if (!(data->flags & MMC_DATA_WRITE)) {
host->dma_dir = DMA_FROM_DEVICE;
if (host->id == OMAP_MMC1_DEVID)
sync_dev = OMAP24XX_DMA_MMC1_RX;
else
sync_dev = OMAP24XX_DMA_MMC2_RX;
} else {
host->dma_dir = DMA_TO_DEVICE;
if (host->id == OMAP_MMC1_DEVID)
sync_dev = OMAP24XX_DMA_MMC1_TX;
else
sync_dev = OMAP24XX_DMA_MMC2_TX;
}
ret = omap_request_dma(sync_dev, "MMC/SD", mmc_omap_dma_cb,
host, &dma_ch);
ret = omap_request_dma(mmc_omap_get_dma_sync_dev(host, data), "MMC/SD",
mmc_omap_dma_cb,host, &dma_ch);
if (ret != 0) {
dev_dbg(mmc_dev(host->mmc),
dev_err(mmc_dev(host->mmc),
"%s: omap_request_dma() failed with %d\n",
mmc_hostname(host->mmc), ret);
return ret;
}
host->dma_len = dma_map_sg(mmc_dev(host->mmc), data->sg,
data->sg_len, host->dma_dir);
data->sg_len, mmc_omap_get_dma_dir(host, data));
host->dma_ch = dma_ch;
host->dma_sg_idx = 0;
if (!(data->flags & MMC_DATA_WRITE))
mmc_omap_config_dma_param(1, host, data);
else
mmc_omap_config_dma_param(0, host, data);
if ((data->blksz % 4) == 0)
omap_set_dma_transfer_params(dma_ch, OMAP_DMA_DATA_TYPE_S32,
(data->blksz / 4), data->blocks, OMAP_DMA_SYNC_FRAME,
sync_dev, sync_dir);
else
/* REVISIT: The MMC buffer increments only when MSB is written.
* Return error for blksz which is non multiple of four.
*/
return -EINVAL;
mmc_omap_config_dma_params(host, data, data->sg);
omap_start_dma(dma_ch);
return 0;
}
......@@ -739,7 +788,6 @@ mmc_omap_prepare_data(struct mmc_omap_host *host, struct mmc_request *req)
host->data = req->data;
if (req->data == NULL) {
host->datadir = OMAP_MMC_DATADIR_NONE;
OMAP_HSMMC_WRITE(host->base, BLK, 0);
return 0;
}
......@@ -748,9 +796,6 @@ mmc_omap_prepare_data(struct mmc_omap_host *host, struct mmc_request *req)
| (req->data->blocks << 16));
set_data_timeout(host, req);
host->datadir = (req->data->flags & MMC_DATA_WRITE) ?
OMAP_MMC_DATADIR_WRITE : OMAP_MMC_DATADIR_READ;
if (host->use_dma) {
ret = mmc_omap_start_dma_transfer(host, req);
if (ret != 0) {
......@@ -782,36 +827,29 @@ static void omap_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
u16 dsor = 0;
unsigned long regval;
unsigned long timeout;
u32 con;
switch (ios->power_mode) {
case MMC_POWER_OFF:
mmc_slot(host).set_power(host->dev, host->slot_id, 0, 0);
/*
* Reset interface voltage to 3V if it's 1.8V now;
* only relevant on MMC-1, the others always use 1.8V.
*
* REVISIT: If we are able to detect cards after unplugging
* a 1.8V card, this code should not be needed.
*/
if (host->id != OMAP_MMC1_DEVID)
break;
if (!(OMAP_HSMMC_READ(host->base, HCTL) & SDVSDET)) {
int vdd = fls(host->mmc->ocr_avail) - 1;
if (omap_mmc_switch_opcond(host, vdd) != 0)
host->mmc->ios.vdd = vdd;
}
break;
case MMC_POWER_UP:
mmc_slot(host).set_power(host->dev, host->slot_id, 1, ios->vdd);
break;
}
con = OMAP_HSMMC_READ(host->base, CON);
switch (mmc->ios.bus_width) {
case MMC_BUS_WIDTH_8:
OMAP_HSMMC_WRITE(host->base, CON, con | DW8);
break;
case MMC_BUS_WIDTH_4:
OMAP_HSMMC_WRITE(host->base, CON, con & ~DW8);
OMAP_HSMMC_WRITE(host->base, HCTL,
OMAP_HSMMC_READ(host->base, HCTL) | FOUR_BIT);
break;
case MMC_BUS_WIDTH_1:
OMAP_HSMMC_WRITE(host->base, CON, con & ~DW8);
OMAP_HSMMC_WRITE(host->base, HCTL,
OMAP_HSMMC_READ(host->base, HCTL) & ~FOUR_BIT);
break;
......@@ -891,6 +929,33 @@ static int omap_hsmmc_get_ro(struct mmc_host *mmc)
return pdata->slots[0].get_ro(host->dev, 0);
}
static void omap_hsmmc_init(struct mmc_omap_host *host)
{
u32 hctl, capa, value;
/* Only MMC1 supports 3.0V */
if (host->id == OMAP_MMC1_DEVID) {
hctl = SDVS30;
capa = VS30 | VS18;
} else {
hctl = SDVS18;
capa = VS18;
}
value = OMAP_HSMMC_READ(host->base, HCTL) & ~SDVS_MASK;
OMAP_HSMMC_WRITE(host->base, HCTL, value | hctl);
value = OMAP_HSMMC_READ(host->base, CAPA);
OMAP_HSMMC_WRITE(host->base, CAPA, value | capa);
/* Set the controller to AUTO IDLE mode */
value = OMAP_HSMMC_READ(host->base, SYSCONFIG);
OMAP_HSMMC_WRITE(host->base, SYSCONFIG, value | AUTOIDLE);
/* Set SD bus power bit */
set_sd_bus_power(host);
}
static struct mmc_host_ops mmc_omap_ops = {
.request = omap_mmc_request,
.set_ios = omap_mmc_set_ios,
......@@ -906,7 +971,6 @@ static int __init omap_mmc_probe(struct platform_device *pdev)
struct mmc_omap_host *host = NULL;
struct resource *res;
int ret = 0, irq;
u32 hctl, capa;
if (pdata == NULL) {
dev_err(&pdev->dev, "Platform Data is missing\n");
......@@ -996,10 +1060,11 @@ static int __init omap_mmc_probe(struct platform_device *pdev)
else
host->dbclk_enabled = 1;
#ifdef CONFIG_MMC_BLOCK_BOUNCE
mmc->max_phys_segs = 1;
mmc->max_hw_segs = 1;
#endif
/* Since we do only SG emulation, we can have as many segs
* as we want. */
mmc->max_phys_segs = 1024;
mmc->max_hw_segs = 1024;
mmc->max_blk_size = 512; /* Block Length at max can be 1024 */
mmc->max_blk_count = 0xFFFF; /* No. of Blocks is 16 bits */
mmc->max_req_size = mmc->max_blk_size * mmc->max_blk_count;
......@@ -1008,31 +1073,31 @@ static int __init omap_mmc_probe(struct platform_device *pdev)
mmc->ocr_avail = mmc_slot(host).ocr_mask;
mmc->caps |= MMC_CAP_MMC_HIGHSPEED | MMC_CAP_SD_HIGHSPEED;
if (pdata->slots[host->slot_id].wires >= 4)
if (pdata->slots[host->slot_id].wires >= 8)
mmc->caps |= MMC_CAP_8_BIT_DATA;
else if (pdata->slots[host->slot_id].wires >= 4)
mmc->caps |= MMC_CAP_4_BIT_DATA;
/* Only MMC1 supports 3.0V */
if (host->id == OMAP_MMC1_DEVID) {
hctl = SDVS30;
capa = VS30 | VS18;
} else {
hctl = SDVS18;
capa = VS18;
}
OMAP_HSMMC_WRITE(host->base, HCTL,
OMAP_HSMMC_READ(host->base, HCTL) | hctl);
OMAP_HSMMC_WRITE(host->base, CAPA,
OMAP_HSMMC_READ(host->base, CAPA) | capa);
/* Set the controller to AUTO IDLE mode */
OMAP_HSMMC_WRITE(host->base, SYSCONFIG,
OMAP_HSMMC_READ(host->base, SYSCONFIG) | AUTOIDLE);
omap_hsmmc_init(host);
/* Set SD bus power bit */
OMAP_HSMMC_WRITE(host->base, HCTL,
OMAP_HSMMC_READ(host->base, HCTL) | SDBP);
/* Select DMA lines */
switch (host->id) {
case OMAP_MMC1_DEVID:
host->dma_line_tx = OMAP24XX_DMA_MMC1_TX;
host->dma_line_rx = OMAP24XX_DMA_MMC1_RX;
break;
case OMAP_MMC2_DEVID:
host->dma_line_tx = OMAP24XX_DMA_MMC2_TX;
host->dma_line_rx = OMAP24XX_DMA_MMC2_RX;
break;
case OMAP_MMC3_DEVID:
host->dma_line_tx = OMAP34XX_DMA_MMC3_TX;
host->dma_line_rx = OMAP34XX_DMA_MMC3_RX;
break;
default:
dev_err(mmc_dev(host->mmc), "Invalid MMC id\n");
goto err_irq;
}
/* Request IRQ for MMC operations */
ret = request_irq(host->irq, mmc_omap_irq, IRQF_DISABLED,
......@@ -1051,7 +1116,7 @@ static int __init omap_mmc_probe(struct platform_device *pdev)
}
/* Request IRQ for card detect */
if ((mmc_slot(host).card_detect_irq) && (mmc_slot(host).card_detect)) {
if ((mmc_slot(host).card_detect_irq)) {
ret = request_irq(mmc_slot(host).card_detect_irq,
omap_mmc_cd_handler,
IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING
......@@ -1074,8 +1139,8 @@ static int __init omap_mmc_probe(struct platform_device *pdev)
if (ret < 0)
goto err_slot_name;
}
if (mmc_slot(host).card_detect_irq && mmc_slot(host).card_detect &&
host->pdata->slots[host->slot_id].get_cover_state) {
if (mmc_slot(host).card_detect_irq &&
host->pdata->slots[host->slot_id].get_cover_state) {
ret = device_create_file(&mmc->class_dev,
&dev_attr_cover_switch);
if (ret < 0)
......@@ -1173,20 +1238,8 @@ static int omap_mmc_suspend(struct platform_device *pdev, pm_message_t state)
" level suspend\n");
}
if (host->id == OMAP_MMC1_DEVID
&& !(OMAP_HSMMC_READ(host->base, HCTL)
& SDVSDET)) {
OMAP_HSMMC_WRITE(host->base, HCTL,
OMAP_HSMMC_READ(host->base, HCTL)
& SDVSCLR);
OMAP_HSMMC_WRITE(host->base, HCTL,
OMAP_HSMMC_READ(host->base, HCTL)
| SDVS30);
OMAP_HSMMC_WRITE(host->base, HCTL,
OMAP_HSMMC_READ(host->base, HCTL)
| SDBP);
}
OMAP_HSMMC_WRITE(host->base, HCTL,
OMAP_HSMMC_READ(host->base, HCTL) & ~SDBP);
clk_disable(host->fclk);
clk_disable(host->iclk);
clk_disable(host->dbclk);
......@@ -1222,6 +1275,8 @@ static int omap_mmc_resume(struct platform_device *pdev)
dev_dbg(mmc_dev(host->mmc),
"Enabling debounce clk failed\n");
omap_hsmmc_init(host);
if (host->pdata->resume) {
ret = host->pdata->resume(&pdev->dev, host->slot_id);
if (ret)
......
/*
* 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");
此差异已折叠。
......@@ -10,6 +10,9 @@
*/
#include <linux/scatterlist.h>
#include <linux/compiler.h>
#include <linux/types.h>
#include <linux/io.h>
/*
* Controller registers
......@@ -123,6 +126,7 @@
SDHCI_INT_DATA_AVAIL | SDHCI_INT_SPACE_AVAIL | \
SDHCI_INT_DATA_TIMEOUT | SDHCI_INT_DATA_CRC | \
SDHCI_INT_DATA_END_BIT)
#define SDHCI_INT_ALL_MASK ((unsigned int)-1)
#define SDHCI_ACMD12_ERR 0x3C
......@@ -210,6 +214,18 @@ struct sdhci_host {
#define SDHCI_QUIRK_BROKEN_SMALL_PIO (1<<13)
/* Controller does not provide transfer-complete interrupt when not busy */
#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 */
void __iomem * ioaddr; /* Mapped address */
......@@ -267,9 +283,105 @@ struct sdhci_host {
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);
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,
size_t priv_size);
......
......@@ -568,11 +568,11 @@ static int __devinit tmio_mmc_probe(struct platform_device *dev)
host->mmc = 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)
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)
goto unmap_ctl;
......@@ -650,10 +650,10 @@ static int __devexit tmio_mmc_remove(struct platform_device *dev)
if (mmc) {
struct tmio_mmc_host *host = mmc_priv(mmc);
mmc_remove_host(mmc);
mmc_free_host(mmc);
free_irq(host->irq, host);
iounmap(host->ctl);
iounmap(host->cnf);
mmc_free_host(mmc);
}
return 0;
......
......@@ -8,6 +8,9 @@
* published by the Free Software Foundation.
*
*/
#include <linux/highmem.h>
#define CNF_CMD 0x04
#define CNF_CTL_BASE 0x10
#define CNF_INT_PIN 0x3d
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册