diff options
author | Philippe Langlais <philippe.langlais@stericsson.com> | 2012-03-07 10:11:36 +0100 |
---|---|---|
committer | Philippe Langlais <philippe.langlais@stericsson.com> | 2012-03-07 10:11:36 +0100 |
commit | f7153e4a3a83403ce13f42359d87697d5d93194f (patch) | |
tree | bddf9f45ee0e46691db5cf16ee342782a429cf69 | |
parent | c18b10aa6e4c7564df321f234c05a5782f1ffb8d (diff) | |
parent | 51bf840d7a90df41856ac4afa34e1534e290e633 (diff) |
Merge branch 'storage-mmc' into integration-linux-ux500
Conflicts:
arch/arm/mach-ux500/board-mop500-sdi.c
drivers/mmc/card/block.c
drivers/mmc/host/mmci.c
drivers/mmc/host/mmci.h
-rw-r--r-- | arch/arm/mach-ux500/board-mop500-sdi.c | 53 | ||||
-rw-r--r-- | drivers/mmc/card/block.c | 1 | ||||
-rw-r--r-- | drivers/mmc/core/core.c | 38 | ||||
-rw-r--r-- | drivers/mmc/core/mmc.c | 3 | ||||
-rw-r--r-- | drivers/mmc/core/sd.c | 8 | ||||
-rw-r--r-- | drivers/mmc/core/sdio.c | 9 | ||||
-rw-r--r-- | drivers/mmc/host/mmci.c | 503 | ||||
-rw-r--r-- | drivers/mmc/host/mmci.h | 8 |
8 files changed, 361 insertions, 262 deletions
diff --git a/arch/arm/mach-ux500/board-mop500-sdi.c b/arch/arm/mach-ux500/board-mop500-sdi.c index 238f830d25a..118d29b863f 100644 --- a/arch/arm/mach-ux500/board-mop500-sdi.c +++ b/arch/arm/mach-ux500/board-mop500-sdi.c @@ -38,8 +38,37 @@ static int sdi0_vsel = -1; static int mop500_sdi0_ios_handler(struct device *dev, struct mmc_ios *ios) { - static int power_mode = -1; + static unsigned char power_mode = MMC_POWER_ON; + static unsigned char signal_voltage = MMC_SIGNAL_VOLTAGE_330; + if (signal_voltage == ios->signal_voltage) + goto do_power; + + /* + * We need to re-init the levelshifter when switching I/O voltage level. + * Max discharge time according to ST6G3244ME spec is 1 ms. + */ + if (power_mode == MMC_POWER_ON) { + power_mode = MMC_POWER_OFF; + gpio_direction_output(sdi0_en, 0); + msleep(1); + } + + switch (ios->signal_voltage) { + case MMC_SIGNAL_VOLTAGE_330: + gpio_direction_output(sdi0_vsel, 0); + break; + case MMC_SIGNAL_VOLTAGE_180: + gpio_direction_output(sdi0_vsel, 1); + break; + default: + pr_warning("Non supported signal voltage for levelshifter.\n"); + break; + } + + signal_voltage = ios->signal_voltage; + +do_power: if (power_mode == ios->power_mode) return 0; @@ -47,25 +76,17 @@ static int mop500_sdi0_ios_handler(struct device *dev, struct mmc_ios *ios) case MMC_POWER_UP: break; case MMC_POWER_ON: - /* - * Level shifter voltage should depend on vdd to when deciding - * on either 1.8V or 2.9V. Once the decision has been made the - * level shifter must be disabled and re-enabled with a changed - * select signal in order to switch the voltage. Since there is - * no framework support yet for indicating 1.8V in vdd, use the - * default 2.9V. - */ - gpio_direction_output(sdi0_vsel, 0); gpio_direction_output(sdi0_en, 1); + /* Max settling time according to ST6G3244ME spec is 100 us. */ udelay(100); break; case MMC_POWER_OFF: - gpio_direction_output(sdi0_vsel, 0); gpio_direction_output(sdi0_en, 0); break; } power_mode = ios->power_mode; + return 0; } @@ -95,11 +116,12 @@ static struct stedma40_chan_cfg mop500_sdi0_dma_cfg_tx = { static struct mmci_platform_data mop500_sdi0_data = { .ios_handler = mop500_sdi0_ios_handler, - .ocr_mask = MMC_VDD_29_30, .f_max = 50000000, .capabilities = MMC_CAP_4_BIT_DATA | MMC_CAP_SD_HIGHSPEED | - MMC_CAP_MMC_HIGHSPEED, + MMC_CAP_MMC_HIGHSPEED | + MMC_CAP_UHS_SDR12 | + MMC_CAP_UHS_SDR25, .gpio_wp = -1, .sigdir = MCI_ST_FBCLKEN | MCI_ST_CMDDIREN | @@ -206,7 +228,8 @@ static struct mmci_platform_data mop500_sdi2_data = { .ocr_mask = MMC_VDD_165_195, .f_max = 50000000, .capabilities = MMC_CAP_4_BIT_DATA | MMC_CAP_8_BIT_DATA | - MMC_CAP_MMC_HIGHSPEED, + MMC_CAP_MMC_HIGHSPEED | + MMC_CAP_ERASE, .capabilities2 = MMC_CAP2_NO_SLEEP_CMD, .gpio_cd = -1, .gpio_wp = -1, @@ -242,11 +265,9 @@ static struct stedma40_chan_cfg mop500_sdi4_dma_cfg_tx = { #endif static struct mmci_platform_data mop500_sdi4_data = { - .ocr_mask = MMC_VDD_29_30, .f_max = 50000000, .capabilities = MMC_CAP_4_BIT_DATA | MMC_CAP_8_BIT_DATA | MMC_CAP_MMC_HIGHSPEED, - .capabilities2 = MMC_CAP2_NO_SLEEP_CMD, .gpio_cd = -1, .gpio_wp = -1, #ifdef CONFIG_STE_DMA40 diff --git a/drivers/mmc/card/block.c b/drivers/mmc/card/block.c index 3bf0d2c841b..6b7ab103da2 100644 --- a/drivers/mmc/card/block.c +++ b/drivers/mmc/card/block.c @@ -1783,6 +1783,7 @@ static int mmc_add_disk(struct mmc_blk_data *md) md->power_ro_lock_legacy.show = boot_partition_ro_lock_show; md->power_ro_lock_legacy.store = boot_partition_ro_lock_store; + sysfs_attr_init(&md->power_ro_lock_legacy.attr); md->power_ro_lock_legacy.attr.mode = mode; md->power_ro_lock_legacy.attr.name = "ro_lock"; ret = device_create_file(disk_to_dev(md->disk), diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c index db681e1a845..976856af759 100644 --- a/drivers/mmc/core/core.c +++ b/drivers/mmc/core/core.c @@ -243,16 +243,17 @@ static void mmc_wait_done(struct mmc_request *mrq) complete(&mrq->completion); } -static void __mmc_start_req(struct mmc_host *host, struct mmc_request *mrq) +static int __mmc_start_req(struct mmc_host *host, struct mmc_request *mrq) { init_completion(&mrq->completion); mrq->done = mmc_wait_done; if (mmc_card_removed(host->card)) { mrq->cmd->error = -ENOMEDIUM; complete(&mrq->completion); - return; + return -ENOMEDIUM; } mmc_start_request(host, mrq); + return 0; } static void mmc_wait_for_req_done(struct mmc_host *host, @@ -336,6 +337,7 @@ struct mmc_async_req *mmc_start_req(struct mmc_host *host, struct mmc_async_req *areq, int *error) { int err = 0; + int start_err = 0; struct mmc_async_req *data = host->areq; /* Prepare a new request */ @@ -345,30 +347,23 @@ struct mmc_async_req *mmc_start_req(struct mmc_host *host, if (host->areq) { mmc_wait_for_req_done(host, host->areq->mrq); err = host->areq->err_check(host->card, host->areq); - if (err) { - /* post process the completed failed request */ - mmc_post_req(host, host->areq->mrq, 0); - if (areq) - /* - * Cancel the new prepared request, because - * it can't run until the failed - * request has been properly handled. - */ - mmc_post_req(host, areq->mrq, -EINVAL); - - host->areq = NULL; - goto out; - } } - if (areq) - __mmc_start_req(host, areq->mrq); + if (!err && areq) + start_err = __mmc_start_req(host, areq->mrq); if (host->areq) mmc_post_req(host, host->areq->mrq, 0); - host->areq = areq; - out: + /* Cancel a prepared request if it was not started. */ + if ((err || start_err) && areq) + mmc_post_req(host, areq->mrq, -EINVAL); + + if (err) + host->areq = NULL; + else + host->areq = areq; + if (error) *error = err; return data; @@ -2068,6 +2063,9 @@ static int mmc_rescan_try_freq(struct mmc_host *host, unsigned freq) */ mmc_hw_reset_for_init(host); + /* Initialization should be done at 3.3 V I/O voltage. */ + mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_330, 0); + /* * sdio_reset sends CMD52 to reset card. Since we do not know * if the card is being re-initialized, just send it. CMD52 diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c index a48066344fa..2b9ed1401dc 100644 --- a/drivers/mmc/core/mmc.c +++ b/drivers/mmc/core/mmc.c @@ -816,6 +816,9 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr, if (!mmc_host_is_spi(host)) mmc_set_bus_mode(host, MMC_BUSMODE_OPENDRAIN); + /* Initialization should be done at 3.3 V I/O voltage. */ + mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_330, 0); + /* * Since we're changing the OCR value, we seem to * need to tell some cards to go back to the idle diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c index 5017f9354ce..c272c6868ec 100644 --- a/drivers/mmc/core/sd.c +++ b/drivers/mmc/core/sd.c @@ -911,6 +911,9 @@ static int mmc_sd_init_card(struct mmc_host *host, u32 ocr, BUG_ON(!host); WARN_ON(!host->claimed); + /* The initialization should be done at 3.3 V I/O voltage. */ + mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_330, 0); + err = mmc_sd_get_cid(host, ocr, cid, &rocr); if (err) return err; @@ -1156,11 +1159,6 @@ int mmc_attach_sd(struct mmc_host *host) BUG_ON(!host); WARN_ON(!host->claimed); - /* Make sure we are at 3.3V signalling voltage */ - err = mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_330, false); - if (err) - return err; - /* Disable preset value enable if already set since last time */ if (host->ops->enable_preset_value) { mmc_host_clk_hold(host); diff --git a/drivers/mmc/core/sdio.c b/drivers/mmc/core/sdio.c index 12cde6ee17f..dac2e23207e 100644 --- a/drivers/mmc/core/sdio.c +++ b/drivers/mmc/core/sdio.c @@ -585,6 +585,10 @@ static int mmc_sdio_init_card(struct mmc_host *host, u32 ocr, * Inform the card of the voltage */ if (!powered_resume) { + + /* The initialization should be done at 3.3 V I/O voltage. */ + mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_330, 0); + err = mmc_send_io_op_cond(host, host->ocr, &ocr); if (err) goto err; @@ -996,6 +1000,11 @@ static int mmc_sdio_power_restore(struct mmc_host *host) * With these steps taken, mmc_select_voltage() is also required to * restore the correct voltage setting of the card. */ + + if (!mmc_card_keep_power(host)) + /* The initialization should be done at 3.3 V I/O voltage. */ + mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_330, 0); + sdio_reset(host); mmc_go_idle(host); mmc_send_if_cond(host, host->ocr_avail); diff --git a/drivers/mmc/host/mmci.c b/drivers/mmc/host/mmci.c index ac988d7c3e0..28b335fbc8c 100644 --- a/drivers/mmc/host/mmci.c +++ b/drivers/mmc/host/mmci.c @@ -19,7 +19,6 @@ #include <linux/err.h> #include <linux/highmem.h> #include <linux/log2.h> -#include <linux/pm_runtime.h> #include <linux/mmc/pm.h> #include <linux/mmc/host.h> #include <linux/mmc/card.h> @@ -59,6 +58,7 @@ static unsigned int fmax = 515633; * @non_power_of_2_blksize: true if block sizes can be other than power of two * @pwrreg_powerup: power up value for MMCIPOWER register * @signal_direction: input/out direction of bus signals can be indicated + * @pwrreg_ctrl_power: bits in MMCIPOWER register controls ext. power supply */ struct variant_data { unsigned int clkreg; @@ -73,6 +73,7 @@ struct variant_data { bool non_power_of_2_blksize; u32 pwrreg_powerup; bool signal_direction; + bool pwrreg_ctrl_power; }; static struct variant_data variant_arm = { @@ -80,6 +81,7 @@ static struct variant_data variant_arm = { .fifohalfsize = 8 * 4, .datalength_bits = 16, .pwrreg_powerup = MCI_PWR_UP, + .pwrreg_ctrl_power = true, }; static struct variant_data variant_arm_extended_fifo = { @@ -87,6 +89,7 @@ static struct variant_data variant_arm_extended_fifo = { .fifohalfsize = 64 * 4, .datalength_bits = 16, .pwrreg_powerup = MCI_PWR_UP, + .pwrreg_ctrl_power = true, }; static struct variant_data variant_u300 = { @@ -155,6 +158,28 @@ static int mmci_validate_data(struct mmci_host *host, /* * This must be called with host->lock held */ +static void mmci_write_clkreg(struct mmci_host *host, u32 clk) +{ + if (host->clk_reg != clk) { + host->clk_reg = clk; + writel(clk, host->base + MMCICLOCK); + } +} + +/* + * This must be called with host->lock held + */ +static void mmci_write_pwrreg(struct mmci_host *host, u32 pwr) +{ + if (host->pwr_reg != pwr) { + host->pwr_reg = pwr; + writel(pwr, host->base + MMCIPOWER); + } +} + +/* + * This must be called with host->lock held + */ static void mmci_set_clkreg(struct mmci_host *host, unsigned int desired) { struct variant_data *variant = host->variant; @@ -199,7 +224,7 @@ static void mmci_set_clkreg(struct mmci_host *host, unsigned int desired) if (host->mmc->ios.bus_width == MMC_BUS_WIDTH_8) clk |= MCI_ST_8BIT_BUS; - writel(clk, host->base + MMCICLOCK); + mmci_write_clkreg(host, clk); } static void @@ -214,8 +239,8 @@ mmci_request_end(struct mmci_host *host, struct mmc_request *mrq) mmc_request_done(host->mmc, mrq); - pm_runtime_mark_last_busy(host->mmc->parent); - pm_runtime_put_autosuspend(host->mmc->parent); + pm_runtime_mark_last_busy(mmc_dev(host->mmc)); + pm_runtime_put_autosuspend(mmc_dev(host->mmc)); } static void mmci_set_mask1(struct mmci_host *host, unsigned int mask) @@ -239,6 +264,7 @@ static void mmci_stop_data(struct mmci_host *host) writel(0, host->base + MMCIDATACTRL); mmci_set_mask1(host, 0); host->data = NULL; + host->datactrl_reg = 0; } static void mmci_init_sg(struct mmci_host *host, struct mmc_data *data) @@ -349,10 +375,33 @@ static inline void mmci_dma_release(struct mmci_host *host) host->dma_rx_channel = host->dma_tx_channel = NULL; } +static void mmci_dma_data_error(struct mmci_host *host, struct mmc_data *data) +{ + dev_err(mmc_dev(host->mmc), "error during DMA transfer!\n"); + dmaengine_terminate_all(host->dma_current); + host->dma_current = NULL; + host->dma_desc_current = NULL; + data->host_cookie = 0; +} + static void mmci_dma_unmap(struct mmci_host *host, struct mmc_data *data) { - struct dma_chan *chan = host->dma_current; + struct dma_chan *chan; enum dma_data_direction dir; + + if (data->flags & MMC_DATA_READ) { + dir = DMA_FROM_DEVICE; + chan = host->dma_rx_channel; + } else { + dir = DMA_TO_DEVICE; + chan = host->dma_tx_channel; + } + + dma_unmap_sg(chan->device->dev, data->sg, data->sg_len, dir); +} + +static void mmci_dma_finalize(struct mmci_host *host, struct mmc_data *data) +{ u32 status; int i; @@ -371,19 +420,12 @@ static void mmci_dma_unmap(struct mmci_host *host, struct mmc_data *data) * contiguous buffers. On TX, we'll get a FIFO underrun error. */ if (status & MCI_RXDATAAVLBLMASK) { - dmaengine_terminate_all(chan); - if (!data->error) - data->error = -EIO; - } - - if (data->flags & MMC_DATA_WRITE) { - dir = DMA_TO_DEVICE; - } else { - dir = DMA_FROM_DEVICE; + data->error = -EIO; + mmci_dma_data_error(host, data); } if (!data->host_cookie) - dma_unmap_sg(chan->device->dev, data->sg, data->sg_len, dir); + mmci_dma_unmap(host, data); /* * Use of DMA with scatter-gather is impossible. @@ -393,16 +435,15 @@ static void mmci_dma_unmap(struct mmci_host *host, struct mmc_data *data) dev_err(mmc_dev(host->mmc), "buggy DMA detected. Taking evasive action.\n"); mmci_dma_release(host); } -} -static void mmci_dma_data_error(struct mmci_host *host) -{ - dev_err(mmc_dev(host->mmc), "error during DMA transfer!\n"); - dmaengine_terminate_all(host->dma_current); + host->dma_current = NULL; + host->dma_desc_current = NULL; } -static int mmci_dma_prep_data(struct mmci_host *host, struct mmc_data *data, - struct mmci_host_next *next) +/* prepares DMA channel and DMA descriptor, returns non-zero on failure */ +static int __mmci_dma_prep_data(struct mmci_host *host, struct mmc_data *data, + struct dma_chan **dma_chan, + struct dma_async_tx_descriptor **dma_desc) { struct variant_data *variant = host->variant; struct dma_slave_config conf = { @@ -419,16 +460,6 @@ static int mmci_dma_prep_data(struct mmci_host *host, struct mmc_data *data, enum dma_data_direction buffer_dirn; int nr_sg; - /* Check if next job is already prepared */ - if (data->host_cookie && !next && - host->dma_current && host->dma_desc_current) - return 0; - - if (!next) { - host->dma_current = NULL; - host->dma_desc_current = NULL; - } - if (data->flags & MMC_DATA_READ) { conf.direction = DMA_DEV_TO_MEM; buffer_dirn = DMA_FROM_DEVICE; @@ -462,30 +493,42 @@ static int mmci_dma_prep_data(struct mmci_host *host, struct mmc_data *data, if (!desc) goto unmap_exit; - if (next) { - next->dma_chan = chan; - next->dma_desc = desc; - } else { - host->dma_current = chan; - host->dma_desc_current = desc; - } + *dma_chan = chan; + *dma_desc = desc; return 0; unmap_exit: - if (!next) - dmaengine_terminate_all(chan); dma_unmap_sg(device->dev, data->sg, data->sg_len, buffer_dirn); return -ENOMEM; } -static int mmci_dma_start_data(struct mmci_host *host, unsigned int datactrl) +static int inline mmci_dma_prep_data(struct mmci_host *host, + struct mmc_data *data) +{ + /* Check if next job is already prepared. */ + if (host->dma_current && host->dma_desc_current) + return 0; + + /* No job were prepared thus do it now. */ + return __mmci_dma_prep_data(host, data, &host->dma_current, + &host->dma_desc_current); +} + +static inline int mmci_dma_prep_next(struct mmci_host *host, + struct mmc_data *data) +{ + struct mmci_host_next *nd = &host->next_data; + return __mmci_dma_prep_data(host, data, &nd->dma_chan, &nd->dma_desc); +} + +static int mmci_dma_start_data(struct mmci_host *host) { int ret; struct mmc_data *data = host->data; struct variant_data *variant = host->variant; - ret = mmci_dma_prep_data(host, host->data, NULL); + ret = mmci_dma_prep_data(host, host->data); if (ret) return ret; @@ -496,15 +539,15 @@ static int mmci_dma_start_data(struct mmci_host *host, unsigned int datactrl) dmaengine_submit(host->dma_desc_current); dma_async_issue_pending(host->dma_current); - datactrl |= MCI_DPSM_DMAENABLE; + host->datactrl_reg |= MCI_DPSM_DMAENABLE; /* Some hardware versions need special flags for SDIO DMA write */ if (variant->sdio && host->mmc->card && mmc_card_sdio(host->mmc->card) && (data->flags & MMC_DATA_WRITE)) - datactrl |= variant->dma_sdio_req_ctrl; + host->datactrl_reg |= variant->dma_sdio_req_ctrl; /* Trigger the DMA transfer */ - writel(datactrl, host->base + MMCIDATACTRL); + writel(host->datactrl_reg, host->base + MMCIDATACTRL); /* * Let the MMCI say when the data is ended and it's time @@ -521,18 +564,14 @@ static void mmci_get_next_data(struct mmci_host *host, struct mmc_data *data) struct mmci_host_next *next = &host->next_data; if (data->host_cookie && data->host_cookie != next->cookie) { - pr_warning("[%s] invalid cookie: data->host_cookie %d" + pr_err("[%s] invalid cookie: data->host_cookie %d" " host->next_data.cookie %d\n", __func__, data->host_cookie, host->next_data.cookie); - data->host_cookie = 0; + BUG(); } - if (!data->host_cookie) - return; - host->dma_desc_current = next->dma_desc; host->dma_current = next->dma_chan; - next->dma_desc = NULL; next->dma_chan = NULL; } @@ -547,22 +586,18 @@ static void mmci_pre_request(struct mmc_host *mmc, struct mmc_request *mrq, if (!data) return; - if (mmci_validate_data(host, mrq->data)) - return; + BUG_ON(data->host_cookie); - if (data->host_cookie) { - data->host_cookie = 0; + if (mmci_validate_data(host, data)) return; - } - /* if config for dma */ - if (((data->flags & MMC_DATA_WRITE) && host->dma_tx_channel) || - ((data->flags & MMC_DATA_READ) && host->dma_rx_channel)) { - if (mmci_dma_prep_data(host, data, nd)) - data->host_cookie = 0; - else - data->host_cookie = ++nd->cookie < 0 ? 1 : nd->cookie; - } + /* + * Don't prepare DMA if there is no previous request, + * is_first_req is set. Instead, prepare DMA while + * start command is being issued. + */ + if (!is_first_req && !mmci_dma_prep_next(host, data)) + data->host_cookie = ++nd->cookie < 0 ? 1 : nd->cookie; } static void mmci_post_request(struct mmc_host *mmc, struct mmc_request *mrq, @@ -570,29 +605,23 @@ static void mmci_post_request(struct mmc_host *mmc, struct mmc_request *mrq, { struct mmci_host *host = mmc_priv(mmc); struct mmc_data *data = mrq->data; - struct dma_chan *chan; - enum dma_data_direction dir; - if (!data) + if (!data || !data->host_cookie) return; - if (data->flags & MMC_DATA_READ) { - dir = DMA_FROM_DEVICE; - chan = host->dma_rx_channel; - } else { - dir = DMA_TO_DEVICE; - chan = host->dma_tx_channel; - } + mmci_dma_unmap(host, data); + if (err) { + struct mmci_host_next *next = &host->next_data; + struct dma_chan *chan; + if (data->flags & MMC_DATA_READ) + chan = host->dma_rx_channel; + else + chan = host->dma_tx_channel; + dmaengine_terminate_all(chan); - /* if config for dma */ - if (chan) { - if (err) - dmaengine_terminate_all(chan); - if (data->host_cookie) - dma_unmap_sg(mmc_dev(host->mmc), data->sg, - data->sg_len, dir); - mrq->data->host_cookie = 0; + next->dma_desc = NULL; + next->dma_chan = NULL; } } @@ -613,11 +642,20 @@ static inline void mmci_dma_unmap(struct mmci_host *host, struct mmc_data *data) { } -static inline void mmci_dma_data_error(struct mmci_host *host) +static inline void mmci_dma_finalize(struct mmci_host *host, struct mmc_data *data) +{ +} + +static inline void mmci_dma_data_error(struct mmci_host *host, struct mmc_data *data) +{ +} + +static inline int mmci_dma_start_data(struct mmci_host *host) { + return -ENOSYS; } -static inline int mmci_dma_start_data(struct mmci_host *host, unsigned int datactrl) +static inline int mmci_dma_prep_data(struct mmci_host *host, struct mmc_data *data) { return -ENOSYS; } @@ -627,10 +665,10 @@ static inline int mmci_dma_start_data(struct mmci_host *host, unsigned int datac #endif -static void mmci_start_data(struct mmci_host *host, struct mmc_data *data) +static void mmci_setup_datactrl(struct mmci_host *host, struct mmc_data *data) { struct variant_data *variant = host->variant; - unsigned int datactrl, timeout, irqmask; + unsigned int datactrl, timeout; unsigned long long clks; void __iomem *base; int blksz_bits; @@ -664,33 +702,41 @@ static void mmci_start_data(struct mmci_host *host, struct mmc_data *data) /* The ST Micro variants has a special bit to enable SDIO */ if (variant->sdio && host->mmc->card) if (mmc_card_sdio(host->mmc->card)) { - /* - * The ST Micro variants has a special bit - * to enable SDIO. - */ - datactrl |= MCI_ST_DPSM_SDIOEN; /* * The ST Micro variant for SDIO write transfer sizes * less then 8 bytes needs to have clock H/W flow * control disabled. */ - if ((host->size < 8) && - (data->flags & MMC_DATA_WRITE)) - writel(readl(host->base + MMCICLOCK) & - ~variant->clkreg_enable, - host->base + MMCICLOCK); + u32 clk; + if ((host->size < 8) && (data->flags & MMC_DATA_WRITE)) + clk = host->clk_reg & ~variant->clkreg_enable; else - writel(readl(host->base + MMCICLOCK) | - variant->clkreg_enable, - host->base + MMCICLOCK); + clk = host->clk_reg | variant->clkreg_enable; + + mmci_write_clkreg(host, clk); + + /* + * The ST Micro variants has a special bit + * to enable SDIO. + */ + datactrl |= MCI_ST_DPSM_SDIOEN; } + host->datactrl_reg = datactrl; + writel(datactrl, base + MMCIDATACTRL); +} + +static void mmci_start_data(struct mmci_host *host, struct mmc_data *data) +{ + unsigned int irqmask; + struct variant_data *variant = host->variant; + void __iomem *base = host->base; /* * Attempt to use DMA operation mode, if this * should fail, fall back to PIO mode */ - if (!mmci_dma_start_data(host, datactrl)) + if (!mmci_dma_start_data(host)) return; /* IRQ mode, map the SG list for CPU reading/writing */ @@ -714,7 +760,6 @@ static void mmci_start_data(struct mmci_host *host, struct mmc_data *data) irqmask = MCI_TXFIFOHALFEMPTYMASK; } - writel(datactrl, base + MMCIDATACTRL); writel(readl(base + MMCIMASK0) & ~MCI_DATAENDMASK, base + MMCIMASK0); mmci_set_mask1(host, irqmask); } @@ -757,8 +802,10 @@ mmci_data_irq(struct mmci_host *host, struct mmc_data *data, u32 remain, success; /* Terminate the DMA transfer */ - if (dma_inprogress(host)) - mmci_dma_data_error(host); + if (dma_inprogress(host)) { + mmci_dma_data_error(host, data); + mmci_dma_unmap(host, data); + } /* * Calculate how far we are into the transfer. Note that @@ -797,7 +844,7 @@ mmci_data_irq(struct mmci_host *host, struct mmc_data *data, if (status & MCI_DATAEND || data->error) { if (dma_inprogress(host)) - mmci_dma_unmap(host, data); + mmci_dma_finalize(host, data); mmci_stop_data(host); if (!data->error) @@ -832,14 +879,16 @@ mmci_cmd_irq(struct mmci_host *host, struct mmc_command *cmd, } if (!cmd->data || cmd->error) { - if (host->data) { - /* Terminate the DMA transfer */ - if (dma_inprogress(host)) - mmci_dma_data_error(host); - mmci_stop_data(host); + /* Terminate the DMA transfer */ + if (dma_inprogress(host)) { + mmci_dma_data_error(host, host->mrq->data); + mmci_dma_unmap(host, host->mrq->data); } + if (host->data) + mmci_stop_data(host); mmci_request_end(host, cmd->mrq); } else if (!(cmd->data->flags & MMC_DATA_READ)) { + mmci_setup_datactrl(host, cmd->data); mmci_start_data(host, cmd->data); } } @@ -1058,6 +1107,7 @@ static void mmci_request(struct mmc_host *mmc, struct mmc_request *mrq) { struct mmci_host *host = mmc_priv(mmc); unsigned long flags; + bool dmaprep_after_cmd = false; WARN_ON(host->mrq != NULL); @@ -1073,14 +1123,28 @@ static void mmci_request(struct mmc_host *mmc, struct mmc_request *mrq) host->mrq = mrq; - if (mrq->data) + if (mrq->data) { + dmaprep_after_cmd = + (host->variant->clkreg_enable && + (mrq->data->flags & MMC_DATA_READ)) || + !(mrq->data->flags & MMC_DATA_READ); mmci_get_next_data(host, mrq->data); - - if (mrq->data && mrq->data->flags & MMC_DATA_READ) - mmci_start_data(host, mrq->data); + if (mrq->data->flags & MMC_DATA_READ) { + mmci_setup_datactrl(host, mrq->data); + if (!dmaprep_after_cmd) + mmci_start_data(host, mrq->data); + } + } mmci_start_command(host, mrq->cmd, 0); + if (mrq->data && dmaprep_after_cmd) { + mmci_dma_prep_data(host, mrq->data); + + if (mrq->data->flags & MMC_DATA_READ) + mmci_start_data(host, mrq->data); + } + spin_unlock_irqrestore(&host->lock, flags); } @@ -1092,7 +1156,7 @@ static void mmci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) unsigned long flags; int ret; - pm_runtime_get_sync(mmc->parent); + pm_runtime_get_sync(mmc_dev(mmc)); if (host->plat->ios_handler && host->plat->ios_handler(mmc_dev(mmc), ios)) @@ -1114,7 +1178,7 @@ static void mmci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) * power should be rare so we print an error * and return here. */ - return; + goto out; } } /* @@ -1161,16 +1225,13 @@ static void mmci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) spin_lock_irqsave(&host->lock, flags); mmci_set_clkreg(host, ios->clock); - - if (host->pwr_reg != pwr) { - host->pwr_reg = pwr; - writel(pwr, host->base + MMCIPOWER); - } + mmci_write_pwrreg(host, pwr); spin_unlock_irqrestore(&host->lock, flags); - pm_runtime_mark_last_busy(mmc->parent); - pm_runtime_put_autosuspend(mmc->parent); + out: + pm_runtime_mark_last_busy(mmc_dev(mmc)); + pm_runtime_put_autosuspend(mmc_dev(mmc)); } static int mmci_get_ro(struct mmc_host *mmc) @@ -1205,6 +1266,21 @@ static int mmci_get_cd(struct mmc_host *mmc) return status; } +static int mmci_sig_volt_switch(struct mmc_host *mmc, struct mmc_ios *ios) +{ + struct mmci_host *host = mmc_priv(mmc); + int ret = 0; + + if (host->plat->ios_handler) { + pm_runtime_get_sync(mmc_dev(mmc)); + ret = host->plat->ios_handler(mmc_dev(mmc), ios); + pm_runtime_mark_last_busy(mmc_dev(mmc)); + pm_runtime_put_autosuspend(mmc_dev(mmc)); + } + + return ret; +} + static irqreturn_t mmci_cd_irq(int irq, void *dev_id) { struct mmci_host *host = dev_id; @@ -1221,6 +1297,7 @@ static const struct mmc_host_ops mmci_ops = { .set_ios = mmci_set_ios, .get_ro = mmci_get_ro, .get_cd = mmci_get_cd, + .start_signal_voltage_switch = mmci_sig_volt_switch, }; static int __devinit mmci_probe(struct amba_device *dev, @@ -1254,9 +1331,6 @@ static int __devinit mmci_probe(struct amba_device *dev, host->gpio_wp = -ENOSYS; host->gpio_cd = -ENOSYS; host->gpio_cd_irq = -1; - host->irqmask0_reg = 0; - host->pwr_reg = 0; - host->clk_reg = 0; host->hw_designer = amba_manf(dev); host->hw_revision = amba_rev(dev); @@ -1438,7 +1512,6 @@ static int __devinit mmci_probe(struct amba_device *dev, goto irq0_free; } - host->irqmask0_reg = MCI_IRQENABLE; writel(MCI_IRQENABLE, host->base + MMCIMASK0); amba_set_drvdata(dev, mmc); @@ -1450,8 +1523,8 @@ static int __devinit mmci_probe(struct amba_device *dev, mmci_dma_setup(host); - pm_runtime_set_autosuspend_delay(mmc->parent, 50); - pm_runtime_use_autosuspend(mmc->parent); + pm_runtime_set_autosuspend_delay(&dev->dev, 50); + pm_runtime_use_autosuspend(&dev->dev); pm_runtime_put(&dev->dev); mmc_add_host(mmc); @@ -1494,11 +1567,10 @@ static int __devexit mmci_remove(struct amba_device *dev) struct mmci_host *host = mmc_priv(mmc); /* - * Make sure the host is resumed and undo the - * pm_runtime_put in probe. + * Undo pm_runtime_put() in probe. We use the _sync + * version here so that we can access the primecell. */ - pm_runtime_resume(mmc->parent); - pm_runtime_get_noresume(mmc->parent); + pm_runtime_get_sync(&dev->dev); mmc_remove_host(mmc); @@ -1521,7 +1593,6 @@ static int __devexit mmci_remove(struct amba_device *dev) gpio_free(host->gpio_cd); iounmap(host->base); - clk_disable(host->clk); clk_unprepare(host->clk); clk_put(host->clk); @@ -1538,72 +1609,18 @@ static int __devexit mmci_remove(struct amba_device *dev) return 0; } -#ifdef CONFIG_SUSPEND - -#ifdef CONFIG_PM_RUNTIME -static void mmci_disable_irq(struct mmci_host *host) {} -static void mmci_enable_irq(struct mmci_host *host) {} -#else -static void mmci_disable_irq(struct mmci_host *host) -{ - writel(0, host->base + MMCIMASK0); -} -static void mmci_enable_irq(struct mmci_host *host) -{ - writel(host->irqmask0_reg, host->base + MMCIMASK0); -} -#endif - -static int mmci_suspend(struct device *dev) +#if defined(CONFIG_SUSPEND) || defined(CONFIG_PM_RUNTIME) +static int mmci_save(struct amba_device *dev) { - struct amba_device *adev = to_amba_device(dev); - struct mmc_host *mmc = amba_get_drvdata(adev); - int ret = 0; - - if (mmc) { - struct mmci_host *host = mmc_priv(mmc); - - ret = mmc_suspend_host(mmc); - if (!ret) - mmci_disable_irq(host); - } - - return ret; -} - -static int mmci_resume(struct device *dev) -{ - struct amba_device *adev = to_amba_device(dev); - struct mmc_host *mmc = amba_get_drvdata(adev); - int ret = 0; - - if (mmc) { - struct mmci_host *host = mmc_priv(mmc); - - mmci_enable_irq(host); - ret = mmc_resume_host(mmc); - } - - return ret; -} -#endif - -#ifdef CONFIG_PM_RUNTIME -static int mmci_runtime_suspend(struct device *dev) -{ - struct amba_device *adev = to_amba_device(dev); - struct mmc_host *mmc = amba_get_drvdata(adev); + struct mmc_host *mmc = amba_get_drvdata(dev); unsigned long flags; - int ret; struct mmc_ios ios; + int ret = 0; if (mmc) { struct mmci_host *host = mmc_priv(mmc); - /* - * Let the ios_handler act on a POWER_OFF to potentially do some - * power save actions. - */ + /* Let the ios_handler act on a POWER_OFF to save power. */ if (host->plat->ios_handler) { memcpy(&ios, &mmc->ios, sizeof(struct mmc_ios)); ios.power_mode = MMC_POWER_OFF; @@ -1615,11 +1632,6 @@ static int mmci_runtime_suspend(struct device *dev) spin_lock_irqsave(&host->lock, flags); - /* Save registers for POWER, CLOCK and IRQMASK0 */ - host->irqmask0_reg = readl(host->base + MMCIMASK0); - host->pwr_reg = readl(host->base + MMCIPOWER); - host->clk_reg = readl(host->base + MMCICLOCK); - /* * Make sure we do not get any interrupts when we disabled the * clock and the regulator and as well make sure to clear the @@ -1632,37 +1644,33 @@ static int mmci_runtime_suspend(struct device *dev) spin_unlock_irqrestore(&host->lock, flags); clk_disable(host->clk); - amba_vcore_disable(adev); + amba_vcore_disable(dev); } - return 0; + return ret; } -static int mmci_runtime_resume(struct device *dev) +static int mmci_restore(struct amba_device *dev) { - struct amba_device *adev = to_amba_device(dev); - struct mmc_host *mmc = amba_get_drvdata(adev); + struct mmc_host *mmc = amba_get_drvdata(dev); unsigned long flags; if (mmc) { struct mmci_host *host = mmc_priv(mmc); - amba_vcore_enable(adev); + amba_vcore_enable(dev); clk_enable(host->clk); spin_lock_irqsave(&host->lock, flags); - /* Restore registers for POWER, CLOCK and IRQMASK0 */ + /* Restore registers and re-enable interrupts. */ writel(host->clk_reg, host->base + MMCICLOCK); writel(host->pwr_reg, host->base + MMCIPOWER); - writel(host->irqmask0_reg, host->base + MMCIMASK0); + writel(MCI_IRQENABLE, host->base + MMCIMASK0); spin_unlock_irqrestore(&host->lock, flags); - /* - * Restore settings done by the ios_handler. This shall be done - * quickly to keep request latency low. - */ + /* Restore settings done by the ios_handler. */ if (host->plat->ios_handler) host->plat->ios_handler(mmc_dev(mmc), &mmc->ios); @@ -1670,19 +1678,82 @@ static int mmci_runtime_resume(struct device *dev) return 0; } +#endif -static int mmci_runtime_idle(struct device *dev) +#ifdef CONFIG_SUSPEND +static int mmci_suspend(struct device *dev) { - pm_runtime_suspend(dev); - return 0; + struct amba_device *adev = to_amba_device(dev); + struct mmc_host *mmc = amba_get_drvdata(adev); + int ret = 0; + + if (mmc) { + ret = mmc_suspend_host(mmc); + if (ret == 0) { + pm_runtime_get_sync(dev); + mmci_save(adev); + amba_pclk_disable(adev); + } + } + + return ret; +} + +static int mmci_resume(struct device *dev) +{ + struct amba_device *adev = to_amba_device(dev); + struct mmc_host *mmc = amba_get_drvdata(adev); + int ret = 0; + + if (mmc) { + amba_pclk_enable(adev); + mmci_restore(adev); + pm_runtime_put(dev); + + ret = mmc_resume_host(mmc); + } + + return ret; +} +#endif + +#ifdef CONFIG_PM_RUNTIME +static int mmci_runtime_suspend(struct device *dev) +{ + struct amba_device *adev = to_amba_device(dev); + struct mmc_host *mmc = amba_get_drvdata(adev); + int ret = 0; + + if (mmc) { + struct mmci_host *host = mmc_priv(mmc); + struct variant_data *variant = host->variant; + if (!variant->pwrreg_ctrl_power) + ret = mmci_save(adev); + } + + return ret; +} + +static int mmci_runtime_resume(struct device *dev) +{ + struct amba_device *adev = to_amba_device(dev); + struct mmc_host *mmc = amba_get_drvdata(adev); + int ret = 0; + + if (mmc) { + struct mmci_host *host = mmc_priv(mmc); + struct variant_data *variant = host->variant; + if (!variant->pwrreg_ctrl_power) + ret = mmci_restore(adev); + } + + return ret; } #endif static const struct dev_pm_ops mmci_dev_pm_ops = { SET_SYSTEM_SLEEP_PM_OPS(mmci_suspend, mmci_resume) - SET_RUNTIME_PM_OPS(mmci_runtime_suspend, - mmci_runtime_resume, - mmci_runtime_idle) + SET_RUNTIME_PM_OPS(mmci_runtime_suspend, mmci_runtime_resume, NULL) }; static struct amba_id mmci_ids[] = { diff --git a/drivers/mmc/host/mmci.h b/drivers/mmc/host/mmci.h index 2c0069d24e6..ba09a603787 100644 --- a/drivers/mmc/host/mmci.h +++ b/drivers/mmc/host/mmci.h @@ -186,6 +186,9 @@ struct mmci_host { unsigned int mclk; unsigned int cclk; + u32 pwr_reg; + u32 clk_reg; + u32 datactrl_reg; struct mmci_platform_data *plat; struct variant_data *variant; @@ -195,11 +198,6 @@ struct mmci_host { struct timer_list timer; unsigned int oldstat; - /* register cache */ - u32 irqmask0_reg; - u32 pwr_reg; - u32 clk_reg; - /* pio stuff */ struct sg_mapping_iter sg_miter; unsigned int size; |