diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2010-05-21 14:38:35 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2010-05-21 14:38:35 -0700 |
commit | da7806f9b0579a1150f01ade3b562e543ddcbf2c (patch) | |
tree | 185b67eb27abfa5ea65f0365b1f95b7f9d3abd9d /drivers/mmc | |
parent | d515e86e639890b33a09390d062b0831664f04a2 (diff) | |
parent | 1cd2296909e77702c68021ede9d87a1d967a6a99 (diff) | |
download | linux-da7806f9b0579a1150f01ade3b562e543ddcbf2c.tar.bz2 |
Merge branch 'msm-mmc_sdcc' of git://codeaurora.org/quic/kernel/dwalker/linux-msm
* 'msm-mmc_sdcc' of git://codeaurora.org/quic/kernel/dwalker/linux-msm:
drivers: mmc: msm_sdcc: Add EMBEDDED_SDIO support
mmc: msm_sdcc: Fix issue where clocks could be disabled mid transaction
mmc: msm_sdcc: Fix the dma exec function to use the proper delays
mmc: msm_sdcc: Don't set host->curr.mrq until after we're sure the busclk timer won't fire
mmc: msm_sdcc: Enable busclk idle timer for power savings
mmc: msm_sdcc: Don't disable interrupts while suspending
mmc: msm_sdcc: Fix issue where we might not end a sucessfull request
mmc: msm_sdcc: Featurize busclock power save and disable it by default
mmc: msm_sdcc: Fix bug where busclk expiry timer was not properly disabled
mmc: msm_sdcc: Reduce command timeouts and improve reliability.
mmc: msm_sdcc: Schedule clock disable after probe
mmc: msm_sdcc: Wrap readl/writel calls with appropriate clk delays
mmc: msm_sdcc: Driver clocking/irq improvements
msm: Add 'execute' datamover callback
mmc: msm_sdcc: Snoop SDIO_CCCR_ABORT register
mmc: msm_sdcc: Clean up clock management and add a 10us delay after enabling clocks
Diffstat (limited to 'drivers/mmc')
-rw-r--r-- | drivers/mmc/host/msm_sdcc.c | 472 | ||||
-rw-r--r-- | drivers/mmc/host/msm_sdcc.h | 15 |
2 files changed, 301 insertions, 186 deletions
diff --git a/drivers/mmc/host/msm_sdcc.c b/drivers/mmc/host/msm_sdcc.c index 04ae884383f6..61f1d27fed3f 100644 --- a/drivers/mmc/host/msm_sdcc.c +++ b/drivers/mmc/host/msm_sdcc.c @@ -3,6 +3,7 @@ * * Copyright (C) 2007 Google Inc, * Copyright (C) 2003 Deep Blue Solutions, Ltd, All Rights Reserved. + * Copyright (C) 2009, Code Aurora Forum. All Rights Reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -26,6 +27,7 @@ #include <linux/log2.h> #include <linux/mmc/host.h> #include <linux/mmc/card.h> +#include <linux/mmc/sdio.h> #include <linux/clk.h> #include <linux/scatterlist.h> #include <linux/platform_device.h> @@ -47,6 +49,8 @@ #define DRIVER_NAME "msm-sdcc" +#define BUSCLK_PWRSAVE 1 +#define BUSCLK_TIMEOUT (HZ) static unsigned int msmsdcc_fmin = 144000; static unsigned int msmsdcc_fmax = 50000000; static unsigned int msmsdcc_4bit = 1; @@ -57,6 +61,67 @@ static unsigned int msmsdcc_sdioirq; #define PIO_SPINMAX 30 #define CMD_SPINMAX 20 + +static inline void +msmsdcc_disable_clocks(struct msmsdcc_host *host, int deferr) +{ + WARN_ON(!host->clks_on); + + BUG_ON(host->curr.mrq); + + if (deferr) { + mod_timer(&host->busclk_timer, jiffies + BUSCLK_TIMEOUT); + } else { + del_timer_sync(&host->busclk_timer); + /* Need to check clks_on again in case the busclk + * timer fired + */ + if (host->clks_on) { + clk_disable(host->clk); + clk_disable(host->pclk); + host->clks_on = 0; + } + } +} + +static inline int +msmsdcc_enable_clocks(struct msmsdcc_host *host) +{ + int rc; + + del_timer_sync(&host->busclk_timer); + + if (!host->clks_on) { + 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; + } + 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, u32 c); @@ -64,8 +129,6 @@ 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); - BUG_ON(host->curr.data); host->curr.mrq = NULL; @@ -76,6 +139,9 @@ msmsdcc_request_end(struct msmsdcc_host *host, struct mmc_request *mrq) if (mrq->cmd->error == -ETIMEDOUT) mdelay(5); +#if BUSCLK_PWRSAVE + msmsdcc_disable_clocks(host, 1); +#endif /* * Need to drop the host lock here; mmc_request_done may call * back into the driver... @@ -88,7 +154,6 @@ 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); host->curr.data = NULL; host->curr.got_dataend = host->curr.got_datablkend = 0; } @@ -109,6 +174,31 @@ uint32_t msmsdcc_fifo_addr(struct msmsdcc_host *host) return 0; } +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; + + msmsdcc_writel(host, host->cmd_timeout, MMCIDATATIMER); + msmsdcc_writel(host, (unsigned int)host->curr.xfer_size, + MMCIDATALENGTH); + msmsdcc_writel(host, host->cmd_pio_irqmask, MMCIMASK1); + msmsdcc_writel(host, host->cmd_datactrl, MMCIDATACTRL); + + if (host->cmd_cmd) { + msmsdcc_start_command_exec(host, + (u32) host->cmd_cmd->arg, + (u32) host->cmd_c); + } + host->dma.active = 1; +} + static void msmsdcc_dma_complete_func(struct msm_dmov_cmd *cmd, unsigned int result, @@ -121,8 +211,11 @@ msmsdcc_dma_complete_func(struct msm_dmov_cmd *cmd, struct mmc_request *mrq; spin_lock_irqsave(&host->lock, flags); + host->dma.active = 0; + mrq = host->curr.mrq; BUG_ON(!mrq); + WARN_ON(!mrq->data); if (!(result & DMOV_RSLT_VALID)) { pr_err("msmsdcc: Invalid DataMover result\n"); @@ -146,7 +239,6 @@ msmsdcc_dma_complete_func(struct msm_dmov_cmd *cmd, if (!mrq->data->error) mrq->data->error = -EIO; } - host->dma.busy = 0; dma_unmap_sg(mmc_dev(host->mmc), host->dma.sg, host->dma.num_ents, host->dma.dir); @@ -159,6 +251,7 @@ msmsdcc_dma_complete_func(struct msm_dmov_cmd *cmd, } host->dma.sg = NULL; + host->dma.busy = 0; if ((host->curr.got_dataend && host->curr.got_datablkend) || mrq->data->error) { @@ -172,12 +265,14 @@ 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); host->curr.mrq = NULL; host->curr.cmd = NULL; mrq->data->bytes_xfered = host->curr.data_xfered; spin_unlock_irqrestore(&host->lock, flags); +#if BUSCLK_PWRSAVE + msmsdcc_disable_clocks(host, 1); +#endif mmc_request_done(host->mmc, mrq); return; } else @@ -218,6 +313,8 @@ static int msmsdcc_config_dma(struct msmsdcc_host *host, struct mmc_data *data) host->dma.sg = data->sg; host->dma.num_ents = data->sg_len; + BUG_ON(host->dma.num_ents > NR_SG); /* Prevent memory corruption */ + nc = host->dma.nc; switch (host->pdev_id) { @@ -246,22 +343,15 @@ static int msmsdcc_config_dma(struct msmsdcc_host *host, struct mmc_data *data) host->curr.user_pages = 0; - n = dma_map_sg(mmc_dev(host->mmc), host->dma.sg, - host->dma.num_ents, host->dma.dir); - - if (n != host->dma.num_ents) { - pr_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; - } - box = &nc->cmd[0]; for (i = 0; i < host->dma.num_ents; i++) { box->cmd = CMD_MODE_BOX; - if (i == (host->dma.num_ents - 1)) + /* 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)) box->cmd |= CMD_LC; rows = (sg_dma_len(sg) % MCI_FIFOSIZE) ? (sg_dma_len(sg) / MCI_FIFOSIZE) + 1 : @@ -300,15 +390,70 @@ static int msmsdcc_config_dma(struct msmsdcc_host *host, struct mmc_data *data) DMOV_CMD_ADDR(host->dma.cmdptr_busaddr); host->dma.hdr.complete_func = msmsdcc_dma_complete_func; + 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; return 0; } static void -msmsdcc_start_data(struct msmsdcc_host *host, struct mmc_data *data) +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) { unsigned int datactrl, timeout; unsigned long long clks; - void __iomem *base = host->base; unsigned int pio_irqmask = 0; host->curr.data = data; @@ -320,13 +465,6 @@ msmsdcc_start_data(struct msmsdcc_host *host, struct mmc_data *data) memset(&host->pio, 0, sizeof(host->pio)); - 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); - - writel(host->curr.xfer_size, base + MMCIDATALENGTH); - datactrl = MCI_DPSM_ENABLE | (data->blksz << 4); if (!msmsdcc_config_dma(host, data)) @@ -347,47 +485,51 @@ 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); + clks = (unsigned long long)data->timeout_ns * host->clk_rate; + do_div(clks, NSEC_PER_SEC); + timeout = data->timeout_clks + (unsigned int)clks*2 ; if (datactrl & MCI_DPSM_DMAENABLE) { + /* 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; host->dma.busy = 1; + + if (cmd) { + msmsdcc_start_command_deferred(host, cmd, &c); + host->cmd_c = c; + } msm_dmov_enqueue_cmd(host->dma.channel, &host->dma.hdr); + } else { + msmsdcc_writel(host, timeout, MMCIDATATIMER); + + 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); + } } } 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)); - } - - 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 (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; - host->curr.cmd = cmd; - host->stats.cmds++; - writel(cmd->arg, base + MMCIARGUMENT); - writel(c, base + MMCICOMMAND); + msmsdcc_start_command_deferred(host, cmd, &c); + msmsdcc_start_command_exec(host, cmd->arg, c); } static void @@ -421,13 +563,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); @@ -459,7 +599,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; @@ -469,7 +609,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; @@ -477,14 +617,13 @@ msmsdcc_spin_on_status(struct msmsdcc_host *host, uint32_t mask, int maxspin) return -ETIMEDOUT; } -static int +static irqreturn_t 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; @@ -539,14 +678,14 @@ 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; } @@ -554,15 +693,13 @@ msmsdcc_pio_irq(int irq, void *dev_id) 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) { cmd->error = -ETIMEDOUT; } else if (status & MCI_CMDCRCFAIL && @@ -580,8 +717,10 @@ static void msmsdcc_do_cmdirq(struct msmsdcc_host *host, uint32_t status) msmsdcc_request_end(host, cmd->mrq); } else /* host->data == NULL */ msmsdcc_request_end(host, cmd->mrq); - } else if (!(cmd->data->flags & MMC_DATA_READ)) - msmsdcc_start_data(host, cmd->data); + } else if (cmd->data) + if (!(cmd->data->flags & MMC_DATA_READ)) + msmsdcc_start_data(host, cmd->data, + NULL, 0); } static void @@ -590,6 +729,11 @@ msmsdcc_handle_irq_data(struct msmsdcc_host *host, u32 status, { struct mmc_data *data = host->curr.data; + if (status & (MCI_CMDSENT | MCI_CMDRESPEND | MCI_CMDCRCFAIL | + MCI_CMDTIMEOUT) && host->curr.cmd) { + msmsdcc_do_cmdirq(host, status); + } + if (!data) return; @@ -602,7 +746,8 @@ msmsdcc_handle_irq_data(struct msmsdcc_host *host, u32 status, msm_dmov_stop_cmd(host->dma.channel, &host->dma.hdr, 0); else { - msmsdcc_stop_data(host); + if (host->curr.data) + msmsdcc_stop_data(host); if (!data->stop) msmsdcc_request_end(host, data->mrq); else @@ -657,17 +802,18 @@ msmsdcc_irq(int irq, void *dev_id) spin_lock(&host->lock); do { - status = readl(base + MMCISTATUS); + status = msmsdcc_readl(host, MMCISTATUS); + status &= (msmsdcc_readl(host, MMCIMASK0) | + MCI_DATABLOCKENDMASK); + msmsdcc_writel(host, status, MMCICLEAR); - status &= (readl(base + MMCIMASK0) | MCI_DATABLOCKENDMASK); - writel(status, base + MMCICLEAR); + if (status & MCI_SDIOINTR) + status &= ~MCI_SDIOINTR; - msmsdcc_handle_irq_data(host, status, base); + if (!status) + break; - if (status & (MCI_CMDSENT | MCI_CMDRESPEND | MCI_CMDCRCFAIL | - MCI_CMDTIMEOUT) && host->curr.cmd) { - msmsdcc_do_cmdirq(host, status); - } + msmsdcc_handle_irq_data(host, status, base); if (status & MCI_SDIOINTOPER) { cardint = 1; @@ -714,24 +860,27 @@ msmsdcc_request(struct mmc_host *mmc, struct mmc_request *mrq) return; } + msmsdcc_enable_clocks(host); + host->curr.mrq = mrq; if (mrq->data && mrq->data->flags & MMC_DATA_READ) - msmsdcc_start_data(host, mrq->data); - - msmsdcc_start_command(host, mrq->cmd, 0); + /* 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); 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++; - mod_timer(&host->command_timer, jiffies + HZ); } spin_unlock_irqrestore(&host->lock, flags); } @@ -742,14 +891,13 @@ 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; + unsigned long flags; - if (ios->clock) { + spin_lock_irqsave(&host->lock, flags); - if (!host->clks_on) { - clk_enable(host->pclk); - clk_enable(host->clk); - host->clks_on = 1; - } + msmsdcc_enable_clocks(host); + + if (ios->clock) { if (ios->clock != host->clk_rate) { rc = clk_set_rate(host->clk, ios->clock); if (rc < 0) @@ -787,18 +935,16 @@ 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); - } - - if (!(clk & MCI_CLK_ENABLE) && host->clks_on) { - clk_disable(host->clk); - clk_disable(host->pclk); - host->clks_on = 0; + msmsdcc_writel(host, pwr, MMCIPOWER); } +#if BUSCLK_PWRSAVE + msmsdcc_disable_clocks(host, 1); +#endif + spin_unlock_irqrestore(&host->lock, flags); } static void msmsdcc_enable_sdio_irq(struct mmc_host *mmc, int enable) @@ -809,13 +955,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); } @@ -875,42 +1021,13 @@ msmsdcc_status_notify_cb(int card_present, void *dev_id) msmsdcc_check_status((unsigned long) host); } -/* - * called when a command expires. - * Dump some debugging, and then error - * out the transaction. - */ static void -msmsdcc_command_expired(unsigned long _data) +msmsdcc_busclk_expired(unsigned long _data) { struct msmsdcc_host *host = (struct msmsdcc_host *) _data; - struct mmc_request *mrq; - unsigned long flags; - - spin_lock_irqsave(&host->lock, flags); - 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); - - mrq->cmd->error = -ETIMEDOUT; - msmsdcc_stop_data(host); - writel(0, host->base + MMCICOMMAND); - - host->curr.mrq = NULL; - host->curr.cmd = NULL; - - spin_unlock_irqrestore(&host->lock, flags); - mmc_request_done(host->mmc, mrq); + if (host->clks_on) + msmsdcc_disable_clocks(host, 0); } static int @@ -1012,6 +1129,7 @@ msmsdcc_probe(struct platform_device *pdev) host->pdev_id = pdev->id; host->plat = plat; host->mmc = mmc; + host->curr.cmd = NULL; host->cmdpoll = 1; @@ -1027,36 +1145,35 @@ msmsdcc_probe(struct platform_device *pdev) host->dmares = dmares; spin_lock_init(&host->lock); +#ifdef CONFIG_MMC_EMBEDDED_SDIO + if (plat->embedded_sdio) + mmc_set_embedded_sdio_data(mmc, + &plat->embedded_sdio->cis, + &plat->embedded_sdio->cccr, + plat->embedded_sdio->funcs, + plat->embedded_sdio->num_funcs); +#endif + /* * Setup DMA */ msmsdcc_init_dma(host); - /* - * Setup main peripheral bus clock - */ + /* Get our clocks */ host->pclk = clk_get(&pdev->dev, "sdc_pclk"); if (IS_ERR(host->pclk)) { ret = PTR_ERR(host->pclk); goto host_free; } - ret = clk_enable(host->pclk); - if (ret) - goto pclk_put; - - host->pclk_rate = clk_get_rate(host->pclk); - - /* - * Setup SDC MMC clock - */ host->clk = clk_get(&pdev->dev, "sdc_clk"); if (IS_ERR(host->clk)) { ret = PTR_ERR(host->clk); - goto pclk_disable; + goto pclk_put; } - ret = clk_enable(host->clk); + /* Enable clocks */ + ret = msmsdcc_enable_clocks(host); if (ret) goto clk_put; @@ -1066,10 +1183,9 @@ msmsdcc_probe(struct platform_device *pdev) goto clk_disable; } + host->pclk_rate = clk_get_rate(host->pclk); host->clk_rate = clk_get_rate(host->clk); - host->clks_on = 1; - /* * Setup MMC host structure */ @@ -1092,10 +1208,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; /* @@ -1137,13 +1253,9 @@ msmsdcc_probe(struct platform_device *pdev) host->eject = !host->oldstat; } - /* - * Setup a command timer. We currently need this due to - * some 'strange' timeout / error handling situations. - */ - init_timer(&host->command_timer); - host->command_timer.data = (unsigned long) host; - host->command_timer.function = msmsdcc_command_expired; + init_timer(&host->busclk_timer); + host->busclk_timer.data = (unsigned long) host; + host->busclk_timer.function = msmsdcc_busclk_expired; ret = request_irq(cmd_irqres->start, msmsdcc_irq, IRQF_SHARED, DRIVER_NAME " (cmd)", host); @@ -1181,6 +1293,9 @@ msmsdcc_probe(struct platform_device *pdev) if (host->timer.function) pr_info("%s: Polling status mode enabled\n", mmc_hostname(mmc)); +#if BUSCLK_PWRSAVE + msmsdcc_disable_clocks(host, 1); +#endif return 0; cmd_irq_free: free_irq(cmd_irqres->start, host); @@ -1188,11 +1303,9 @@ msmsdcc_probe(struct platform_device *pdev) if (host->stat_irq) free_irq(host->stat_irq, host); clk_disable: - clk_disable(host->clk); + msmsdcc_disable_clocks(host, 0); clk_put: clk_put(host->clk); - pclk_disable: - clk_disable(host->pclk); pclk_put: clk_put(host->pclk); host_free: @@ -1215,15 +1328,10 @@ 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); - - if (host->clks_on) { - clk_disable(host->clk); - clk_disable(host->pclk); - host->clks_on = 0; - } - } + if (!rc) + msmsdcc_writel(host, 0, MMCIMASK0); + if (host->clks_on) + msmsdcc_disable_clocks(host, 0); } return rc; } @@ -1232,27 +1340,21 @@ static int msmsdcc_resume(struct platform_device *dev) { struct mmc_host *mmc = mmc_get_drvdata(dev); - unsigned long flags; if (mmc) { struct msmsdcc_host *host = mmc_priv(mmc); - spin_lock_irqsave(&host->lock, flags); + msmsdcc_enable_clocks(host); - if (!host->clks_on) { - clk_enable(host->pclk); - clk_enable(host->clk); - host->clks_on = 1; - } - - writel(host->saved_irq0mask, host->base + MMCIMASK0); - - spin_unlock_irqrestore(&host->lock, flags); + msmsdcc_writel(host, host->saved_irq0mask, MMCIMASK0); if (mmc->card && mmc->card->type != MMC_TYPE_SDIO) mmc_resume_host(mmc); if (host->stat_irq) enable_irq(host->stat_irq); +#if BUSCLK_PWRSAVE + msmsdcc_disable_clocks(host, 1); +#endif } return 0; } diff --git a/drivers/mmc/host/msm_sdcc.h b/drivers/mmc/host/msm_sdcc.h index 8c8448469811..da0039c9285e 100644 --- a/drivers/mmc/host/msm_sdcc.h +++ b/drivers/mmc/host/msm_sdcc.h @@ -171,6 +171,7 @@ struct msmsdcc_dma_data { int channel; struct msmsdcc_host *host; int busy; /* Set if DM is busy */ + int active; }; struct msmsdcc_pio_data { @@ -213,7 +214,7 @@ struct msmsdcc_host { struct clk *clk; /* main MMC bus clock */ struct clk *pclk; /* SDCC peripheral bus clock */ unsigned int clks_on; /* set if clocks are enabled */ - struct timer_list command_timer; + struct timer_list busclk_timer; unsigned int eject; /* eject state */ @@ -233,6 +234,18 @@ struct msmsdcc_host { struct msmsdcc_pio_data pio; int cmdpoll; struct msmsdcc_stats stats; + +#ifdef CONFIG_MMC_MSM7X00A_RESUME_IN_WQ + struct work_struct resume_task; +#endif + + /* Command parameters */ + unsigned int cmd_timeout; + unsigned int cmd_pio_irqmask; + unsigned int cmd_datactrl; + struct mmc_command *cmd_cmd; + u32 cmd_c; + }; #endif |