From 4adbbcc7b6cfb3dcf5ab49b06edb7752391b0e80 Mon Sep 17 00:00:00 2001 From: San Mehat Date: Sun, 8 Nov 2009 13:00:37 -0800 Subject: [PATCH 01/16] 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(-) diff --git a/drivers/mmc/host/msm_sdcc.c b/drivers/mmc/host/msm_sdcc.c index 4c068e5fe6b..977932a4bf4 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); From b3fa579118b239e218e690f5ef76870aff6fe738 Mon Sep 17 00:00:00 2001 From: San Mehat Date: Mon, 2 Nov 2009 18:46:09 -0800 Subject: [PATCH 02/16] 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(+) diff --git a/drivers/mmc/host/msm_sdcc.c b/drivers/mmc/host/msm_sdcc.c index 977932a4bf4..f4f7883271f 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++; From 5b00f40f90e7b17c11cf388680f43e8466b3666d Mon Sep 17 00:00:00 2001 From: San Mehat Date: Sat, 21 Nov 2009 09:22:14 -0800 Subject: [PATCH 03/16] 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 --- arch/arm/mach-msm/dma.c | 5 +++++ arch/arm/mach-msm/include/mach/dma.h | 2 ++ drivers/mmc/host/msm_sdcc.c | 1 + 3 files changed, 8 insertions(+) diff --git a/arch/arm/mach-msm/dma.c b/arch/arm/mach-msm/dma.c index f5420f9585c..8df798ac70c 100644 --- a/arch/arm/mach-msm/dma.c +++ b/arch/arm/mach-msm/dma.c @@ -63,6 +63,8 @@ void msm_dmov_enqueue_cmd(unsigned id, struct msm_dmov_cmd *cmd) writel(DMOV_CONFIG_IRQ_EN, DMOV_CONFIG(id)); } #endif + if (cmd->execute_func) + cmd->execute_func(cmd); PRINT_IO("msm_dmov_enqueue_cmd(%d), start command, status %x\n", id, status); list_add_tail(&cmd->list, &active_commands[id]); if (!channel_active) @@ -108,6 +110,7 @@ int msm_dmov_exec_cmd(unsigned id, unsigned int cmdptr) cmd.dmov_cmd.cmdptr = cmdptr; cmd.dmov_cmd.complete_func = dmov_exec_cmdptr_complete_func; + cmd.dmov_cmd.execute_func = NULL; cmd.id = id; init_completion(&cmd.complete); @@ -210,6 +213,8 @@ static irqreturn_t msm_datamover_irq_handler(int irq, void *dev_id) cmd = list_entry(ready_commands[id].next, typeof(*cmd), list); list_del(&cmd->list); list_add_tail(&cmd->list, &active_commands[id]); + if (cmd->execute_func) + cmd->execute_func(cmd); PRINT_FLOW("msm_datamover_irq_handler id %d, start command\n", id); writel(cmd->cmdptr, DMOV_CMD_PTR(id)); } diff --git a/arch/arm/mach-msm/include/mach/dma.h b/arch/arm/mach-msm/include/mach/dma.h index 5ab5bdffab0..78b0ffdf27e 100644 --- a/arch/arm/mach-msm/include/mach/dma.h +++ b/arch/arm/mach-msm/include/mach/dma.h @@ -28,6 +28,8 @@ struct msm_dmov_cmd { void (*complete_func)(struct msm_dmov_cmd *cmd, unsigned int result, struct msm_dmov_errdata *err); + void (*execute_func)(struct msm_dmov_cmd *cmd); + void *data; }; void msm_dmov_enqueue_cmd(unsigned id, struct msm_dmov_cmd *cmd); diff --git a/drivers/mmc/host/msm_sdcc.c b/drivers/mmc/host/msm_sdcc.c index f4f7883271f..02bec7c739e 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; } From 865c8064a2fb07100525097983966b8e789bde1a Mon Sep 17 00:00:00 2001 From: San Mehat Date: Fri, 13 Nov 2009 13:42:06 -0800 Subject: [PATCH 04/16] 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(-) diff --git a/drivers/mmc/host/msm_sdcc.c b/drivers/mmc/host/msm_sdcc.c index 02bec7c739e..b4b637223b7 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 8c844846981..6846bd7dff2 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 */ From 8b1c2ba274c8416afb7eab3bd788f98a917efe06 Mon Sep 17 00:00:00 2001 From: San Mehat Date: Mon, 16 Nov 2009 10:17:30 -0800 Subject: [PATCH 05/16] 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(-) diff --git a/drivers/mmc/host/msm_sdcc.c b/drivers/mmc/host/msm_sdcc.c index b4b637223b7..3b096f64ec5 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); From 51905dcbcf1f72a17f491c64485d513986110a6f Mon Sep 17 00:00:00 2001 From: San Mehat Date: Mon, 16 Nov 2009 11:59:01 -0800 Subject: [PATCH 06/16] 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(+) diff --git a/drivers/mmc/host/msm_sdcc.c b/drivers/mmc/host/msm_sdcc.c index 3b096f64ec5..84b284e3a28 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); From 56a8b5b8ae81bd766e527a0e5274a087c3c1109d Mon Sep 17 00:00:00 2001 From: San Mehat Date: Sat, 21 Nov 2009 12:29:46 -0800 Subject: [PATCH 07/16] 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 | 271 ++++++++++++++++++++---------------- drivers/mmc/host/msm_sdcc.h | 14 +- 2 files changed, 163 insertions(+), 122 deletions(-) diff --git a/drivers/mmc/host/msm_sdcc.c b/drivers/mmc/host/msm_sdcc.c index 84b284e3a28..52485859790 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) { - host->dma.busy = 1; - msm_dmov_enqueue_cmd(host->dma.channel, &host->dma.hdr); - } -} + /* Save parameters for the exec function */ + host->cmd_timeout = timeout; + host->cmd_pio_irqmask = pio_irqmask; + host->cmd_datactrl = datactrl; + host->cmd_cmd = cmd; -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; + 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) { - 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 6846bd7dff2..361cb6efd24 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 From c7fc9370df1433486dfa9460a833fae664e8be6c Mon Sep 17 00:00:00 2001 From: San Mehat Date: Sun, 22 Nov 2009 17:19:07 -0800 Subject: [PATCH 08/16] 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(-) diff --git a/drivers/mmc/host/msm_sdcc.c b/drivers/mmc/host/msm_sdcc.c index 52485859790..591ef3c4e9a 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 361cb6efd24..da0039c9285 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 */ From f4748499d3dc5e7cadecb977f0d4f1f4f4a8d8c5 Mon Sep 17 00:00:00 2001 From: San Mehat Date: Mon, 23 Nov 2009 15:36:31 -0800 Subject: [PATCH 09/16] 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(+) diff --git a/drivers/mmc/host/msm_sdcc.c b/drivers/mmc/host/msm_sdcc.c index 591ef3c4e9a..bdafb642a71 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; } From b3b0ca84cfec581fba3ea8efaa8052cb5e6fc857 Mon Sep 17 00:00:00 2001 From: San Mehat Date: Tue, 24 Nov 2009 12:24:55 -0800 Subject: [PATCH 10/16] 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(-) diff --git a/drivers/mmc/host/msm_sdcc.c b/drivers/mmc/host/msm_sdcc.c index bdafb642a71..3ea66971edf 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 From 673ce00d7cb4ec060b5091992959da4a1d91c634 Mon Sep 17 00:00:00 2001 From: San Mehat Date: Wed, 25 Nov 2009 11:16:57 -0800 Subject: [PATCH 11/16] 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(-) diff --git a/drivers/mmc/host/msm_sdcc.c b/drivers/mmc/host/msm_sdcc.c index 3ea66971edf..6e50939b6f8 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); } From 24bbd7d5b422cde6a149ac2f9ac6e61e66536532 Mon Sep 17 00:00:00 2001 From: San Mehat Date: Tue, 1 Dec 2009 10:10:47 -0800 Subject: [PATCH 12/16] 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(-) diff --git a/drivers/mmc/host/msm_sdcc.c b/drivers/mmc/host/msm_sdcc.c index 6e50939b6f8..d42a2dd6932 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; From 91bb64952a8c57826b01878925bea8831c71a492 Mon Sep 17 00:00:00 2001 From: Dmitry Shmidt Date: Wed, 2 Dec 2009 17:21:07 -0800 Subject: [PATCH 13/16] 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(-) diff --git a/drivers/mmc/host/msm_sdcc.c b/drivers/mmc/host/msm_sdcc.c index d42a2dd6932..28899ee392e 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); From 6ac9ea69069804d357064357d0082b0eab4c87ce Mon Sep 17 00:00:00 2001 From: San Mehat Date: Wed, 2 Dec 2009 17:24:58 -0800 Subject: [PATCH 14/16] 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(-) diff --git a/drivers/mmc/host/msm_sdcc.c b/drivers/mmc/host/msm_sdcc.c index 28899ee392e..8329fd650c5 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; } From d0719e59f4ad96616f7c02ef0201667e41778c88 Mon Sep 17 00:00:00 2001 From: San Mehat Date: Thu, 3 Dec 2009 10:58:54 -0800 Subject: [PATCH 15/16] 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(-) diff --git a/drivers/mmc/host/msm_sdcc.c b/drivers/mmc/host/msm_sdcc.c index 8329fd650c5..47b1f252652 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); } From 1cd2296909e77702c68021ede9d87a1d967a6a99 Mon Sep 17 00:00:00 2001 From: San Mehat Date: Wed, 3 Feb 2010 12:59:29 -0800 Subject: [PATCH 16/16] 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(-) diff --git a/drivers/mmc/host/msm_sdcc.c b/drivers/mmc/host/msm_sdcc.c index 47b1f252652..b40558e18e8 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 */