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
  ...
This commit is contained in:
Linus Torvalds 2009-04-05 10:18:21 -07:00
Родитель 601cc11d05 32ab83a56f
Коммит 0a053e8c71
19 изменённых файлов: 2110 добавлений и 273 удалений

Просмотреть файл

@ -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);

885
drivers/mmc/host/mvsdio.c Normal file
Просмотреть файл

@ -0,0 +1,885 @@
/*
* Marvell MMC/SD/SDIO driver
*
* Authors: Maen Suleiman, Nicolas Pitre
* Copyright (C) 2008-2009 Marvell Ltd.
*
* 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 <linux/module.h>
#include <linux/init.h>
#include <linux/io.h>
#include <linux/platform_device.h>
#include <linux/mbus.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
#include <linux/dma-mapping.h>
#include <linux/scatterlist.h>
#include <linux/irq.h>
#include <linux/gpio.h>
#include <linux/mmc/host.h>
#include <asm/sizes.h>
#include <asm/unaligned.h>
#include <plat/mvsdio.h>
#include "mvsdio.h"
#define DRIVER_NAME "mvsdio"
static int maxfreq = MVSD_CLOCKRATE_MAX;
static int nodma;
struct mvsd_host {
void __iomem *base;
struct mmc_request *mrq;
spinlock_t lock;
unsigned int xfer_mode;
unsigned int intr_en;
unsigned int ctrl;
unsigned int pio_size;
void *pio_ptr;
unsigned int sg_frags;
unsigned int ns_per_clk;
unsigned int clock;
unsigned int base_clock;
struct timer_list timer;
struct mmc_host *mmc;
struct device *dev;
struct resource *res;
int irq;
int gpio_card_detect;
int gpio_write_protect;
};
#define mvsd_write(offs, val) writel(val, iobase + (offs))
#define mvsd_read(offs) readl(iobase + (offs))
static int mvsd_setup_data(struct mvsd_host *host, struct mmc_data *data)
{
void __iomem *iobase = host->base;
unsigned int tmout;
int tmout_index;
/* If timeout=0 then maximum timeout index is used. */
tmout = DIV_ROUND_UP(data->timeout_ns, host->ns_per_clk);
tmout += data->timeout_clks;
tmout_index = fls(tmout - 1) - 12;
if (tmout_index < 0)
tmout_index = 0;
if (tmout_index > MVSD_HOST_CTRL_TMOUT_MAX)
tmout_index = MVSD_HOST_CTRL_TMOUT_MAX;
dev_dbg(host->dev, "data %s at 0x%08x: blocks=%d blksz=%d tmout=%u (%d)\n",
(data->flags & MMC_DATA_READ) ? "read" : "write",
(u32)sg_virt(data->sg), data->blocks, data->blksz,
tmout, tmout_index);
host->ctrl &= ~MVSD_HOST_CTRL_TMOUT_MASK;
host->ctrl |= MVSD_HOST_CTRL_TMOUT(tmout_index);
mvsd_write(MVSD_HOST_CTRL, host->ctrl);
mvsd_write(MVSD_BLK_COUNT, data->blocks);
mvsd_write(MVSD_BLK_SIZE, data->blksz);
if (nodma || (data->blksz | data->sg->offset) & 3) {
/*
* We cannot do DMA on a buffer which offset or size
* is not aligned on a 4-byte boundary.
*/
host->pio_size = data->blocks * data->blksz;
host->pio_ptr = sg_virt(data->sg);
if (!nodma)
printk(KERN_DEBUG "%s: fallback to PIO for data "
"at 0x%p size %d\n",
mmc_hostname(host->mmc),
host->pio_ptr, host->pio_size);
return 1;
} else {
dma_addr_t phys_addr;
int dma_dir = (data->flags & MMC_DATA_READ) ?
DMA_FROM_DEVICE : DMA_TO_DEVICE;
host->sg_frags = dma_map_sg(mmc_dev(host->mmc), data->sg,
data->sg_len, dma_dir);
phys_addr = sg_dma_address(data->sg);
mvsd_write(MVSD_SYS_ADDR_LOW, (u32)phys_addr & 0xffff);
mvsd_write(MVSD_SYS_ADDR_HI, (u32)phys_addr >> 16);
return 0;
}
}
static void mvsd_request(struct mmc_host *mmc, struct mmc_request *mrq)
{
struct mvsd_host *host = mmc_priv(mmc);
void __iomem *iobase = host->base;
struct mmc_command *cmd = mrq->cmd;
u32 cmdreg = 0, xfer = 0, intr = 0;
unsigned long flags;
BUG_ON(host->mrq != NULL);
host->mrq = mrq;
dev_dbg(host->dev, "cmd %d (hw state 0x%04x)\n",
cmd->opcode, mvsd_read(MVSD_HW_STATE));
cmdreg = MVSD_CMD_INDEX(cmd->opcode);
if (cmd->flags & MMC_RSP_BUSY)
cmdreg |= MVSD_CMD_RSP_48BUSY;
else if (cmd->flags & MMC_RSP_136)
cmdreg |= MVSD_CMD_RSP_136;
else if (cmd->flags & MMC_RSP_PRESENT)
cmdreg |= MVSD_CMD_RSP_48;
else
cmdreg |= MVSD_CMD_RSP_NONE;
if (cmd->flags & MMC_RSP_CRC)
cmdreg |= MVSD_CMD_CHECK_CMDCRC;
if (cmd->flags & MMC_RSP_OPCODE)
cmdreg |= MVSD_CMD_INDX_CHECK;
if (cmd->flags & MMC_RSP_PRESENT) {
cmdreg |= MVSD_UNEXPECTED_RESP;
intr |= MVSD_NOR_UNEXP_RSP;
}
if (mrq->data) {
struct mmc_data *data = mrq->data;
int pio;
cmdreg |= MVSD_CMD_DATA_PRESENT | MVSD_CMD_CHECK_DATACRC16;
xfer |= MVSD_XFER_MODE_HW_WR_DATA_EN;
if (data->flags & MMC_DATA_READ)
xfer |= MVSD_XFER_MODE_TO_HOST;
pio = mvsd_setup_data(host, data);
if (pio) {
xfer |= MVSD_XFER_MODE_PIO;
/* PIO section of mvsd_irq has comments on those bits */
if (data->flags & MMC_DATA_WRITE)
intr |= MVSD_NOR_TX_AVAIL;
else if (host->pio_size > 32)
intr |= MVSD_NOR_RX_FIFO_8W;
else
intr |= MVSD_NOR_RX_READY;
}
if (data->stop) {
struct mmc_command *stop = data->stop;
u32 cmd12reg = 0;
mvsd_write(MVSD_AUTOCMD12_ARG_LOW, stop->arg & 0xffff);
mvsd_write(MVSD_AUTOCMD12_ARG_HI, stop->arg >> 16);
if (stop->flags & MMC_RSP_BUSY)
cmd12reg |= MVSD_AUTOCMD12_BUSY;
if (stop->flags & MMC_RSP_OPCODE)
cmd12reg |= MVSD_AUTOCMD12_INDX_CHECK;
cmd12reg |= MVSD_AUTOCMD12_INDEX(stop->opcode);
mvsd_write(MVSD_AUTOCMD12_CMD, cmd12reg);
xfer |= MVSD_XFER_MODE_AUTO_CMD12;
intr |= MVSD_NOR_AUTOCMD12_DONE;
} else {
intr |= MVSD_NOR_XFER_DONE;
}
} else {
intr |= MVSD_NOR_CMD_DONE;
}
mvsd_write(MVSD_ARG_LOW, cmd->arg & 0xffff);
mvsd_write(MVSD_ARG_HI, cmd->arg >> 16);
spin_lock_irqsave(&host->lock, flags);
host->xfer_mode &= MVSD_XFER_MODE_INT_CHK_EN;
host->xfer_mode |= xfer;
mvsd_write(MVSD_XFER_MODE, host->xfer_mode);
mvsd_write(MVSD_NOR_INTR_STATUS, ~MVSD_NOR_CARD_INT);
mvsd_write(MVSD_ERR_INTR_STATUS, 0xffff);
mvsd_write(MVSD_CMD, cmdreg);
host->intr_en &= MVSD_NOR_CARD_INT;
host->intr_en |= intr | MVSD_NOR_ERROR;
mvsd_write(MVSD_NOR_INTR_EN, host->intr_en);
mvsd_write(MVSD_ERR_INTR_EN, 0xffff);
mod_timer(&host->timer, jiffies + 5 * HZ);
spin_unlock_irqrestore(&host->lock, flags);
}
static u32 mvsd_finish_cmd(struct mvsd_host *host, struct mmc_command *cmd,
u32 err_status)
{
void __iomem *iobase = host->base;
if (cmd->flags & MMC_RSP_136) {
unsigned int response[8], i;
for (i = 0; i < 8; i++)
response[i] = mvsd_read(MVSD_RSP(i));
cmd->resp[0] = ((response[0] & 0x03ff) << 22) |
((response[1] & 0xffff) << 6) |
((response[2] & 0xfc00) >> 10);
cmd->resp[1] = ((response[2] & 0x03ff) << 22) |
((response[3] & 0xffff) << 6) |
((response[4] & 0xfc00) >> 10);
cmd->resp[2] = ((response[4] & 0x03ff) << 22) |
((response[5] & 0xffff) << 6) |
((response[6] & 0xfc00) >> 10);
cmd->resp[3] = ((response[6] & 0x03ff) << 22) |
((response[7] & 0x3fff) << 8);
} else if (cmd->flags & MMC_RSP_PRESENT) {
unsigned int response[3], i;
for (i = 0; i < 3; i++)
response[i] = mvsd_read(MVSD_RSP(i));
cmd->resp[0] = ((response[2] & 0x003f) << (8 - 8)) |
((response[1] & 0xffff) << (14 - 8)) |
((response[0] & 0x03ff) << (30 - 8));
cmd->resp[1] = ((response[0] & 0xfc00) >> 10);
cmd->resp[2] = 0;
cmd->resp[3] = 0;
}
if (err_status & MVSD_ERR_CMD_TIMEOUT) {
cmd->error = -ETIMEDOUT;
} else if (err_status & (MVSD_ERR_CMD_CRC | MVSD_ERR_CMD_ENDBIT |
MVSD_ERR_CMD_INDEX | MVSD_ERR_CMD_STARTBIT)) {
cmd->error = -EILSEQ;
}
err_status &= ~(MVSD_ERR_CMD_TIMEOUT | MVSD_ERR_CMD_CRC |
MVSD_ERR_CMD_ENDBIT | MVSD_ERR_CMD_INDEX |
MVSD_ERR_CMD_STARTBIT);
return err_status;
}
static u32 mvsd_finish_data(struct mvsd_host *host, struct mmc_data *data,
u32 err_status)
{
void __iomem *iobase = host->base;
if (host->pio_ptr) {
host->pio_ptr = NULL;
host->pio_size = 0;
} else {
dma_unmap_sg(mmc_dev(host->mmc), data->sg, host->sg_frags,
(data->flags & MMC_DATA_READ) ?
DMA_FROM_DEVICE : DMA_TO_DEVICE);
}
if (err_status & MVSD_ERR_DATA_TIMEOUT)
data->error = -ETIMEDOUT;
else if (err_status & (MVSD_ERR_DATA_CRC | MVSD_ERR_DATA_ENDBIT))
data->error = -EILSEQ;
else if (err_status & MVSD_ERR_XFER_SIZE)
data->error = -EBADE;
err_status &= ~(MVSD_ERR_DATA_TIMEOUT | MVSD_ERR_DATA_CRC |
MVSD_ERR_DATA_ENDBIT | MVSD_ERR_XFER_SIZE);
dev_dbg(host->dev, "data done: blocks_left=%d, bytes_left=%d\n",
mvsd_read(MVSD_CURR_BLK_LEFT), mvsd_read(MVSD_CURR_BYTE_LEFT));
data->bytes_xfered =
(data->blocks - mvsd_read(MVSD_CURR_BLK_LEFT)) * data->blksz;
/* We can't be sure about the last block when errors are detected */
if (data->bytes_xfered && data->error)
data->bytes_xfered -= data->blksz;
/* Handle Auto cmd 12 response */
if (data->stop) {
unsigned int response[3], i;
for (i = 0; i < 3; i++)
response[i] = mvsd_read(MVSD_AUTO_RSP(i));
data->stop->resp[0] = ((response[2] & 0x003f) << (8 - 8)) |
((response[1] & 0xffff) << (14 - 8)) |
((response[0] & 0x03ff) << (30 - 8));
data->stop->resp[1] = ((response[0] & 0xfc00) >> 10);
data->stop->resp[2] = 0;
data->stop->resp[3] = 0;
if (err_status & MVSD_ERR_AUTOCMD12) {
u32 err_cmd12 = mvsd_read(MVSD_AUTOCMD12_ERR_STATUS);
dev_dbg(host->dev, "c12err 0x%04x\n", err_cmd12);
if (err_cmd12 & MVSD_AUTOCMD12_ERR_NOTEXE)
data->stop->error = -ENOEXEC;
else if (err_cmd12 & MVSD_AUTOCMD12_ERR_TIMEOUT)
data->stop->error = -ETIMEDOUT;
else if (err_cmd12)
data->stop->error = -EILSEQ;
err_status &= ~MVSD_ERR_AUTOCMD12;
}
}
return err_status;
}
static irqreturn_t mvsd_irq(int irq, void *dev)
{
struct mvsd_host *host = dev;
void __iomem *iobase = host->base;
u32 intr_status, intr_done_mask;
int irq_handled = 0;
intr_status = mvsd_read(MVSD_NOR_INTR_STATUS);
dev_dbg(host->dev, "intr 0x%04x intr_en 0x%04x hw_state 0x%04x\n",
intr_status, mvsd_read(MVSD_NOR_INTR_EN),
mvsd_read(MVSD_HW_STATE));
spin_lock(&host->lock);
/* PIO handling, if needed. Messy business... */
if (host->pio_size &&
(intr_status & host->intr_en &
(MVSD_NOR_RX_READY | MVSD_NOR_RX_FIFO_8W))) {
u16 *p = host->pio_ptr;
int s = host->pio_size;
while (s >= 32 && (intr_status & MVSD_NOR_RX_FIFO_8W)) {
readsw(iobase + MVSD_FIFO, p, 16);
p += 16;
s -= 32;
intr_status = mvsd_read(MVSD_NOR_INTR_STATUS);
}
/*
* Normally we'd use < 32 here, but the RX_FIFO_8W bit
* doesn't appear to assert when there is exactly 32 bytes
* (8 words) left to fetch in a transfer.
*/
if (s <= 32) {
while (s >= 4 && (intr_status & MVSD_NOR_RX_READY)) {
put_unaligned(mvsd_read(MVSD_FIFO), p++);
put_unaligned(mvsd_read(MVSD_FIFO), p++);
s -= 4;
intr_status = mvsd_read(MVSD_NOR_INTR_STATUS);
}
if (s && s < 4 && (intr_status & MVSD_NOR_RX_READY)) {
u16 val[2] = {0, 0};
val[0] = mvsd_read(MVSD_FIFO);
val[1] = mvsd_read(MVSD_FIFO);
memcpy(p, &val, s);
s = 0;
intr_status = mvsd_read(MVSD_NOR_INTR_STATUS);
}
if (s == 0) {
host->intr_en &=
~(MVSD_NOR_RX_READY | MVSD_NOR_RX_FIFO_8W);
mvsd_write(MVSD_NOR_INTR_EN, host->intr_en);
} else if (host->intr_en & MVSD_NOR_RX_FIFO_8W) {
host->intr_en &= ~MVSD_NOR_RX_FIFO_8W;
host->intr_en |= MVSD_NOR_RX_READY;
mvsd_write(MVSD_NOR_INTR_EN, host->intr_en);
}
}
dev_dbg(host->dev, "pio %d intr 0x%04x hw_state 0x%04x\n",
s, intr_status, mvsd_read(MVSD_HW_STATE));
host->pio_ptr = p;
host->pio_size = s;
irq_handled = 1;
} else if (host->pio_size &&
(intr_status & host->intr_en &
(MVSD_NOR_TX_AVAIL | MVSD_NOR_TX_FIFO_8W))) {
u16 *p = host->pio_ptr;
int s = host->pio_size;
/*
* The TX_FIFO_8W bit is unreliable. When set, bursting
* 16 halfwords all at once in the FIFO drops data. Actually
* TX_AVAIL does go off after only one word is pushed even if
* TX_FIFO_8W remains set.
*/
while (s >= 4 && (intr_status & MVSD_NOR_TX_AVAIL)) {
mvsd_write(MVSD_FIFO, get_unaligned(p++));
mvsd_write(MVSD_FIFO, get_unaligned(p++));
s -= 4;
intr_status = mvsd_read(MVSD_NOR_INTR_STATUS);
}
if (s < 4) {
if (s && (intr_status & MVSD_NOR_TX_AVAIL)) {
u16 val[2] = {0, 0};
memcpy(&val, p, s);
mvsd_write(MVSD_FIFO, val[0]);
mvsd_write(MVSD_FIFO, val[1]);
s = 0;
intr_status = mvsd_read(MVSD_NOR_INTR_STATUS);
}
if (s == 0) {
host->intr_en &=
~(MVSD_NOR_TX_AVAIL | MVSD_NOR_TX_FIFO_8W);
mvsd_write(MVSD_NOR_INTR_EN, host->intr_en);
}
}
dev_dbg(host->dev, "pio %d intr 0x%04x hw_state 0x%04x\n",
s, intr_status, mvsd_read(MVSD_HW_STATE));
host->pio_ptr = p;
host->pio_size = s;
irq_handled = 1;
}
mvsd_write(MVSD_NOR_INTR_STATUS, intr_status);
intr_done_mask = MVSD_NOR_CARD_INT | MVSD_NOR_RX_READY |
MVSD_NOR_RX_FIFO_8W | MVSD_NOR_TX_FIFO_8W;
if (intr_status & host->intr_en & ~intr_done_mask) {
struct mmc_request *mrq = host->mrq;
struct mmc_command *cmd = mrq->cmd;
u32 err_status = 0;
del_timer(&host->timer);
host->mrq = NULL;
host->intr_en &= MVSD_NOR_CARD_INT;
mvsd_write(MVSD_NOR_INTR_EN, host->intr_en);
mvsd_write(MVSD_ERR_INTR_EN, 0);
spin_unlock(&host->lock);
if (intr_status & MVSD_NOR_UNEXP_RSP) {
cmd->error = -EPROTO;
} else if (intr_status & MVSD_NOR_ERROR) {
err_status = mvsd_read(MVSD_ERR_INTR_STATUS);
dev_dbg(host->dev, "err 0x%04x\n", err_status);
}
err_status = mvsd_finish_cmd(host, cmd, err_status);
if (mrq->data)
err_status = mvsd_finish_data(host, mrq->data, err_status);
if (err_status) {
printk(KERN_ERR "%s: unhandled error status %#04x\n",
mmc_hostname(host->mmc), err_status);
cmd->error = -ENOMSG;
}
mmc_request_done(host->mmc, mrq);
irq_handled = 1;
} else
spin_unlock(&host->lock);
if (intr_status & MVSD_NOR_CARD_INT) {
mmc_signal_sdio_irq(host->mmc);
irq_handled = 1;
}
if (irq_handled)
return IRQ_HANDLED;
printk(KERN_ERR "%s: unhandled interrupt status=0x%04x en=0x%04x "
"pio=%d\n", mmc_hostname(host->mmc), intr_status,
host->intr_en, host->pio_size);
return IRQ_NONE;
}
static void mvsd_timeout_timer(unsigned long data)
{
struct mvsd_host *host = (struct mvsd_host *)data;
void __iomem *iobase = host->base;
struct mmc_request *mrq;
unsigned long flags;
spin_lock_irqsave(&host->lock, flags);
mrq = host->mrq;
if (mrq) {
printk(KERN_ERR "%s: Timeout waiting for hardware interrupt.\n",
mmc_hostname(host->mmc));
printk(KERN_ERR "%s: hw_state=0x%04x, intr_status=0x%04x "
"intr_en=0x%04x\n", mmc_hostname(host->mmc),
mvsd_read(MVSD_HW_STATE),
mvsd_read(MVSD_NOR_INTR_STATUS),
mvsd_read(MVSD_NOR_INTR_EN));
host->mrq = NULL;
mvsd_write(MVSD_SW_RESET, MVSD_SW_RESET_NOW);
host->xfer_mode &= MVSD_XFER_MODE_INT_CHK_EN;
mvsd_write(MVSD_XFER_MODE, host->xfer_mode);
host->intr_en &= MVSD_NOR_CARD_INT;
mvsd_write(MVSD_NOR_INTR_EN, host->intr_en);
mvsd_write(MVSD_ERR_INTR_EN, 0);
mvsd_write(MVSD_ERR_INTR_STATUS, 0xffff);
mrq->cmd->error = -ETIMEDOUT;
mvsd_finish_cmd(host, mrq->cmd, 0);
if (mrq->data) {
mrq->data->error = -ETIMEDOUT;
mvsd_finish_data(host, mrq->data, 0);
}
}
spin_unlock_irqrestore(&host->lock, flags);
if (mrq)
mmc_request_done(host->mmc, mrq);
}
static irqreturn_t mvsd_card_detect_irq(int irq, void *dev)
{
struct mvsd_host *host = dev;
mmc_detect_change(host->mmc, msecs_to_jiffies(100));
return IRQ_HANDLED;
}
static void mvsd_enable_sdio_irq(struct mmc_host *mmc, int enable)
{
struct mvsd_host *host = mmc_priv(mmc);
void __iomem *iobase = host->base;
unsigned long flags;
spin_lock_irqsave(&host->lock, flags);
if (enable) {
host->xfer_mode |= MVSD_XFER_MODE_INT_CHK_EN;
host->intr_en |= MVSD_NOR_CARD_INT;
} else {
host->xfer_mode &= ~MVSD_XFER_MODE_INT_CHK_EN;
host->intr_en &= ~MVSD_NOR_CARD_INT;
}
mvsd_write(MVSD_XFER_MODE, host->xfer_mode);
mvsd_write(MVSD_NOR_INTR_EN, host->intr_en);
spin_unlock_irqrestore(&host->lock, flags);
}
static int mvsd_get_ro(struct mmc_host *mmc)
{
struct mvsd_host *host = mmc_priv(mmc);
if (host->gpio_write_protect)
return gpio_get_value(host->gpio_write_protect);
/*
* Board doesn't support read only detection; let the mmc core
* decide what to do.
*/
return -ENOSYS;
}
static void mvsd_power_up(struct mvsd_host *host)
{
void __iomem *iobase = host->base;
dev_dbg(host->dev, "power up\n");
mvsd_write(MVSD_NOR_INTR_EN, 0);
mvsd_write(MVSD_ERR_INTR_EN, 0);
mvsd_write(MVSD_SW_RESET, MVSD_SW_RESET_NOW);
mvsd_write(MVSD_XFER_MODE, 0);
mvsd_write(MVSD_NOR_STATUS_EN, 0xffff);
mvsd_write(MVSD_ERR_STATUS_EN, 0xffff);
mvsd_write(MVSD_NOR_INTR_STATUS, 0xffff);
mvsd_write(MVSD_ERR_INTR_STATUS, 0xffff);
}
static void mvsd_power_down(struct mvsd_host *host)
{
void __iomem *iobase = host->base;
dev_dbg(host->dev, "power down\n");
mvsd_write(MVSD_NOR_INTR_EN, 0);
mvsd_write(MVSD_ERR_INTR_EN, 0);
mvsd_write(MVSD_SW_RESET, MVSD_SW_RESET_NOW);
mvsd_write(MVSD_XFER_MODE, MVSD_XFER_MODE_STOP_CLK);
mvsd_write(MVSD_NOR_STATUS_EN, 0);
mvsd_write(MVSD_ERR_STATUS_EN, 0);
mvsd_write(MVSD_NOR_INTR_STATUS, 0xffff);
mvsd_write(MVSD_ERR_INTR_STATUS, 0xffff);
}
static void mvsd_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
{
struct mvsd_host *host = mmc_priv(mmc);
void __iomem *iobase = host->base;
u32 ctrl_reg = 0;
if (ios->power_mode == MMC_POWER_UP)
mvsd_power_up(host);
if (ios->clock == 0) {
mvsd_write(MVSD_XFER_MODE, MVSD_XFER_MODE_STOP_CLK);
mvsd_write(MVSD_CLK_DIV, MVSD_BASE_DIV_MAX);
host->clock = 0;
dev_dbg(host->dev, "clock off\n");
} else if (ios->clock != host->clock) {
u32 m = DIV_ROUND_UP(host->base_clock, ios->clock) - 1;
if (m > MVSD_BASE_DIV_MAX)
m = MVSD_BASE_DIV_MAX;
mvsd_write(MVSD_CLK_DIV, m);
host->clock = ios->clock;
host->ns_per_clk = 1000000000 / (host->base_clock / (m+1));
dev_dbg(host->dev, "clock=%d (%d), div=0x%04x\n",
ios->clock, host->base_clock / (m+1), m);
}
/* default transfer mode */
ctrl_reg |= MVSD_HOST_CTRL_BIG_ENDIAN;
ctrl_reg &= ~MVSD_HOST_CTRL_LSB_FIRST;
/* default to maximum timeout */
ctrl_reg |= MVSD_HOST_CTRL_TMOUT_MASK;
ctrl_reg |= MVSD_HOST_CTRL_TMOUT_EN;
if (ios->bus_mode == MMC_BUSMODE_PUSHPULL)
ctrl_reg |= MVSD_HOST_CTRL_PUSH_PULL_EN;
if (ios->bus_width == MMC_BUS_WIDTH_4)
ctrl_reg |= MVSD_HOST_CTRL_DATA_WIDTH_4_BITS;
if (ios->timing == MMC_TIMING_MMC_HS ||
ios->timing == MMC_TIMING_SD_HS)
ctrl_reg |= MVSD_HOST_CTRL_HI_SPEED_EN;
host->ctrl = ctrl_reg;
mvsd_write(MVSD_HOST_CTRL, ctrl_reg);
dev_dbg(host->dev, "ctrl 0x%04x: %s %s %s\n", ctrl_reg,
(ctrl_reg & MVSD_HOST_CTRL_PUSH_PULL_EN) ?
"push-pull" : "open-drain",
(ctrl_reg & MVSD_HOST_CTRL_DATA_WIDTH_4_BITS) ?
"4bit-width" : "1bit-width",
(ctrl_reg & MVSD_HOST_CTRL_HI_SPEED_EN) ?
"high-speed" : "");
if (ios->power_mode == MMC_POWER_OFF)
mvsd_power_down(host);
}
static const struct mmc_host_ops mvsd_ops = {
.request = mvsd_request,
.get_ro = mvsd_get_ro,
.set_ios = mvsd_set_ios,
.enable_sdio_irq = mvsd_enable_sdio_irq,
};
static void __init mv_conf_mbus_windows(struct mvsd_host *host,
struct mbus_dram_target_info *dram)
{
void __iomem *iobase = host->base;
int i;
for (i = 0; i < 4; i++) {
writel(0, iobase + MVSD_WINDOW_CTRL(i));
writel(0, iobase + MVSD_WINDOW_BASE(i));
}
for (i = 0; i < dram->num_cs; i++) {
struct mbus_dram_window *cs = dram->cs + i;
writel(((cs->size - 1) & 0xffff0000) |
(cs->mbus_attr << 8) |
(dram->mbus_dram_target_id << 4) | 1,
iobase + MVSD_WINDOW_CTRL(i));
writel(cs->base, iobase + MVSD_WINDOW_BASE(i));
}
}
static int __init mvsd_probe(struct platform_device *pdev)
{
struct mmc_host *mmc = NULL;
struct mvsd_host *host = NULL;
const struct mvsdio_platform_data *mvsd_data;
struct resource *r;
int ret, irq;
r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
irq = platform_get_irq(pdev, 0);
mvsd_data = pdev->dev.platform_data;
if (!r || irq < 0 || !mvsd_data)
return -ENXIO;
r = request_mem_region(r->start, SZ_1K, DRIVER_NAME);
if (!r)
return -EBUSY;
mmc = mmc_alloc_host(sizeof(struct mvsd_host), &pdev->dev);
if (!mmc) {
ret = -ENOMEM;
goto out;
}
host = mmc_priv(mmc);
host->mmc = mmc;
host->dev = &pdev->dev;
host->res = r;
host->base_clock = mvsd_data->clock / 2;
mmc->ops = &mvsd_ops;
mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34;
mmc->caps = MMC_CAP_4_BIT_DATA | MMC_CAP_SDIO_IRQ |
MMC_CAP_SD_HIGHSPEED | MMC_CAP_MMC_HIGHSPEED;
mmc->f_min = DIV_ROUND_UP(host->base_clock, MVSD_BASE_DIV_MAX);
mmc->f_max = maxfreq;
mmc->max_blk_size = 2048;
mmc->max_blk_count = 65535;
mmc->max_hw_segs = 1;
mmc->max_phys_segs = 1;
mmc->max_seg_size = mmc->max_blk_size * mmc->max_blk_count;
mmc->max_req_size = mmc->max_blk_size * mmc->max_blk_count;
spin_lock_init(&host->lock);
host->base = ioremap(r->start, SZ_4K);
if (!host->base) {
ret = -ENOMEM;
goto out;
}
/* (Re-)program MBUS remapping windows if we are asked to. */
if (mvsd_data->dram != NULL)
mv_conf_mbus_windows(host, mvsd_data->dram);
mvsd_power_down(host);
ret = request_irq(irq, mvsd_irq, 0, DRIVER_NAME, host);
if (ret) {
printk(KERN_ERR "%s: cannot assign irq %d\n", DRIVER_NAME, irq);
goto out;
} else
host->irq = irq;
if (mvsd_data->gpio_card_detect) {
ret = gpio_request(mvsd_data->gpio_card_detect,
DRIVER_NAME " cd");
if (ret == 0) {
gpio_direction_input(mvsd_data->gpio_card_detect);
irq = gpio_to_irq(mvsd_data->gpio_card_detect);
ret = request_irq(irq, mvsd_card_detect_irq,
IRQ_TYPE_EDGE_RISING | IRQ_TYPE_EDGE_FALLING,
DRIVER_NAME " cd", host);
if (ret == 0)
host->gpio_card_detect =
mvsd_data->gpio_card_detect;
else
gpio_free(mvsd_data->gpio_card_detect);
}
}
if (!host->gpio_card_detect)
mmc->caps |= MMC_CAP_NEEDS_POLL;
if (mvsd_data->gpio_write_protect) {
ret = gpio_request(mvsd_data->gpio_write_protect,
DRIVER_NAME " wp");
if (ret == 0) {
gpio_direction_input(mvsd_data->gpio_write_protect);
host->gpio_write_protect =
mvsd_data->gpio_write_protect;
}
}
setup_timer(&host->timer, mvsd_timeout_timer, (unsigned long)host);
platform_set_drvdata(pdev, mmc);
ret = mmc_add_host(mmc);
if (ret)
goto out;
printk(KERN_NOTICE "%s: %s driver initialized, ",
mmc_hostname(mmc), DRIVER_NAME);
if (host->gpio_card_detect)
printk("using GPIO %d for card detection\n",
host->gpio_card_detect);
else
printk("lacking card detect (fall back to polling)\n");
return 0;
out:
if (host) {
if (host->irq)
free_irq(host->irq, host);
if (host->gpio_card_detect) {
free_irq(gpio_to_irq(host->gpio_card_detect), host);
gpio_free(host->gpio_card_detect);
}
if (host->gpio_write_protect)
gpio_free(host->gpio_write_protect);
if (host->base)
iounmap(host->base);
}
if (r)
release_resource(r);
if (mmc)
mmc_free_host(mmc);
return ret;
}
static int __exit mvsd_remove(struct platform_device *pdev)
{
struct mmc_host *mmc = platform_get_drvdata(pdev);
if (mmc) {
struct mvsd_host *host = mmc_priv(mmc);
if (host->gpio_card_detect) {
free_irq(gpio_to_irq(host->gpio_card_detect), host);
gpio_free(host->gpio_card_detect);
}
mmc_remove_host(mmc);
free_irq(host->irq, host);
if (host->gpio_write_protect)
gpio_free(host->gpio_write_protect);
del_timer_sync(&host->timer);
mvsd_power_down(host);
iounmap(host->base);
release_resource(host->res);
mmc_free_host(mmc);
}
platform_set_drvdata(pdev, NULL);
return 0;
}
#ifdef CONFIG_PM
static int mvsd_suspend(struct platform_device *dev, pm_message_t state,
u32 level)
{
struct mmc_host *mmc = platform_get_drvdata(dev);
int ret = 0;
if (mmc && level == SUSPEND_DISABLE)
ret = mmc_suspend_host(mmc, state);
return ret;
}
static int mvsd_resume(struct platform_device *dev, u32 level)
{
struct mmc_host *mmc = platform_dev_get_drvdata(dev);
int ret = 0;
if (mmc && level == RESUME_ENABLE)
ret = mmc_resume_host(mmc);
return ret;
}
#else
#define mvsd_suspend NULL
#define mvsd_resume NULL
#endif
static struct platform_driver mvsd_driver = {
.remove = __exit_p(mvsd_remove),
.suspend = mvsd_suspend,
.resume = mvsd_resume,
.driver = {
.name = DRIVER_NAME,
},
};
static int __init mvsd_init(void)
{
return platform_driver_probe(&mvsd_driver, mvsd_probe);
}
static void __exit mvsd_exit(void)
{
platform_driver_unregister(&mvsd_driver);
}
module_init(mvsd_init);
module_exit(mvsd_exit);
/* maximum card clock frequency (default 50MHz) */
module_param(maxfreq, int, 0);
/* force PIO transfers all the time */
module_param(nodma, int, 0);
MODULE_AUTHOR("Maen Suleiman, Nicolas Pitre");
MODULE_DESCRIPTION("Marvell MMC,SD,SDIO Host Controller driver");
MODULE_LICENSE("GPL");

190
drivers/mmc/host/mvsdio.h Normal file
Просмотреть файл

@ -0,0 +1,190 @@
/*
* 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);
mmc_omap_config_dma_params(host, data, data->sg);
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;
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,32 +1073,32 @@ 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_init(host);
/* 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;
}
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);
/* Set SD bus power bit */
OMAP_HSMMC_WRITE(host->base, HCTL,
OMAP_HSMMC_READ(host->base, HCTL) | SDBP);
/* Request IRQ for MMC operations */
ret = request_irq(host->irq, mmc_omap_irq, IRQF_DISABLED,
mmc_hostname(mmc), host);
@ -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)

309
drivers/mmc/host/sdhci-of.c Normal file
Просмотреть файл

@ -0,0 +1,309 @@
/*
* 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");

Просмотреть файл

@ -48,35 +48,35 @@ static void sdhci_dumpregs(struct sdhci_host *host)
printk(KERN_DEBUG DRIVER_NAME ": ============== REGISTER DUMP ==============\n");
printk(KERN_DEBUG DRIVER_NAME ": Sys addr: 0x%08x | Version: 0x%08x\n",
readl(host->ioaddr + SDHCI_DMA_ADDRESS),
readw(host->ioaddr + SDHCI_HOST_VERSION));
sdhci_readl(host, SDHCI_DMA_ADDRESS),
sdhci_readw(host, SDHCI_HOST_VERSION));
printk(KERN_DEBUG DRIVER_NAME ": Blk size: 0x%08x | Blk cnt: 0x%08x\n",
readw(host->ioaddr + SDHCI_BLOCK_SIZE),
readw(host->ioaddr + SDHCI_BLOCK_COUNT));
sdhci_readw(host, SDHCI_BLOCK_SIZE),
sdhci_readw(host, SDHCI_BLOCK_COUNT));
printk(KERN_DEBUG DRIVER_NAME ": Argument: 0x%08x | Trn mode: 0x%08x\n",
readl(host->ioaddr + SDHCI_ARGUMENT),
readw(host->ioaddr + SDHCI_TRANSFER_MODE));
sdhci_readl(host, SDHCI_ARGUMENT),
sdhci_readw(host, SDHCI_TRANSFER_MODE));
printk(KERN_DEBUG DRIVER_NAME ": Present: 0x%08x | Host ctl: 0x%08x\n",
readl(host->ioaddr + SDHCI_PRESENT_STATE),
readb(host->ioaddr + SDHCI_HOST_CONTROL));
sdhci_readl(host, SDHCI_PRESENT_STATE),
sdhci_readb(host, SDHCI_HOST_CONTROL));
printk(KERN_DEBUG DRIVER_NAME ": Power: 0x%08x | Blk gap: 0x%08x\n",
readb(host->ioaddr + SDHCI_POWER_CONTROL),
readb(host->ioaddr + SDHCI_BLOCK_GAP_CONTROL));
sdhci_readb(host, SDHCI_POWER_CONTROL),
sdhci_readb(host, SDHCI_BLOCK_GAP_CONTROL));
printk(KERN_DEBUG DRIVER_NAME ": Wake-up: 0x%08x | Clock: 0x%08x\n",
readb(host->ioaddr + SDHCI_WAKE_UP_CONTROL),
readw(host->ioaddr + SDHCI_CLOCK_CONTROL));
sdhci_readb(host, SDHCI_WAKE_UP_CONTROL),
sdhci_readw(host, SDHCI_CLOCK_CONTROL));
printk(KERN_DEBUG DRIVER_NAME ": Timeout: 0x%08x | Int stat: 0x%08x\n",
readb(host->ioaddr + SDHCI_TIMEOUT_CONTROL),
readl(host->ioaddr + SDHCI_INT_STATUS));
sdhci_readb(host, SDHCI_TIMEOUT_CONTROL),
sdhci_readl(host, SDHCI_INT_STATUS));
printk(KERN_DEBUG DRIVER_NAME ": Int enab: 0x%08x | Sig enab: 0x%08x\n",
readl(host->ioaddr + SDHCI_INT_ENABLE),
readl(host->ioaddr + SDHCI_SIGNAL_ENABLE));
sdhci_readl(host, SDHCI_INT_ENABLE),
sdhci_readl(host, SDHCI_SIGNAL_ENABLE));
printk(KERN_DEBUG DRIVER_NAME ": AC12 err: 0x%08x | Slot int: 0x%08x\n",
readw(host->ioaddr + SDHCI_ACMD12_ERR),
readw(host->ioaddr + SDHCI_SLOT_INT_STATUS));
sdhci_readw(host, SDHCI_ACMD12_ERR),
sdhci_readw(host, SDHCI_SLOT_INT_STATUS));
printk(KERN_DEBUG DRIVER_NAME ": Caps: 0x%08x | Max curr: 0x%08x\n",
readl(host->ioaddr + SDHCI_CAPABILITIES),
readl(host->ioaddr + SDHCI_MAX_CURRENT));
sdhci_readl(host, SDHCI_CAPABILITIES),
sdhci_readl(host, SDHCI_MAX_CURRENT));
printk(KERN_DEBUG DRIVER_NAME ": ===========================================\n");
}
@ -87,17 +87,65 @@ static void sdhci_dumpregs(struct sdhci_host *host)
* *
\*****************************************************************************/
static void sdhci_clear_set_irqs(struct sdhci_host *host, u32 clear, u32 set)
{
u32 ier;
ier = sdhci_readl(host, SDHCI_INT_ENABLE);
ier &= ~clear;
ier |= set;
sdhci_writel(host, ier, SDHCI_INT_ENABLE);
sdhci_writel(host, ier, SDHCI_SIGNAL_ENABLE);
}
static void sdhci_unmask_irqs(struct sdhci_host *host, u32 irqs)
{
sdhci_clear_set_irqs(host, 0, irqs);
}
static void sdhci_mask_irqs(struct sdhci_host *host, u32 irqs)
{
sdhci_clear_set_irqs(host, irqs, 0);
}
static void sdhci_set_card_detection(struct sdhci_host *host, bool enable)
{
u32 irqs = SDHCI_INT_CARD_REMOVE | SDHCI_INT_CARD_INSERT;
if (host->quirks & SDHCI_QUIRK_BROKEN_CARD_DETECTION)
return;
if (enable)
sdhci_unmask_irqs(host, irqs);
else
sdhci_mask_irqs(host, irqs);
}
static void sdhci_enable_card_detection(struct sdhci_host *host)
{
sdhci_set_card_detection(host, true);
}
static void sdhci_disable_card_detection(struct sdhci_host *host)
{
sdhci_set_card_detection(host, false);
}
static void sdhci_reset(struct sdhci_host *host, u8 mask)
{
unsigned long timeout;
u32 uninitialized_var(ier);
if (host->quirks & SDHCI_QUIRK_NO_CARD_NO_RESET) {
if (!(readl(host->ioaddr + SDHCI_PRESENT_STATE) &
if (!(sdhci_readl(host, SDHCI_PRESENT_STATE) &
SDHCI_CARD_PRESENT))
return;
}
writeb(mask, host->ioaddr + SDHCI_SOFTWARE_RESET);
if (host->quirks & SDHCI_QUIRK_RESTORE_IRQS_AFTER_RESET)
ier = sdhci_readl(host, SDHCI_INT_ENABLE);
sdhci_writeb(host, mask, SDHCI_SOFTWARE_RESET);
if (mask & SDHCI_RESET_ALL)
host->clock = 0;
@ -106,7 +154,7 @@ static void sdhci_reset(struct sdhci_host *host, u8 mask)
timeout = 100;
/* hw clears the bit when it's done */
while (readb(host->ioaddr + SDHCI_SOFTWARE_RESET) & mask) {
while (sdhci_readb(host, SDHCI_SOFTWARE_RESET) & mask) {
if (timeout == 0) {
printk(KERN_ERR "%s: Reset 0x%x never completed.\n",
mmc_hostname(host->mmc), (int)mask);
@ -116,42 +164,44 @@ static void sdhci_reset(struct sdhci_host *host, u8 mask)
timeout--;
mdelay(1);
}
if (host->quirks & SDHCI_QUIRK_RESTORE_IRQS_AFTER_RESET)
sdhci_clear_set_irqs(host, SDHCI_INT_ALL_MASK, ier);
}
static void sdhci_init(struct sdhci_host *host)
{
u32 intmask;
sdhci_reset(host, SDHCI_RESET_ALL);
intmask = SDHCI_INT_BUS_POWER | SDHCI_INT_DATA_END_BIT |
sdhci_clear_set_irqs(host, SDHCI_INT_ALL_MASK,
SDHCI_INT_BUS_POWER | SDHCI_INT_DATA_END_BIT |
SDHCI_INT_DATA_CRC | SDHCI_INT_DATA_TIMEOUT | SDHCI_INT_INDEX |
SDHCI_INT_END_BIT | SDHCI_INT_CRC | SDHCI_INT_TIMEOUT |
SDHCI_INT_CARD_REMOVE | SDHCI_INT_CARD_INSERT |
SDHCI_INT_DATA_AVAIL | SDHCI_INT_SPACE_AVAIL |
SDHCI_INT_DMA_END | SDHCI_INT_DATA_END | SDHCI_INT_RESPONSE |
SDHCI_INT_ADMA_ERROR;
SDHCI_INT_DATA_END | SDHCI_INT_RESPONSE);
}
writel(intmask, host->ioaddr + SDHCI_INT_ENABLE);
writel(intmask, host->ioaddr + SDHCI_SIGNAL_ENABLE);
static void sdhci_reinit(struct sdhci_host *host)
{
sdhci_init(host);
sdhci_enable_card_detection(host);
}
static void sdhci_activate_led(struct sdhci_host *host)
{
u8 ctrl;
ctrl = readb(host->ioaddr + SDHCI_HOST_CONTROL);
ctrl = sdhci_readb(host, SDHCI_HOST_CONTROL);
ctrl |= SDHCI_CTRL_LED;
writeb(ctrl, host->ioaddr + SDHCI_HOST_CONTROL);
sdhci_writeb(host, ctrl, SDHCI_HOST_CONTROL);
}
static void sdhci_deactivate_led(struct sdhci_host *host)
{
u8 ctrl;
ctrl = readb(host->ioaddr + SDHCI_HOST_CONTROL);
ctrl = sdhci_readb(host, SDHCI_HOST_CONTROL);
ctrl &= ~SDHCI_CTRL_LED;
writeb(ctrl, host->ioaddr + SDHCI_HOST_CONTROL);
sdhci_writeb(host, ctrl, SDHCI_HOST_CONTROL);
}
#ifdef SDHCI_USE_LEDS_CLASS
@ -205,7 +255,7 @@ static void sdhci_read_block_pio(struct sdhci_host *host)
while (len) {
if (chunk == 0) {
scratch = readl(host->ioaddr + SDHCI_BUFFER);
scratch = sdhci_readl(host, SDHCI_BUFFER);
chunk = 4;
}
@ -257,7 +307,7 @@ static void sdhci_write_block_pio(struct sdhci_host *host)
len--;
if ((chunk == 4) || ((len == 0) && (blksize == 0))) {
writel(scratch, host->ioaddr + SDHCI_BUFFER);
sdhci_writel(host, scratch, SDHCI_BUFFER);
chunk = 0;
scratch = 0;
}
@ -292,7 +342,10 @@ static void sdhci_transfer_pio(struct sdhci_host *host)
(host->data->blocks == 1))
mask = ~0;
while (readl(host->ioaddr + SDHCI_PRESENT_STATE) & mask) {
while (sdhci_readl(host, SDHCI_PRESENT_STATE) & mask) {
if (host->quirks & SDHCI_QUIRK_PIO_NEEDS_DELAY)
udelay(100);
if (host->data->flags & MMC_DATA_READ)
sdhci_read_block_pio(host);
else
@ -561,6 +614,17 @@ static u8 sdhci_calc_timeout(struct sdhci_host *host, struct mmc_data *data)
return count;
}
static void sdhci_set_transfer_irqs(struct sdhci_host *host)
{
u32 pio_irqs = SDHCI_INT_DATA_AVAIL | SDHCI_INT_SPACE_AVAIL;
u32 dma_irqs = SDHCI_INT_DMA_END | SDHCI_INT_ADMA_ERROR;
if (host->flags & SDHCI_REQ_USE_DMA)
sdhci_clear_set_irqs(host, pio_irqs, dma_irqs);
else
sdhci_clear_set_irqs(host, dma_irqs, pio_irqs);
}
static void sdhci_prepare_data(struct sdhci_host *host, struct mmc_data *data)
{
u8 count;
@ -581,7 +645,7 @@ static void sdhci_prepare_data(struct sdhci_host *host, struct mmc_data *data)
host->data_early = 0;
count = sdhci_calc_timeout(host, data);
writeb(count, host->ioaddr + SDHCI_TIMEOUT_CONTROL);
sdhci_writeb(host, count, SDHCI_TIMEOUT_CONTROL);
if (host->flags & SDHCI_USE_DMA)
host->flags |= SDHCI_REQ_USE_DMA;
@ -661,8 +725,8 @@ static void sdhci_prepare_data(struct sdhci_host *host, struct mmc_data *data)
WARN_ON(1);
host->flags &= ~SDHCI_REQ_USE_DMA;
} else {
writel(host->adma_addr,
host->ioaddr + SDHCI_ADMA_ADDRESS);
sdhci_writel(host, host->adma_addr,
SDHCI_ADMA_ADDRESS);
}
} else {
int sg_cnt;
@ -681,8 +745,8 @@ static void sdhci_prepare_data(struct sdhci_host *host, struct mmc_data *data)
host->flags &= ~SDHCI_REQ_USE_DMA;
} else {
WARN_ON(sg_cnt != 1);
writel(sg_dma_address(data->sg),
host->ioaddr + SDHCI_DMA_ADDRESS);
sdhci_writel(host, sg_dma_address(data->sg),
SDHCI_DMA_ADDRESS);
}
}
}
@ -693,14 +757,14 @@ static void sdhci_prepare_data(struct sdhci_host *host, struct mmc_data *data)
* is ADMA.
*/
if (host->version >= SDHCI_SPEC_200) {
ctrl = readb(host->ioaddr + SDHCI_HOST_CONTROL);
ctrl = sdhci_readb(host, SDHCI_HOST_CONTROL);
ctrl &= ~SDHCI_CTRL_DMA_MASK;
if ((host->flags & SDHCI_REQ_USE_DMA) &&
(host->flags & SDHCI_USE_ADMA))
ctrl |= SDHCI_CTRL_ADMA32;
else
ctrl |= SDHCI_CTRL_SDMA;
writeb(ctrl, host->ioaddr + SDHCI_HOST_CONTROL);
sdhci_writeb(host, ctrl, SDHCI_HOST_CONTROL);
}
if (!(host->flags & SDHCI_REQ_USE_DMA)) {
@ -709,10 +773,11 @@ static void sdhci_prepare_data(struct sdhci_host *host, struct mmc_data *data)
host->blocks = data->blocks;
}
sdhci_set_transfer_irqs(host);
/* We do not handle DMA boundaries, so set it to max (512 KiB) */
writew(SDHCI_MAKE_BLKSZ(7, data->blksz),
host->ioaddr + SDHCI_BLOCK_SIZE);
writew(data->blocks, host->ioaddr + SDHCI_BLOCK_COUNT);
sdhci_writew(host, SDHCI_MAKE_BLKSZ(7, data->blksz), SDHCI_BLOCK_SIZE);
sdhci_writew(host, data->blocks, SDHCI_BLOCK_COUNT);
}
static void sdhci_set_transfer_mode(struct sdhci_host *host,
@ -733,7 +798,7 @@ static void sdhci_set_transfer_mode(struct sdhci_host *host,
if (host->flags & SDHCI_REQ_USE_DMA)
mode |= SDHCI_TRNS_DMA;
writew(mode, host->ioaddr + SDHCI_TRANSFER_MODE);
sdhci_writew(host, mode, SDHCI_TRANSFER_MODE);
}
static void sdhci_finish_data(struct sdhci_host *host)
@ -802,7 +867,7 @@ static void sdhci_send_command(struct sdhci_host *host, struct mmc_command *cmd)
if (host->mrq->data && (cmd == host->mrq->data->stop))
mask &= ~SDHCI_DATA_INHIBIT;
while (readl(host->ioaddr + SDHCI_PRESENT_STATE) & mask) {
while (sdhci_readl(host, SDHCI_PRESENT_STATE) & mask) {
if (timeout == 0) {
printk(KERN_ERR "%s: Controller never released "
"inhibit bit(s).\n", mmc_hostname(host->mmc));
@ -821,7 +886,7 @@ static void sdhci_send_command(struct sdhci_host *host, struct mmc_command *cmd)
sdhci_prepare_data(host, cmd->data);
writel(cmd->arg, host->ioaddr + SDHCI_ARGUMENT);
sdhci_writel(host, cmd->arg, SDHCI_ARGUMENT);
sdhci_set_transfer_mode(host, cmd->data);
@ -849,8 +914,7 @@ static void sdhci_send_command(struct sdhci_host *host, struct mmc_command *cmd)
if (cmd->data)
flags |= SDHCI_CMD_DATA;
writew(SDHCI_MAKE_CMD(cmd->opcode, flags),
host->ioaddr + SDHCI_COMMAND);
sdhci_writew(host, SDHCI_MAKE_CMD(cmd->opcode, flags), SDHCI_COMMAND);
}
static void sdhci_finish_command(struct sdhci_host *host)
@ -863,15 +927,15 @@ static void sdhci_finish_command(struct sdhci_host *host)
if (host->cmd->flags & MMC_RSP_136) {
/* CRC is stripped so we need to do some shifting. */
for (i = 0;i < 4;i++) {
host->cmd->resp[i] = readl(host->ioaddr +
host->cmd->resp[i] = sdhci_readl(host,
SDHCI_RESPONSE + (3-i)*4) << 8;
if (i != 3)
host->cmd->resp[i] |=
readb(host->ioaddr +
sdhci_readb(host,
SDHCI_RESPONSE + (3-i)*4-1);
}
} else {
host->cmd->resp[0] = readl(host->ioaddr + SDHCI_RESPONSE);
host->cmd->resp[0] = sdhci_readl(host, SDHCI_RESPONSE);
}
}
@ -895,7 +959,13 @@ static void sdhci_set_clock(struct sdhci_host *host, unsigned int clock)
if (clock == host->clock)
return;
writew(0, host->ioaddr + SDHCI_CLOCK_CONTROL);
if (host->ops->set_clock) {
host->ops->set_clock(host, clock);
if (host->quirks & SDHCI_QUIRK_NONSTANDARD_CLOCK)
return;
}
sdhci_writew(host, 0, SDHCI_CLOCK_CONTROL);
if (clock == 0)
goto out;
@ -908,11 +978,11 @@ static void sdhci_set_clock(struct sdhci_host *host, unsigned int clock)
clk = div << SDHCI_DIVIDER_SHIFT;
clk |= SDHCI_CLOCK_INT_EN;
writew(clk, host->ioaddr + SDHCI_CLOCK_CONTROL);
sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL);
/* Wait max 10 ms */
timeout = 10;
while (!((clk = readw(host->ioaddr + SDHCI_CLOCK_CONTROL))
while (!((clk = sdhci_readw(host, SDHCI_CLOCK_CONTROL))
& SDHCI_CLOCK_INT_STABLE)) {
if (timeout == 0) {
printk(KERN_ERR "%s: Internal clock never "
@ -925,7 +995,7 @@ static void sdhci_set_clock(struct sdhci_host *host, unsigned int clock)
}
clk |= SDHCI_CLOCK_CARD_EN;
writew(clk, host->ioaddr + SDHCI_CLOCK_CONTROL);
sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL);
out:
host->clock = clock;
@ -939,7 +1009,7 @@ static void sdhci_set_power(struct sdhci_host *host, unsigned short power)
return;
if (power == (unsigned short)-1) {
writeb(0, host->ioaddr + SDHCI_POWER_CONTROL);
sdhci_writeb(host, 0, SDHCI_POWER_CONTROL);
goto out;
}
@ -948,7 +1018,7 @@ static void sdhci_set_power(struct sdhci_host *host, unsigned short power)
* a new value. Some controllers don't seem to like this though.
*/
if (!(host->quirks & SDHCI_QUIRK_SINGLE_POWER_WRITE))
writeb(0, host->ioaddr + SDHCI_POWER_CONTROL);
sdhci_writeb(host, 0, SDHCI_POWER_CONTROL);
pwr = SDHCI_POWER_ON;
@ -973,10 +1043,9 @@ static void sdhci_set_power(struct sdhci_host *host, unsigned short power)
* and set turn on power at the same time, so set the voltage first.
*/
if ((host->quirks & SDHCI_QUIRK_NO_SIMULT_VDD_AND_POWER))
writeb(pwr & ~SDHCI_POWER_ON,
host->ioaddr + SDHCI_POWER_CONTROL);
sdhci_writeb(host, pwr & ~SDHCI_POWER_ON, SDHCI_POWER_CONTROL);
writeb(pwr, host->ioaddr + SDHCI_POWER_CONTROL);
sdhci_writeb(host, pwr, SDHCI_POWER_CONTROL);
out:
host->power = power;
@ -991,6 +1060,7 @@ out:
static void sdhci_request(struct mmc_host *mmc, struct mmc_request *mrq)
{
struct sdhci_host *host;
bool present;
unsigned long flags;
host = mmc_priv(mmc);
@ -1005,8 +1075,14 @@ static void sdhci_request(struct mmc_host *mmc, struct mmc_request *mrq)
host->mrq = mrq;
if (!(readl(host->ioaddr + SDHCI_PRESENT_STATE) & SDHCI_CARD_PRESENT)
|| (host->flags & SDHCI_DEVICE_DEAD)) {
/* If polling, assume that the card is always present. */
if (host->quirks & SDHCI_QUIRK_BROKEN_CARD_DETECTION)
present = true;
else
present = sdhci_readl(host, SDHCI_PRESENT_STATE) &
SDHCI_CARD_PRESENT;
if (!present || host->flags & SDHCI_DEVICE_DEAD) {
host->mrq->cmd->error = -ENOMEDIUM;
tasklet_schedule(&host->finish_tasklet);
} else
@ -1034,8 +1110,8 @@ static void sdhci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
* Should clear out any weird states.
*/
if (ios->power_mode == MMC_POWER_OFF) {
writel(0, host->ioaddr + SDHCI_SIGNAL_ENABLE);
sdhci_init(host);
sdhci_writel(host, 0, SDHCI_SIGNAL_ENABLE);
sdhci_reinit(host);
}
sdhci_set_clock(host, ios->clock);
@ -1045,7 +1121,7 @@ static void sdhci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
else
sdhci_set_power(host, ios->vdd);
ctrl = readb(host->ioaddr + SDHCI_HOST_CONTROL);
ctrl = sdhci_readb(host, SDHCI_HOST_CONTROL);
if (ios->bus_width == MMC_BUS_WIDTH_4)
ctrl |= SDHCI_CTRL_4BITBUS;
@ -1057,7 +1133,7 @@ static void sdhci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
else
ctrl &= ~SDHCI_CTRL_HISPD;
writeb(ctrl, host->ioaddr + SDHCI_HOST_CONTROL);
sdhci_writeb(host, ctrl, SDHCI_HOST_CONTROL);
/*
* Some (ENE) controllers go apeshit on some ios operation,
@ -1085,10 +1161,12 @@ static int sdhci_get_ro(struct mmc_host *mmc)
if (host->flags & SDHCI_DEVICE_DEAD)
present = 0;
else
present = readl(host->ioaddr + SDHCI_PRESENT_STATE);
present = sdhci_readl(host, SDHCI_PRESENT_STATE);
spin_unlock_irqrestore(&host->lock, flags);
if (host->quirks & SDHCI_QUIRK_INVERTED_WRITE_PROTECT)
return !!(present & SDHCI_WRITE_PROTECT);
return !(present & SDHCI_WRITE_PROTECT);
}
@ -1096,7 +1174,6 @@ static void sdhci_enable_sdio_irq(struct mmc_host *mmc, int enable)
{
struct sdhci_host *host;
unsigned long flags;
u32 ier;
host = mmc_priv(mmc);
@ -1105,15 +1182,10 @@ static void sdhci_enable_sdio_irq(struct mmc_host *mmc, int enable)
if (host->flags & SDHCI_DEVICE_DEAD)
goto out;
ier = readl(host->ioaddr + SDHCI_INT_ENABLE);
ier &= ~SDHCI_INT_CARD_INT;
if (enable)
ier |= SDHCI_INT_CARD_INT;
writel(ier, host->ioaddr + SDHCI_INT_ENABLE);
writel(ier, host->ioaddr + SDHCI_SIGNAL_ENABLE);
sdhci_unmask_irqs(host, SDHCI_INT_CARD_INT);
else
sdhci_mask_irqs(host, SDHCI_INT_CARD_INT);
out:
mmiowb();
@ -1142,7 +1214,7 @@ static void sdhci_tasklet_card(unsigned long param)
spin_lock_irqsave(&host->lock, flags);
if (!(readl(host->ioaddr + SDHCI_PRESENT_STATE) & SDHCI_CARD_PRESENT)) {
if (!(sdhci_readl(host, SDHCI_PRESENT_STATE) & SDHCI_CARD_PRESENT)) {
if (host->mrq) {
printk(KERN_ERR "%s: Card removed during transfer!\n",
mmc_hostname(host->mmc));
@ -1346,8 +1418,8 @@ static void sdhci_data_irq(struct sdhci_host *host, u32 intmask)
* we need to at least restart the transfer.
*/
if (intmask & SDHCI_INT_DMA_END)
writel(readl(host->ioaddr + SDHCI_DMA_ADDRESS),
host->ioaddr + SDHCI_DMA_ADDRESS);
sdhci_writel(host, sdhci_readl(host, SDHCI_DMA_ADDRESS),
SDHCI_DMA_ADDRESS);
if (intmask & SDHCI_INT_DATA_END) {
if (host->cmd) {
@ -1373,7 +1445,7 @@ static irqreturn_t sdhci_irq(int irq, void *dev_id)
spin_lock(&host->lock);
intmask = readl(host->ioaddr + SDHCI_INT_STATUS);
intmask = sdhci_readl(host, SDHCI_INT_STATUS);
if (!intmask || intmask == 0xffffffff) {
result = IRQ_NONE;
@ -1384,22 +1456,22 @@ static irqreturn_t sdhci_irq(int irq, void *dev_id)
mmc_hostname(host->mmc), intmask);
if (intmask & (SDHCI_INT_CARD_INSERT | SDHCI_INT_CARD_REMOVE)) {
writel(intmask & (SDHCI_INT_CARD_INSERT | SDHCI_INT_CARD_REMOVE),
host->ioaddr + SDHCI_INT_STATUS);
sdhci_writel(host, intmask & (SDHCI_INT_CARD_INSERT |
SDHCI_INT_CARD_REMOVE), SDHCI_INT_STATUS);
tasklet_schedule(&host->card_tasklet);
}
intmask &= ~(SDHCI_INT_CARD_INSERT | SDHCI_INT_CARD_REMOVE);
if (intmask & SDHCI_INT_CMD_MASK) {
writel(intmask & SDHCI_INT_CMD_MASK,
host->ioaddr + SDHCI_INT_STATUS);
sdhci_writel(host, intmask & SDHCI_INT_CMD_MASK,
SDHCI_INT_STATUS);
sdhci_cmd_irq(host, intmask & SDHCI_INT_CMD_MASK);
}
if (intmask & SDHCI_INT_DATA_MASK) {
writel(intmask & SDHCI_INT_DATA_MASK,
host->ioaddr + SDHCI_INT_STATUS);
sdhci_writel(host, intmask & SDHCI_INT_DATA_MASK,
SDHCI_INT_STATUS);
sdhci_data_irq(host, intmask & SDHCI_INT_DATA_MASK);
}
@ -1410,7 +1482,7 @@ static irqreturn_t sdhci_irq(int irq, void *dev_id)
if (intmask & SDHCI_INT_BUS_POWER) {
printk(KERN_ERR "%s: Card is consuming too much power!\n",
mmc_hostname(host->mmc));
writel(SDHCI_INT_BUS_POWER, host->ioaddr + SDHCI_INT_STATUS);
sdhci_writel(host, SDHCI_INT_BUS_POWER, SDHCI_INT_STATUS);
}
intmask &= ~SDHCI_INT_BUS_POWER;
@ -1425,7 +1497,7 @@ static irqreturn_t sdhci_irq(int irq, void *dev_id)
mmc_hostname(host->mmc), intmask);
sdhci_dumpregs(host);
writel(intmask, host->ioaddr + SDHCI_INT_STATUS);
sdhci_writel(host, intmask, SDHCI_INT_STATUS);
}
result = IRQ_HANDLED;
@ -1455,6 +1527,8 @@ int sdhci_suspend_host(struct sdhci_host *host, pm_message_t state)
{
int ret;
sdhci_disable_card_detection(host);
ret = mmc_suspend_host(host->mmc, state);
if (ret)
return ret;
@ -1487,6 +1561,8 @@ int sdhci_resume_host(struct sdhci_host *host)
if (ret)
return ret;
sdhci_enable_card_detection(host);
return 0;
}
@ -1537,7 +1613,7 @@ int sdhci_add_host(struct sdhci_host *host)
sdhci_reset(host, SDHCI_RESET_ALL);
host->version = readw(host->ioaddr + SDHCI_HOST_VERSION);
host->version = sdhci_readw(host, SDHCI_HOST_VERSION);
host->version = (host->version & SDHCI_SPEC_VER_MASK)
>> SDHCI_SPEC_VER_SHIFT;
if (host->version > SDHCI_SPEC_200) {
@ -1546,7 +1622,7 @@ int sdhci_add_host(struct sdhci_host *host)
host->version);
}
caps = readl(host->ioaddr + SDHCI_CAPABILITIES);
caps = sdhci_readl(host, SDHCI_CAPABILITIES);
if (host->quirks & SDHCI_QUIRK_FORCE_DMA)
host->flags |= SDHCI_USE_DMA;
@ -1614,19 +1690,27 @@ int sdhci_add_host(struct sdhci_host *host)
host->max_clk =
(caps & SDHCI_CLOCK_BASE_MASK) >> SDHCI_CLOCK_BASE_SHIFT;
if (host->max_clk == 0) {
printk(KERN_ERR "%s: Hardware doesn't specify base clock "
"frequency.\n", mmc_hostname(mmc));
return -ENODEV;
}
host->max_clk *= 1000000;
if (host->max_clk == 0) {
if (!host->ops->get_max_clock) {
printk(KERN_ERR
"%s: Hardware doesn't specify base clock "
"frequency.\n", mmc_hostname(mmc));
return -ENODEV;
}
host->max_clk = host->ops->get_max_clock(host);
}
host->timeout_clk =
(caps & SDHCI_TIMEOUT_CLK_MASK) >> SDHCI_TIMEOUT_CLK_SHIFT;
if (host->timeout_clk == 0) {
printk(KERN_ERR "%s: Hardware doesn't specify timeout clock "
"frequency.\n", mmc_hostname(mmc));
return -ENODEV;
if (!host->ops->get_timeout_clock) {
printk(KERN_ERR
"%s: Hardware doesn't specify timeout clock "
"frequency.\n", mmc_hostname(mmc));
return -ENODEV;
}
host->timeout_clk = host->ops->get_timeout_clock(host);
}
if (caps & SDHCI_TIMEOUT_CLK_UNIT)
host->timeout_clk *= 1000;
@ -1642,6 +1726,9 @@ int sdhci_add_host(struct sdhci_host *host)
if (caps & SDHCI_CAN_DO_HISPD)
mmc->caps |= MMC_CAP_SD_HIGHSPEED;
if (host->quirks & SDHCI_QUIRK_BROKEN_CARD_DETECTION)
mmc->caps |= MMC_CAP_NEEDS_POLL;
mmc->ocr_avail = 0;
if (caps & SDHCI_CAN_VDD_330)
mmc->ocr_avail |= MMC_VDD_32_33|MMC_VDD_33_34;
@ -1690,13 +1777,19 @@ int sdhci_add_host(struct sdhci_host *host)
* Maximum block size. This varies from controller to controller and
* is specified in the capabilities register.
*/
mmc->max_blk_size = (caps & SDHCI_MAX_BLOCK_MASK) >> SDHCI_MAX_BLOCK_SHIFT;
if (mmc->max_blk_size >= 3) {
printk(KERN_WARNING "%s: Invalid maximum block size, "
"assuming 512 bytes\n", mmc_hostname(mmc));
mmc->max_blk_size = 512;
} else
mmc->max_blk_size = 512 << mmc->max_blk_size;
if (host->quirks & SDHCI_QUIRK_FORCE_BLK_SZ_2048) {
mmc->max_blk_size = 2;
} else {
mmc->max_blk_size = (caps & SDHCI_MAX_BLOCK_MASK) >>
SDHCI_MAX_BLOCK_SHIFT;
if (mmc->max_blk_size >= 3) {
printk(KERN_WARNING "%s: Invalid maximum block size, "
"assuming 512 bytes\n", mmc_hostname(mmc));
mmc->max_blk_size = 0;
}
}
mmc->max_blk_size = 512 << mmc->max_blk_size;
/*
* Maximum block count.
@ -1746,6 +1839,8 @@ int sdhci_add_host(struct sdhci_host *host)
(host->flags & SDHCI_USE_ADMA)?"A":"",
(host->flags & SDHCI_USE_DMA)?"DMA":"PIO");
sdhci_enable_card_detection(host);
return 0;
#ifdef SDHCI_USE_LEDS_CLASS
@ -1782,6 +1877,8 @@ void sdhci_remove_host(struct sdhci_host *host, int dead)
spin_unlock_irqrestore(&host->lock, flags);
}
sdhci_disable_card_detection(host);
mmc_remove_host(host->mmc);
#ifdef SDHCI_USE_LEDS_CLASS

Просмотреть файл

@ -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