Skip to content

Commit

Permalink
mmc: msm_sdcc: Wrap readl/writel calls with appropriate clk delays
Browse files Browse the repository at this point in the history
As it turns out, all sdcc register writes must be delayed by at
least 3 core clock cycles for the writes to take effect. *sigh*

    Also removes the 30us constant delay on clock enable in favor
of a 3 core clock delay.

Signed-off-by: San Mehat <san@google.com>
Signed-off-by: Daniel Walker <dwalker@codeaurora.org>
  • Loading branch information
San Mehat authored and Daniel Walker committed Mar 18, 2010
1 parent 865c806 commit 8b1c2ba
Showing 1 changed file with 61 additions and 55 deletions.
116 changes: 61 additions & 55 deletions drivers/mmc/host/msm_sdcc.c
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ static inline int
msmsdcc_enable_clocks(struct msmsdcc_host *host, int enable)
{
int rc;

WARN_ON(enable == host->clks_on);
if (enable) {
rc = clk_enable(host->pclk);
Expand All @@ -72,7 +73,8 @@ msmsdcc_enable_clocks(struct msmsdcc_host *host, int enable)
clk_disable(host->pclk);
return rc;
}
udelay(30);
udelay(1 + ((3 * USEC_PER_SEC) /
(host->clk_rate ? host->clk_rate : msmsdcc_fmin)));
host->clks_on = 1;
} else {
clk_disable(host->clk);
Expand All @@ -82,6 +84,20 @@ msmsdcc_enable_clocks(struct msmsdcc_host *host, int enable)
return 0;
}

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

static void
msmsdcc_start_command(struct msmsdcc_host *host, struct mmc_command *cmd,
Expand All @@ -90,7 +106,7 @@ msmsdcc_start_command(struct msmsdcc_host *host, struct mmc_command *cmd,
static void
msmsdcc_request_end(struct msmsdcc_host *host, struct mmc_request *mrq)
{
writel(0, host->base + MMCICOMMAND);
msmsdcc_writel(host, 0, MMCICOMMAND);

BUG_ON(host->curr.data);

Expand All @@ -116,7 +132,7 @@ msmsdcc_request_end(struct msmsdcc_host *host, struct mmc_request *mrq)
static void
msmsdcc_stop_data(struct msmsdcc_host *host)
{
writel(0, host->base + MMCIDATACTRL);
msmsdcc_writel(host, 0, MMCIDATACTRL);
host->curr.data = NULL;
host->curr.got_dataend = host->curr.got_datablkend = 0;
}
Expand Down Expand Up @@ -200,7 +216,7 @@ msmsdcc_dma_complete_func(struct msm_dmov_cmd *cmd,
if (!mrq->data->error)
host->curr.data_xfered = host->curr.xfer_size;
if (!mrq->data->stop || mrq->cmd->error) {
writel(0, host->base + MMCICOMMAND);
msmsdcc_writel(host, 0, MMCICOMMAND);
host->curr.mrq = NULL;
host->curr.cmd = NULL;
mrq->data->bytes_xfered = host->curr.data_xfered;
Expand Down Expand Up @@ -337,7 +353,6 @@ msmsdcc_start_data(struct msmsdcc_host *host, struct mmc_data *data)
{
unsigned int datactrl, timeout;
unsigned long long clks;
void __iomem *base = host->base;
unsigned int pio_irqmask = 0;

host->curr.data = data;
Expand All @@ -352,9 +367,9 @@ msmsdcc_start_data(struct msmsdcc_host *host, struct mmc_data *data)
clks = (unsigned long long)data->timeout_ns * host->clk_rate;
do_div(clks, NSEC_PER_SEC);
timeout = data->timeout_clks + (unsigned int)clks;
writel(timeout, base + MMCIDATATIMER);
msmsdcc_writel(host, timeout, MMCIDATATIMER);

writel(host->curr.xfer_size, base + MMCIDATALENGTH);
msmsdcc_writel(host, host->curr.xfer_size, MMCIDATALENGTH);

datactrl = MCI_DPSM_ENABLE | (data->blksz << 4);

Expand All @@ -376,8 +391,8 @@ msmsdcc_start_data(struct msmsdcc_host *host, struct mmc_data *data)
if (data->flags & MMC_DATA_READ)
datactrl |= MCI_DPSM_DIRECTION;

writel(pio_irqmask, base + MMCIMASK1);
writel(datactrl, base + MMCIDATACTRL);
msmsdcc_writel(host, pio_irqmask, MMCIMASK1);
msmsdcc_writel(host, datactrl, MMCIDATACTRL);

if (datactrl & MCI_DPSM_DMAENABLE) {
host->dma.busy = 1;
Expand All @@ -398,12 +413,8 @@ snoop_cccr_abort(struct mmc_command *cmd)
static void
msmsdcc_start_command(struct msmsdcc_host *host, struct mmc_command *cmd, u32 c)
{
void __iomem *base = host->base;

if (readl(base + MMCICOMMAND) & MCI_CPSM_ENABLE) {
writel(0, base + MMCICOMMAND);
udelay(2 + ((5 * 1000000) / host->clk_rate));
}
if (msmsdcc_readl(host, MMCICOMMAND) & MCI_CPSM_ENABLE)
msmsdcc_writel(host, 0, MMCICOMMAND);

c |= cmd->opcode | MCI_CPSM_ENABLE;

Expand All @@ -428,8 +439,8 @@ msmsdcc_start_command(struct msmsdcc_host *host, struct mmc_command *cmd, u32 c)

host->stats.cmds++;

writel(cmd->arg, base + MMCIARGUMENT);
writel(c, base + MMCICOMMAND);
msmsdcc_writel(host, cmd->arg, MMCIARGUMENT);
msmsdcc_writel(host, c, MMCICOMMAND);
}

static void
Expand Down Expand Up @@ -463,13 +474,11 @@ msmsdcc_data_err(struct msmsdcc_host *host, struct mmc_data *data,
static int
msmsdcc_pio_read(struct msmsdcc_host *host, char *buffer, unsigned int remain)
{
void __iomem *base = host->base;
uint32_t *ptr = (uint32_t *) buffer;
int count = 0;

while (readl(base + MMCISTATUS) & MCI_RXDATAAVLBL) {

*ptr = readl(base + MMCIFIFO + (count % MCI_FIFOSIZE));
while (msmsdcc_readl(host, MMCISTATUS) & MCI_RXDATAAVLBL) {
*ptr = msmsdcc_readl(host, MMCIFIFO + (count % MCI_FIFOSIZE));
ptr++;
count += sizeof(uint32_t);

Expand Down Expand Up @@ -501,7 +510,7 @@ msmsdcc_pio_write(struct msmsdcc_host *host, char *buffer,
if (remain == 0)
break;

status = readl(base + MMCISTATUS);
status = msmsdcc_readl(host, MMCISTATUS);
} while (status & MCI_TXFIFOHALFEMPTY);

return ptr - buffer;
Expand All @@ -511,7 +520,7 @@ static int
msmsdcc_spin_on_status(struct msmsdcc_host *host, uint32_t mask, int maxspin)
{
while (maxspin) {
if ((readl(host->base + MMCISTATUS) & mask))
if ((msmsdcc_readl(host, MMCISTATUS) & mask))
return 0;
udelay(1);
--maxspin;
Expand All @@ -523,10 +532,9 @@ static int
msmsdcc_pio_irq(int irq, void *dev_id)
{
struct msmsdcc_host *host = dev_id;
void __iomem *base = host->base;
uint32_t status;

status = readl(base + MMCISTATUS);
status = msmsdcc_readl(host, MMCISTATUS);

do {
unsigned long flags;
Expand Down Expand Up @@ -581,28 +589,27 @@ msmsdcc_pio_irq(int irq, void *dev_id)
host->pio.sg_off = 0;
}

status = readl(base + MMCISTATUS);
status = msmsdcc_readl(host, MMCISTATUS);
} while (1);

if (status & MCI_RXACTIVE && host->curr.xfer_remain < MCI_FIFOSIZE)
writel(MCI_RXDATAAVLBLMASK, base + MMCIMASK1);
msmsdcc_writel(host, MCI_RXDATAAVLBLMASK, MMCIMASK1);

if (!host->curr.xfer_remain)
writel(0, base + MMCIMASK1);
msmsdcc_writel(host, 0, MMCIMASK1);

return IRQ_HANDLED;
}

static void msmsdcc_do_cmdirq(struct msmsdcc_host *host, uint32_t status)
{
struct mmc_command *cmd = host->curr.cmd;
void __iomem *base = host->base;

host->curr.cmd = NULL;
cmd->resp[0] = readl(base + MMCIRESPONSE0);
cmd->resp[1] = readl(base + MMCIRESPONSE1);
cmd->resp[2] = readl(base + MMCIRESPONSE2);
cmd->resp[3] = readl(base + MMCIRESPONSE3);
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);

del_timer(&host->command_timer);
if (status & MCI_CMDTIMEOUT) {
Expand Down Expand Up @@ -699,10 +706,11 @@ msmsdcc_irq(int irq, void *dev_id)
spin_lock(&host->lock);

do {
status = readl(base + MMCISTATUS);

status &= (readl(base + MMCIMASK0) | MCI_DATABLOCKENDMASK);
writel(status, base + MMCICLEAR);
struct mmc_data *data;
status = msmsdcc_readl(host, MMCISTATUS);
status &= (msmsdcc_readl(host, MMCIMASK0) |
MCI_DATABLOCKENDMASK);
msmsdcc_writel(host, status, MMCICLEAR);

if (status & MCI_SDIOINTR)
status &= ~MCI_SDIOINTR;
Expand Down Expand Up @@ -774,10 +782,11 @@ msmsdcc_request(struct mmc_host *mmc, struct mmc_request *mrq)
if (host->cmdpoll && !msmsdcc_spin_on_status(host,
MCI_CMDRESPEND|MCI_CMDCRCFAIL|MCI_CMDTIMEOUT,
CMD_SPINMAX)) {
uint32_t status = readl(host->base + MMCISTATUS);
uint32_t status = msmsdcc_readl(host, MMCISTATUS);
msmsdcc_do_cmdirq(host, status);
writel(MCI_CMDRESPEND | MCI_CMDCRCFAIL | MCI_CMDTIMEOUT,
host->base + MMCICLEAR);
msmsdcc_writel(host,
MCI_CMDRESPEND | MCI_CMDCRCFAIL | MCI_CMDTIMEOUT,
MMCICLEAR);
host->stats.cmdpoll_hits++;
} else {
host->stats.cmdpoll_misses++;
Expand Down Expand Up @@ -836,11 +845,11 @@ msmsdcc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
if (ios->bus_mode == MMC_BUSMODE_OPENDRAIN)
pwr |= MCI_OD;

writel(clk, host->base + MMCICLOCK);
msmsdcc_writel(host, clk, MMCICLOCK);

if (host->pwr != pwr) {
host->pwr = pwr;
writel(pwr, host->base + MMCIPOWER);
msmsdcc_writel(host, pwr, MMCIPOWER);
}
if (host->clks_on)
msmsdcc_enable_clocks(host, 0);
Expand All @@ -855,13 +864,13 @@ static void msmsdcc_enable_sdio_irq(struct mmc_host *mmc, int enable)

spin_lock_irqsave(&host->lock, flags);
if (msmsdcc_sdioirq == 1) {
status = readl(host->base + MMCIMASK0);
status = msmsdcc_readl(host, MMCIMASK0);
if (enable)
status |= MCI_SDIOINTOPERMASK;
else
status &= ~MCI_SDIOINTOPERMASK;
host->saved_irq0mask = status;
writel(status, host->base + MMCIMASK0);
msmsdcc_writel(host, status, MMCIMASK0);
}
spin_unlock_irqrestore(&host->lock, flags);
}
Expand Down Expand Up @@ -950,19 +959,16 @@ msmsdcc_command_expired(unsigned long _data)
mrq = host->curr.mrq;

if (!mrq) {
pr_info("%s: Command expiry misfire\n",
mmc_hostname(host->mmc));
spin_unlock_irqrestore(&host->lock, flags);
return;
}

pr_err("%s: Command timeout (%p %p %p %p)\n",
mmc_hostname(host->mmc), mrq, mrq->cmd,
mrq->data, host->dma.sg);
pr_err("%s: Controller lockup detected\n",
mmc_hostname(host->mmc));
mrq->cmd->error = -ETIMEDOUT;
msmsdcc_stop_data(host);

writel(0, host->base + MMCICOMMAND);
msmsdcc_writel(host, 0, MMCICOMMAND);

host->curr.mrq = NULL;
host->curr.cmd = NULL;
Expand Down Expand Up @@ -1143,10 +1149,10 @@ msmsdcc_probe(struct platform_device *pdev)
mmc->max_req_size = 33554432; /* MCI_DATA_LENGTH is 25 bits */
mmc->max_seg_size = mmc->max_req_size;

writel(0, host->base + MMCIMASK0);
writel(0x5e007ff, host->base + MMCICLEAR); /* Add: 1 << 25 */
msmsdcc_writel(host, 0, MMCIMASK0);
msmsdcc_writel(host, 0x5e007ff, MMCICLEAR);

writel(MCI_IRQENABLE, host->base + MMCIMASK0);
msmsdcc_writel(host, MCI_IRQENABLE, MMCIMASK0);
host->saved_irq0mask = MCI_IRQENABLE;

/*
Expand Down Expand Up @@ -1269,7 +1275,7 @@ msmsdcc_suspend(struct platform_device *dev, pm_message_t state)
if (mmc->card && mmc->card->type != MMC_TYPE_SDIO)
rc = mmc_suspend_host(mmc, state);
if (!rc) {
writel(0, host->base + MMCIMASK0);
msmsdcc_writel(host, 0, MMCIMASK0);

if (host->clks_on)
msmsdcc_enable_clocks(host, 0);
Expand All @@ -1292,7 +1298,7 @@ msmsdcc_resume(struct platform_device *dev)
if (!host->clks_on)
msmsdcc_enable_clocks(host, 1);

writel(host->saved_irq0mask, host->base + MMCIMASK0);
msmsdcc_writel(host, host->saved_irq0mask, MMCIMASK0);

spin_unlock_irqrestore(&host->lock, flags);

Expand Down

0 comments on commit 8b1c2ba

Please sign in to comment.