2009-09-22 23:44:22 +00:00
|
|
|
/*
|
|
|
|
* linux/drivers/mmc/host/msm_sdcc.c - Qualcomm MSM 7X00A SDCC Driver
|
|
|
|
*
|
|
|
|
* Copyright (C) 2007 Google Inc,
|
|
|
|
* Copyright (C) 2003 Deep Blue Solutions, Ltd, All Rights Reserved.
|
mmc: msm_sdcc: Reduce command timeouts and improve reliability.
Based on an original patch by Brent DeGraaf:
"Previous versions of the SD driver were beset with excessive command
timeouts. These timeouts were silent by default, but happened
frequently, especially during heavy system activity and concurrent
access of two or more SD devices. Worst case, these timeouts would
occasionally hit at the end of a successful write, resulting in false
failures that could adversely affect journaling file systems if timing
was unfortunate. This update tightens the association and timing between
dma transfers and the commands that trigger them by utilizing a new api
implemented in the datamover. In addition, it also fixes a dma cache
coherency issue that was exposed during testing of this fix that
occasionally resulted in card corruption. Processing of results in the
interrupt status routine was modified to process command results prior to
data because overwritten command results were observed during testing
since the data section can result in command issuances of its own.
This change also eliminates the software command timeout, relying entirely
on the hardware version, since the software timeout was found to cause
problems of its own after extensive testing (having hardware timer and
software timers addressing the same issue was found to cause a race
condition under heavy system load)."
This change originally added PROG_DONE handling, which has been split out
into a separate patch. Also on our platform, the data mover driver maintains
coherency to ensure API reliability, so the above mentioned cache corruption
issue was not an issue for us.
Signed-off-by: San Mehat <san@google.com>
Cc: Brian Swetland <swetland@google.com>
Change-Id: Ifbf17cfafb858106d73bf49af52b5161a265a484
Signed-off-by: San Mehat <san@google.com>
Signed-off-by: Daniel Walker <dwalker@codeaurora.org>
2009-11-21 20:29:46 +00:00
|
|
|
* Copyright (C) 2009, Code Aurora Forum. All Rights Reserved.
|
2009-09-22 23:44:22 +00:00
|
|
|
*
|
|
|
|
* 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.
|
|
|
|
*
|
|
|
|
* Based on mmci.c
|
|
|
|
*
|
|
|
|
* Author: San Mehat (san@android.com)
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <linux/module.h>
|
|
|
|
#include <linux/moduleparam.h>
|
|
|
|
#include <linux/init.h>
|
|
|
|
#include <linux/ioport.h>
|
|
|
|
#include <linux/device.h>
|
|
|
|
#include <linux/interrupt.h>
|
|
|
|
#include <linux/delay.h>
|
|
|
|
#include <linux/err.h>
|
|
|
|
#include <linux/highmem.h>
|
|
|
|
#include <linux/log2.h>
|
|
|
|
#include <linux/mmc/host.h>
|
|
|
|
#include <linux/mmc/card.h>
|
2009-11-03 02:46:09 +00:00
|
|
|
#include <linux/mmc/sdio.h>
|
2009-09-22 23:44:22 +00:00
|
|
|
#include <linux/clk.h>
|
|
|
|
#include <linux/scatterlist.h>
|
|
|
|
#include <linux/platform_device.h>
|
|
|
|
#include <linux/dma-mapping.h>
|
|
|
|
#include <linux/debugfs.h>
|
|
|
|
#include <linux/io.h>
|
|
|
|
#include <linux/memory.h>
|
|
|
|
|
|
|
|
#include <asm/cacheflush.h>
|
|
|
|
#include <asm/div64.h>
|
|
|
|
#include <asm/sizes.h>
|
|
|
|
|
2009-12-08 19:11:36 +00:00
|
|
|
#include <mach/mmc.h>
|
2009-09-22 23:44:22 +00:00
|
|
|
#include <mach/msm_iomap.h>
|
|
|
|
#include <mach/dma.h>
|
|
|
|
|
|
|
|
#include "msm_sdcc.h"
|
|
|
|
|
|
|
|
#define DRIVER_NAME "msm-sdcc"
|
|
|
|
|
2009-12-01 18:10:47 +00:00
|
|
|
#define BUSCLK_PWRSAVE 1
|
2009-11-23 01:19:07 +00:00
|
|
|
#define BUSCLK_TIMEOUT (HZ)
|
2009-09-22 23:44:22 +00:00
|
|
|
static unsigned int msmsdcc_fmin = 144000;
|
|
|
|
static unsigned int msmsdcc_fmax = 50000000;
|
|
|
|
static unsigned int msmsdcc_4bit = 1;
|
|
|
|
static unsigned int msmsdcc_pwrsave = 1;
|
|
|
|
static unsigned int msmsdcc_piopoll = 1;
|
|
|
|
static unsigned int msmsdcc_sdioirq;
|
|
|
|
|
|
|
|
#define PIO_SPINMAX 30
|
|
|
|
#define CMD_SPINMAX 20
|
|
|
|
|
2009-11-13 21:42:06 +00:00
|
|
|
|
2009-11-23 01:19:07 +00:00
|
|
|
static inline void
|
|
|
|
msmsdcc_disable_clocks(struct msmsdcc_host *host, int deferr)
|
2009-11-13 21:42:06 +00:00
|
|
|
{
|
2009-11-23 01:19:07 +00:00
|
|
|
WARN_ON(!host->clks_on);
|
2009-11-16 18:17:30 +00:00
|
|
|
|
2009-11-23 23:36:31 +00:00
|
|
|
BUG_ON(host->curr.mrq);
|
|
|
|
|
2009-11-23 01:19:07 +00:00
|
|
|
if (deferr) {
|
|
|
|
mod_timer(&host->busclk_timer, jiffies + BUSCLK_TIMEOUT);
|
2009-11-13 21:42:06 +00:00
|
|
|
} else {
|
2009-11-23 01:19:07 +00:00
|
|
|
del_timer_sync(&host->busclk_timer);
|
2009-11-13 21:42:06 +00:00
|
|
|
clk_disable(host->clk);
|
|
|
|
clk_disable(host->pclk);
|
|
|
|
host->clks_on = 0;
|
|
|
|
}
|
2009-11-23 01:19:07 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static inline int
|
|
|
|
msmsdcc_enable_clocks(struct msmsdcc_host *host)
|
|
|
|
{
|
|
|
|
int rc;
|
|
|
|
|
|
|
|
WARN_ON(host->clks_on);
|
|
|
|
|
|
|
|
del_timer_sync(&host->busclk_timer);
|
|
|
|
|
|
|
|
rc = clk_enable(host->pclk);
|
|
|
|
if (rc)
|
|
|
|
return rc;
|
|
|
|
rc = clk_enable(host->clk);
|
|
|
|
if (rc) {
|
|
|
|
clk_disable(host->pclk);
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
udelay(1 + ((3 * USEC_PER_SEC) /
|
|
|
|
(host->clk_rate ? host->clk_rate : msmsdcc_fmin)));
|
|
|
|
host->clks_on = 1;
|
2009-11-13 21:42:06 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2009-11-16 18:17:30 +00:00
|
|
|
static inline unsigned int
|
|
|
|
msmsdcc_readl(struct msmsdcc_host *host, unsigned int reg)
|
|
|
|
{
|
|
|
|
return readl(host->base + reg);
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline void
|
|
|
|
msmsdcc_writel(struct msmsdcc_host *host, u32 data, unsigned int reg)
|
|
|
|
{
|
|
|
|
writel(data, host->base + reg);
|
|
|
|
/* 3 clk delay required! */
|
|
|
|
udelay(1 + ((3 * USEC_PER_SEC) /
|
|
|
|
(host->clk_rate ? host->clk_rate : msmsdcc_fmin)));
|
|
|
|
}
|
2009-11-13 21:42:06 +00:00
|
|
|
|
2009-09-22 23:44:22 +00:00
|
|
|
static void
|
|
|
|
msmsdcc_start_command(struct msmsdcc_host *host, struct mmc_command *cmd,
|
|
|
|
u32 c);
|
|
|
|
|
|
|
|
static void
|
|
|
|
msmsdcc_request_end(struct msmsdcc_host *host, struct mmc_request *mrq)
|
|
|
|
{
|
|
|
|
BUG_ON(host->curr.data);
|
|
|
|
|
|
|
|
host->curr.mrq = NULL;
|
|
|
|
host->curr.cmd = NULL;
|
|
|
|
|
|
|
|
if (mrq->data)
|
|
|
|
mrq->data->bytes_xfered = host->curr.data_xfered;
|
|
|
|
if (mrq->cmd->error == -ETIMEDOUT)
|
|
|
|
mdelay(5);
|
|
|
|
|
2009-11-23 23:36:31 +00:00
|
|
|
#if BUSCLK_PWRSAVE
|
2009-11-23 01:19:07 +00:00
|
|
|
msmsdcc_disable_clocks(host, 1);
|
2009-11-23 23:36:31 +00:00
|
|
|
#endif
|
2009-09-22 23:44:22 +00:00
|
|
|
/*
|
|
|
|
* Need to drop the host lock here; mmc_request_done may call
|
|
|
|
* back into the driver...
|
|
|
|
*/
|
|
|
|
spin_unlock(&host->lock);
|
|
|
|
mmc_request_done(host->mmc, mrq);
|
|
|
|
spin_lock(&host->lock);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
msmsdcc_stop_data(struct msmsdcc_host *host)
|
|
|
|
{
|
|
|
|
host->curr.data = NULL;
|
|
|
|
host->curr.got_dataend = host->curr.got_datablkend = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
uint32_t msmsdcc_fifo_addr(struct msmsdcc_host *host)
|
|
|
|
{
|
2009-09-22 23:44:24 +00:00
|
|
|
switch (host->pdev_id) {
|
|
|
|
case 1:
|
2009-09-22 23:44:22 +00:00
|
|
|
return MSM_SDC1_PHYS + MMCIFIFO;
|
2009-09-22 23:44:24 +00:00
|
|
|
case 2:
|
2009-09-22 23:44:22 +00:00
|
|
|
return MSM_SDC2_PHYS + MMCIFIFO;
|
2009-09-22 23:44:24 +00:00
|
|
|
case 3:
|
2009-09-22 23:44:22 +00:00
|
|
|
return MSM_SDC3_PHYS + MMCIFIFO;
|
2009-09-22 23:44:24 +00:00
|
|
|
case 4:
|
2009-09-22 23:44:22 +00:00
|
|
|
return MSM_SDC4_PHYS + MMCIFIFO;
|
2009-09-22 23:44:24 +00:00
|
|
|
}
|
|
|
|
BUG();
|
2009-09-22 23:44:22 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
mmc: msm_sdcc: Reduce command timeouts and improve reliability.
Based on an original patch by Brent DeGraaf:
"Previous versions of the SD driver were beset with excessive command
timeouts. These timeouts were silent by default, but happened
frequently, especially during heavy system activity and concurrent
access of two or more SD devices. Worst case, these timeouts would
occasionally hit at the end of a successful write, resulting in false
failures that could adversely affect journaling file systems if timing
was unfortunate. This update tightens the association and timing between
dma transfers and the commands that trigger them by utilizing a new api
implemented in the datamover. In addition, it also fixes a dma cache
coherency issue that was exposed during testing of this fix that
occasionally resulted in card corruption. Processing of results in the
interrupt status routine was modified to process command results prior to
data because overwritten command results were observed during testing
since the data section can result in command issuances of its own.
This change also eliminates the software command timeout, relying entirely
on the hardware version, since the software timeout was found to cause
problems of its own after extensive testing (having hardware timer and
software timers addressing the same issue was found to cause a race
condition under heavy system load)."
This change originally added PROG_DONE handling, which has been split out
into a separate patch. Also on our platform, the data mover driver maintains
coherency to ensure API reliability, so the above mentioned cache corruption
issue was not an issue for us.
Signed-off-by: San Mehat <san@google.com>
Cc: Brian Swetland <swetland@google.com>
Change-Id: Ifbf17cfafb858106d73bf49af52b5161a265a484
Signed-off-by: San Mehat <san@google.com>
Signed-off-by: Daniel Walker <dwalker@codeaurora.org>
2009-11-21 20:29:46 +00:00
|
|
|
static inline void
|
|
|
|
msmsdcc_start_command_exec(struct msmsdcc_host *host, u32 arg, u32 c) {
|
|
|
|
msmsdcc_writel(host, arg, MMCIARGUMENT);
|
|
|
|
msmsdcc_writel(host, c, MMCICOMMAND);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
msmsdcc_dma_exec_func(struct msm_dmov_cmd *cmd)
|
|
|
|
{
|
|
|
|
struct msmsdcc_host *host = (struct msmsdcc_host *)cmd->data;
|
|
|
|
|
|
|
|
writel(host->cmd_timeout, host->base + MMCIDATATIMER);
|
|
|
|
writel((unsigned int)host->curr.xfer_size, host->base + MMCIDATALENGTH);
|
|
|
|
writel(host->cmd_pio_irqmask, host->base + MMCIMASK1);
|
|
|
|
writel(host->cmd_datactrl, host->base + MMCIDATACTRL);
|
|
|
|
|
|
|
|
if (host->cmd_cmd) {
|
|
|
|
msmsdcc_start_command_exec(host,
|
|
|
|
(u32)host->cmd_cmd->arg, (u32)host->cmd_c);
|
|
|
|
}
|
|
|
|
host->dma.active = 1;
|
|
|
|
}
|
|
|
|
|
2009-09-22 23:44:22 +00:00
|
|
|
static void
|
|
|
|
msmsdcc_dma_complete_func(struct msm_dmov_cmd *cmd,
|
|
|
|
unsigned int result,
|
|
|
|
struct msm_dmov_errdata *err)
|
|
|
|
{
|
|
|
|
struct msmsdcc_dma_data *dma_data =
|
|
|
|
container_of(cmd, struct msmsdcc_dma_data, hdr);
|
|
|
|
struct msmsdcc_host *host = dma_data->host;
|
|
|
|
unsigned long flags;
|
|
|
|
struct mmc_request *mrq;
|
|
|
|
|
|
|
|
spin_lock_irqsave(&host->lock, flags);
|
mmc: msm_sdcc: Reduce command timeouts and improve reliability.
Based on an original patch by Brent DeGraaf:
"Previous versions of the SD driver were beset with excessive command
timeouts. These timeouts were silent by default, but happened
frequently, especially during heavy system activity and concurrent
access of two or more SD devices. Worst case, these timeouts would
occasionally hit at the end of a successful write, resulting in false
failures that could adversely affect journaling file systems if timing
was unfortunate. This update tightens the association and timing between
dma transfers and the commands that trigger them by utilizing a new api
implemented in the datamover. In addition, it also fixes a dma cache
coherency issue that was exposed during testing of this fix that
occasionally resulted in card corruption. Processing of results in the
interrupt status routine was modified to process command results prior to
data because overwritten command results were observed during testing
since the data section can result in command issuances of its own.
This change also eliminates the software command timeout, relying entirely
on the hardware version, since the software timeout was found to cause
problems of its own after extensive testing (having hardware timer and
software timers addressing the same issue was found to cause a race
condition under heavy system load)."
This change originally added PROG_DONE handling, which has been split out
into a separate patch. Also on our platform, the data mover driver maintains
coherency to ensure API reliability, so the above mentioned cache corruption
issue was not an issue for us.
Signed-off-by: San Mehat <san@google.com>
Cc: Brian Swetland <swetland@google.com>
Change-Id: Ifbf17cfafb858106d73bf49af52b5161a265a484
Signed-off-by: San Mehat <san@google.com>
Signed-off-by: Daniel Walker <dwalker@codeaurora.org>
2009-11-21 20:29:46 +00:00
|
|
|
host->dma.active = 0;
|
|
|
|
|
2009-09-22 23:44:22 +00:00
|
|
|
mrq = host->curr.mrq;
|
|
|
|
BUG_ON(!mrq);
|
2009-11-24 20:24:55 +00:00
|
|
|
WARN_ON(!mrq->data);
|
2009-09-22 23:44:22 +00:00
|
|
|
|
|
|
|
if (!(result & DMOV_RSLT_VALID)) {
|
2009-09-22 23:44:23 +00:00
|
|
|
pr_err("msmsdcc: Invalid DataMover result\n");
|
2009-09-22 23:44:22 +00:00
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (result & DMOV_RSLT_DONE) {
|
|
|
|
host->curr.data_xfered = host->curr.xfer_size;
|
|
|
|
} else {
|
|
|
|
/* Error or flush */
|
|
|
|
if (result & DMOV_RSLT_ERROR)
|
2009-09-22 23:44:23 +00:00
|
|
|
pr_err("%s: DMA error (0x%.8x)\n",
|
2009-09-22 23:44:22 +00:00
|
|
|
mmc_hostname(host->mmc), result);
|
|
|
|
if (result & DMOV_RSLT_FLUSH)
|
2009-09-22 23:44:23 +00:00
|
|
|
pr_err("%s: DMA channel flushed (0x%.8x)\n",
|
2009-09-22 23:44:22 +00:00
|
|
|
mmc_hostname(host->mmc), result);
|
|
|
|
if (err)
|
2009-09-22 23:44:23 +00:00
|
|
|
pr_err("Flush data: %.8x %.8x %.8x %.8x %.8x %.8x\n",
|
2009-09-22 23:44:22 +00:00
|
|
|
err->flush[0], err->flush[1], err->flush[2],
|
|
|
|
err->flush[3], err->flush[4], err->flush[5]);
|
|
|
|
if (!mrq->data->error)
|
|
|
|
mrq->data->error = -EIO;
|
|
|
|
}
|
|
|
|
dma_unmap_sg(mmc_dev(host->mmc), host->dma.sg, host->dma.num_ents,
|
|
|
|
host->dma.dir);
|
|
|
|
|
|
|
|
if (host->curr.user_pages) {
|
|
|
|
struct scatterlist *sg = host->dma.sg;
|
|
|
|
int i;
|
|
|
|
|
2009-09-22 23:44:24 +00:00
|
|
|
for (i = 0; i < host->dma.num_ents; i++)
|
|
|
|
flush_dcache_page(sg_page(sg++));
|
2009-09-22 23:44:22 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
host->dma.sg = NULL;
|
mmc: msm_sdcc: Reduce command timeouts and improve reliability.
Based on an original patch by Brent DeGraaf:
"Previous versions of the SD driver were beset with excessive command
timeouts. These timeouts were silent by default, but happened
frequently, especially during heavy system activity and concurrent
access of two or more SD devices. Worst case, these timeouts would
occasionally hit at the end of a successful write, resulting in false
failures that could adversely affect journaling file systems if timing
was unfortunate. This update tightens the association and timing between
dma transfers and the commands that trigger them by utilizing a new api
implemented in the datamover. In addition, it also fixes a dma cache
coherency issue that was exposed during testing of this fix that
occasionally resulted in card corruption. Processing of results in the
interrupt status routine was modified to process command results prior to
data because overwritten command results were observed during testing
since the data section can result in command issuances of its own.
This change also eliminates the software command timeout, relying entirely
on the hardware version, since the software timeout was found to cause
problems of its own after extensive testing (having hardware timer and
software timers addressing the same issue was found to cause a race
condition under heavy system load)."
This change originally added PROG_DONE handling, which has been split out
into a separate patch. Also on our platform, the data mover driver maintains
coherency to ensure API reliability, so the above mentioned cache corruption
issue was not an issue for us.
Signed-off-by: San Mehat <san@google.com>
Cc: Brian Swetland <swetland@google.com>
Change-Id: Ifbf17cfafb858106d73bf49af52b5161a265a484
Signed-off-by: San Mehat <san@google.com>
Signed-off-by: Daniel Walker <dwalker@codeaurora.org>
2009-11-21 20:29:46 +00:00
|
|
|
host->dma.busy = 0;
|
2009-09-22 23:44:22 +00:00
|
|
|
|
|
|
|
if ((host->curr.got_dataend && host->curr.got_datablkend)
|
|
|
|
|| mrq->data->error) {
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If we've already gotten our DATAEND / DATABLKEND
|
|
|
|
* for this request, then complete it through here.
|
|
|
|
*/
|
|
|
|
msmsdcc_stop_data(host);
|
|
|
|
|
|
|
|
if (!mrq->data->error)
|
|
|
|
host->curr.data_xfered = host->curr.xfer_size;
|
|
|
|
if (!mrq->data->stop || mrq->cmd->error) {
|
|
|
|
host->curr.mrq = NULL;
|
|
|
|
host->curr.cmd = NULL;
|
|
|
|
mrq->data->bytes_xfered = host->curr.data_xfered;
|
|
|
|
|
|
|
|
spin_unlock_irqrestore(&host->lock, flags);
|
2009-11-23 23:36:31 +00:00
|
|
|
#if BUSCLK_PWRSAVE
|
2009-11-23 01:19:07 +00:00
|
|
|
msmsdcc_disable_clocks(host, 1);
|
2009-11-23 23:36:31 +00:00
|
|
|
#endif
|
2009-09-22 23:44:22 +00:00
|
|
|
mmc_request_done(host->mmc, mrq);
|
|
|
|
return;
|
|
|
|
} else
|
|
|
|
msmsdcc_start_command(host, mrq->data->stop, 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
out:
|
|
|
|
spin_unlock_irqrestore(&host->lock, flags);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int validate_dma(struct msmsdcc_host *host, struct mmc_data *data)
|
|
|
|
{
|
|
|
|
if (host->dma.channel == -1)
|
|
|
|
return -ENOENT;
|
|
|
|
|
|
|
|
if ((data->blksz * data->blocks) < MCI_FIFOSIZE)
|
|
|
|
return -EINVAL;
|
|
|
|
if ((data->blksz * data->blocks) % MCI_FIFOSIZE)
|
|
|
|
return -EINVAL;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int msmsdcc_config_dma(struct msmsdcc_host *host, struct mmc_data *data)
|
|
|
|
{
|
|
|
|
struct msmsdcc_nc_dmadata *nc;
|
|
|
|
dmov_box *box;
|
|
|
|
uint32_t rows;
|
|
|
|
uint32_t crci;
|
|
|
|
unsigned int n;
|
|
|
|
int i, rc;
|
|
|
|
struct scatterlist *sg = data->sg;
|
|
|
|
|
|
|
|
rc = validate_dma(host, data);
|
|
|
|
if (rc)
|
|
|
|
return rc;
|
|
|
|
|
|
|
|
host->dma.sg = data->sg;
|
|
|
|
host->dma.num_ents = data->sg_len;
|
|
|
|
|
mmc: msm_sdcc: Reduce command timeouts and improve reliability.
Based on an original patch by Brent DeGraaf:
"Previous versions of the SD driver were beset with excessive command
timeouts. These timeouts were silent by default, but happened
frequently, especially during heavy system activity and concurrent
access of two or more SD devices. Worst case, these timeouts would
occasionally hit at the end of a successful write, resulting in false
failures that could adversely affect journaling file systems if timing
was unfortunate. This update tightens the association and timing between
dma transfers and the commands that trigger them by utilizing a new api
implemented in the datamover. In addition, it also fixes a dma cache
coherency issue that was exposed during testing of this fix that
occasionally resulted in card corruption. Processing of results in the
interrupt status routine was modified to process command results prior to
data because overwritten command results were observed during testing
since the data section can result in command issuances of its own.
This change also eliminates the software command timeout, relying entirely
on the hardware version, since the software timeout was found to cause
problems of its own after extensive testing (having hardware timer and
software timers addressing the same issue was found to cause a race
condition under heavy system load)."
This change originally added PROG_DONE handling, which has been split out
into a separate patch. Also on our platform, the data mover driver maintains
coherency to ensure API reliability, so the above mentioned cache corruption
issue was not an issue for us.
Signed-off-by: San Mehat <san@google.com>
Cc: Brian Swetland <swetland@google.com>
Change-Id: Ifbf17cfafb858106d73bf49af52b5161a265a484
Signed-off-by: San Mehat <san@google.com>
Signed-off-by: Daniel Walker <dwalker@codeaurora.org>
2009-11-21 20:29:46 +00:00
|
|
|
BUG_ON(host->dma.num_ents > NR_SG); /* Prevent memory corruption */
|
|
|
|
|
2009-09-22 23:44:22 +00:00
|
|
|
nc = host->dma.nc;
|
|
|
|
|
2009-09-22 23:44:24 +00:00
|
|
|
switch (host->pdev_id) {
|
|
|
|
case 1:
|
2009-09-22 23:44:22 +00:00
|
|
|
crci = MSMSDCC_CRCI_SDC1;
|
2009-09-22 23:44:24 +00:00
|
|
|
break;
|
|
|
|
case 2:
|
2009-09-22 23:44:22 +00:00
|
|
|
crci = MSMSDCC_CRCI_SDC2;
|
2009-09-22 23:44:24 +00:00
|
|
|
break;
|
|
|
|
case 3:
|
2009-09-22 23:44:22 +00:00
|
|
|
crci = MSMSDCC_CRCI_SDC3;
|
2009-09-22 23:44:24 +00:00
|
|
|
break;
|
|
|
|
case 4:
|
2009-09-22 23:44:22 +00:00
|
|
|
crci = MSMSDCC_CRCI_SDC4;
|
2009-09-22 23:44:24 +00:00
|
|
|
break;
|
|
|
|
default:
|
2009-09-22 23:44:22 +00:00
|
|
|
host->dma.sg = NULL;
|
|
|
|
host->dma.num_ents = 0;
|
|
|
|
return -ENOENT;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (data->flags & MMC_DATA_READ)
|
|
|
|
host->dma.dir = DMA_FROM_DEVICE;
|
|
|
|
else
|
|
|
|
host->dma.dir = DMA_TO_DEVICE;
|
|
|
|
|
|
|
|
host->curr.user_pages = 0;
|
|
|
|
|
|
|
|
box = &nc->cmd[0];
|
|
|
|
for (i = 0; i < host->dma.num_ents; i++) {
|
|
|
|
box->cmd = CMD_MODE_BOX;
|
|
|
|
|
mmc: msm_sdcc: Reduce command timeouts and improve reliability.
Based on an original patch by Brent DeGraaf:
"Previous versions of the SD driver were beset with excessive command
timeouts. These timeouts were silent by default, but happened
frequently, especially during heavy system activity and concurrent
access of two or more SD devices. Worst case, these timeouts would
occasionally hit at the end of a successful write, resulting in false
failures that could adversely affect journaling file systems if timing
was unfortunate. This update tightens the association and timing between
dma transfers and the commands that trigger them by utilizing a new api
implemented in the datamover. In addition, it also fixes a dma cache
coherency issue that was exposed during testing of this fix that
occasionally resulted in card corruption. Processing of results in the
interrupt status routine was modified to process command results prior to
data because overwritten command results were observed during testing
since the data section can result in command issuances of its own.
This change also eliminates the software command timeout, relying entirely
on the hardware version, since the software timeout was found to cause
problems of its own after extensive testing (having hardware timer and
software timers addressing the same issue was found to cause a race
condition under heavy system load)."
This change originally added PROG_DONE handling, which has been split out
into a separate patch. Also on our platform, the data mover driver maintains
coherency to ensure API reliability, so the above mentioned cache corruption
issue was not an issue for us.
Signed-off-by: San Mehat <san@google.com>
Cc: Brian Swetland <swetland@google.com>
Change-Id: Ifbf17cfafb858106d73bf49af52b5161a265a484
Signed-off-by: San Mehat <san@google.com>
Signed-off-by: Daniel Walker <dwalker@codeaurora.org>
2009-11-21 20:29:46 +00:00
|
|
|
/* Initialize sg dma address */
|
|
|
|
sg->dma_address = page_to_dma(mmc_dev(host->mmc), sg_page(sg))
|
|
|
|
+ sg->offset;
|
|
|
|
|
|
|
|
if (i == (host->dma.num_ents - 1))
|
2009-09-22 23:44:22 +00:00
|
|
|
box->cmd |= CMD_LC;
|
|
|
|
rows = (sg_dma_len(sg) % MCI_FIFOSIZE) ?
|
|
|
|
(sg_dma_len(sg) / MCI_FIFOSIZE) + 1 :
|
|
|
|
(sg_dma_len(sg) / MCI_FIFOSIZE) ;
|
|
|
|
|
|
|
|
if (data->flags & MMC_DATA_READ) {
|
|
|
|
box->src_row_addr = msmsdcc_fifo_addr(host);
|
|
|
|
box->dst_row_addr = sg_dma_address(sg);
|
|
|
|
|
|
|
|
box->src_dst_len = (MCI_FIFOSIZE << 16) |
|
|
|
|
(MCI_FIFOSIZE);
|
|
|
|
box->row_offset = MCI_FIFOSIZE;
|
|
|
|
|
|
|
|
box->num_rows = rows * ((1 << 16) + 1);
|
|
|
|
box->cmd |= CMD_SRC_CRCI(crci);
|
|
|
|
} else {
|
|
|
|
box->src_row_addr = sg_dma_address(sg);
|
|
|
|
box->dst_row_addr = msmsdcc_fifo_addr(host);
|
|
|
|
|
|
|
|
box->src_dst_len = (MCI_FIFOSIZE << 16) |
|
|
|
|
(MCI_FIFOSIZE);
|
|
|
|
box->row_offset = (MCI_FIFOSIZE << 16);
|
|
|
|
|
|
|
|
box->num_rows = rows * ((1 << 16) + 1);
|
|
|
|
box->cmd |= CMD_DST_CRCI(crci);
|
|
|
|
}
|
|
|
|
box++;
|
|
|
|
sg++;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* location of command block must be 64 bit aligned */
|
|
|
|
BUG_ON(host->dma.cmd_busaddr & 0x07);
|
|
|
|
|
|
|
|
nc->cmdptr = (host->dma.cmd_busaddr >> 3) | CMD_PTR_LP;
|
|
|
|
host->dma.hdr.cmdptr = DMOV_CMD_PTR_LIST |
|
|
|
|
DMOV_CMD_ADDR(host->dma.cmdptr_busaddr);
|
|
|
|
host->dma.hdr.complete_func = msmsdcc_dma_complete_func;
|
|
|
|
|
mmc: msm_sdcc: Reduce command timeouts and improve reliability.
Based on an original patch by Brent DeGraaf:
"Previous versions of the SD driver were beset with excessive command
timeouts. These timeouts were silent by default, but happened
frequently, especially during heavy system activity and concurrent
access of two or more SD devices. Worst case, these timeouts would
occasionally hit at the end of a successful write, resulting in false
failures that could adversely affect journaling file systems if timing
was unfortunate. This update tightens the association and timing between
dma transfers and the commands that trigger them by utilizing a new api
implemented in the datamover. In addition, it also fixes a dma cache
coherency issue that was exposed during testing of this fix that
occasionally resulted in card corruption. Processing of results in the
interrupt status routine was modified to process command results prior to
data because overwritten command results were observed during testing
since the data section can result in command issuances of its own.
This change also eliminates the software command timeout, relying entirely
on the hardware version, since the software timeout was found to cause
problems of its own after extensive testing (having hardware timer and
software timers addressing the same issue was found to cause a race
condition under heavy system load)."
This change originally added PROG_DONE handling, which has been split out
into a separate patch. Also on our platform, the data mover driver maintains
coherency to ensure API reliability, so the above mentioned cache corruption
issue was not an issue for us.
Signed-off-by: San Mehat <san@google.com>
Cc: Brian Swetland <swetland@google.com>
Change-Id: Ifbf17cfafb858106d73bf49af52b5161a265a484
Signed-off-by: San Mehat <san@google.com>
Signed-off-by: Daniel Walker <dwalker@codeaurora.org>
2009-11-21 20:29:46 +00:00
|
|
|
n = dma_map_sg(mmc_dev(host->mmc), host->dma.sg,
|
|
|
|
host->dma.num_ents, host->dma.dir);
|
|
|
|
/* dsb inside dma_map_sg will write nc out to mem as well */
|
|
|
|
|
|
|
|
if (n != host->dma.num_ents) {
|
|
|
|
printk(KERN_ERR "%s: Unable to map in all sg elements\n",
|
|
|
|
mmc_hostname(host->mmc));
|
|
|
|
host->dma.sg = NULL;
|
|
|
|
host->dma.num_ents = 0;
|
|
|
|
return -ENOMEM;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
snoop_cccr_abort(struct mmc_command *cmd)
|
|
|
|
{
|
|
|
|
if ((cmd->opcode == 52) &&
|
|
|
|
(cmd->arg & 0x80000000) &&
|
|
|
|
(((cmd->arg >> 9) & 0x1ffff) == SDIO_CCCR_ABORT))
|
|
|
|
return 1;
|
2009-09-22 23:44:22 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
mmc: msm_sdcc: Reduce command timeouts and improve reliability.
Based on an original patch by Brent DeGraaf:
"Previous versions of the SD driver were beset with excessive command
timeouts. These timeouts were silent by default, but happened
frequently, especially during heavy system activity and concurrent
access of two or more SD devices. Worst case, these timeouts would
occasionally hit at the end of a successful write, resulting in false
failures that could adversely affect journaling file systems if timing
was unfortunate. This update tightens the association and timing between
dma transfers and the commands that trigger them by utilizing a new api
implemented in the datamover. In addition, it also fixes a dma cache
coherency issue that was exposed during testing of this fix that
occasionally resulted in card corruption. Processing of results in the
interrupt status routine was modified to process command results prior to
data because overwritten command results were observed during testing
since the data section can result in command issuances of its own.
This change also eliminates the software command timeout, relying entirely
on the hardware version, since the software timeout was found to cause
problems of its own after extensive testing (having hardware timer and
software timers addressing the same issue was found to cause a race
condition under heavy system load)."
This change originally added PROG_DONE handling, which has been split out
into a separate patch. Also on our platform, the data mover driver maintains
coherency to ensure API reliability, so the above mentioned cache corruption
issue was not an issue for us.
Signed-off-by: San Mehat <san@google.com>
Cc: Brian Swetland <swetland@google.com>
Change-Id: Ifbf17cfafb858106d73bf49af52b5161a265a484
Signed-off-by: San Mehat <san@google.com>
Signed-off-by: Daniel Walker <dwalker@codeaurora.org>
2009-11-21 20:29:46 +00:00
|
|
|
msmsdcc_start_command_deferred(struct msmsdcc_host *host,
|
|
|
|
struct mmc_command *cmd, u32 *c)
|
|
|
|
{
|
|
|
|
*c |= (cmd->opcode | MCI_CPSM_ENABLE);
|
|
|
|
|
|
|
|
if (cmd->flags & MMC_RSP_PRESENT) {
|
|
|
|
if (cmd->flags & MMC_RSP_136)
|
|
|
|
*c |= MCI_CPSM_LONGRSP;
|
|
|
|
*c |= MCI_CPSM_RESPONSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (/*interrupt*/0)
|
|
|
|
*c |= MCI_CPSM_INTERRUPT;
|
|
|
|
|
|
|
|
if ((((cmd->opcode == 17) || (cmd->opcode == 18)) ||
|
|
|
|
((cmd->opcode == 24) || (cmd->opcode == 25))) ||
|
|
|
|
(cmd->opcode == 53))
|
|
|
|
*c |= MCI_CSPM_DATCMD;
|
|
|
|
|
|
|
|
if (cmd == cmd->mrq->stop)
|
|
|
|
*c |= MCI_CSPM_MCIABORT;
|
|
|
|
|
|
|
|
if (snoop_cccr_abort(cmd))
|
|
|
|
*c |= MCI_CSPM_MCIABORT;
|
|
|
|
|
|
|
|
if (host->curr.cmd != NULL) {
|
|
|
|
printk(KERN_ERR "%s: Overlapping command requests\n",
|
|
|
|
mmc_hostname(host->mmc));
|
|
|
|
}
|
|
|
|
host->curr.cmd = cmd;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
msmsdcc_start_data(struct msmsdcc_host *host, struct mmc_data *data,
|
|
|
|
struct mmc_command *cmd, u32 c)
|
2009-09-22 23:44:22 +00:00
|
|
|
{
|
|
|
|
unsigned int datactrl, timeout;
|
|
|
|
unsigned long long clks;
|
|
|
|
unsigned int pio_irqmask = 0;
|
|
|
|
|
|
|
|
host->curr.data = data;
|
|
|
|
host->curr.xfer_size = data->blksz * data->blocks;
|
|
|
|
host->curr.xfer_remain = host->curr.xfer_size;
|
|
|
|
host->curr.data_xfered = 0;
|
|
|
|
host->curr.got_dataend = 0;
|
|
|
|
host->curr.got_datablkend = 0;
|
|
|
|
|
|
|
|
memset(&host->pio, 0, sizeof(host->pio));
|
|
|
|
|
|
|
|
datactrl = MCI_DPSM_ENABLE | (data->blksz << 4);
|
|
|
|
|
|
|
|
if (!msmsdcc_config_dma(host, data))
|
|
|
|
datactrl |= MCI_DPSM_DMAENABLE;
|
|
|
|
else {
|
|
|
|
host->pio.sg = data->sg;
|
|
|
|
host->pio.sg_len = data->sg_len;
|
|
|
|
host->pio.sg_off = 0;
|
|
|
|
|
|
|
|
if (data->flags & MMC_DATA_READ) {
|
|
|
|
pio_irqmask = MCI_RXFIFOHALFFULLMASK;
|
|
|
|
if (host->curr.xfer_remain < MCI_FIFOSIZE)
|
|
|
|
pio_irqmask |= MCI_RXDATAAVLBLMASK;
|
|
|
|
} else
|
|
|
|
pio_irqmask = MCI_TXFIFOHALFEMPTYMASK;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (data->flags & MMC_DATA_READ)
|
|
|
|
datactrl |= MCI_DPSM_DIRECTION;
|
|
|
|
|
mmc: msm_sdcc: Reduce command timeouts and improve reliability.
Based on an original patch by Brent DeGraaf:
"Previous versions of the SD driver were beset with excessive command
timeouts. These timeouts were silent by default, but happened
frequently, especially during heavy system activity and concurrent
access of two or more SD devices. Worst case, these timeouts would
occasionally hit at the end of a successful write, resulting in false
failures that could adversely affect journaling file systems if timing
was unfortunate. This update tightens the association and timing between
dma transfers and the commands that trigger them by utilizing a new api
implemented in the datamover. In addition, it also fixes a dma cache
coherency issue that was exposed during testing of this fix that
occasionally resulted in card corruption. Processing of results in the
interrupt status routine was modified to process command results prior to
data because overwritten command results were observed during testing
since the data section can result in command issuances of its own.
This change also eliminates the software command timeout, relying entirely
on the hardware version, since the software timeout was found to cause
problems of its own after extensive testing (having hardware timer and
software timers addressing the same issue was found to cause a race
condition under heavy system load)."
This change originally added PROG_DONE handling, which has been split out
into a separate patch. Also on our platform, the data mover driver maintains
coherency to ensure API reliability, so the above mentioned cache corruption
issue was not an issue for us.
Signed-off-by: San Mehat <san@google.com>
Cc: Brian Swetland <swetland@google.com>
Change-Id: Ifbf17cfafb858106d73bf49af52b5161a265a484
Signed-off-by: San Mehat <san@google.com>
Signed-off-by: Daniel Walker <dwalker@codeaurora.org>
2009-11-21 20:29:46 +00:00
|
|
|
clks = (unsigned long long)data->timeout_ns * host->clk_rate;
|
|
|
|
do_div(clks, NSEC_PER_SEC);
|
|
|
|
timeout = data->timeout_clks + (unsigned int)clks*2 ;
|
2009-09-22 23:44:22 +00:00
|
|
|
|
|
|
|
if (datactrl & MCI_DPSM_DMAENABLE) {
|
mmc: msm_sdcc: Reduce command timeouts and improve reliability.
Based on an original patch by Brent DeGraaf:
"Previous versions of the SD driver were beset with excessive command
timeouts. These timeouts were silent by default, but happened
frequently, especially during heavy system activity and concurrent
access of two or more SD devices. Worst case, these timeouts would
occasionally hit at the end of a successful write, resulting in false
failures that could adversely affect journaling file systems if timing
was unfortunate. This update tightens the association and timing between
dma transfers and the commands that trigger them by utilizing a new api
implemented in the datamover. In addition, it also fixes a dma cache
coherency issue that was exposed during testing of this fix that
occasionally resulted in card corruption. Processing of results in the
interrupt status routine was modified to process command results prior to
data because overwritten command results were observed during testing
since the data section can result in command issuances of its own.
This change also eliminates the software command timeout, relying entirely
on the hardware version, since the software timeout was found to cause
problems of its own after extensive testing (having hardware timer and
software timers addressing the same issue was found to cause a race
condition under heavy system load)."
This change originally added PROG_DONE handling, which has been split out
into a separate patch. Also on our platform, the data mover driver maintains
coherency to ensure API reliability, so the above mentioned cache corruption
issue was not an issue for us.
Signed-off-by: San Mehat <san@google.com>
Cc: Brian Swetland <swetland@google.com>
Change-Id: Ifbf17cfafb858106d73bf49af52b5161a265a484
Signed-off-by: San Mehat <san@google.com>
Signed-off-by: Daniel Walker <dwalker@codeaurora.org>
2009-11-21 20:29:46 +00:00
|
|
|
/* Save parameters for the exec function */
|
|
|
|
host->cmd_timeout = timeout;
|
|
|
|
host->cmd_pio_irqmask = pio_irqmask;
|
|
|
|
host->cmd_datactrl = datactrl;
|
|
|
|
host->cmd_cmd = cmd;
|
|
|
|
|
|
|
|
host->dma.hdr.execute_func = msmsdcc_dma_exec_func;
|
|
|
|
host->dma.hdr.data = (void *)host;
|
2009-09-22 23:44:22 +00:00
|
|
|
host->dma.busy = 1;
|
mmc: msm_sdcc: Reduce command timeouts and improve reliability.
Based on an original patch by Brent DeGraaf:
"Previous versions of the SD driver were beset with excessive command
timeouts. These timeouts were silent by default, but happened
frequently, especially during heavy system activity and concurrent
access of two or more SD devices. Worst case, these timeouts would
occasionally hit at the end of a successful write, resulting in false
failures that could adversely affect journaling file systems if timing
was unfortunate. This update tightens the association and timing between
dma transfers and the commands that trigger them by utilizing a new api
implemented in the datamover. In addition, it also fixes a dma cache
coherency issue that was exposed during testing of this fix that
occasionally resulted in card corruption. Processing of results in the
interrupt status routine was modified to process command results prior to
data because overwritten command results were observed during testing
since the data section can result in command issuances of its own.
This change also eliminates the software command timeout, relying entirely
on the hardware version, since the software timeout was found to cause
problems of its own after extensive testing (having hardware timer and
software timers addressing the same issue was found to cause a race
condition under heavy system load)."
This change originally added PROG_DONE handling, which has been split out
into a separate patch. Also on our platform, the data mover driver maintains
coherency to ensure API reliability, so the above mentioned cache corruption
issue was not an issue for us.
Signed-off-by: San Mehat <san@google.com>
Cc: Brian Swetland <swetland@google.com>
Change-Id: Ifbf17cfafb858106d73bf49af52b5161a265a484
Signed-off-by: San Mehat <san@google.com>
Signed-off-by: Daniel Walker <dwalker@codeaurora.org>
2009-11-21 20:29:46 +00:00
|
|
|
|
|
|
|
if (cmd) {
|
|
|
|
msmsdcc_start_command_deferred(host, cmd, &c);
|
|
|
|
host->cmd_c = c;
|
|
|
|
}
|
2009-09-22 23:44:22 +00:00
|
|
|
msm_dmov_enqueue_cmd(host->dma.channel, &host->dma.hdr);
|
mmc: msm_sdcc: Reduce command timeouts and improve reliability.
Based on an original patch by Brent DeGraaf:
"Previous versions of the SD driver were beset with excessive command
timeouts. These timeouts were silent by default, but happened
frequently, especially during heavy system activity and concurrent
access of two or more SD devices. Worst case, these timeouts would
occasionally hit at the end of a successful write, resulting in false
failures that could adversely affect journaling file systems if timing
was unfortunate. This update tightens the association and timing between
dma transfers and the commands that trigger them by utilizing a new api
implemented in the datamover. In addition, it also fixes a dma cache
coherency issue that was exposed during testing of this fix that
occasionally resulted in card corruption. Processing of results in the
interrupt status routine was modified to process command results prior to
data because overwritten command results were observed during testing
since the data section can result in command issuances of its own.
This change also eliminates the software command timeout, relying entirely
on the hardware version, since the software timeout was found to cause
problems of its own after extensive testing (having hardware timer and
software timers addressing the same issue was found to cause a race
condition under heavy system load)."
This change originally added PROG_DONE handling, which has been split out
into a separate patch. Also on our platform, the data mover driver maintains
coherency to ensure API reliability, so the above mentioned cache corruption
issue was not an issue for us.
Signed-off-by: San Mehat <san@google.com>
Cc: Brian Swetland <swetland@google.com>
Change-Id: Ifbf17cfafb858106d73bf49af52b5161a265a484
Signed-off-by: San Mehat <san@google.com>
Signed-off-by: Daniel Walker <dwalker@codeaurora.org>
2009-11-21 20:29:46 +00:00
|
|
|
} else {
|
|
|
|
msmsdcc_writel(host, timeout, MMCIDATATIMER);
|
2009-09-22 23:44:22 +00:00
|
|
|
|
mmc: msm_sdcc: Reduce command timeouts and improve reliability.
Based on an original patch by Brent DeGraaf:
"Previous versions of the SD driver were beset with excessive command
timeouts. These timeouts were silent by default, but happened
frequently, especially during heavy system activity and concurrent
access of two or more SD devices. Worst case, these timeouts would
occasionally hit at the end of a successful write, resulting in false
failures that could adversely affect journaling file systems if timing
was unfortunate. This update tightens the association and timing between
dma transfers and the commands that trigger them by utilizing a new api
implemented in the datamover. In addition, it also fixes a dma cache
coherency issue that was exposed during testing of this fix that
occasionally resulted in card corruption. Processing of results in the
interrupt status routine was modified to process command results prior to
data because overwritten command results were observed during testing
since the data section can result in command issuances of its own.
This change also eliminates the software command timeout, relying entirely
on the hardware version, since the software timeout was found to cause
problems of its own after extensive testing (having hardware timer and
software timers addressing the same issue was found to cause a race
condition under heavy system load)."
This change originally added PROG_DONE handling, which has been split out
into a separate patch. Also on our platform, the data mover driver maintains
coherency to ensure API reliability, so the above mentioned cache corruption
issue was not an issue for us.
Signed-off-by: San Mehat <san@google.com>
Cc: Brian Swetland <swetland@google.com>
Change-Id: Ifbf17cfafb858106d73bf49af52b5161a265a484
Signed-off-by: San Mehat <san@google.com>
Signed-off-by: Daniel Walker <dwalker@codeaurora.org>
2009-11-21 20:29:46 +00:00
|
|
|
msmsdcc_writel(host, host->curr.xfer_size, MMCIDATALENGTH);
|
|
|
|
|
|
|
|
msmsdcc_writel(host, pio_irqmask, MMCIMASK1);
|
|
|
|
msmsdcc_writel(host, datactrl, MMCIDATACTRL);
|
|
|
|
|
|
|
|
if (cmd) {
|
|
|
|
/* Daisy-chain the command if requested */
|
|
|
|
msmsdcc_start_command(host, cmd, c);
|
|
|
|
}
|
|
|
|
}
|
2009-11-03 02:46:09 +00:00
|
|
|
}
|
|
|
|
|
2009-09-22 23:44:22 +00:00
|
|
|
static void
|
|
|
|
msmsdcc_start_command(struct msmsdcc_host *host, struct mmc_command *cmd, u32 c)
|
|
|
|
{
|
|
|
|
if (cmd == cmd->mrq->stop)
|
|
|
|
c |= MCI_CSPM_MCIABORT;
|
|
|
|
|
|
|
|
host->stats.cmds++;
|
|
|
|
|
mmc: msm_sdcc: Reduce command timeouts and improve reliability.
Based on an original patch by Brent DeGraaf:
"Previous versions of the SD driver were beset with excessive command
timeouts. These timeouts were silent by default, but happened
frequently, especially during heavy system activity and concurrent
access of two or more SD devices. Worst case, these timeouts would
occasionally hit at the end of a successful write, resulting in false
failures that could adversely affect journaling file systems if timing
was unfortunate. This update tightens the association and timing between
dma transfers and the commands that trigger them by utilizing a new api
implemented in the datamover. In addition, it also fixes a dma cache
coherency issue that was exposed during testing of this fix that
occasionally resulted in card corruption. Processing of results in the
interrupt status routine was modified to process command results prior to
data because overwritten command results were observed during testing
since the data section can result in command issuances of its own.
This change also eliminates the software command timeout, relying entirely
on the hardware version, since the software timeout was found to cause
problems of its own after extensive testing (having hardware timer and
software timers addressing the same issue was found to cause a race
condition under heavy system load)."
This change originally added PROG_DONE handling, which has been split out
into a separate patch. Also on our platform, the data mover driver maintains
coherency to ensure API reliability, so the above mentioned cache corruption
issue was not an issue for us.
Signed-off-by: San Mehat <san@google.com>
Cc: Brian Swetland <swetland@google.com>
Change-Id: Ifbf17cfafb858106d73bf49af52b5161a265a484
Signed-off-by: San Mehat <san@google.com>
Signed-off-by: Daniel Walker <dwalker@codeaurora.org>
2009-11-21 20:29:46 +00:00
|
|
|
msmsdcc_start_command_deferred(host, cmd, &c);
|
|
|
|
msmsdcc_start_command_exec(host, cmd->arg, c);
|
2009-09-22 23:44:22 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
msmsdcc_data_err(struct msmsdcc_host *host, struct mmc_data *data,
|
|
|
|
unsigned int status)
|
|
|
|
{
|
|
|
|
if (status & MCI_DATACRCFAIL) {
|
2009-09-22 23:44:23 +00:00
|
|
|
pr_err("%s: Data CRC error\n", mmc_hostname(host->mmc));
|
|
|
|
pr_err("%s: opcode 0x%.8x\n", __func__,
|
2009-09-22 23:44:22 +00:00
|
|
|
data->mrq->cmd->opcode);
|
2009-09-22 23:44:23 +00:00
|
|
|
pr_err("%s: blksz %d, blocks %d\n", __func__,
|
2009-09-22 23:44:22 +00:00
|
|
|
data->blksz, data->blocks);
|
|
|
|
data->error = -EILSEQ;
|
|
|
|
} else if (status & MCI_DATATIMEOUT) {
|
2009-09-22 23:44:23 +00:00
|
|
|
pr_err("%s: Data timeout\n", mmc_hostname(host->mmc));
|
2009-09-22 23:44:22 +00:00
|
|
|
data->error = -ETIMEDOUT;
|
|
|
|
} else if (status & MCI_RXOVERRUN) {
|
2009-09-22 23:44:23 +00:00
|
|
|
pr_err("%s: RX overrun\n", mmc_hostname(host->mmc));
|
2009-09-22 23:44:22 +00:00
|
|
|
data->error = -EIO;
|
|
|
|
} else if (status & MCI_TXUNDERRUN) {
|
2009-09-22 23:44:23 +00:00
|
|
|
pr_err("%s: TX underrun\n", mmc_hostname(host->mmc));
|
2009-09-22 23:44:22 +00:00
|
|
|
data->error = -EIO;
|
|
|
|
} else {
|
2009-09-22 23:44:23 +00:00
|
|
|
pr_err("%s: Unknown error (0x%.8x)\n",
|
|
|
|
mmc_hostname(host->mmc), status);
|
2009-09-22 23:44:22 +00:00
|
|
|
data->error = -EIO;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int
|
|
|
|
msmsdcc_pio_read(struct msmsdcc_host *host, char *buffer, unsigned int remain)
|
|
|
|
{
|
|
|
|
uint32_t *ptr = (uint32_t *) buffer;
|
|
|
|
int count = 0;
|
|
|
|
|
2009-11-16 18:17:30 +00:00
|
|
|
while (msmsdcc_readl(host, MMCISTATUS) & MCI_RXDATAAVLBL) {
|
|
|
|
*ptr = msmsdcc_readl(host, MMCIFIFO + (count % MCI_FIFOSIZE));
|
2009-09-22 23:44:22 +00:00
|
|
|
ptr++;
|
|
|
|
count += sizeof(uint32_t);
|
|
|
|
|
|
|
|
remain -= sizeof(uint32_t);
|
|
|
|
if (remain == 0)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
return count;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
msmsdcc_pio_write(struct msmsdcc_host *host, char *buffer,
|
|
|
|
unsigned int remain, u32 status)
|
|
|
|
{
|
|
|
|
void __iomem *base = host->base;
|
|
|
|
char *ptr = buffer;
|
|
|
|
|
|
|
|
do {
|
|
|
|
unsigned int count, maxcnt;
|
|
|
|
|
|
|
|
maxcnt = status & MCI_TXFIFOEMPTY ? MCI_FIFOSIZE :
|
|
|
|
MCI_FIFOHALFSIZE;
|
|
|
|
count = min(remain, maxcnt);
|
|
|
|
|
|
|
|
writesl(base + MMCIFIFO, ptr, count >> 2);
|
|
|
|
ptr += count;
|
|
|
|
remain -= count;
|
|
|
|
|
|
|
|
if (remain == 0)
|
|
|
|
break;
|
|
|
|
|
2009-11-16 18:17:30 +00:00
|
|
|
status = msmsdcc_readl(host, MMCISTATUS);
|
2009-09-22 23:44:22 +00:00
|
|
|
} while (status & MCI_TXFIFOHALFEMPTY);
|
|
|
|
|
|
|
|
return ptr - buffer;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
msmsdcc_spin_on_status(struct msmsdcc_host *host, uint32_t mask, int maxspin)
|
|
|
|
{
|
|
|
|
while (maxspin) {
|
2009-11-16 18:17:30 +00:00
|
|
|
if ((msmsdcc_readl(host, MMCISTATUS) & mask))
|
2009-09-22 23:44:22 +00:00
|
|
|
return 0;
|
|
|
|
udelay(1);
|
|
|
|
--maxspin;
|
|
|
|
}
|
|
|
|
return -ETIMEDOUT;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
msmsdcc_pio_irq(int irq, void *dev_id)
|
|
|
|
{
|
|
|
|
struct msmsdcc_host *host = dev_id;
|
|
|
|
uint32_t status;
|
|
|
|
|
2009-11-16 18:17:30 +00:00
|
|
|
status = msmsdcc_readl(host, MMCISTATUS);
|
2009-09-22 23:44:22 +00:00
|
|
|
|
|
|
|
do {
|
|
|
|
unsigned long flags;
|
|
|
|
unsigned int remain, len;
|
|
|
|
char *buffer;
|
|
|
|
|
|
|
|
if (!(status & (MCI_TXFIFOHALFEMPTY | MCI_RXDATAAVLBL))) {
|
|
|
|
if (host->curr.xfer_remain == 0 || !msmsdcc_piopoll)
|
|
|
|
break;
|
|
|
|
|
|
|
|
if (msmsdcc_spin_on_status(host,
|
|
|
|
(MCI_TXFIFOHALFEMPTY |
|
|
|
|
MCI_RXDATAAVLBL),
|
|
|
|
PIO_SPINMAX)) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Map the current scatter buffer */
|
|
|
|
local_irq_save(flags);
|
|
|
|
buffer = kmap_atomic(sg_page(host->pio.sg),
|
|
|
|
KM_BIO_SRC_IRQ) + host->pio.sg->offset;
|
|
|
|
buffer += host->pio.sg_off;
|
|
|
|
remain = host->pio.sg->length - host->pio.sg_off;
|
|
|
|
len = 0;
|
|
|
|
if (status & MCI_RXACTIVE)
|
|
|
|
len = msmsdcc_pio_read(host, buffer, remain);
|
|
|
|
if (status & MCI_TXACTIVE)
|
|
|
|
len = msmsdcc_pio_write(host, buffer, remain, status);
|
|
|
|
|
|
|
|
/* Unmap the buffer */
|
|
|
|
kunmap_atomic(buffer, KM_BIO_SRC_IRQ);
|
|
|
|
local_irq_restore(flags);
|
|
|
|
|
|
|
|
host->pio.sg_off += len;
|
|
|
|
host->curr.xfer_remain -= len;
|
|
|
|
host->curr.data_xfered += len;
|
|
|
|
remain -= len;
|
|
|
|
|
|
|
|
if (remain == 0) {
|
|
|
|
/* This sg page is full - do some housekeeping */
|
|
|
|
if (status & MCI_RXACTIVE && host->curr.user_pages)
|
|
|
|
flush_dcache_page(sg_page(host->pio.sg));
|
|
|
|
|
|
|
|
if (!--host->pio.sg_len) {
|
|
|
|
memset(&host->pio, 0, sizeof(host->pio));
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Advance to next sg */
|
|
|
|
host->pio.sg++;
|
|
|
|
host->pio.sg_off = 0;
|
|
|
|
}
|
|
|
|
|
2009-11-16 18:17:30 +00:00
|
|
|
status = msmsdcc_readl(host, MMCISTATUS);
|
2009-09-22 23:44:22 +00:00
|
|
|
} while (1);
|
|
|
|
|
|
|
|
if (status & MCI_RXACTIVE && host->curr.xfer_remain < MCI_FIFOSIZE)
|
2009-11-16 18:17:30 +00:00
|
|
|
msmsdcc_writel(host, MCI_RXDATAAVLBLMASK, MMCIMASK1);
|
2009-09-22 23:44:22 +00:00
|
|
|
|
|
|
|
if (!host->curr.xfer_remain)
|
2009-11-16 18:17:30 +00:00
|
|
|
msmsdcc_writel(host, 0, MMCIMASK1);
|
2009-09-22 23:44:22 +00:00
|
|
|
|
|
|
|
return IRQ_HANDLED;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void msmsdcc_do_cmdirq(struct msmsdcc_host *host, uint32_t status)
|
|
|
|
{
|
|
|
|
struct mmc_command *cmd = host->curr.cmd;
|
|
|
|
|
|
|
|
host->curr.cmd = NULL;
|
2009-11-16 18:17:30 +00:00
|
|
|
cmd->resp[0] = msmsdcc_readl(host, MMCIRESPONSE0);
|
|
|
|
cmd->resp[1] = msmsdcc_readl(host, MMCIRESPONSE1);
|
|
|
|
cmd->resp[2] = msmsdcc_readl(host, MMCIRESPONSE2);
|
|
|
|
cmd->resp[3] = msmsdcc_readl(host, MMCIRESPONSE3);
|
2009-09-22 23:44:22 +00:00
|
|
|
|
|
|
|
if (status & MCI_CMDTIMEOUT) {
|
|
|
|
cmd->error = -ETIMEDOUT;
|
|
|
|
} else if (status & MCI_CMDCRCFAIL &&
|
|
|
|
cmd->flags & MMC_RSP_CRC) {
|
2009-09-22 23:44:23 +00:00
|
|
|
pr_err("%s: Command CRC error\n", mmc_hostname(host->mmc));
|
2009-09-22 23:44:22 +00:00
|
|
|
cmd->error = -EILSEQ;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!cmd->data || cmd->error) {
|
|
|
|
if (host->curr.data && host->dma.sg)
|
|
|
|
msm_dmov_stop_cmd(host->dma.channel,
|
|
|
|
&host->dma.hdr, 0);
|
|
|
|
else if (host->curr.data) { /* Non DMA */
|
|
|
|
msmsdcc_stop_data(host);
|
|
|
|
msmsdcc_request_end(host, cmd->mrq);
|
|
|
|
} else /* host->data == NULL */
|
|
|
|
msmsdcc_request_end(host, cmd->mrq);
|
mmc: msm_sdcc: Reduce command timeouts and improve reliability.
Based on an original patch by Brent DeGraaf:
"Previous versions of the SD driver were beset with excessive command
timeouts. These timeouts were silent by default, but happened
frequently, especially during heavy system activity and concurrent
access of two or more SD devices. Worst case, these timeouts would
occasionally hit at the end of a successful write, resulting in false
failures that could adversely affect journaling file systems if timing
was unfortunate. This update tightens the association and timing between
dma transfers and the commands that trigger them by utilizing a new api
implemented in the datamover. In addition, it also fixes a dma cache
coherency issue that was exposed during testing of this fix that
occasionally resulted in card corruption. Processing of results in the
interrupt status routine was modified to process command results prior to
data because overwritten command results were observed during testing
since the data section can result in command issuances of its own.
This change also eliminates the software command timeout, relying entirely
on the hardware version, since the software timeout was found to cause
problems of its own after extensive testing (having hardware timer and
software timers addressing the same issue was found to cause a race
condition under heavy system load)."
This change originally added PROG_DONE handling, which has been split out
into a separate patch. Also on our platform, the data mover driver maintains
coherency to ensure API reliability, so the above mentioned cache corruption
issue was not an issue for us.
Signed-off-by: San Mehat <san@google.com>
Cc: Brian Swetland <swetland@google.com>
Change-Id: Ifbf17cfafb858106d73bf49af52b5161a265a484
Signed-off-by: San Mehat <san@google.com>
Signed-off-by: Daniel Walker <dwalker@codeaurora.org>
2009-11-21 20:29:46 +00:00
|
|
|
} else if (cmd->data)
|
|
|
|
if (!(cmd->data->flags & MMC_DATA_READ))
|
|
|
|
msmsdcc_start_data(host, cmd->data,
|
|
|
|
NULL, 0);
|
2009-09-22 23:44:22 +00:00
|
|
|
}
|
|
|
|
|
2009-09-22 23:44:25 +00:00
|
|
|
static void
|
|
|
|
msmsdcc_handle_irq_data(struct msmsdcc_host *host, u32 status,
|
|
|
|
void __iomem *base)
|
|
|
|
{
|
2009-11-24 20:24:55 +00:00
|
|
|
struct mmc_data *data = host->curr.data;
|
2009-09-22 23:44:25 +00:00
|
|
|
|
mmc: msm_sdcc: Reduce command timeouts and improve reliability.
Based on an original patch by Brent DeGraaf:
"Previous versions of the SD driver were beset with excessive command
timeouts. These timeouts were silent by default, but happened
frequently, especially during heavy system activity and concurrent
access of two or more SD devices. Worst case, these timeouts would
occasionally hit at the end of a successful write, resulting in false
failures that could adversely affect journaling file systems if timing
was unfortunate. This update tightens the association and timing between
dma transfers and the commands that trigger them by utilizing a new api
implemented in the datamover. In addition, it also fixes a dma cache
coherency issue that was exposed during testing of this fix that
occasionally resulted in card corruption. Processing of results in the
interrupt status routine was modified to process command results prior to
data because overwritten command results were observed during testing
since the data section can result in command issuances of its own.
This change also eliminates the software command timeout, relying entirely
on the hardware version, since the software timeout was found to cause
problems of its own after extensive testing (having hardware timer and
software timers addressing the same issue was found to cause a race
condition under heavy system load)."
This change originally added PROG_DONE handling, which has been split out
into a separate patch. Also on our platform, the data mover driver maintains
coherency to ensure API reliability, so the above mentioned cache corruption
issue was not an issue for us.
Signed-off-by: San Mehat <san@google.com>
Cc: Brian Swetland <swetland@google.com>
Change-Id: Ifbf17cfafb858106d73bf49af52b5161a265a484
Signed-off-by: San Mehat <san@google.com>
Signed-off-by: Daniel Walker <dwalker@codeaurora.org>
2009-11-21 20:29:46 +00:00
|
|
|
if (status & (MCI_CMDSENT | MCI_CMDRESPEND | MCI_CMDCRCFAIL |
|
|
|
|
MCI_CMDTIMEOUT) && host->curr.cmd) {
|
|
|
|
msmsdcc_do_cmdirq(host, status);
|
|
|
|
}
|
|
|
|
|
2009-09-22 23:44:25 +00:00
|
|
|
if (!data)
|
|
|
|
return;
|
|
|
|
|
|
|
|
/* Check for data errors */
|
|
|
|
if (status & (MCI_DATACRCFAIL | MCI_DATATIMEOUT |
|
|
|
|
MCI_TXUNDERRUN | MCI_RXOVERRUN)) {
|
|
|
|
msmsdcc_data_err(host, data, status);
|
|
|
|
host->curr.data_xfered = 0;
|
|
|
|
if (host->dma.sg)
|
|
|
|
msm_dmov_stop_cmd(host->dma.channel,
|
|
|
|
&host->dma.hdr, 0);
|
|
|
|
else {
|
2009-11-24 20:24:55 +00:00
|
|
|
if (host->curr.data)
|
|
|
|
msmsdcc_stop_data(host);
|
2009-09-22 23:44:25 +00:00
|
|
|
if (!data->stop)
|
|
|
|
msmsdcc_request_end(host, data->mrq);
|
|
|
|
else
|
|
|
|
msmsdcc_start_command(host, data->stop, 0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Check for data done */
|
|
|
|
if (!host->curr.got_dataend && (status & MCI_DATAEND))
|
|
|
|
host->curr.got_dataend = 1;
|
|
|
|
|
|
|
|
if (!host->curr.got_datablkend && (status & MCI_DATABLOCKEND))
|
|
|
|
host->curr.got_datablkend = 1;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If DMA is still in progress, we complete via the completion handler
|
|
|
|
*/
|
|
|
|
if (host->curr.got_dataend && host->curr.got_datablkend &&
|
|
|
|
!host->dma.busy) {
|
|
|
|
/*
|
|
|
|
* There appears to be an issue in the controller where
|
|
|
|
* if you request a small block transfer (< fifo size),
|
|
|
|
* you may get your DATAEND/DATABLKEND irq without the
|
|
|
|
* PIO data irq.
|
|
|
|
*
|
|
|
|
* Check to see if there is still data to be read,
|
|
|
|
* and simulate a PIO irq.
|
|
|
|
*/
|
|
|
|
if (readl(base + MMCISTATUS) & MCI_RXDATAAVLBL)
|
|
|
|
msmsdcc_pio_irq(1, host);
|
|
|
|
|
|
|
|
msmsdcc_stop_data(host);
|
|
|
|
if (!data->error)
|
|
|
|
host->curr.data_xfered = host->curr.xfer_size;
|
|
|
|
|
|
|
|
if (!data->stop)
|
|
|
|
msmsdcc_request_end(host, data->mrq);
|
|
|
|
else
|
|
|
|
msmsdcc_start_command(host, data->stop, 0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-09-22 23:44:22 +00:00
|
|
|
static irqreturn_t
|
|
|
|
msmsdcc_irq(int irq, void *dev_id)
|
|
|
|
{
|
|
|
|
struct msmsdcc_host *host = dev_id;
|
|
|
|
void __iomem *base = host->base;
|
|
|
|
u32 status;
|
|
|
|
int ret = 0;
|
|
|
|
int cardint = 0;
|
|
|
|
|
|
|
|
spin_lock(&host->lock);
|
|
|
|
|
|
|
|
do {
|
2009-11-16 18:17:30 +00:00
|
|
|
struct mmc_data *data;
|
|
|
|
status = msmsdcc_readl(host, MMCISTATUS);
|
|
|
|
status &= (msmsdcc_readl(host, MMCIMASK0) |
|
|
|
|
MCI_DATABLOCKENDMASK);
|
|
|
|
msmsdcc_writel(host, status, MMCICLEAR);
|
2009-09-22 23:44:22 +00:00
|
|
|
|
2009-11-13 21:42:06 +00:00
|
|
|
if (status & MCI_SDIOINTR)
|
|
|
|
status &= ~MCI_SDIOINTR;
|
|
|
|
|
|
|
|
if (!status)
|
|
|
|
break;
|
|
|
|
|
2009-09-22 23:44:25 +00:00
|
|
|
msmsdcc_handle_irq_data(host, status, base);
|
2009-09-22 23:44:22 +00:00
|
|
|
|
|
|
|
if (status & MCI_SDIOINTOPER) {
|
|
|
|
cardint = 1;
|
|
|
|
status &= ~MCI_SDIOINTOPER;
|
|
|
|
}
|
|
|
|
ret = 1;
|
|
|
|
} while (status);
|
|
|
|
|
|
|
|
spin_unlock(&host->lock);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* We have to delay handling the card interrupt as it calls
|
|
|
|
* back into the driver.
|
|
|
|
*/
|
|
|
|
if (cardint)
|
|
|
|
mmc_signal_sdio_irq(host->mmc);
|
|
|
|
|
|
|
|
return IRQ_RETVAL(ret);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
msmsdcc_request(struct mmc_host *mmc, struct mmc_request *mrq)
|
|
|
|
{
|
|
|
|
struct msmsdcc_host *host = mmc_priv(mmc);
|
|
|
|
unsigned long flags;
|
|
|
|
|
|
|
|
WARN_ON(host->curr.mrq != NULL);
|
|
|
|
WARN_ON(host->pwr == 0);
|
|
|
|
|
|
|
|
spin_lock_irqsave(&host->lock, flags);
|
|
|
|
|
|
|
|
host->stats.reqs++;
|
|
|
|
|
|
|
|
if (host->eject) {
|
|
|
|
if (mrq->data && !(mrq->data->flags & MMC_DATA_READ)) {
|
|
|
|
mrq->cmd->error = 0;
|
|
|
|
mrq->data->bytes_xfered = mrq->data->blksz *
|
|
|
|
mrq->data->blocks;
|
|
|
|
} else
|
|
|
|
mrq->cmd->error = -ENOMEDIUM;
|
|
|
|
|
|
|
|
spin_unlock_irqrestore(&host->lock, flags);
|
|
|
|
mmc_request_done(mmc, mrq);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2009-11-23 01:19:07 +00:00
|
|
|
/* Need to drop the host lock here in case
|
|
|
|
* the busclk wd fires
|
|
|
|
*/
|
|
|
|
spin_unlock_irqrestore(&host->lock, flags);
|
2009-11-13 21:42:06 +00:00
|
|
|
if (!host->clks_on)
|
2009-11-23 01:19:07 +00:00
|
|
|
msmsdcc_enable_clocks(host);
|
|
|
|
spin_lock_irqsave(&host->lock, flags);
|
2009-09-22 23:44:22 +00:00
|
|
|
|
2009-12-03 01:21:07 +00:00
|
|
|
host->curr.mrq = mrq;
|
|
|
|
|
2009-09-22 23:44:22 +00:00
|
|
|
if (mrq->data && mrq->data->flags & MMC_DATA_READ)
|
mmc: msm_sdcc: Reduce command timeouts and improve reliability.
Based on an original patch by Brent DeGraaf:
"Previous versions of the SD driver were beset with excessive command
timeouts. These timeouts were silent by default, but happened
frequently, especially during heavy system activity and concurrent
access of two or more SD devices. Worst case, these timeouts would
occasionally hit at the end of a successful write, resulting in false
failures that could adversely affect journaling file systems if timing
was unfortunate. This update tightens the association and timing between
dma transfers and the commands that trigger them by utilizing a new api
implemented in the datamover. In addition, it also fixes a dma cache
coherency issue that was exposed during testing of this fix that
occasionally resulted in card corruption. Processing of results in the
interrupt status routine was modified to process command results prior to
data because overwritten command results were observed during testing
since the data section can result in command issuances of its own.
This change also eliminates the software command timeout, relying entirely
on the hardware version, since the software timeout was found to cause
problems of its own after extensive testing (having hardware timer and
software timers addressing the same issue was found to cause a race
condition under heavy system load)."
This change originally added PROG_DONE handling, which has been split out
into a separate patch. Also on our platform, the data mover driver maintains
coherency to ensure API reliability, so the above mentioned cache corruption
issue was not an issue for us.
Signed-off-by: San Mehat <san@google.com>
Cc: Brian Swetland <swetland@google.com>
Change-Id: Ifbf17cfafb858106d73bf49af52b5161a265a484
Signed-off-by: San Mehat <san@google.com>
Signed-off-by: Daniel Walker <dwalker@codeaurora.org>
2009-11-21 20:29:46 +00:00
|
|
|
/* Queue/read data, daisy-chain command when data starts */
|
|
|
|
msmsdcc_start_data(host, mrq->data, mrq->cmd, 0);
|
|
|
|
else
|
|
|
|
msmsdcc_start_command(host, mrq->cmd, 0);
|
2009-09-22 23:44:22 +00:00
|
|
|
|
|
|
|
if (host->cmdpoll && !msmsdcc_spin_on_status(host,
|
|
|
|
MCI_CMDRESPEND|MCI_CMDCRCFAIL|MCI_CMDTIMEOUT,
|
|
|
|
CMD_SPINMAX)) {
|
2009-11-16 18:17:30 +00:00
|
|
|
uint32_t status = msmsdcc_readl(host, MMCISTATUS);
|
2009-09-22 23:44:22 +00:00
|
|
|
msmsdcc_do_cmdirq(host, status);
|
2009-11-16 18:17:30 +00:00
|
|
|
msmsdcc_writel(host,
|
|
|
|
MCI_CMDRESPEND | MCI_CMDCRCFAIL | MCI_CMDTIMEOUT,
|
|
|
|
MMCICLEAR);
|
2009-09-22 23:44:22 +00:00
|
|
|
host->stats.cmdpoll_hits++;
|
|
|
|
} else {
|
|
|
|
host->stats.cmdpoll_misses++;
|
|
|
|
}
|
|
|
|
spin_unlock_irqrestore(&host->lock, flags);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
msmsdcc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
|
|
|
|
{
|
|
|
|
struct msmsdcc_host *host = mmc_priv(mmc);
|
|
|
|
u32 clk = 0, pwr = 0;
|
|
|
|
int rc;
|
2009-11-08 21:00:37 +00:00
|
|
|
unsigned long flags;
|
2009-09-22 23:44:22 +00:00
|
|
|
|
2009-11-13 21:42:06 +00:00
|
|
|
if (!host->clks_on)
|
2009-11-23 01:19:07 +00:00
|
|
|
msmsdcc_enable_clocks(host);
|
|
|
|
|
|
|
|
spin_lock_irqsave(&host->lock, flags);
|
2009-09-22 23:44:22 +00:00
|
|
|
|
2009-11-13 21:42:06 +00:00
|
|
|
if (ios->clock) {
|
2009-09-22 23:44:22 +00:00
|
|
|
if (ios->clock != host->clk_rate) {
|
|
|
|
rc = clk_set_rate(host->clk, ios->clock);
|
|
|
|
if (rc < 0)
|
2009-09-22 23:44:23 +00:00
|
|
|
pr_err("%s: Error setting clock rate (%d)\n",
|
|
|
|
mmc_hostname(host->mmc), rc);
|
2009-09-22 23:44:22 +00:00
|
|
|
else
|
|
|
|
host->clk_rate = ios->clock;
|
|
|
|
}
|
|
|
|
clk |= MCI_CLK_ENABLE;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ios->bus_width == MMC_BUS_WIDTH_4)
|
|
|
|
clk |= (2 << 10); /* Set WIDEBUS */
|
|
|
|
|
|
|
|
if (ios->clock > 400000 && msmsdcc_pwrsave)
|
|
|
|
clk |= (1 << 9); /* PWRSAVE */
|
|
|
|
|
|
|
|
clk |= (1 << 12); /* FLOW_ENA */
|
|
|
|
clk |= (1 << 15); /* feedback clock */
|
|
|
|
|
|
|
|
if (host->plat->translate_vdd)
|
|
|
|
pwr |= host->plat->translate_vdd(mmc_dev(mmc), ios->vdd);
|
|
|
|
|
|
|
|
switch (ios->power_mode) {
|
|
|
|
case MMC_POWER_OFF:
|
|
|
|
break;
|
|
|
|
case MMC_POWER_UP:
|
|
|
|
pwr |= MCI_PWR_UP;
|
|
|
|
break;
|
|
|
|
case MMC_POWER_ON:
|
|
|
|
pwr |= MCI_PWR_ON;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ios->bus_mode == MMC_BUSMODE_OPENDRAIN)
|
|
|
|
pwr |= MCI_OD;
|
|
|
|
|
2009-11-16 18:17:30 +00:00
|
|
|
msmsdcc_writel(host, clk, MMCICLOCK);
|
2009-09-22 23:44:22 +00:00
|
|
|
|
|
|
|
if (host->pwr != pwr) {
|
|
|
|
host->pwr = pwr;
|
2009-11-16 18:17:30 +00:00
|
|
|
msmsdcc_writel(host, pwr, MMCIPOWER);
|
2009-09-22 23:44:22 +00:00
|
|
|
}
|
2009-11-23 23:36:31 +00:00
|
|
|
#if BUSCLK_PWRSAVE
|
2009-11-23 01:19:07 +00:00
|
|
|
msmsdcc_disable_clocks(host, 1);
|
2009-11-23 23:36:31 +00:00
|
|
|
#endif
|
2009-11-08 21:00:37 +00:00
|
|
|
spin_unlock_irqrestore(&host->lock, flags);
|
2009-09-22 23:44:22 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void msmsdcc_enable_sdio_irq(struct mmc_host *mmc, int enable)
|
|
|
|
{
|
|
|
|
struct msmsdcc_host *host = mmc_priv(mmc);
|
|
|
|
unsigned long flags;
|
|
|
|
u32 status;
|
|
|
|
|
|
|
|
spin_lock_irqsave(&host->lock, flags);
|
|
|
|
if (msmsdcc_sdioirq == 1) {
|
2009-11-16 18:17:30 +00:00
|
|
|
status = msmsdcc_readl(host, MMCIMASK0);
|
2009-09-22 23:44:22 +00:00
|
|
|
if (enable)
|
|
|
|
status |= MCI_SDIOINTOPERMASK;
|
|
|
|
else
|
|
|
|
status &= ~MCI_SDIOINTOPERMASK;
|
|
|
|
host->saved_irq0mask = status;
|
2009-11-16 18:17:30 +00:00
|
|
|
msmsdcc_writel(host, status, MMCIMASK0);
|
2009-09-22 23:44:22 +00:00
|
|
|
}
|
|
|
|
spin_unlock_irqrestore(&host->lock, flags);
|
|
|
|
}
|
|
|
|
|
|
|
|
static const struct mmc_host_ops msmsdcc_ops = {
|
|
|
|
.request = msmsdcc_request,
|
|
|
|
.set_ios = msmsdcc_set_ios,
|
|
|
|
.enable_sdio_irq = msmsdcc_enable_sdio_irq,
|
|
|
|
};
|
|
|
|
|
|
|
|
static void
|
|
|
|
msmsdcc_check_status(unsigned long data)
|
|
|
|
{
|
|
|
|
struct msmsdcc_host *host = (struct msmsdcc_host *)data;
|
|
|
|
unsigned int status;
|
|
|
|
|
|
|
|
if (!host->plat->status) {
|
|
|
|
mmc_detect_change(host->mmc, 0);
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
status = host->plat->status(mmc_dev(host->mmc));
|
|
|
|
host->eject = !status;
|
|
|
|
if (status ^ host->oldstat) {
|
2009-09-22 23:44:23 +00:00
|
|
|
pr_info("%s: Slot status change detected (%d -> %d)\n",
|
|
|
|
mmc_hostname(host->mmc), host->oldstat, status);
|
2009-09-22 23:44:22 +00:00
|
|
|
if (status)
|
|
|
|
mmc_detect_change(host->mmc, (5 * HZ) / 2);
|
|
|
|
else
|
|
|
|
mmc_detect_change(host->mmc, 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
host->oldstat = status;
|
|
|
|
|
|
|
|
out:
|
|
|
|
if (host->timer.function)
|
|
|
|
mod_timer(&host->timer, jiffies + HZ);
|
|
|
|
}
|
|
|
|
|
|
|
|
static irqreturn_t
|
|
|
|
msmsdcc_platform_status_irq(int irq, void *dev_id)
|
|
|
|
{
|
|
|
|
struct msmsdcc_host *host = dev_id;
|
|
|
|
|
|
|
|
printk(KERN_DEBUG "%s: %d\n", __func__, irq);
|
|
|
|
msmsdcc_check_status((unsigned long) host);
|
|
|
|
return IRQ_HANDLED;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
msmsdcc_status_notify_cb(int card_present, void *dev_id)
|
|
|
|
{
|
|
|
|
struct msmsdcc_host *host = dev_id;
|
|
|
|
|
|
|
|
printk(KERN_DEBUG "%s: card_present %d\n", mmc_hostname(host->mmc),
|
|
|
|
card_present);
|
|
|
|
msmsdcc_check_status((unsigned long) host);
|
|
|
|
}
|
|
|
|
|
2009-11-13 21:42:06 +00:00
|
|
|
static void
|
|
|
|
msmsdcc_busclk_expired(unsigned long _data)
|
|
|
|
{
|
|
|
|
struct msmsdcc_host *host = (struct msmsdcc_host *) _data;
|
|
|
|
unsigned long flags;
|
|
|
|
|
|
|
|
spin_lock_irqsave(&host->lock, flags);
|
2009-11-23 01:19:07 +00:00
|
|
|
dev_info(mmc_dev(host->mmc), "Bus clock timer expired\n");
|
2009-11-13 21:42:06 +00:00
|
|
|
if (host->clks_on)
|
2009-11-23 01:19:07 +00:00
|
|
|
msmsdcc_disable_clocks(host, 0);
|
2009-11-13 21:42:06 +00:00
|
|
|
spin_unlock_irqrestore(&host->lock, flags);
|
|
|
|
}
|
|
|
|
|
2009-09-22 23:44:22 +00:00
|
|
|
static int
|
|
|
|
msmsdcc_init_dma(struct msmsdcc_host *host)
|
|
|
|
{
|
|
|
|
memset(&host->dma, 0, sizeof(struct msmsdcc_dma_data));
|
|
|
|
host->dma.host = host;
|
|
|
|
host->dma.channel = -1;
|
|
|
|
|
|
|
|
if (!host->dmares)
|
|
|
|
return -ENODEV;
|
|
|
|
|
|
|
|
host->dma.nc = dma_alloc_coherent(NULL,
|
|
|
|
sizeof(struct msmsdcc_nc_dmadata),
|
|
|
|
&host->dma.nc_busaddr,
|
|
|
|
GFP_KERNEL);
|
|
|
|
if (host->dma.nc == NULL) {
|
2009-09-22 23:44:23 +00:00
|
|
|
pr_err("Unable to allocate DMA buffer\n");
|
2009-09-22 23:44:22 +00:00
|
|
|
return -ENOMEM;
|
|
|
|
}
|
|
|
|
memset(host->dma.nc, 0x00, sizeof(struct msmsdcc_nc_dmadata));
|
|
|
|
host->dma.cmd_busaddr = host->dma.nc_busaddr;
|
|
|
|
host->dma.cmdptr_busaddr = host->dma.nc_busaddr +
|
|
|
|
offsetof(struct msmsdcc_nc_dmadata, cmdptr);
|
|
|
|
host->dma.channel = host->dmares->start;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef CONFIG_MMC_MSM7X00A_RESUME_IN_WQ
|
|
|
|
static void
|
|
|
|
do_resume_work(struct work_struct *work)
|
|
|
|
{
|
|
|
|
struct msmsdcc_host *host =
|
|
|
|
container_of(work, struct msmsdcc_host, resume_task);
|
|
|
|
struct mmc_host *mmc = host->mmc;
|
|
|
|
|
|
|
|
if (mmc) {
|
|
|
|
mmc_resume_host(mmc);
|
|
|
|
if (host->stat_irq)
|
|
|
|
enable_irq(host->stat_irq);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
static int
|
|
|
|
msmsdcc_probe(struct platform_device *pdev)
|
|
|
|
{
|
|
|
|
struct mmc_platform_data *plat = pdev->dev.platform_data;
|
|
|
|
struct msmsdcc_host *host;
|
|
|
|
struct mmc_host *mmc;
|
|
|
|
struct resource *cmd_irqres = NULL;
|
|
|
|
struct resource *pio_irqres = NULL;
|
|
|
|
struct resource *stat_irqres = NULL;
|
|
|
|
struct resource *memres = NULL;
|
|
|
|
struct resource *dmares = NULL;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
/* must have platform data */
|
|
|
|
if (!plat) {
|
2009-09-22 23:44:23 +00:00
|
|
|
pr_err("%s: Platform data not available\n", __func__);
|
2009-09-22 23:44:22 +00:00
|
|
|
ret = -EINVAL;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (pdev->id < 1 || pdev->id > 4)
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
if (pdev->resource == NULL || pdev->num_resources < 2) {
|
2009-09-22 23:44:23 +00:00
|
|
|
pr_err("%s: Invalid resource\n", __func__);
|
2009-09-22 23:44:22 +00:00
|
|
|
return -ENXIO;
|
|
|
|
}
|
|
|
|
|
|
|
|
memres = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
|
|
|
dmares = platform_get_resource(pdev, IORESOURCE_DMA, 0);
|
|
|
|
cmd_irqres = platform_get_resource_byname(pdev, IORESOURCE_IRQ,
|
|
|
|
"cmd_irq");
|
|
|
|
pio_irqres = platform_get_resource_byname(pdev, IORESOURCE_IRQ,
|
|
|
|
"pio_irq");
|
|
|
|
stat_irqres = platform_get_resource_byname(pdev, IORESOURCE_IRQ,
|
|
|
|
"status_irq");
|
|
|
|
|
|
|
|
if (!cmd_irqres || !pio_irqres || !memres) {
|
2009-09-22 23:44:23 +00:00
|
|
|
pr_err("%s: Invalid resource\n", __func__);
|
2009-09-22 23:44:22 +00:00
|
|
|
return -ENXIO;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Setup our host structure
|
|
|
|
*/
|
|
|
|
|
|
|
|
mmc = mmc_alloc_host(sizeof(struct msmsdcc_host), &pdev->dev);
|
|
|
|
if (!mmc) {
|
|
|
|
ret = -ENOMEM;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
host = mmc_priv(mmc);
|
|
|
|
host->pdev_id = pdev->id;
|
|
|
|
host->plat = plat;
|
|
|
|
host->mmc = mmc;
|
mmc: msm_sdcc: Reduce command timeouts and improve reliability.
Based on an original patch by Brent DeGraaf:
"Previous versions of the SD driver were beset with excessive command
timeouts. These timeouts were silent by default, but happened
frequently, especially during heavy system activity and concurrent
access of two or more SD devices. Worst case, these timeouts would
occasionally hit at the end of a successful write, resulting in false
failures that could adversely affect journaling file systems if timing
was unfortunate. This update tightens the association and timing between
dma transfers and the commands that trigger them by utilizing a new api
implemented in the datamover. In addition, it also fixes a dma cache
coherency issue that was exposed during testing of this fix that
occasionally resulted in card corruption. Processing of results in the
interrupt status routine was modified to process command results prior to
data because overwritten command results were observed during testing
since the data section can result in command issuances of its own.
This change also eliminates the software command timeout, relying entirely
on the hardware version, since the software timeout was found to cause
problems of its own after extensive testing (having hardware timer and
software timers addressing the same issue was found to cause a race
condition under heavy system load)."
This change originally added PROG_DONE handling, which has been split out
into a separate patch. Also on our platform, the data mover driver maintains
coherency to ensure API reliability, so the above mentioned cache corruption
issue was not an issue for us.
Signed-off-by: San Mehat <san@google.com>
Cc: Brian Swetland <swetland@google.com>
Change-Id: Ifbf17cfafb858106d73bf49af52b5161a265a484
Signed-off-by: San Mehat <san@google.com>
Signed-off-by: Daniel Walker <dwalker@codeaurora.org>
2009-11-21 20:29:46 +00:00
|
|
|
host->curr.cmd = NULL;
|
2009-09-22 23:44:22 +00:00
|
|
|
|
|
|
|
host->cmdpoll = 1;
|
|
|
|
|
|
|
|
host->base = ioremap(memres->start, PAGE_SIZE);
|
|
|
|
if (!host->base) {
|
|
|
|
ret = -ENOMEM;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
host->cmd_irqres = cmd_irqres;
|
|
|
|
host->pio_irqres = pio_irqres;
|
|
|
|
host->memres = memres;
|
|
|
|
host->dmares = dmares;
|
|
|
|
spin_lock_init(&host->lock);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Setup DMA
|
|
|
|
*/
|
|
|
|
msmsdcc_init_dma(host);
|
|
|
|
|
2009-11-08 21:00:37 +00:00
|
|
|
/* Get our clocks */
|
2009-09-22 23:44:22 +00:00
|
|
|
host->pclk = clk_get(&pdev->dev, "sdc_pclk");
|
|
|
|
if (IS_ERR(host->pclk)) {
|
|
|
|
ret = PTR_ERR(host->pclk);
|
|
|
|
goto host_free;
|
|
|
|
}
|
|
|
|
|
|
|
|
host->clk = clk_get(&pdev->dev, "sdc_clk");
|
|
|
|
if (IS_ERR(host->clk)) {
|
|
|
|
ret = PTR_ERR(host->clk);
|
2009-11-08 21:00:37 +00:00
|
|
|
goto pclk_put;
|
2009-09-22 23:44:22 +00:00
|
|
|
}
|
|
|
|
|
2009-11-08 21:00:37 +00:00
|
|
|
/* Enable clocks */
|
2009-11-23 01:19:07 +00:00
|
|
|
ret = msmsdcc_enable_clocks(host);
|
2009-09-22 23:44:22 +00:00
|
|
|
if (ret)
|
|
|
|
goto clk_put;
|
|
|
|
|
|
|
|
ret = clk_set_rate(host->clk, msmsdcc_fmin);
|
|
|
|
if (ret) {
|
2009-09-22 23:44:23 +00:00
|
|
|
pr_err("%s: Clock rate set failed (%d)\n", __func__, ret);
|
2009-09-22 23:44:22 +00:00
|
|
|
goto clk_disable;
|
|
|
|
}
|
|
|
|
|
2009-11-08 21:00:37 +00:00
|
|
|
host->pclk_rate = clk_get_rate(host->pclk);
|
2009-09-22 23:44:22 +00:00
|
|
|
host->clk_rate = clk_get_rate(host->clk);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Setup MMC host structure
|
|
|
|
*/
|
|
|
|
mmc->ops = &msmsdcc_ops;
|
|
|
|
mmc->f_min = msmsdcc_fmin;
|
|
|
|
mmc->f_max = msmsdcc_fmax;
|
|
|
|
mmc->ocr_avail = plat->ocr_mask;
|
|
|
|
|
|
|
|
if (msmsdcc_4bit)
|
|
|
|
mmc->caps |= MMC_CAP_4_BIT_DATA;
|
|
|
|
if (msmsdcc_sdioirq)
|
|
|
|
mmc->caps |= MMC_CAP_SDIO_IRQ;
|
|
|
|
mmc->caps |= MMC_CAP_MMC_HIGHSPEED | MMC_CAP_SD_HIGHSPEED;
|
|
|
|
|
|
|
|
mmc->max_phys_segs = NR_SG;
|
|
|
|
mmc->max_hw_segs = NR_SG;
|
|
|
|
mmc->max_blk_size = 4096; /* MCI_DATA_CTL BLOCKSIZE up to 4096 */
|
|
|
|
mmc->max_blk_count = 65536;
|
|
|
|
|
|
|
|
mmc->max_req_size = 33554432; /* MCI_DATA_LENGTH is 25 bits */
|
|
|
|
mmc->max_seg_size = mmc->max_req_size;
|
|
|
|
|
2009-11-16 18:17:30 +00:00
|
|
|
msmsdcc_writel(host, 0, MMCIMASK0);
|
|
|
|
msmsdcc_writel(host, 0x5e007ff, MMCICLEAR);
|
2009-09-22 23:44:22 +00:00
|
|
|
|
2009-11-16 18:17:30 +00:00
|
|
|
msmsdcc_writel(host, MCI_IRQENABLE, MMCIMASK0);
|
2009-09-22 23:44:22 +00:00
|
|
|
host->saved_irq0mask = MCI_IRQENABLE;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Setup card detect change
|
|
|
|
*/
|
|
|
|
|
|
|
|
memset(&host->timer, 0, sizeof(host->timer));
|
|
|
|
|
|
|
|
if (stat_irqres && !(stat_irqres->flags & IORESOURCE_DISABLED)) {
|
|
|
|
unsigned long irqflags = IRQF_SHARED |
|
|
|
|
(stat_irqres->flags & IRQF_TRIGGER_MASK);
|
|
|
|
|
|
|
|
host->stat_irq = stat_irqres->start;
|
|
|
|
ret = request_irq(host->stat_irq,
|
|
|
|
msmsdcc_platform_status_irq,
|
|
|
|
irqflags,
|
|
|
|
DRIVER_NAME " (slot)",
|
|
|
|
host);
|
|
|
|
if (ret) {
|
2009-09-22 23:44:23 +00:00
|
|
|
pr_err("%s: Unable to get slot IRQ %d (%d)\n",
|
|
|
|
mmc_hostname(mmc), host->stat_irq, ret);
|
2009-09-22 23:44:22 +00:00
|
|
|
goto clk_disable;
|
|
|
|
}
|
|
|
|
} else if (plat->register_status_notify) {
|
|
|
|
plat->register_status_notify(msmsdcc_status_notify_cb, host);
|
|
|
|
} else if (!plat->status)
|
2009-09-22 23:44:23 +00:00
|
|
|
pr_err("%s: No card detect facilities available\n",
|
2009-09-22 23:44:22 +00:00
|
|
|
mmc_hostname(mmc));
|
|
|
|
else {
|
|
|
|
init_timer(&host->timer);
|
|
|
|
host->timer.data = (unsigned long)host;
|
|
|
|
host->timer.function = msmsdcc_check_status;
|
|
|
|
host->timer.expires = jiffies + HZ;
|
|
|
|
add_timer(&host->timer);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (plat->status) {
|
|
|
|
host->oldstat = host->plat->status(mmc_dev(host->mmc));
|
|
|
|
host->eject = !host->oldstat;
|
|
|
|
}
|
|
|
|
|
2009-11-13 21:42:06 +00:00
|
|
|
init_timer(&host->busclk_timer);
|
|
|
|
host->busclk_timer.data = (unsigned long) host;
|
|
|
|
host->busclk_timer.function = msmsdcc_busclk_expired;
|
|
|
|
|
2009-09-22 23:44:22 +00:00
|
|
|
ret = request_irq(cmd_irqres->start, msmsdcc_irq, IRQF_SHARED,
|
|
|
|
DRIVER_NAME " (cmd)", host);
|
|
|
|
if (ret)
|
|
|
|
goto stat_irq_free;
|
|
|
|
|
|
|
|
ret = request_irq(pio_irqres->start, msmsdcc_pio_irq, IRQF_SHARED,
|
|
|
|
DRIVER_NAME " (pio)", host);
|
|
|
|
if (ret)
|
|
|
|
goto cmd_irq_free;
|
|
|
|
|
|
|
|
mmc_set_drvdata(pdev, mmc);
|
|
|
|
mmc_add_host(mmc);
|
|
|
|
|
2009-09-22 23:44:23 +00:00
|
|
|
pr_info("%s: Qualcomm MSM SDCC at 0x%016llx irq %d,%d dma %d\n",
|
|
|
|
mmc_hostname(mmc), (unsigned long long)memres->start,
|
|
|
|
(unsigned int) cmd_irqres->start,
|
|
|
|
(unsigned int) host->stat_irq, host->dma.channel);
|
|
|
|
pr_info("%s: 4 bit data mode %s\n", mmc_hostname(mmc),
|
|
|
|
(mmc->caps & MMC_CAP_4_BIT_DATA ? "enabled" : "disabled"));
|
|
|
|
pr_info("%s: MMC clock %u -> %u Hz, PCLK %u Hz\n",
|
|
|
|
mmc_hostname(mmc), msmsdcc_fmin, msmsdcc_fmax, host->pclk_rate);
|
|
|
|
pr_info("%s: Slot eject status = %d\n", mmc_hostname(mmc), host->eject);
|
|
|
|
pr_info("%s: Power save feature enable = %d\n",
|
|
|
|
mmc_hostname(mmc), msmsdcc_pwrsave);
|
2009-09-22 23:44:22 +00:00
|
|
|
|
|
|
|
if (host->dma.channel != -1) {
|
2009-09-22 23:44:23 +00:00
|
|
|
pr_info("%s: DM non-cached buffer at %p, dma_addr 0x%.8x\n",
|
|
|
|
mmc_hostname(mmc), host->dma.nc, host->dma.nc_busaddr);
|
|
|
|
pr_info("%s: DM cmd busaddr 0x%.8x, cmdptr busaddr 0x%.8x\n",
|
|
|
|
mmc_hostname(mmc), host->dma.cmd_busaddr,
|
|
|
|
host->dma.cmdptr_busaddr);
|
2009-09-22 23:44:22 +00:00
|
|
|
} else
|
2009-09-22 23:44:23 +00:00
|
|
|
pr_info("%s: PIO transfer enabled\n", mmc_hostname(mmc));
|
2009-09-22 23:44:22 +00:00
|
|
|
if (host->timer.function)
|
2009-09-22 23:44:23 +00:00
|
|
|
pr_info("%s: Polling status mode enabled\n", mmc_hostname(mmc));
|
2009-09-22 23:44:22 +00:00
|
|
|
|
2009-11-23 23:36:31 +00:00
|
|
|
#if BUSCLK_PWRSAVE
|
2009-11-23 01:19:07 +00:00
|
|
|
msmsdcc_disable_clocks(host, 1);
|
2009-11-23 23:36:31 +00:00
|
|
|
#endif
|
2009-09-22 23:44:22 +00:00
|
|
|
return 0;
|
|
|
|
cmd_irq_free:
|
|
|
|
free_irq(cmd_irqres->start, host);
|
|
|
|
stat_irq_free:
|
|
|
|
if (host->stat_irq)
|
|
|
|
free_irq(host->stat_irq, host);
|
|
|
|
clk_disable:
|
2009-11-23 01:19:07 +00:00
|
|
|
msmsdcc_disable_clocks(host, 0);
|
2009-09-22 23:44:22 +00:00
|
|
|
clk_put:
|
|
|
|
clk_put(host->clk);
|
|
|
|
pclk_put:
|
|
|
|
clk_put(host->pclk);
|
|
|
|
host_free:
|
|
|
|
mmc_free_host(mmc);
|
|
|
|
out:
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
msmsdcc_suspend(struct platform_device *dev, pm_message_t state)
|
|
|
|
{
|
|
|
|
struct mmc_host *mmc = mmc_get_drvdata(dev);
|
|
|
|
int rc = 0;
|
|
|
|
|
|
|
|
if (mmc) {
|
|
|
|
struct msmsdcc_host *host = mmc_priv(mmc);
|
|
|
|
|
|
|
|
if (host->stat_irq)
|
|
|
|
disable_irq(host->stat_irq);
|
|
|
|
|
|
|
|
if (mmc->card && mmc->card->type != MMC_TYPE_SDIO)
|
|
|
|
rc = mmc_suspend_host(mmc, state);
|
|
|
|
if (!rc) {
|
2009-11-16 18:17:30 +00:00
|
|
|
msmsdcc_writel(host, 0, MMCIMASK0);
|
2009-09-22 23:44:22 +00:00
|
|
|
|
|
|
|
}
|
2009-11-23 01:19:07 +00:00
|
|
|
if (host->clks_on)
|
|
|
|
msmsdcc_disable_clocks(host, 0);
|
2009-09-22 23:44:22 +00:00
|
|
|
}
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
msmsdcc_resume(struct platform_device *dev)
|
|
|
|
{
|
|
|
|
struct mmc_host *mmc = mmc_get_drvdata(dev);
|
|
|
|
|
|
|
|
if (mmc) {
|
|
|
|
struct msmsdcc_host *host = mmc_priv(mmc);
|
|
|
|
|
2009-11-23 01:19:07 +00:00
|
|
|
msmsdcc_enable_clocks(host);
|
mmc: msm_sdcc: Reduce command timeouts and improve reliability.
Based on an original patch by Brent DeGraaf:
"Previous versions of the SD driver were beset with excessive command
timeouts. These timeouts were silent by default, but happened
frequently, especially during heavy system activity and concurrent
access of two or more SD devices. Worst case, these timeouts would
occasionally hit at the end of a successful write, resulting in false
failures that could adversely affect journaling file systems if timing
was unfortunate. This update tightens the association and timing between
dma transfers and the commands that trigger them by utilizing a new api
implemented in the datamover. In addition, it also fixes a dma cache
coherency issue that was exposed during testing of this fix that
occasionally resulted in card corruption. Processing of results in the
interrupt status routine was modified to process command results prior to
data because overwritten command results were observed during testing
since the data section can result in command issuances of its own.
This change also eliminates the software command timeout, relying entirely
on the hardware version, since the software timeout was found to cause
problems of its own after extensive testing (having hardware timer and
software timers addressing the same issue was found to cause a race
condition under heavy system load)."
This change originally added PROG_DONE handling, which has been split out
into a separate patch. Also on our platform, the data mover driver maintains
coherency to ensure API reliability, so the above mentioned cache corruption
issue was not an issue for us.
Signed-off-by: San Mehat <san@google.com>
Cc: Brian Swetland <swetland@google.com>
Change-Id: Ifbf17cfafb858106d73bf49af52b5161a265a484
Signed-off-by: San Mehat <san@google.com>
Signed-off-by: Daniel Walker <dwalker@codeaurora.org>
2009-11-21 20:29:46 +00:00
|
|
|
|
2009-11-16 18:17:30 +00:00
|
|
|
msmsdcc_writel(host, host->saved_irq0mask, MMCIMASK0);
|
2009-09-22 23:44:22 +00:00
|
|
|
|
|
|
|
if (mmc->card && mmc->card->type != MMC_TYPE_SDIO)
|
|
|
|
mmc_resume_host(mmc);
|
2010-01-17 19:25:36 +00:00
|
|
|
if (host->stat_irq)
|
2009-09-22 23:44:22 +00:00
|
|
|
enable_irq(host->stat_irq);
|
2009-11-23 23:36:31 +00:00
|
|
|
#if BUSCLK_PWRSAVE
|
2009-11-23 01:19:07 +00:00
|
|
|
msmsdcc_disable_clocks(host, 1);
|
2009-11-23 23:36:31 +00:00
|
|
|
#endif
|
2009-09-22 23:44:22 +00:00
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct platform_driver msmsdcc_driver = {
|
|
|
|
.probe = msmsdcc_probe,
|
|
|
|
.suspend = msmsdcc_suspend,
|
|
|
|
.resume = msmsdcc_resume,
|
|
|
|
.driver = {
|
|
|
|
.name = "msm_sdcc",
|
|
|
|
},
|
|
|
|
};
|
|
|
|
|
|
|
|
static int __init msmsdcc_init(void)
|
|
|
|
{
|
|
|
|
return platform_driver_register(&msmsdcc_driver);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void __exit msmsdcc_exit(void)
|
|
|
|
{
|
|
|
|
platform_driver_unregister(&msmsdcc_driver);
|
|
|
|
}
|
|
|
|
|
|
|
|
module_init(msmsdcc_init);
|
|
|
|
module_exit(msmsdcc_exit);
|
|
|
|
|
|
|
|
MODULE_DESCRIPTION("Qualcomm MSM 7X00A Multimedia Card Interface driver");
|
|
|
|
MODULE_LICENSE("GPL");
|