aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPhilippe Langlais <philippe.langlais@stericsson.com>2012-03-07 10:11:36 +0100
committerPhilippe Langlais <philippe.langlais@stericsson.com>2012-03-07 10:11:36 +0100
commitf7153e4a3a83403ce13f42359d87697d5d93194f (patch)
treebddf9f45ee0e46691db5cf16ee342782a429cf69
parentc18b10aa6e4c7564df321f234c05a5782f1ffb8d (diff)
parent51bf840d7a90df41856ac4afa34e1534e290e633 (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.c53
-rw-r--r--drivers/mmc/card/block.c1
-rw-r--r--drivers/mmc/core/core.c38
-rw-r--r--drivers/mmc/core/mmc.c3
-rw-r--r--drivers/mmc/core/sd.c8
-rw-r--r--drivers/mmc/core/sdio.c9
-rw-r--r--drivers/mmc/host/mmci.c503
-rw-r--r--drivers/mmc/host/mmci.h8
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;