From 4adbbcc7b6cfb3dcf5ab49b06edb7752391b0e80 Mon Sep 17 00:00:00 2001 From: San Mehat Date: Sun, 8 Nov 2009 13:00:37 -0800 Subject: mmc: msm_sdcc: Clean up clock management and add a 10us delay after enabling clocks It appears that in some cases there may be a delay on the ARM9 in enabling our clock. As a result, we may put the controller into a bad state. Delay 10us after enabling clocks to let the peripheral settle. Note - this is all imperical. Also ensure set_ios() callback grabs the host lock. Signed-off-by: San Mehat Signed-off-by: Daniel Walker --- drivers/mmc/host/msm_sdcc.c | 80 ++++++++++++++++++++++----------------------- 1 file changed, 40 insertions(+), 40 deletions(-) (limited to 'drivers/mmc') diff --git a/drivers/mmc/host/msm_sdcc.c b/drivers/mmc/host/msm_sdcc.c index 4c068e5..977932a 100644 --- a/drivers/mmc/host/msm_sdcc.c +++ b/drivers/mmc/host/msm_sdcc.c @@ -735,20 +735,42 @@ msmsdcc_request(struct mmc_host *mmc, struct mmc_request *mrq) spin_unlock_irqrestore(&host->lock, flags); } +static int inline +msmsdcc_enable_clocks(struct msmsdcc_host *host, int enable) +{ + int rc; + if (enable) { + rc = clk_enable(host->pclk); + if (rc) + return rc; + rc = clk_enable(host->clk); + if (rc) { + clk_disable(host->pclk); + return rc; + } + host->clks_on = 1; + udelay(10); + } else { + clk_disable(host->clk); + clk_disable(host->pclk); + host->clks_on = 0; + } + return 0; +} + 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; + unsigned long flags; + spin_lock_irqsave(&host->lock, flags); if (ios->clock) { - if (!host->clks_on) { - clk_enable(host->pclk); - clk_enable(host->clk); - host->clks_on = 1; - } + if (!host->clks_on) + msmsdcc_enable_clocks(host, 1); if (ios->clock != host->clk_rate) { rc = clk_set_rate(host->clk, ios->clock); if (rc < 0) @@ -793,11 +815,9 @@ msmsdcc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) 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; - } + if (!(clk & MCI_CLK_ENABLE) && host->clks_on) + msmsdcc_enable_clocks(host, 0); + spin_unlock_irqrestore(&host->lock, flags); } static void msmsdcc_enable_sdio_irq(struct mmc_host *mmc, int enable) @@ -899,7 +919,6 @@ msmsdcc_command_expired(unsigned long _data) 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); @@ -1031,31 +1050,21 @@ msmsdcc_probe(struct platform_device *pdev) */ 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, 1); if (ret) goto clk_put; @@ -1065,10 +1074,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 */ @@ -1187,11 +1195,9 @@ msmsdcc_probe(struct platform_device *pdev) if (host->stat_irq) free_irq(host->stat_irq, host); clk_disable: - clk_disable(host->clk); + msmsdcc_enable_clocks(host, 0); clk_put: clk_put(host->clk); - pclk_disable: - clk_disable(host->pclk); pclk_put: clk_put(host->pclk); host_free: @@ -1217,11 +1223,8 @@ msmsdcc_suspend(struct platform_device *dev, pm_message_t 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 (host->clks_on) + msmsdcc_enable_clocks(host, 0); } } return rc; @@ -1238,11 +1241,8 @@ msmsdcc_resume(struct platform_device *dev) spin_lock_irqsave(&host->lock, flags); - if (!host->clks_on) { - clk_enable(host->pclk); - clk_enable(host->clk); - host->clks_on = 1; - } + if (!host->clks_on) + msmsdcc_enable_clocks(host, 1); writel(host->saved_irq0mask, host->base + MMCIMASK0); -- cgit v1.1 From b3fa579118b239e218e690f5ef76870aff6fe738 Mon Sep 17 00:00:00 2001 From: San Mehat Date: Mon, 2 Nov 2009 18:46:09 -0800 Subject: mmc: msm_sdcc: Snoop SDIO_CCCR_ABORT register Signed-off-by: San Mehat Signed-off-by: Daniel Walker --- drivers/mmc/host/msm_sdcc.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) (limited to 'drivers/mmc') diff --git a/drivers/mmc/host/msm_sdcc.c b/drivers/mmc/host/msm_sdcc.c index 977932a..f4f7883 100644 --- a/drivers/mmc/host/msm_sdcc.c +++ b/drivers/mmc/host/msm_sdcc.c @@ -26,6 +26,7 @@ #include #include #include +#include #include #include #include @@ -355,6 +356,16 @@ msmsdcc_start_data(struct msmsdcc_host *host, struct mmc_data *data) } } +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_command(struct msmsdcc_host *host, struct mmc_command *cmd, u32 c) { @@ -381,6 +392,9 @@ msmsdcc_start_command(struct msmsdcc_host *host, struct mmc_command *cmd, u32 c) if (cmd == cmd->mrq->stop) c |= MCI_CSPM_MCIABORT; + if (snoop_cccr_abort(cmd)) + c |= MCI_CSPM_MCIABORT; + host->curr.cmd = cmd; host->stats.cmds++; -- cgit v1.1 From 5b00f40f90e7b17c11cf388680f43e8466b3666d Mon Sep 17 00:00:00 2001 From: San Mehat Date: Sat, 21 Nov 2009 09:22:14 -0800 Subject: msm: Add 'execute' datamover callback Based on a patch from Brent DeGraaf: "The datamover supports channels which can be shared amongst devices. As a result, the actual data transfer may occur some time after the request is queued up. Some devices such as mmc host controllers will timeout if a command is issued too far in advance of the actual transfer, so if dma to other devices on the same channel is already in progress or queued up, the added delay can cause pending transfers to fail before they start. This change extends the api to allow a user callback to be invoked just before the actual transfer takes place, thus allowing actions directly associated with the dma transfer, such as device commands, to be invoked with precise timing. Without this mechanism, there is no way for a driver to realize this timing. Also adds a user pointer to the command structure for use by the caller to reference information that may be needed by the callback routine for proper identification and processing associated with that specific request. This change is necessary to fix problems associated with excessive command timeouts and race conditions in the mmc driver." This patch also fixes all the callers of msm_dmov_enqueue_cmd() to ensure their callback function is NULL. Signed-off-by: San Mehat Cc: Brent DeGraaf Cc: Brian Swetland Signed-off-by: Daniel Walker --- drivers/mmc/host/msm_sdcc.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/mmc') diff --git a/drivers/mmc/host/msm_sdcc.c b/drivers/mmc/host/msm_sdcc.c index f4f7883..02bec7c 100644 --- a/drivers/mmc/host/msm_sdcc.c +++ b/drivers/mmc/host/msm_sdcc.c @@ -299,6 +299,7 @@ static int msmsdcc_config_dma(struct msmsdcc_host *host, struct mmc_data *data) host->dma.hdr.cmdptr = DMOV_CMD_PTR_LIST | DMOV_CMD_ADDR(host->dma.cmdptr_busaddr); host->dma.hdr.complete_func = msmsdcc_dma_complete_func; + host->dma.hdr.execute_func = NULL; return 0; } -- cgit v1.1 From 865c8064a2fb07100525097983966b8e789bde1a Mon Sep 17 00:00:00 2001 From: San Mehat Date: Fri, 13 Nov 2009 13:42:06 -0800 Subject: mmc: msm_sdcc: Driver clocking/irq improvements - Clocks are now disabled after 1 second of inactivity - Fixed issue which was causing us to loop through our ISR twice - Bump core clock enable delay to 30us Signed-off-by: San Mehat Signed-off-by: Daniel Walker --- drivers/mmc/host/msm_sdcc.c | 89 +++++++++++++++++++++++++++++++-------------- drivers/mmc/host/msm_sdcc.h | 2 + 2 files changed, 63 insertions(+), 28 deletions(-) (limited to 'drivers/mmc') diff --git a/drivers/mmc/host/msm_sdcc.c b/drivers/mmc/host/msm_sdcc.c index 02bec7c..b4b6372 100644 --- a/drivers/mmc/host/msm_sdcc.c +++ b/drivers/mmc/host/msm_sdcc.c @@ -57,6 +57,32 @@ static unsigned int msmsdcc_sdioirq; #define PIO_SPINMAX 30 #define CMD_SPINMAX 20 + +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); + if (rc) + return rc; + rc = clk_enable(host->clk); + if (rc) { + clk_disable(host->pclk); + return rc; + } + udelay(30); + host->clks_on = 1; + } else { + clk_disable(host->clk); + clk_disable(host->pclk); + host->clks_on = 0; + } + return 0; +} + + static void msmsdcc_start_command(struct msmsdcc_host *host, struct mmc_command *cmd, u32 c); @@ -76,6 +102,8 @@ msmsdcc_request_end(struct msmsdcc_host *host, struct mmc_request *mrq) if (mrq->cmd->error == -ETIMEDOUT) mdelay(5); + if (host->use_bustimer) + mod_timer(&host->busclk_timer, jiffies + HZ); /* * Need to drop the host lock here; mmc_request_done may call * back into the driver... @@ -676,6 +704,12 @@ msmsdcc_irq(int irq, void *dev_id) status &= (readl(base + MMCIMASK0) | MCI_DATABLOCKENDMASK); writel(status, base + MMCICLEAR); + if (status & MCI_SDIOINTR) + status &= ~MCI_SDIOINTR; + + if (!status) + break; + msmsdcc_handle_irq_data(host, status, base); if (status & (MCI_CMDSENT | MCI_CMDRESPEND | MCI_CMDCRCFAIL | @@ -729,6 +763,8 @@ msmsdcc_request(struct mmc_host *mmc, struct mmc_request *mrq) } host->curr.mrq = mrq; + if (!host->clks_on) + msmsdcc_enable_clocks(host, 1); if (mrq->data && mrq->data->flags & MMC_DATA_READ) msmsdcc_start_data(host, mrq->data); @@ -750,29 +786,6 @@ msmsdcc_request(struct mmc_host *mmc, struct mmc_request *mrq) spin_unlock_irqrestore(&host->lock, flags); } -static int inline -msmsdcc_enable_clocks(struct msmsdcc_host *host, int enable) -{ - int rc; - if (enable) { - rc = clk_enable(host->pclk); - if (rc) - return rc; - rc = clk_enable(host->clk); - if (rc) { - clk_disable(host->pclk); - return rc; - } - host->clks_on = 1; - udelay(10); - } else { - clk_disable(host->clk); - clk_disable(host->pclk); - host->clks_on = 0; - } - return 0; -} - static void msmsdcc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) { @@ -782,10 +795,10 @@ msmsdcc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) unsigned long flags; spin_lock_irqsave(&host->lock, flags); - if (ios->clock) { + if (!host->clks_on) + msmsdcc_enable_clocks(host, 1); - if (!host->clks_on) - msmsdcc_enable_clocks(host, 1); + if (ios->clock) { if (ios->clock != host->clk_rate) { rc = clk_set_rate(host->clk, ios->clock); if (rc < 0) @@ -829,8 +842,7 @@ msmsdcc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) host->pwr = pwr; writel(pwr, host->base + MMCIPOWER); } - - if (!(clk & MCI_CLK_ENABLE) && host->clks_on) + if (host->clks_on) msmsdcc_enable_clocks(host, 0); spin_unlock_irqrestore(&host->lock, flags); } @@ -909,6 +921,19 @@ msmsdcc_status_notify_cb(int card_present, void *dev_id) msmsdcc_check_status((unsigned long) host); } +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); + if (host->clks_on) + msmsdcc_enable_clocks(host, 0); + + spin_unlock_irqrestore(&host->lock, flags); +} + /* * called when a command expires. * Dump some debugging, and then error @@ -942,6 +967,8 @@ msmsdcc_command_expired(unsigned long _data) host->curr.mrq = NULL; host->curr.cmd = NULL; + if (host->clks_on) + msmsdcc_enable_clocks(host, 0); spin_unlock_irqrestore(&host->lock, flags); mmc_request_done(host->mmc, mrq); } @@ -1048,6 +1075,8 @@ msmsdcc_probe(struct platform_device *pdev) host->cmdpoll = 1; + host->use_bustimer = 1; + host->base = ioremap(memres->start, PAGE_SIZE); if (!host->base) { ret = -ENOMEM; @@ -1167,6 +1196,10 @@ msmsdcc_probe(struct platform_device *pdev) 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); if (ret) diff --git a/drivers/mmc/host/msm_sdcc.h b/drivers/mmc/host/msm_sdcc.h index 8c84484..6846bd7 100644 --- a/drivers/mmc/host/msm_sdcc.h +++ b/drivers/mmc/host/msm_sdcc.h @@ -214,6 +214,8 @@ struct msmsdcc_host { 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; + int use_bustimer; unsigned int eject; /* eject state */ -- cgit v1.1 From 8b1c2ba274c8416afb7eab3bd788f98a917efe06 Mon Sep 17 00:00:00 2001 From: San Mehat Date: Mon, 16 Nov 2009 10:17:30 -0800 Subject: mmc: msm_sdcc: Wrap readl/writel calls with appropriate clk delays 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 Signed-off-by: Daniel Walker --- drivers/mmc/host/msm_sdcc.c | 116 +++++++++++++++++++++++--------------------- 1 file changed, 61 insertions(+), 55 deletions(-) (limited to 'drivers/mmc') diff --git a/drivers/mmc/host/msm_sdcc.c b/drivers/mmc/host/msm_sdcc.c index b4b6372..3b096f6 100644 --- a/drivers/mmc/host/msm_sdcc.c +++ b/drivers/mmc/host/msm_sdcc.c @@ -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); @@ -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); @@ -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, @@ -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); @@ -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; } @@ -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; @@ -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; @@ -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); @@ -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; @@ -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; @@ -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 @@ -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); @@ -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; @@ -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; @@ -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; @@ -581,14 +589,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; } @@ -596,13 +604,12 @@ 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) { @@ -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; @@ -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++; @@ -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); @@ -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); } @@ -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; @@ -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; /* @@ -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); @@ -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); -- cgit v1.1 From 51905dcbcf1f72a17f491c64485d513986110a6f Mon Sep 17 00:00:00 2001 From: San Mehat Date: Mon, 16 Nov 2009 11:59:01 -0800 Subject: mmc: msm_sdcc: Schedule clock disable after probe Signed-off-by: San Mehat Signed-off-by: Daniel Walker --- drivers/mmc/host/msm_sdcc.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers/mmc') diff --git a/drivers/mmc/host/msm_sdcc.c b/drivers/mmc/host/msm_sdcc.c index 3b096f6..84b284e 100644 --- a/drivers/mmc/host/msm_sdcc.c +++ b/drivers/mmc/host/msm_sdcc.c @@ -1242,6 +1242,8 @@ msmsdcc_probe(struct platform_device *pdev) if (host->timer.function) pr_info("%s: Polling status mode enabled\n", mmc_hostname(mmc)); + if (host->use_bustimer) + mod_timer(&host->busclk_timer, jiffies + HZ); return 0; cmd_irq_free: free_irq(cmd_irqres->start, host); -- cgit v1.1 From 56a8b5b8ae81bd766e527a0e5274a087c3c1109d Mon Sep 17 00:00:00 2001 From: San Mehat Date: Sat, 21 Nov 2009 12:29:46 -0800 Subject: 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 Cc: Brian Swetland Change-Id: Ifbf17cfafb858106d73bf49af52b5161a265a484 Signed-off-by: San Mehat Signed-off-by: Daniel Walker --- drivers/mmc/host/msm_sdcc.c | 267 ++++++++++++++++++++++++-------------------- drivers/mmc/host/msm_sdcc.h | 14 ++- 2 files changed, 161 insertions(+), 120 deletions(-) (limited to 'drivers/mmc') diff --git a/drivers/mmc/host/msm_sdcc.c b/drivers/mmc/host/msm_sdcc.c index 84b284e..5248585 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 @@ -47,6 +48,7 @@ #define DRIVER_NAME "msm-sdcc" +#define BUSCLK_TIMEOUT (HZ * 5) static unsigned int msmsdcc_fmin = 144000; static unsigned int msmsdcc_fmax = 50000000; static unsigned int msmsdcc_4bit = 1; @@ -106,8 +108,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) { - msmsdcc_writel(host, 0, MMCICOMMAND); - BUG_ON(host->curr.data); host->curr.mrq = NULL; @@ -119,7 +119,7 @@ msmsdcc_request_end(struct msmsdcc_host *host, struct mmc_request *mrq) mdelay(5); if (host->use_bustimer) - mod_timer(&host->busclk_timer, jiffies + HZ); + mod_timer(&host->busclk_timer, jiffies + BUSCLK_TIMEOUT); /* * Need to drop the host lock here; mmc_request_done may call * back into the driver... @@ -132,7 +132,6 @@ msmsdcc_request_end(struct msmsdcc_host *host, struct mmc_request *mrq) static void msmsdcc_stop_data(struct msmsdcc_host *host) { - msmsdcc_writel(host, 0, MMCIDATACTRL); host->curr.data = NULL; host->curr.got_dataend = host->curr.got_datablkend = 0; } @@ -153,6 +152,29 @@ 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; + + 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; +} + static void msmsdcc_dma_complete_func(struct msm_dmov_cmd *cmd, unsigned int result, @@ -165,6 +187,8 @@ 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); @@ -190,7 +214,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); @@ -203,6 +226,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) { @@ -262,6 +286,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) { @@ -290,22 +316,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 : @@ -343,13 +362,68 @@ static int msmsdcc_config_dma(struct msmsdcc_host *host, struct mmc_data *data) host->dma.hdr.cmdptr = DMOV_CMD_PTR_LIST | DMOV_CMD_ADDR(host->dma.cmdptr_busaddr); host->dma.hdr.complete_func = msmsdcc_dma_complete_func; - host->dma.hdr.execute_func = NULL; + 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; @@ -364,13 +438,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; - msmsdcc_writel(host, timeout, MMCIDATATIMER); - - msmsdcc_writel(host, host->curr.xfer_size, MMCIDATALENGTH); - datactrl = MCI_DPSM_ENABLE | (data->blksz << 4); if (!msmsdcc_config_dma(host, data)) @@ -391,56 +458,51 @@ msmsdcc_start_data(struct msmsdcc_host *host, struct mmc_data *data) if (data->flags & MMC_DATA_READ) datactrl |= MCI_DPSM_DIRECTION; - msmsdcc_writel(host, pio_irqmask, MMCIMASK1); - msmsdcc_writel(host, datactrl, 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); -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; + 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) { - if (msmsdcc_readl(host, MMCICOMMAND) & MCI_CPSM_ENABLE) - msmsdcc_writel(host, 0, MMCICOMMAND); - - 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; - if (snoop_cccr_abort(cmd)) - c |= MCI_CSPM_MCIABORT; - - host->curr.cmd = cmd; - host->stats.cmds++; - msmsdcc_writel(host, cmd->arg, MMCIARGUMENT); - msmsdcc_writel(host, c, MMCICOMMAND); + msmsdcc_start_command_deferred(host, cmd, &c); + msmsdcc_start_command_exec(host, cmd->arg, c); } static void @@ -611,7 +673,6 @@ static void msmsdcc_do_cmdirq(struct msmsdcc_host *host, uint32_t status) 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 && @@ -629,16 +690,24 @@ 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 msmsdcc_handle_irq_data(struct msmsdcc_host *host, u32 status, void __iomem *base) { - struct mmc_data *data = host->curr.data; + struct mmc_data *data; + if (status & (MCI_CMDSENT | MCI_CMDRESPEND | MCI_CMDCRCFAIL | + MCI_CMDTIMEOUT) && host->curr.cmd) { + msmsdcc_do_cmdirq(host, status); + } + + data = host->curr.data; if (!data) return; @@ -720,11 +789,6 @@ msmsdcc_irq(int irq, void *dev_id) msmsdcc_handle_irq_data(host, status, base); - if (status & (MCI_CMDSENT | MCI_CMDRESPEND | MCI_CMDCRCFAIL | - MCI_CMDTIMEOUT) && host->curr.cmd) { - msmsdcc_do_cmdirq(host, status); - } - if (status & MCI_SDIOINTOPER) { cardint = 1; status &= ~MCI_SDIOINTOPER; @@ -775,9 +839,10 @@ msmsdcc_request(struct mmc_host *mmc, struct mmc_request *mrq) msmsdcc_enable_clocks(host, 1); 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, @@ -790,7 +855,6 @@ msmsdcc_request(struct mmc_host *mmc, struct mmc_request *mrq) host->stats.cmdpoll_hits++; } else { host->stats.cmdpoll_misses++; - mod_timer(&host->command_timer, jiffies + HZ); } spin_unlock_irqrestore(&host->lock, flags); } @@ -943,42 +1007,6 @@ msmsdcc_busclk_expired(unsigned long _data) spin_unlock_irqrestore(&host->lock, flags); } -/* - * called when a command expires. - * Dump some debugging, and then error - * out the transaction. - */ -static void -msmsdcc_command_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) { - spin_unlock_irqrestore(&host->lock, flags); - return; - } - - pr_err("%s: Controller lockup detected\n", - mmc_hostname(host->mmc)); - mrq->cmd->error = -ETIMEDOUT; - msmsdcc_stop_data(host); - - msmsdcc_writel(host, 0, MMCICOMMAND); - - host->curr.mrq = NULL; - host->curr.cmd = NULL; - - if (host->clks_on) - msmsdcc_enable_clocks(host, 0); - spin_unlock_irqrestore(&host->lock, flags); - mmc_request_done(host->mmc, mrq); -} - static int msmsdcc_init_dma(struct msmsdcc_host *host) { @@ -1078,6 +1106,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; @@ -1194,14 +1223,6 @@ 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; @@ -1243,7 +1264,7 @@ msmsdcc_probe(struct platform_device *pdev) pr_info("%s: Polling status mode enabled\n", mmc_hostname(mmc)); if (host->use_bustimer) - mod_timer(&host->busclk_timer, jiffies + HZ); + mod_timer(&host->busclk_timer, jiffies + BUSCLK_TIMEOUT); return 0; cmd_irq_free: free_irq(cmd_irqres->start, host); @@ -1267,10 +1288,14 @@ msmsdcc_suspend(struct platform_device *dev, pm_message_t state) { struct mmc_host *mmc = mmc_get_drvdata(dev); int rc = 0; + unsigned long flags; if (mmc) { struct msmsdcc_host *host = mmc_priv(mmc); + if (host->use_bustimer) + del_timer_sync(&host->busclk_timer); + spin_lock_irqsave(&host->lock, flags); if (host->stat_irq) disable_irq(host->stat_irq); @@ -1282,6 +1307,7 @@ msmsdcc_suspend(struct platform_device *dev, pm_message_t state) if (host->clks_on) msmsdcc_enable_clocks(host, 0); } + spin_unlock_irqrestore(&host->lock, flags); } return rc; } @@ -1300,6 +1326,9 @@ msmsdcc_resume(struct platform_device *dev) if (!host->clks_on) msmsdcc_enable_clocks(host, 1); + if (host->use_bustimer) + mod_timer(&host->busclk_timer, jiffies + BUSCLK_TIMEOUT); + msmsdcc_writel(host, host->saved_irq0mask, MMCIMASK0); spin_unlock_irqrestore(&host->lock, flags); diff --git a/drivers/mmc/host/msm_sdcc.h b/drivers/mmc/host/msm_sdcc.h index 6846bd7..361cb6e 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,6 @@ 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; int use_bustimer; @@ -235,6 +235,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 -- cgit v1.1 From c7fc9370df1433486dfa9460a833fae664e8be6c Mon Sep 17 00:00:00 2001 From: San Mehat Date: Sun, 22 Nov 2009 17:19:07 -0800 Subject: mmc: msm_sdcc: Fix bug where busclk expiry timer was not properly disabled Signed-off-by: San Mehat Signed-off-by: Daniel Walker --- drivers/mmc/host/msm_sdcc.c | 99 ++++++++++++++++++++++++--------------------- drivers/mmc/host/msm_sdcc.h | 1 - 2 files changed, 52 insertions(+), 48 deletions(-) (limited to 'drivers/mmc') diff --git a/drivers/mmc/host/msm_sdcc.c b/drivers/mmc/host/msm_sdcc.c index 5248585..591ef3c 100644 --- a/drivers/mmc/host/msm_sdcc.c +++ b/drivers/mmc/host/msm_sdcc.c @@ -48,7 +48,7 @@ #define DRIVER_NAME "msm-sdcc" -#define BUSCLK_TIMEOUT (HZ * 5) +#define BUSCLK_TIMEOUT (HZ) static unsigned int msmsdcc_fmin = 144000; static unsigned int msmsdcc_fmax = 50000000; static unsigned int msmsdcc_4bit = 1; @@ -60,29 +60,42 @@ static unsigned int msmsdcc_sdioirq; #define CMD_SPINMAX 20 -static inline int -msmsdcc_enable_clocks(struct msmsdcc_host *host, int enable) +static inline void +msmsdcc_disable_clocks(struct msmsdcc_host *host, int deferr) { - int rc; + WARN_ON(!host->clks_on); - WARN_ON(enable == host->clks_on); - if (enable) { - 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; + if (deferr) { + mod_timer(&host->busclk_timer, jiffies + BUSCLK_TIMEOUT); } else { + del_timer_sync(&host->busclk_timer); +// dev_info(mmc_dev(host->mmc), "Immediate clock shutdown\n"); clk_disable(host->clk); clk_disable(host->pclk); host->clks_on = 0; } +} + +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; return 0; } @@ -118,8 +131,7 @@ msmsdcc_request_end(struct msmsdcc_host *host, struct mmc_request *mrq) if (mrq->cmd->error == -ETIMEDOUT) mdelay(5); - if (host->use_bustimer) - mod_timer(&host->busclk_timer, jiffies + BUSCLK_TIMEOUT); + msmsdcc_disable_clocks(host, 1); /* * Need to drop the host lock here; mmc_request_done may call * back into the driver... @@ -240,12 +252,12 @@ 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) { - msmsdcc_writel(host, 0, MMCICOMMAND); host->curr.mrq = NULL; host->curr.cmd = NULL; mrq->data->bytes_xfered = host->curr.data_xfered; spin_unlock_irqrestore(&host->lock, flags); + msmsdcc_disable_clocks(host, 1); mmc_request_done(host->mmc, mrq); return; } else @@ -835,8 +847,14 @@ msmsdcc_request(struct mmc_host *mmc, struct mmc_request *mrq) } host->curr.mrq = mrq; + + /* Need to drop the host lock here in case + * the busclk wd fires + */ + spin_unlock_irqrestore(&host->lock, flags); if (!host->clks_on) - msmsdcc_enable_clocks(host, 1); + msmsdcc_enable_clocks(host); + spin_lock_irqsave(&host->lock, flags); if (mrq->data && mrq->data->flags & MMC_DATA_READ) /* Queue/read data, daisy-chain command when data starts */ @@ -867,9 +885,10 @@ msmsdcc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) int rc; unsigned long flags; - spin_lock_irqsave(&host->lock, flags); if (!host->clks_on) - msmsdcc_enable_clocks(host, 1); + msmsdcc_enable_clocks(host); + + spin_lock_irqsave(&host->lock, flags); if (ios->clock) { if (ios->clock != host->clk_rate) { @@ -915,8 +934,7 @@ msmsdcc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) host->pwr = pwr; msmsdcc_writel(host, pwr, MMCIPOWER); } - if (host->clks_on) - msmsdcc_enable_clocks(host, 0); + msmsdcc_disable_clocks(host, 1); spin_unlock_irqrestore(&host->lock, flags); } @@ -1001,9 +1019,9 @@ msmsdcc_busclk_expired(unsigned long _data) unsigned long flags; spin_lock_irqsave(&host->lock, flags); + dev_info(mmc_dev(host->mmc), "Bus clock timer expired\n"); if (host->clks_on) - msmsdcc_enable_clocks(host, 0); - + msmsdcc_disable_clocks(host, 0); spin_unlock_irqrestore(&host->lock, flags); } @@ -1110,8 +1128,6 @@ msmsdcc_probe(struct platform_device *pdev) host->cmdpoll = 1; - host->use_bustimer = 1; - host->base = ioremap(memres->start, PAGE_SIZE); if (!host->base) { ret = -ENOMEM; @@ -1143,7 +1159,7 @@ msmsdcc_probe(struct platform_device *pdev) } /* Enable clocks */ - ret = msmsdcc_enable_clocks(host, 1); + ret = msmsdcc_enable_clocks(host); if (ret) goto clk_put; @@ -1263,8 +1279,7 @@ msmsdcc_probe(struct platform_device *pdev) if (host->timer.function) pr_info("%s: Polling status mode enabled\n", mmc_hostname(mmc)); - if (host->use_bustimer) - mod_timer(&host->busclk_timer, jiffies + BUSCLK_TIMEOUT); + msmsdcc_disable_clocks(host, 1); return 0; cmd_irq_free: free_irq(cmd_irqres->start, host); @@ -1272,7 +1287,7 @@ msmsdcc_probe(struct platform_device *pdev) if (host->stat_irq) free_irq(host->stat_irq, host); clk_disable: - msmsdcc_enable_clocks(host, 0); + msmsdcc_disable_clocks(host, 0); clk_put: clk_put(host->clk); pclk_put: @@ -1293,8 +1308,6 @@ msmsdcc_suspend(struct platform_device *dev, pm_message_t state) if (mmc) { struct msmsdcc_host *host = mmc_priv(mmc); - if (host->use_bustimer) - del_timer_sync(&host->busclk_timer); spin_lock_irqsave(&host->lock, flags); if (host->stat_irq) disable_irq(host->stat_irq); @@ -1304,10 +1317,10 @@ msmsdcc_suspend(struct platform_device *dev, pm_message_t state) if (!rc) { msmsdcc_writel(host, 0, MMCIMASK0); - if (host->clks_on) - msmsdcc_enable_clocks(host, 0); } spin_unlock_irqrestore(&host->lock, flags); + if (host->clks_on) + msmsdcc_disable_clocks(host, 0); } return rc; } @@ -1316,27 +1329,19 @@ 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); - - if (!host->clks_on) - msmsdcc_enable_clocks(host, 1); - - if (host->use_bustimer) - mod_timer(&host->busclk_timer, jiffies + BUSCLK_TIMEOUT); + msmsdcc_enable_clocks(host); msmsdcc_writel(host, host->saved_irq0mask, MMCIMASK0); - spin_unlock_irqrestore(&host->lock, flags); - if (mmc->card && mmc->card->type != MMC_TYPE_SDIO) mmc_resume_host(mmc); if (host->stat_irq) enable_irq(host->stat_irq); + msmsdcc_disable_clocks(host, 1); } return 0; } diff --git a/drivers/mmc/host/msm_sdcc.h b/drivers/mmc/host/msm_sdcc.h index 361cb6e..da0039c 100644 --- a/drivers/mmc/host/msm_sdcc.h +++ b/drivers/mmc/host/msm_sdcc.h @@ -215,7 +215,6 @@ struct msmsdcc_host { struct clk *pclk; /* SDCC peripheral bus clock */ unsigned int clks_on; /* set if clocks are enabled */ struct timer_list busclk_timer; - int use_bustimer; unsigned int eject; /* eject state */ -- cgit v1.1 From f4748499d3dc5e7cadecb977f0d4f1f4f4a8d8c5 Mon Sep 17 00:00:00 2001 From: San Mehat Date: Mon, 23 Nov 2009 15:36:31 -0800 Subject: mmc: msm_sdcc: Featurize busclock power save and disable it by default Signed-off-by: San Mehat Signed-off-by: Daniel Walker --- drivers/mmc/host/msm_sdcc.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) (limited to 'drivers/mmc') diff --git a/drivers/mmc/host/msm_sdcc.c b/drivers/mmc/host/msm_sdcc.c index 591ef3c..bdafb64 100644 --- a/drivers/mmc/host/msm_sdcc.c +++ b/drivers/mmc/host/msm_sdcc.c @@ -48,6 +48,7 @@ #define DRIVER_NAME "msm-sdcc" +#define BUSCLK_PWRSAVE 0 #define BUSCLK_TIMEOUT (HZ) static unsigned int msmsdcc_fmin = 144000; static unsigned int msmsdcc_fmax = 50000000; @@ -65,6 +66,8 @@ 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 { @@ -131,7 +134,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... @@ -257,7 +262,9 @@ msmsdcc_dma_complete_func(struct msm_dmov_cmd *cmd, 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 @@ -934,7 +941,9 @@ msmsdcc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) host->pwr = pwr; msmsdcc_writel(host, pwr, MMCIPOWER); } +#if BUSCLK_PWRSAVE msmsdcc_disable_clocks(host, 1); +#endif spin_unlock_irqrestore(&host->lock, flags); } @@ -1279,7 +1288,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); @@ -1341,7 +1352,9 @@ msmsdcc_resume(struct platform_device *dev) mmc_resume_host(mmc); if (host->stat_irq) enable_irq(host->stat_irq); +#if BUSCLK_PWRSAVE msmsdcc_disable_clocks(host, 1); +#endif } return 0; } -- cgit v1.1 From b3b0ca84cfec581fba3ea8efaa8052cb5e6fc857 Mon Sep 17 00:00:00 2001 From: San Mehat Date: Tue, 24 Nov 2009 12:24:55 -0800 Subject: mmc: msm_sdcc: Fix issue where we might not end a sucessfull request Signed-off-by: San Mehat Signed-off-by: Daniel Walker --- drivers/mmc/host/msm_sdcc.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'drivers/mmc') diff --git a/drivers/mmc/host/msm_sdcc.c b/drivers/mmc/host/msm_sdcc.c index bdafb64..3ea6697 100644 --- a/drivers/mmc/host/msm_sdcc.c +++ b/drivers/mmc/host/msm_sdcc.c @@ -208,6 +208,7 @@ msmsdcc_dma_complete_func(struct msm_dmov_cmd *cmd, mrq = host->curr.mrq; BUG_ON(!mrq); + WARN_ON(!mrq->data); if (!(result & DMOV_RSLT_VALID)) { pr_err("msmsdcc: Invalid DataMover result\n"); @@ -719,14 +720,13 @@ static void msmsdcc_handle_irq_data(struct msmsdcc_host *host, u32 status, void __iomem *base) { - struct mmc_data *data; + 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); } - data = host->curr.data; if (!data) return; @@ -739,7 +739,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 -- cgit v1.1 From 673ce00d7cb4ec060b5091992959da4a1d91c634 Mon Sep 17 00:00:00 2001 From: San Mehat Date: Wed, 25 Nov 2009 11:16:57 -0800 Subject: mmc: msm_sdcc: Don't disable interrupts while suspending Signed-off-by: San Mehat Signed-off-by: Daniel Walker --- drivers/mmc/host/msm_sdcc.c | 3 --- 1 file changed, 3 deletions(-) (limited to 'drivers/mmc') diff --git a/drivers/mmc/host/msm_sdcc.c b/drivers/mmc/host/msm_sdcc.c index 3ea6697..6e50939 100644 --- a/drivers/mmc/host/msm_sdcc.c +++ b/drivers/mmc/host/msm_sdcc.c @@ -1315,12 +1315,10 @@ msmsdcc_suspend(struct platform_device *dev, pm_message_t state) { struct mmc_host *mmc = mmc_get_drvdata(dev); int rc = 0; - unsigned long flags; if (mmc) { struct msmsdcc_host *host = mmc_priv(mmc); - spin_lock_irqsave(&host->lock, flags); if (host->stat_irq) disable_irq(host->stat_irq); @@ -1330,7 +1328,6 @@ msmsdcc_suspend(struct platform_device *dev, pm_message_t state) msmsdcc_writel(host, 0, MMCIMASK0); } - spin_unlock_irqrestore(&host->lock, flags); if (host->clks_on) msmsdcc_disable_clocks(host, 0); } -- cgit v1.1 From 24bbd7d5b422cde6a149ac2f9ac6e61e66536532 Mon Sep 17 00:00:00 2001 From: San Mehat Date: Tue, 1 Dec 2009 10:10:47 -0800 Subject: mmc: msm_sdcc: Enable busclk idle timer for power savings Signed-off-by: San Mehat Signed-off-by: Daniel Walker --- drivers/mmc/host/msm_sdcc.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'drivers/mmc') diff --git a/drivers/mmc/host/msm_sdcc.c b/drivers/mmc/host/msm_sdcc.c index 6e50939..d42a2dd 100644 --- a/drivers/mmc/host/msm_sdcc.c +++ b/drivers/mmc/host/msm_sdcc.c @@ -48,7 +48,7 @@ #define DRIVER_NAME "msm-sdcc" -#define BUSCLK_PWRSAVE 0 +#define BUSCLK_PWRSAVE 1 #define BUSCLK_TIMEOUT (HZ) static unsigned int msmsdcc_fmin = 144000; static unsigned int msmsdcc_fmax = 50000000; @@ -72,7 +72,6 @@ msmsdcc_disable_clocks(struct msmsdcc_host *host, int deferr) mod_timer(&host->busclk_timer, jiffies + BUSCLK_TIMEOUT); } else { del_timer_sync(&host->busclk_timer); -// dev_info(mmc_dev(host->mmc), "Immediate clock shutdown\n"); clk_disable(host->clk); clk_disable(host->pclk); host->clks_on = 0; -- cgit v1.1 From 91bb64952a8c57826b01878925bea8831c71a492 Mon Sep 17 00:00:00 2001 From: Dmitry Shmidt Date: Wed, 2 Dec 2009 17:21:07 -0800 Subject: mmc: msm_sdcc: Don't set host->curr.mrq until after we're sure the busclk timer won't fire Signed-off-by: San Mehat Signed-off-by: Daniel Walker --- drivers/mmc/host/msm_sdcc.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/mmc') diff --git a/drivers/mmc/host/msm_sdcc.c b/drivers/mmc/host/msm_sdcc.c index d42a2dd..28899ee 100644 --- a/drivers/mmc/host/msm_sdcc.c +++ b/drivers/mmc/host/msm_sdcc.c @@ -853,8 +853,6 @@ msmsdcc_request(struct mmc_host *mmc, struct mmc_request *mrq) return; } - host->curr.mrq = mrq; - /* Need to drop the host lock here in case * the busclk wd fires */ @@ -863,6 +861,8 @@ msmsdcc_request(struct mmc_host *mmc, struct mmc_request *mrq) msmsdcc_enable_clocks(host); spin_lock_irqsave(&host->lock, flags); + host->curr.mrq = mrq; + if (mrq->data && mrq->data->flags & MMC_DATA_READ) /* Queue/read data, daisy-chain command when data starts */ msmsdcc_start_data(host, mrq->data, mrq->cmd, 0); -- cgit v1.1 From 6ac9ea69069804d357064357d0082b0eab4c87ce Mon Sep 17 00:00:00 2001 From: San Mehat Date: Wed, 2 Dec 2009 17:24:58 -0800 Subject: mmc: msm_sdcc: Fix the dma exec function to use the proper delays Signed-off-by: San Mehat Signed-off-by: Daniel Walker --- drivers/mmc/host/msm_sdcc.c | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) (limited to 'drivers/mmc') diff --git a/drivers/mmc/host/msm_sdcc.c b/drivers/mmc/host/msm_sdcc.c index 28899ee..8329fd6 100644 --- a/drivers/mmc/host/msm_sdcc.c +++ b/drivers/mmc/host/msm_sdcc.c @@ -177,17 +177,18 @@ msmsdcc_start_command_exec(struct msmsdcc_host *host, u32 arg, u32 c) { static void msmsdcc_dma_exec_func(struct msm_dmov_cmd *cmd) { - struct msmsdcc_host *host = (struct msmsdcc_host *)cmd->data; + 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); + 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); - } + if (host->cmd_cmd) { + msmsdcc_start_command_exec(host, + (u32) host->cmd_cmd->arg, + (u32) host->cmd_c); + } host->dma.active = 1; } -- cgit v1.1 From d0719e59f4ad96616f7c02ef0201667e41778c88 Mon Sep 17 00:00:00 2001 From: San Mehat Date: Thu, 3 Dec 2009 10:58:54 -0800 Subject: mmc: msm_sdcc: Fix issue where clocks could be disabled mid transaction msmsdcc_enable_clocks() was incorrectly being called depending on the state of host->clks_on. This means the busclk idle timer was never being deleted if the clock was already on.. Bogus. Also fixes a possible double clk disable if the call to del_timer_sync() in msmsdcc_disable_clocks() raced with the busclk timer. Signed-off-by: San Mehat Signed-off-by: Daniel Walker --- drivers/mmc/host/msm_sdcc.c | 61 ++++++++++++++++++++------------------------- 1 file changed, 27 insertions(+), 34 deletions(-) (limited to 'drivers/mmc') diff --git a/drivers/mmc/host/msm_sdcc.c b/drivers/mmc/host/msm_sdcc.c index 8329fd6..47b1f25 100644 --- a/drivers/mmc/host/msm_sdcc.c +++ b/drivers/mmc/host/msm_sdcc.c @@ -61,7 +61,7 @@ static unsigned int msmsdcc_sdioirq; #define CMD_SPINMAX 20 -static inline void +static inline void msmsdcc_disable_clocks(struct msmsdcc_host *host, int deferr) { WARN_ON(!host->clks_on); @@ -72,9 +72,14 @@ msmsdcc_disable_clocks(struct msmsdcc_host *host, int deferr) mod_timer(&host->busclk_timer, jiffies + BUSCLK_TIMEOUT); } else { del_timer_sync(&host->busclk_timer); - clk_disable(host->clk); - clk_disable(host->pclk); - host->clks_on = 0; + /* 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; + } } } @@ -83,21 +88,21 @@ 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; + 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; } - udelay(1 + ((3 * USEC_PER_SEC) / - (host->clk_rate ? host->clk_rate : msmsdcc_fmin))); - host->clks_on = 1; return 0; } @@ -180,7 +185,8 @@ 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, (unsigned int)host->curr.xfer_size, + MMCIDATALENGTH); msmsdcc_writel(host, host->cmd_pio_irqmask, MMCIMASK1); msmsdcc_writel(host, host->cmd_datactrl, MMCIDATACTRL); @@ -854,13 +860,7 @@ msmsdcc_request(struct mmc_host *mmc, struct mmc_request *mrq) return; } - /* Need to drop the host lock here in case - * the busclk wd fires - */ - spin_unlock_irqrestore(&host->lock, flags); - if (!host->clks_on) - msmsdcc_enable_clocks(host); - spin_lock_irqsave(&host->lock, flags); + msmsdcc_enable_clocks(host); host->curr.mrq = mrq; @@ -893,11 +893,10 @@ msmsdcc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) int rc; unsigned long flags; - if (!host->clks_on) - msmsdcc_enable_clocks(host); - spin_lock_irqsave(&host->lock, flags); + msmsdcc_enable_clocks(host); + if (ios->clock) { if (ios->clock != host->clk_rate) { rc = clk_set_rate(host->clk, ios->clock); @@ -1026,13 +1025,9 @@ 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); - dev_info(mmc_dev(host->mmc), "Bus clock timer expired\n"); if (host->clks_on) msmsdcc_disable_clocks(host, 0); - spin_unlock_irqrestore(&host->lock, flags); } static int @@ -1324,10 +1319,8 @@ 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) { + if (!rc) msmsdcc_writel(host, 0, MMCIMASK0); - - } if (host->clks_on) msmsdcc_disable_clocks(host, 0); } -- cgit v1.1 From 1cd2296909e77702c68021ede9d87a1d967a6a99 Mon Sep 17 00:00:00 2001 From: San Mehat Date: Wed, 3 Feb 2010 12:59:29 -0800 Subject: drivers: mmc: msm_sdcc: Add EMBEDDED_SDIO support Signed-off-by: Dmitry Shmidt Signed-off-by: Daniel Walker --- drivers/mmc/host/msm_sdcc.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) (limited to 'drivers/mmc') diff --git a/drivers/mmc/host/msm_sdcc.c b/drivers/mmc/host/msm_sdcc.c index 47b1f25..b40558e 100644 --- a/drivers/mmc/host/msm_sdcc.c +++ b/drivers/mmc/host/msm_sdcc.c @@ -616,7 +616,7 @@ 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; @@ -801,7 +801,6 @@ msmsdcc_irq(int irq, void *dev_id) spin_lock(&host->lock); do { - struct mmc_data *data; status = msmsdcc_readl(host, MMCISTATUS); status &= (msmsdcc_readl(host, MMCIMASK0) | MCI_DATABLOCKENDMASK); @@ -1145,6 +1144,15 @@ 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 */ -- cgit v1.1 From c3635c78e500a52c9fcd55de381a72928d9e054d Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Fri, 26 Mar 2010 16:44:01 -0700 Subject: DMAENGINE: generic slave control v2 Convert the device_terminate_all() operation on the DMA engine to a generic device_control() operation which can now optionally support also pausing and resuming DMA on a certain channel. Implemented for the COH 901 318 DMAC as an example. [dan.j.williams@intel.com: update for timberdale] Signed-off-by: Linus Walleij Acked-by: Mark Brown Cc: Maciej Sosnowski Cc: Nicolas Ferre Cc: Pavel Machek Cc: Li Yang Cc: Guennadi Liakhovetski Cc: Paul Mundt Cc: Ralf Baechle Cc: Haavard Skinnemoen Cc: Magnus Damm Cc: Liam Girdwood Cc: Joe Perches Cc: Roland Dreier Signed-off-by: Dan Williams --- drivers/mmc/host/atmel-mci.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/mmc') diff --git a/drivers/mmc/host/atmel-mci.c b/drivers/mmc/host/atmel-mci.c index 8072128..ae6d24b 100644 --- a/drivers/mmc/host/atmel-mci.c +++ b/drivers/mmc/host/atmel-mci.c @@ -578,7 +578,7 @@ static void atmci_stop_dma(struct atmel_mci *host) struct dma_chan *chan = host->data_chan; if (chan) { - chan->device->device_terminate_all(chan); + chan->device->device_control(chan, DMA_TERMINATE_ALL); atmci_dma_cleanup(host); } else { /* Data transfer was stopped by the interrupt handler */ -- cgit v1.1 From 771dc157e06d69fcece0b2c8a29b9010345d8e9a Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Thu, 8 Apr 2010 07:38:52 +0100 Subject: ARM: 6032/1: ARM: MMCI: support 8bit mode on the ST Micro version This adds support for an 8bit wide bus to the card (data lines MCIDAT0 through 7 exist) on the ST Micro version and alters the U300 platform to support this. Also add some ST_ prefix to the ST-specific registers. Signed-off-by: Linus Walleij Signed-off-by: Russell King --- drivers/mmc/host/mmci.c | 6 ++++-- drivers/mmc/host/mmci.h | 6 ++++-- 2 files changed, 8 insertions(+), 4 deletions(-) (limited to 'drivers/mmc') diff --git a/drivers/mmc/host/mmci.c b/drivers/mmc/host/mmci.c index 84c103a..7e70c1a 100644 --- a/drivers/mmc/host/mmci.c +++ b/drivers/mmc/host/mmci.c @@ -55,14 +55,16 @@ static void mmci_set_clkreg(struct mmci_host *host, unsigned int desired) host->cclk = host->mclk / (2 * (clk + 1)); } if (host->hw_designer == AMBA_VENDOR_ST) - clk |= MCI_FCEN; /* Bug fix in ST IP block */ + clk |= MCI_ST_FCEN; /* Bug fix in ST IP block */ clk |= MCI_CLK_ENABLE; /* This hasn't proven to be worthwhile */ /* clk |= MCI_CLK_PWRSAVE; */ } if (host->mmc->ios.bus_width == MMC_BUS_WIDTH_4) - clk |= MCI_WIDE_BUS; + clk |= MCI_4BIT_BUS; + if (host->mmc->ios.bus_width == MMC_BUS_WIDTH_8) + clk |= MCI_ST_8BIT_BUS; writel(clk, host->base + MMCICLOCK); } diff --git a/drivers/mmc/host/mmci.h b/drivers/mmc/host/mmci.h index 1ceb9a9..d77062e 100644 --- a/drivers/mmc/host/mmci.h +++ b/drivers/mmc/host/mmci.h @@ -25,9 +25,11 @@ #define MCI_CLK_ENABLE (1 << 8) #define MCI_CLK_PWRSAVE (1 << 9) #define MCI_CLK_BYPASS (1 << 10) -#define MCI_WIDE_BUS (1 << 11) +#define MCI_4BIT_BUS (1 << 11) +/* 8bit wide buses supported in ST Micro versions */ +#define MCI_ST_8BIT_BUS (1 << 12) /* HW flow control on the ST Micro version */ -#define MCI_FCEN (1 << 13) +#define MCI_ST_FCEN (1 << 13) #define MMCIARGUMENT 0x008 #define MMCICOMMAND 0x00c -- cgit v1.1 From 808d97ccbe8e8251b1435e86c762965fd7e8a75e Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Thu, 8 Apr 2010 07:39:38 +0100 Subject: ARM: 6033/1: ARM: MMCI: pass max frequency from platform This introduce the field f_max into the mmci_platform_data, making it possible to pass in a desired block clocking frequency from a board configuration. This is often more desirable than using a module parameter. We keep the module parameter as a fallback as well as the default frequency specified for this parameter if a parameter is not provided. This also adds some kerneldoc style documentation to the platform data struct in mmci.h. Signed-off-by: Linus Walleij Signed-off-by: Russell King --- drivers/mmc/host/mmci.c | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) (limited to 'drivers/mmc') diff --git a/drivers/mmc/host/mmci.c b/drivers/mmc/host/mmci.c index 7e70c1a..ff115d9 100644 --- a/drivers/mmc/host/mmci.c +++ b/drivers/mmc/host/mmci.c @@ -631,7 +631,18 @@ static int __devinit mmci_probe(struct amba_device *dev, struct amba_id *id) mmc->ops = &mmci_ops; mmc->f_min = (host->mclk + 511) / 512; - mmc->f_max = min(host->mclk, fmax); + /* + * If the platform data supplies a maximum operating + * frequency, this takes precedence. Else, we fall back + * to using the module parameter, which has a (low) + * default value in case it is not specified. Either + * value must not exceed the clock rate into the block, + * of course. + */ + if (plat->f_max) + mmc->f_max = min(host->mclk, plat->f_max); + else + mmc->f_max = min(host->mclk, fmax); dev_dbg(mmc_dev(mmc), "clocking block at %u Hz\n", mmc->f_max); #ifdef CONFIG_REGULATOR -- cgit v1.1 From f97cab28b1895ecb0aa317cc785bb209f57fc1e8 Mon Sep 17 00:00:00 2001 From: Eric Miao Date: Wed, 14 Apr 2010 07:00:42 +0800 Subject: [ARM] pxa: make it clear by converting MMC 'delay_detect' to millisecond delay_detect in HZ is confusing, convert it to be millisecond based. And thus remove those unnecessary call to msecs_to_jiffies() at runtime for this field. Other constants are converted assuming HZ == 100, which are basically true for those platforms. The assignment in csb726.c was incorrect, and is fixed in this patch as a result. Signed-off-by: Eric Miao Acked-by: Robert Jarzmik Acked-by: Marc Zyngier Acked-by: Marek Vasut Acked-by: Mike Rapoport Acked-by: Daniel Mack --- drivers/mmc/host/pxamci.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/mmc') diff --git a/drivers/mmc/host/pxamci.c b/drivers/mmc/host/pxamci.c index 0ed4895..e4f00e7 100644 --- a/drivers/mmc/host/pxamci.c +++ b/drivers/mmc/host/pxamci.c @@ -544,7 +544,7 @@ static irqreturn_t pxamci_detect_irq(int irq, void *devid) { struct pxamci_host *host = mmc_priv(devid); - mmc_detect_change(devid, host->pdata->detect_delay); + mmc_detect_change(devid, msecs_to_jiffies(host->pdata->detect_delay_ms)); return IRQ_HANDLED; } -- cgit v1.1 From ebb1fea9b3adf25d7e2f643c614163af4f93a17f Mon Sep 17 00:00:00 2001 From: Nicolas Ferre Date: Tue, 11 May 2010 14:06:48 -0700 Subject: mmc: atmel-mci: fix two parameters swapped Two parameters were swapped in the calls to atmci_init_slot(). Signed-off-by: Nicolas Ferre Reported-by: Anders Grahn Cc: Haavard Skinnemoen Cc: Cc: Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/mmc/host/atmel-mci.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/mmc') diff --git a/drivers/mmc/host/atmel-mci.c b/drivers/mmc/host/atmel-mci.c index 88be37d..57d3cb2d 100644 --- a/drivers/mmc/host/atmel-mci.c +++ b/drivers/mmc/host/atmel-mci.c @@ -1751,13 +1751,13 @@ static int __init atmci_probe(struct platform_device *pdev) ret = -ENODEV; if (pdata->slot[0].bus_width) { ret = atmci_init_slot(host, &pdata->slot[0], - MCI_SDCSEL_SLOT_A, 0); + 0, MCI_SDCSEL_SLOT_A); if (!ret) nr_slots++; } if (pdata->slot[1].bus_width) { ret = atmci_init_slot(host, &pdata->slot[1], - MCI_SDCSEL_SLOT_B, 1); + 1, MCI_SDCSEL_SLOT_B); if (!ret) nr_slots++; } -- cgit v1.1 From 009a891b22395fc86e5f34057d79fffee4509ab5 Mon Sep 17 00:00:00 2001 From: Nicolas Ferre Date: Tue, 11 May 2010 14:06:49 -0700 Subject: mmc: atmel-mci: prevent kernel oops while removing card The removing of an SD card in certain circumstances can lead to a kernel oops if we do not make sure that the "data" field of the host structure is valid. This patch adds a test in atmci_dma_cleanup() function and also calls atmci_stop_dma() before throwing away the reference to data. Signed-off-by: Nicolas Ferre Cc: Haavard Skinnemoen Cc: Cc: Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/mmc/host/atmel-mci.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) (limited to 'drivers/mmc') diff --git a/drivers/mmc/host/atmel-mci.c b/drivers/mmc/host/atmel-mci.c index 57d3cb2d..2a5d5ca 100644 --- a/drivers/mmc/host/atmel-mci.c +++ b/drivers/mmc/host/atmel-mci.c @@ -569,9 +569,10 @@ static void atmci_dma_cleanup(struct atmel_mci *host) { struct mmc_data *data = host->data; - dma_unmap_sg(&host->pdev->dev, data->sg, data->sg_len, - ((data->flags & MMC_DATA_WRITE) - ? DMA_TO_DEVICE : DMA_FROM_DEVICE)); + if (data) + dma_unmap_sg(&host->pdev->dev, data->sg, data->sg_len, + ((data->flags & MMC_DATA_WRITE) + ? DMA_TO_DEVICE : DMA_FROM_DEVICE)); } static void atmci_stop_dma(struct atmel_mci *host) @@ -1099,8 +1100,8 @@ static void atmci_command_complete(struct atmel_mci *host, "command error: status=0x%08x\n", status); if (cmd->data) { - host->data = NULL; atmci_stop_dma(host); + host->data = NULL; mci_writel(host, IDR, MCI_NOTBUSY | MCI_TXRDY | MCI_RXRDY | ATMCI_DATA_ERROR_FLAGS); -- cgit v1.1 From abc2c9fdf636c4335a8d72ac3c5ae152bca44b68 Mon Sep 17 00:00:00 2001 From: Nicolas Ferre Date: Tue, 11 May 2010 14:06:50 -0700 Subject: mmc: atmel-mci: remove data error interrupt after xfer Disable data error interrupts while we are actually recording that there is not such errors. This will prevent, in some cases, the warning message printed at new request queuing (in atmci_start_request()). Signed-off-by: Nicolas Ferre Cc: Haavard Skinnemoen Cc: Cc: Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/mmc/host/atmel-mci.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/mmc') diff --git a/drivers/mmc/host/atmel-mci.c b/drivers/mmc/host/atmel-mci.c index 2a5d5ca..b77b9b0 100644 --- a/drivers/mmc/host/atmel-mci.c +++ b/drivers/mmc/host/atmel-mci.c @@ -1294,6 +1294,7 @@ static void atmci_tasklet_func(unsigned long priv) } else { data->bytes_xfered = data->blocks * data->blksz; data->error = 0; + mci_writel(host, IDR, ATMCI_DATA_ERROR_FLAGS); } if (!data->stop) { -- cgit v1.1 From d586ebbb8814e039545d38f69029533f3f17748d Mon Sep 17 00:00:00 2001 From: Nicolas Ferre Date: Tue, 11 May 2010 14:06:50 -0700 Subject: mmc: atmel-mci: fix in debugfs: response value printing In debugfs, printing of command response reports resp[2] twice: fix it to resp[3]. Signed-off-by: Nicolas Ferre Haavard Skinnemoen Cc: Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/mmc/host/atmel-mci.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/mmc') diff --git a/drivers/mmc/host/atmel-mci.c b/drivers/mmc/host/atmel-mci.c index b77b9b0..fb279f4 100644 --- a/drivers/mmc/host/atmel-mci.c +++ b/drivers/mmc/host/atmel-mci.c @@ -266,7 +266,7 @@ static int atmci_req_show(struct seq_file *s, void *v) "CMD%u(0x%x) flg %x rsp %x %x %x %x err %d\n", cmd->opcode, cmd->arg, cmd->flags, cmd->resp[0], cmd->resp[1], cmd->resp[2], - cmd->resp[2], cmd->error); + cmd->resp[3], cmd->error); if (data) seq_printf(s, "DATA %u / %u * %u flg %x err %d\n", data->bytes_xfered, data->blocks, @@ -276,7 +276,7 @@ static int atmci_req_show(struct seq_file *s, void *v) "CMD%u(0x%x) flg %x rsp %x %x %x %x err %d\n", stop->opcode, stop->arg, stop->flags, stop->resp[0], stop->resp[1], stop->resp[2], - stop->resp[2], stop->error); + stop->resp[3], stop->error); } spin_unlock_bh(&slot->host->lock); -- cgit v1.1 From bdef2fe88b1e4bde7458aedd207929ce3f9d66ee Mon Sep 17 00:00:00 2001 From: Nicolas Ferre Date: Sat, 15 May 2010 12:32:31 -0400 Subject: mmc: at91_mci: modify cache flush routines As we were using an internal dma flushing routine, this patch changes to the DMA API flush_kernel_dcache_page(). Driver is able to compile now. [akpm@linux-foundation.org: flush_kernel_dcache_page() comes before kunmap_atomic()] Signed-off-by: Nicolas Ferre Cc: James Bottomley Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/mmc/host/at91_mci.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/mmc') diff --git a/drivers/mmc/host/at91_mci.c b/drivers/mmc/host/at91_mci.c index a6dd7da..336d9f5 100644 --- a/drivers/mmc/host/at91_mci.c +++ b/drivers/mmc/host/at91_mci.c @@ -314,8 +314,8 @@ static void at91_mci_post_dma_read(struct at91mci_host *host) dmabuf = (unsigned *)tmpv; } + flush_kernel_dcache_page(sg_page(sg)); kunmap_atomic(sgbuffer, KM_BIO_SRC_IRQ); - dmac_flush_range((void *)sgbuffer, ((void *)sgbuffer) + amount); data->bytes_xfered += amount; if (size == 0) break; -- cgit v1.1 From 058276303dbc4ed089c1f7dad0871810b1f5ddf1 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Mon, 17 May 2010 16:30:42 -0700 Subject: DMAENGINE: extend the control command to include an arg This adds an argument to the DMAengine control function, so that we can later provide control commands that need some external data passed in through an argument akin to the ioctl() operation prototype. [dan.j.williams@intel.com: fix up some missed conversions] Signed-off-by: Linus Walleij Signed-off-by: Dan Williams --- drivers/mmc/host/atmel-mci.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/mmc') diff --git a/drivers/mmc/host/atmel-mci.c b/drivers/mmc/host/atmel-mci.c index ae6d24b..fd2d24f 100644 --- a/drivers/mmc/host/atmel-mci.c +++ b/drivers/mmc/host/atmel-mci.c @@ -578,7 +578,7 @@ static void atmci_stop_dma(struct atmel_mci *host) struct dma_chan *chan = host->data_chan; if (chan) { - chan->device->device_control(chan, DMA_TERMINATE_ALL); + chan->device->device_control(chan, DMA_TERMINATE_ALL, 0); atmci_dma_cleanup(host); } else { /* Data transfer was stopped by the interrupt handler */ -- cgit v1.1