aboutsummaryrefslogtreecommitdiff
path: root/drivers/mmc/host/mmci.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/mmc/host/mmci.c')
-rw-r--r--drivers/mmc/host/mmci.c382
1 files changed, 325 insertions, 57 deletions
diff --git a/drivers/mmc/host/mmci.c b/drivers/mmc/host/mmci.c
index 50f4f77ed20..13ee09eb3e2 100644
--- a/drivers/mmc/host/mmci.c
+++ b/drivers/mmc/host/mmci.c
@@ -34,6 +34,11 @@
#include <asm/io.h>
#include <asm/sizes.h>
+#ifdef CONFIG_ARCH_U8500
+/* To be withdrawn when cpu_is_u8500v20_or_later() call will disapear */
+#include <mach/hardware.h>
+#endif
+
#include "mmci.h"
#define DRIVER_NAME "mmci-pl18x"
@@ -52,6 +57,10 @@ static unsigned int fmax = 515633;
* @sdio: variant supports SDIO
* @st_clkdiv: true if using a ST-specific clock divider algorithm
* @blksz_datactrl16: true if Block size is at b16..b30 position in datactrl register
+ * @pwrreg_powerup: power up value for MMCIPOWER register
+ * @non_power_of_2_blksize: variant supports block sizes that are not
+ * a power of two.
+ * @blksz_datactrl16: true if Block size is at b16..b30 position in datactrl register
*/
struct variant_data {
unsigned int clkreg;
@@ -62,12 +71,15 @@ struct variant_data {
bool sdio;
bool st_clkdiv;
bool blksz_datactrl16;
+ unsigned int pwrreg_powerup;
+ bool non_power_of_2_blksize;
};
static struct variant_data variant_arm = {
.fifosize = 16 * 4,
.fifohalfsize = 8 * 4,
.datalength_bits = 16,
+ .pwrreg_powerup = MCI_PWR_UP,
};
static struct variant_data variant_arm_extended_fifo = {
@@ -82,6 +94,7 @@ static struct variant_data variant_u300 = {
.clkreg_enable = MCI_ST_U300_HWFCEN,
.datalength_bits = 16,
.sdio = true,
+ .pwrreg_powerup = MCI_PWR_ON,
};
static struct variant_data variant_ux500 = {
@@ -92,8 +105,108 @@ static struct variant_data variant_ux500 = {
.datalength_bits = 24,
.sdio = true,
.st_clkdiv = true,
+ .pwrreg_powerup = MCI_PWR_ON,
+ .non_power_of_2_blksize = true,
+};
+/*
+ * Debugfs
+ */
+#ifdef CONFIG_DEBUG_FS
+#include <linux/debugfs.h>
+#include <linux/seq_file.h>
+
+static int mmci_regs_show(struct seq_file *seq, void *v)
+{
+ struct mmci_host *host = seq->private;
+ unsigned long iflags;
+ u32 pwr, clk, arg, cmd, rspcmd, r0, r1, r2, r3;
+ u32 dtimer, dlength, dctrl, dcnt;
+ u32 sta, clear, mask0, mask1, fifocnt, fifo;
+
+ mmc_host_enable(host->mmc);
+ spin_lock_irqsave(&host->lock, iflags);
+
+ pwr = readl(host->base + MMCIPOWER);
+ clk = readl(host->base + MMCICLOCK);
+ arg = readl(host->base + MMCIARGUMENT);
+ cmd = readl(host->base + MMCICOMMAND);
+ rspcmd = readl(host->base + MMCIRESPCMD);
+ r0 = readl(host->base + MMCIRESPONSE0);
+ r1 = readl(host->base + MMCIRESPONSE1);
+ r2 = readl(host->base + MMCIRESPONSE2);
+ r3 = readl(host->base + MMCIRESPONSE3);
+ dtimer = readl(host->base + MMCIDATATIMER);
+ dlength = readl(host->base + MMCIDATALENGTH);
+ dctrl = readl(host->base + MMCIDATACTRL);
+ dcnt = readl(host->base + MMCIDATACNT);
+ sta = readl(host->base + MMCISTATUS);
+ clear = readl(host->base + MMCICLEAR);
+ mask0 = readl(host->base + MMCIMASK0);
+ mask1 = readl(host->base + MMCIMASK1);
+ fifocnt = readl(host->base + MMCIFIFOCNT);
+ fifo = readl(host->base + MMCIFIFO);
+
+ spin_unlock_irqrestore(&host->lock, iflags);
+ mmc_host_disable(host->mmc);
+
+ seq_printf(seq, "\033[1;34mMMCI registers\033[0m\n");
+ seq_printf(seq, "%-20s:0x%x\n", "mmci_power", pwr);
+ seq_printf(seq, "%-20s:0x%x\n", "mmci_clock", clk);
+ seq_printf(seq, "%-20s:0x%x\n", "mmci_arg", arg);
+ seq_printf(seq, "%-20s:0x%x\n", "mmci_cmd", cmd);
+ seq_printf(seq, "%-20s:0x%x\n", "mmci_respcmd", rspcmd);
+ seq_printf(seq, "%-20s:0x%x\n", "mmci_resp0", r0);
+ seq_printf(seq, "%-20s:0x%x\n", "mmci_resp1", r1);
+ seq_printf(seq, "%-20s:0x%x\n", "mmci_resp2", r2);
+ seq_printf(seq, "%-20s:0x%x\n", "mmci_resp3", r3);
+ seq_printf(seq, "%-20s:0x%x\n", "mmci_datatimer", dtimer);
+ seq_printf(seq, "%-20s:0x%x\n", "mmci_datalen", dlength);
+ seq_printf(seq, "%-20s:0x%x\n", "mmci_datactrl", dctrl);
+ seq_printf(seq, "%-20s:0x%x\n", "mmci_datacnt", dcnt);
+ seq_printf(seq, "%-20s:0x%x\n", "mmci_status", sta);
+ seq_printf(seq, "%-20s:0x%x\n", "mmci_iclear", clear);
+ seq_printf(seq, "%-20s:0x%x\n", "mmci_imask0", mask0);
+ seq_printf(seq, "%-20s:0x%x\n", "mmci_imask1", mask1);
+ seq_printf(seq, "%-20s:0x%x\n", "mmci_fifocnt", fifocnt);
+ seq_printf(seq, "%-20s:0x%x\n", "mmci_fifo", fifo);
+
+ return 0;
+}
+
+static int mmci_regs_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, mmci_regs_show, inode->i_private);
+}
+
+static const struct file_operations mmci_fops_regs = {
+ .owner = THIS_MODULE,
+ .open = mmci_regs_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
};
+static void mmci_debugfs_create(struct mmci_host *host)
+{
+ host->debug_regs = debugfs_create_file("regs", S_IRUGO,
+ host->mmc->debugfs_root, host,
+ &mmci_fops_regs);
+
+ if (IS_ERR(host->debug_regs))
+ dev_err(mmc_dev(host->mmc),
+ "failed to create debug regs file\n");
+}
+
+static void mmci_debugfs_remove(struct mmci_host *host)
+{
+ debugfs_remove(host->debug_regs);
+}
+
+#else
+static inline void mmci_debugfs_create(struct mmci_host *host) { }
+static inline void mmci_debugfs_remove(struct mmci_host *host) { }
+#endif
+
static struct variant_data variant_ux500v2 = {
.fifosize = 30 * 4,
.fifohalfsize = 8 * 4,
@@ -102,6 +215,8 @@ static struct variant_data variant_ux500v2 = {
.datalength_bits = 24,
.sdio = true,
.st_clkdiv = true,
+ .pwrreg_powerup = MCI_PWR_ON,
+ .non_power_of_2_blksize = true,
.blksz_datactrl16 = true,
};
@@ -192,8 +307,31 @@ static void mmci_set_mask1(struct mmci_host *host, unsigned int mask)
static void mmci_stop_data(struct mmci_host *host)
{
- writel(0, host->base + MMCIDATACTRL);
+ u32 clk;
+ unsigned int datactrl = 0;
+
+ /*
+ * The ST Micro variants has a special bit
+ * to enable SDIO mode. This bit must remain set even when not
+ * doing data transfers, otherwise no SDIO interrupts can be
+ * received.
+ */
+ if (host->variant->sdio &&
+ host->mmc->card &&
+ mmc_card_sdio(host->mmc->card))
+ datactrl |= MCI_ST_DPSM_SDIOEN;
+
+ writel(datactrl, host->base + MMCIDATACTRL);
mmci_set_mask1(host, 0);
+
+ /* Needed for DDR */
+ if (host->mmc->card && mmc_card_ddr_mode(host->mmc->card)) {
+ clk = readl(host->base + MMCICLOCK);
+ clk &= ~MCI_ST_UX500_NEG_EDGE;
+
+ writel(clk, (host->base + MMCICLOCK));
+ }
+
host->data = NULL;
}
@@ -370,6 +508,10 @@ static int mmci_dma_start_data(struct mmci_host *host, unsigned int datactrl)
struct dma_async_tx_descriptor *desc;
int nr_sg;
+ /* If less than or equal to the fifo size, don't bother with DMA */
+ if (host->size <= variant->fifosize)
+ return -EINVAL;
+
host->dma_current = NULL;
if (data->flags & MMC_DATA_READ) {
@@ -458,6 +600,7 @@ static void mmci_start_data(struct mmci_host *host, struct mmc_data *data)
unsigned long long clks;
void __iomem *base;
int blksz_bits;
+ u32 clk;
dev_dbg(mmc_dev(host->mmc), "blksz %04x blks %04x flags %08x\n",
data->blksz, data->blocks, data->flags);
@@ -465,6 +608,8 @@ static void mmci_start_data(struct mmci_host *host, struct mmc_data *data)
host->data = data;
host->size = data->blksz * data->blocks;
data->bytes_xfered = 0;
+ host->cache_len = 0;
+ host->cache = 0;
clks = (unsigned long long)data->timeout_ns * host->cclk;
do_div(clks, 1000000000UL);
@@ -476,7 +621,6 @@ static void mmci_start_data(struct mmci_host *host, struct mmc_data *data)
writel(host->size, base + MMCIDATALENGTH);
blksz_bits = ffs(data->blksz) - 1;
- BUG_ON(1 << blksz_bits != data->blksz);
if (variant->blksz_datactrl16)
datactrl = MCI_DPSM_ENABLE | (data->blksz << 16);
@@ -486,6 +630,48 @@ static void mmci_start_data(struct mmci_host *host, struct mmc_data *data)
if (data->flags & MMC_DATA_READ)
datactrl |= MCI_DPSM_DIRECTION;
+ if (host->mmc->card && mmc_card_ddr_mode(host->mmc->card)) {
+ datactrl |= MCI_ST_DPSM_DDRMODE;
+
+ /* Needed for DDR */
+ clk = readl(base + MMCICLOCK);
+ clk |= MCI_ST_UX500_NEG_EDGE;
+
+ writel(clk, (base + MMCICLOCK));
+ }
+
+ if (variant->sdio &&
+ host->mmc->card &&
+ mmc_card_sdio(host->mmc->card)) {
+ /*
+ * The ST Micro variants has a special bit
+ * to enable SDIO mode. This bit is set the first time
+ * a SDIO data transfer is done and must remain set
+ * after the data transfer is completed. The reason is
+ * because of otherwise no SDIO interrupts can be
+ * received.
+ */
+ datactrl |= MCI_ST_DPSM_SDIOEN;
+
+ /*
+ * The ST Micro variant for SDIO transfer sizes
+ * less than or equal to 8 bytes needs to have clock
+ * H/W flow control disabled. Since flow control is
+ * not really needed for anything that fits in the
+ * FIFO, we can disable it for any write smaller
+ * than the FIFO size.
+ */
+ if ((host->size <= variant->fifosize) &&
+ (data->flags & MMC_DATA_WRITE))
+ writel(readl(host->base + MMCICLOCK) &
+ ~variant->clkreg_enable,
+ host->base + MMCICLOCK);
+ else
+ writel(readl(host->base + MMCICLOCK) |
+ variant->clkreg_enable,
+ host->base + MMCICLOCK);
+ }
+
/*
* Attempt to use DMA operation mode, if this
* should fail, fall back to PIO mode
@@ -514,11 +700,6 @@ static void mmci_start_data(struct mmci_host *host, struct mmc_data *data)
irqmask = MCI_TXFIFOHALFEMPTYMASK;
}
- /* The ST Micro variants has a special bit to enable SDIO */
- if (variant->sdio && host->mmc->card)
- if (mmc_card_sdio(host->mmc->card))
- datactrl |= MCI_ST_DPSM_SDIOEN;
-
writel(datactrl, base + MMCIDATACTRL);
writel(readl(base + MMCIMASK0) & ~MCI_DATAENDMASK, base + MMCIMASK0);
mmci_set_mask1(host, irqmask);
@@ -644,14 +825,14 @@ mmci_cmd_irq(struct mmci_host *host, struct mmc_command *cmd,
}
}
-static int mmci_pio_read(struct mmci_host *host, char *buffer, unsigned int remain)
+static int mmci_pio_read(struct mmci_host *host, char *buffer,
+ unsigned int remain, u32 status)
{
void __iomem *base = host->base;
char *ptr = buffer;
- u32 status;
int host_remain = host->size;
- do {
+ while (status & MCI_RXDATAAVLBL) {
int count = host_remain - (readl(base + MMCIFIFOCNT) << 2);
if (count > remain)
@@ -670,59 +851,107 @@ static int mmci_pio_read(struct mmci_host *host, char *buffer, unsigned int rema
break;
status = readl(base + MMCISTATUS);
- } while (status & MCI_RXDATAAVLBL);
+ }
return ptr - buffer;
}
-static int mmci_pio_write(struct mmci_host *host, char *buffer, unsigned int remain, u32 status)
+static int mmci_pio_write(struct mmci_host *host, char *buffer,
+ unsigned int remain, u32 status)
{
struct variant_data *variant = host->variant;
void __iomem *base = host->base;
+
+ unsigned int data_left = host->size;
+ unsigned int maxcnt;
char *ptr = buffer;
- do {
- unsigned int count, maxcnt;
+ while (status & MCI_TXFIFOHALFEMPTY) {
+ unsigned int count;
+ char *cache_ptr;
+ int i;
- maxcnt = status & MCI_TXFIFOEMPTY ?
- variant->fifosize : variant->fifohalfsize;
- count = min(remain, maxcnt);
+ count = min(remain, variant->fifohalfsize);
/*
- * The ST Micro variant for SDIO transfer sizes
- * less then 8 bytes should have clock H/W flow
- * control disabled.
+ * A write to the FIFO must always be done of 4 bytes aligned
+ * data. If the buffer is not 4 bytes aligned we must pad the
+ * data, but this must only be done for the final write for the
+ * entire data transfer, otherwise we will corrupt the data.
+ * Thus a buffer cache of four bytes is needed to temporary
+ * store data.
*/
- if (variant->sdio &&
- mmc_card_sdio(host->mmc->card)) {
- if (count < 8)
- writel(readl(host->base + MMCICLOCK) &
- ~variant->clkreg_enable,
- host->base + MMCICLOCK);
- else
- writel(readl(host->base + MMCICLOCK) |
- variant->clkreg_enable,
- host->base + MMCICLOCK);
+
+ if (host->cache_len) {
+ cache_ptr = (char *)&host->cache;
+ cache_ptr = cache_ptr + host->cache_len;
+ data_left += host->cache_len;
+
+ while ((host->cache_len < 4) && (remain > 0)) {
+ *cache_ptr = *ptr;
+ cache_ptr++;
+ ptr++;
+ host->cache_len++;
+ remain--;
+ }
+
+ if ((host->cache_len == 4) ||
+ (data_left == host->cache_len)) {
+
+ writesl(base + MMCIFIFO, &host->cache, 1);
+ if (data_left == host->cache_len)
+ break;
+
+ host->cache = 0;
+ host->cache_len = 0;
+ maxcnt -= 4;
+ data_left -= 4;
+ }
+
+ if (remain == 0)
+ break;
}
- /*
- * SDIO especially may want to send something that is
- * not divisible by 4 (as opposed to card sectors
- * etc), and the FIFO only accept full 32-bit writes.
- * So compensate by adding +3 on the count, a single
- * byte become a 32bit write, 7 bytes will be two
- * 32bit writes etc.
- */
- writesl(base + MMCIFIFO, ptr, (count + 3) >> 2);
- ptr += count;
- remain -= count;
+ if (!(count % 4) || (data_left == count)) {
+ /*
+ * The data is either 4-bytes aligned or it is the
+ * last data to write. It is thus fine to potentially
+ * pad the data if needed.
+ */
+ writesl(base + MMCIFIFO, ptr, (count + 3) >> 2);
+ ptr += count;
+ remain -= count;
+ data_left -= count;
+
+ } else {
+
+ host->cache_len = count % 4;
+ count = (count >> 2) << 2;
+
+ if (count)
+ writesl(base + MMCIFIFO, ptr, count >> 2);
+
+ ptr += count;
+ remain -= count;
+ data_left -= count;
+
+ i = 0;
+ cache_ptr = (char *)&host->cache;
+ while (i < host->cache_len) {
+ *cache_ptr = *ptr;
+ cache_ptr++;
+ ptr++;
+ remain--;
+ i++;
+ }
+ }
if (remain == 0)
break;
status = readl(base + MMCISTATUS);
- } while (status & MCI_TXFIFOHALFEMPTY);
+ };
return ptr - buffer;
}
@@ -767,11 +996,14 @@ static irqreturn_t mmci_pio_irq(int irq, void *dev_id)
len = 0;
if (status & MCI_RXACTIVE)
- len = mmci_pio_read(host, buffer, remain);
+ len = mmci_pio_read(host, buffer, remain, status);
if (status & MCI_TXACTIVE)
len = mmci_pio_write(host, buffer, remain, status);
- sg_miter->consumed = len;
+ if (len > sg_miter->consumed)
+ len = sg_miter->consumed;
+ else
+ sg_miter->consumed = len;
host->size -= len;
remain -= len;
@@ -814,6 +1046,7 @@ static irqreturn_t mmci_irq(int irq, void *dev_id)
{
struct mmci_host *host = dev_id;
u32 status;
+ int sdio_irq = 0;
int ret = 0;
spin_lock(&host->lock);
@@ -836,6 +1069,9 @@ static irqreturn_t mmci_irq(int irq, void *dev_id)
dev_dbg(mmc_dev(host->mmc), "irq0 (data+cmd) %08x\n", status);
+ if (status & MCI_ST_SDIOIT)
+ sdio_irq = 1;
+
data = host->data;
if (status & (MCI_DATACRCFAIL|MCI_DATATIMEOUT|MCI_TXUNDERRUN|
MCI_RXOVERRUN|MCI_DATAEND|MCI_DATABLOCKEND) && data)
@@ -850,17 +1086,27 @@ static irqreturn_t mmci_irq(int irq, void *dev_id)
spin_unlock(&host->lock);
+ if (sdio_irq)
+ mmc_signal_sdio_irq(host->mmc);
+
return IRQ_RETVAL(ret);
}
static void mmci_request(struct mmc_host *mmc, struct mmc_request *mrq)
{
struct mmci_host *host = mmc_priv(mmc);
+ struct variant_data *variant = host->variant;
unsigned long flags;
WARN_ON(host->mrq != NULL);
- if (mrq->data && !is_power_of_2(mrq->data->blksz)) {
+ if (mrq->data &&
+ (!variant->non_power_of_2_blksize ||
+#ifdef CONFIG_ARCH_U8500
+ !cpu_is_u8500v20_or_later() ||
+#endif
+ (mmc->card && mmc_card_ddr_mode(mmc->card))) &&
+ !is_power_of_2(mrq->data->blksz)) {
dev_err(mmc_dev(mmc), "unsupported block size (%d bytes)\n",
mrq->data->blksz);
mrq->cmd->error = -EINVAL;
@@ -883,6 +1129,7 @@ static void mmci_request(struct mmc_host *mmc, struct mmc_request *mrq)
static void mmci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
{
struct mmci_host *host = mmc_priv(mmc);
+ struct variant_data *variant = host->variant;
u32 pwr = 0;
unsigned long flags;
int ret;
@@ -909,11 +1156,15 @@ static void mmci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
if (host->plat->vdd_handler)
pwr |= host->plat->vdd_handler(mmc_dev(mmc), ios->vdd,
ios->power_mode);
- /* The ST version does not have this, fall through to POWER_ON */
- if (host->hw_designer != AMBA_VENDOR_ST) {
- pwr |= MCI_PWR_UP;
- break;
- }
+
+ /*
+ * The ST Micro variant doesn't have the PL180s MCI_PWR_UP
+ * and instead uses MCI_PWR_ON so apply whatever value is
+ * configured in the variant data.
+ */
+ pwr |= variant->pwrreg_powerup;
+
+ break;
case MMC_POWER_ON:
pwr |= MCI_PWR_ON;
break;
@@ -984,11 +1235,30 @@ static irqreturn_t mmci_cd_irq(int irq, void *dev_id)
return IRQ_HANDLED;
}
+static void mmci_enable_sdio_irq(struct mmc_host *mmc, int enable)
+{
+ unsigned long flags;
+ unsigned int mask0;
+ struct mmci_host *host = mmc_priv(mmc);
+
+ spin_lock_irqsave(&host->lock, flags);
+
+ mask0 = readl(host->base + MMCIMASK0);
+ if (enable)
+ mask0 |= MCI_ST_SDIOIT;
+ else
+ mask0 &= ~MCI_ST_SDIOIT;
+ writel(mask0, host->base + MMCIMASK0);
+
+ spin_unlock_irqrestore(&host->lock, flags);
+}
+
static const struct mmc_host_ops mmci_ops = {
.request = mmci_request,
.set_ios = mmci_set_ios,
.get_ro = mmci_get_ro,
.get_cd = mmci_get_cd,
+ .enable_sdio_irq = mmci_enable_sdio_irq,
};
static int __devinit mmci_probe(struct amba_device *dev,
@@ -1063,15 +1333,10 @@ static int __devinit mmci_probe(struct amba_device *dev,
}
mmc->ops = &mmci_ops;
- /*
- * The ARM and ST versions of the block have slightly different
- * clock divider equations which means that the minimum divider
- * differs too.
- */
if (variant->st_clkdiv)
- mmc->f_min = DIV_ROUND_UP(host->mclk, 257);
+ mmc->f_min = host->mclk / 257;
else
- mmc->f_min = DIV_ROUND_UP(host->mclk, 512);
+ mmc->f_min = (host->mclk + 511) / 512;
/*
* If the platform data supplies a maximum operating
* frequency, this takes precedence. Else, we fall back
@@ -1208,6 +1473,8 @@ static int __devinit mmci_probe(struct amba_device *dev,
mmc_add_host(mmc);
+ mmci_debugfs_create(host);
+
return 0;
irq0_free:
@@ -1243,6 +1510,7 @@ static int __devexit mmci_remove(struct amba_device *dev)
if (mmc) {
struct mmci_host *host = mmc_priv(mmc);
+ mmci_debugfs_remove(host);
mmc_remove_host(mmc);
writel(0, host->base + MMCIMASK0);