|
@@ -53,6 +53,8 @@ static unsigned int fmax = 515633;
|
|
|
* @sdio: variant supports SDIO
|
|
|
* @st_clkdiv: true if using a ST-specific clock divider algorithm
|
|
|
* @blksz_datactrl16: true if Block size is at b16..b30 position in datactrl register
|
|
|
+ * @pwrreg_powerup: power up value for MMCIPOWER register
|
|
|
+ * @signal_direction: input/out direction of bus signals can be indicated
|
|
|
*/
|
|
|
struct variant_data {
|
|
|
unsigned int clkreg;
|
|
@@ -63,18 +65,22 @@ struct variant_data {
|
|
|
bool sdio;
|
|
|
bool st_clkdiv;
|
|
|
bool blksz_datactrl16;
|
|
|
+ u32 pwrreg_powerup;
|
|
|
+ bool signal_direction;
|
|
|
};
|
|
|
|
|
|
static struct variant_data variant_arm = {
|
|
|
.fifosize = 16 * 4,
|
|
|
.fifohalfsize = 8 * 4,
|
|
|
.datalength_bits = 16,
|
|
|
+ .pwrreg_powerup = MCI_PWR_UP,
|
|
|
};
|
|
|
|
|
|
static struct variant_data variant_arm_extended_fifo = {
|
|
|
.fifosize = 128 * 4,
|
|
|
.fifohalfsize = 64 * 4,
|
|
|
.datalength_bits = 16,
|
|
|
+ .pwrreg_powerup = MCI_PWR_UP,
|
|
|
};
|
|
|
|
|
|
static struct variant_data variant_u300 = {
|
|
@@ -83,6 +89,8 @@ static struct variant_data variant_u300 = {
|
|
|
.clkreg_enable = MCI_ST_U300_HWFCEN,
|
|
|
.datalength_bits = 16,
|
|
|
.sdio = true,
|
|
|
+ .pwrreg_powerup = MCI_PWR_ON,
|
|
|
+ .signal_direction = true,
|
|
|
};
|
|
|
|
|
|
static struct variant_data variant_ux500 = {
|
|
@@ -93,6 +101,8 @@ static struct variant_data variant_ux500 = {
|
|
|
.datalength_bits = 24,
|
|
|
.sdio = true,
|
|
|
.st_clkdiv = true,
|
|
|
+ .pwrreg_powerup = MCI_PWR_ON,
|
|
|
+ .signal_direction = true,
|
|
|
};
|
|
|
|
|
|
static struct variant_data variant_ux500v2 = {
|
|
@@ -104,8 +114,32 @@ static struct variant_data variant_ux500v2 = {
|
|
|
.sdio = true,
|
|
|
.st_clkdiv = true,
|
|
|
.blksz_datactrl16 = true,
|
|
|
+ .pwrreg_powerup = MCI_PWR_ON,
|
|
|
+ .signal_direction = true,
|
|
|
};
|
|
|
|
|
|
+/*
|
|
|
+ * 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
|
|
|
*/
|
|
@@ -153,7 +187,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
|
|
@@ -166,14 +200,10 @@ mmci_request_end(struct mmci_host *host, struct mmc_request *mrq)
|
|
|
host->mrq = NULL;
|
|
|
host->cmd = NULL;
|
|
|
|
|
|
- /*
|
|
|
- * Need to drop the host lock here; mmc_request_done may call
|
|
|
- * back into the driver...
|
|
|
- */
|
|
|
- spin_unlock(&host->lock);
|
|
|
- pm_runtime_put(mmc_dev(host->mmc));
|
|
|
mmc_request_done(host->mmc, mrq);
|
|
|
- spin_lock(&host->lock);
|
|
|
+
|
|
|
+ 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)
|
|
@@ -607,6 +637,11 @@ static void mmci_start_data(struct mmci_host *host, struct mmc_data *data)
|
|
|
if (data->flags & MMC_DATA_READ)
|
|
|
datactrl |= MCI_DPSM_DIRECTION;
|
|
|
|
|
|
+ /* The ST Micro variants has a special bit to enable SDIO */
|
|
|
+ if (variant->sdio && host->mmc->card)
|
|
|
+ if (mmc_card_sdio(host->mmc->card))
|
|
|
+ datactrl |= MCI_ST_DPSM_SDIOEN;
|
|
|
+
|
|
|
/*
|
|
|
* Attempt to use DMA operation mode, if this
|
|
|
* should fail, fall back to PIO mode
|
|
@@ -635,11 +670,6 @@ static void mmci_start_data(struct mmci_host *host, struct mmc_data *data)
|
|
|
irqmask = MCI_TXFIFOHALFEMPTYMASK;
|
|
|
}
|
|
|
|
|
|
- /* The ST Micro variants has a special bit to enable SDIO */
|
|
|
- if (variant->sdio && host->mmc->card)
|
|
|
- if (mmc_card_sdio(host->mmc->card))
|
|
|
- datactrl |= MCI_ST_DPSM_SDIOEN;
|
|
|
-
|
|
|
writel(datactrl, base + MMCIDATACTRL);
|
|
|
writel(readl(base + MMCIMASK0) & ~MCI_DATAENDMASK, base + MMCIMASK0);
|
|
|
mmci_set_mask1(host, irqmask);
|
|
@@ -786,7 +816,24 @@ static int mmci_pio_read(struct mmci_host *host, char *buffer, unsigned int rema
|
|
|
if (count <= 0)
|
|
|
break;
|
|
|
|
|
|
- readsl(base + MMCIFIFO, ptr, count >> 2);
|
|
|
+ /*
|
|
|
+ * SDIO especially may want to send something that is
|
|
|
+ * not divisible by 4 (as opposed to card sectors
|
|
|
+ * etc). Therefore make sure to always read the last bytes
|
|
|
+ * while only doing full 32-bit reads towards the FIFO.
|
|
|
+ */
|
|
|
+ if (unlikely(count & 0x3)) {
|
|
|
+ if (count < 4) {
|
|
|
+ unsigned char buf[4];
|
|
|
+ readsl(base + MMCIFIFO, buf, 1);
|
|
|
+ memcpy(ptr, buf, count);
|
|
|
+ } else {
|
|
|
+ readsl(base + MMCIFIFO, ptr, count >> 2);
|
|
|
+ count &= ~0x3;
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ readsl(base + MMCIFIFO, ptr, count >> 2);
|
|
|
+ }
|
|
|
|
|
|
ptr += count;
|
|
|
remain -= count;
|
|
@@ -821,14 +868,13 @@ static int mmci_pio_write(struct mmci_host *host, char *buffer, unsigned int rem
|
|
|
*/
|
|
|
if (variant->sdio &&
|
|
|
mmc_card_sdio(host->mmc->card)) {
|
|
|
+ u32 clk;
|
|
|
if (count < 8)
|
|
|
- writel(readl(host->base + MMCICLOCK) &
|
|
|
- ~variant->clkreg_enable,
|
|
|
- host->base + MMCICLOCK);
|
|
|
+ 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);
|
|
|
}
|
|
|
|
|
|
/*
|
|
@@ -1015,10 +1061,17 @@ static void mmci_request(struct mmc_host *mmc, struct mmc_request *mrq)
|
|
|
static void mmci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
|
|
|
{
|
|
|
struct mmci_host *host = mmc_priv(mmc);
|
|
|
+ struct variant_data *variant = host->variant;
|
|
|
u32 pwr = 0;
|
|
|
unsigned long flags;
|
|
|
int ret;
|
|
|
|
|
|
+ pm_runtime_get_sync(mmc_dev(mmc));
|
|
|
+
|
|
|
+ if (host->plat->ios_handler &&
|
|
|
+ host->plat->ios_handler(mmc_dev(mmc), ios))
|
|
|
+ dev_err(mmc_dev(mmc), "platform ios_handler failed\n");
|
|
|
+
|
|
|
switch (ios->power_mode) {
|
|
|
case MMC_POWER_OFF:
|
|
|
if (host->vcc)
|
|
@@ -1035,22 +1088,38 @@ 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;
|
|
|
}
|
|
|
}
|
|
|
- if (host->plat->vdd_handler)
|
|
|
- pwr |= host->plat->vdd_handler(mmc_dev(mmc), ios->vdd,
|
|
|
- ios->power_mode);
|
|
|
- /* The ST version does not have this, fall through to POWER_ON */
|
|
|
- if (host->hw_designer != AMBA_VENDOR_ST) {
|
|
|
- pwr |= MCI_PWR_UP;
|
|
|
- break;
|
|
|
- }
|
|
|
+ /*
|
|
|
+ * The ST Micro variant doesn't have the PL180s MCI_PWR_UP
|
|
|
+ * and instead uses MCI_PWR_ON so apply whatever value is
|
|
|
+ * configured in the variant data.
|
|
|
+ */
|
|
|
+ pwr |= variant->pwrreg_powerup;
|
|
|
+
|
|
|
+ break;
|
|
|
case MMC_POWER_ON:
|
|
|
pwr |= MCI_PWR_ON;
|
|
|
break;
|
|
|
}
|
|
|
|
|
|
+ if (variant->signal_direction && ios->power_mode != MMC_POWER_OFF) {
|
|
|
+ /*
|
|
|
+ * The ST Micro variant has some additional bits
|
|
|
+ * indicating signal direction for the signals in
|
|
|
+ * the SD/MMC bus and feedback-clock usage.
|
|
|
+ */
|
|
|
+ pwr |= host->plat->sigdir;
|
|
|
+
|
|
|
+ if (ios->bus_width == MMC_BUS_WIDTH_4)
|
|
|
+ pwr &= ~MCI_ST_DATA74DIREN;
|
|
|
+ else if (ios->bus_width == MMC_BUS_WIDTH_1)
|
|
|
+ pwr &= (~MCI_ST_DATA74DIREN &
|
|
|
+ ~MCI_ST_DATA31DIREN &
|
|
|
+ ~MCI_ST_DATA2DIREN);
|
|
|
+ }
|
|
|
+
|
|
|
if (ios->bus_mode == MMC_BUSMODE_OPENDRAIN) {
|
|
|
if (host->hw_designer != AMBA_VENDOR_ST)
|
|
|
pwr |= MCI_ROD;
|
|
@@ -1066,13 +1135,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 != pwr) {
|
|
|
- host->pwr = pwr;
|
|
|
- writel(pwr, host->base + MMCIPOWER);
|
|
|
- }
|
|
|
+ mmci_write_pwrreg(host, pwr);
|
|
|
|
|
|
spin_unlock_irqrestore(&host->lock, flags);
|
|
|
+
|
|
|
+ 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)
|
|
@@ -1345,6 +1414,8 @@ static int __devinit mmci_probe(struct amba_device *dev,
|
|
|
|
|
|
mmci_dma_setup(host);
|
|
|
|
|
|
+ pm_runtime_set_autosuspend_delay(&dev->dev, 50);
|
|
|
+ pm_runtime_use_autosuspend(&dev->dev);
|
|
|
pm_runtime_put(&dev->dev);
|
|
|
|
|
|
mmc_add_host(mmc);
|
|
@@ -1429,43 +1500,49 @@ static int __devexit mmci_remove(struct amba_device *dev)
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
-#ifdef CONFIG_PM
|
|
|
-static int mmci_suspend(struct amba_device *dev, pm_message_t state)
|
|
|
+#ifdef CONFIG_SUSPEND
|
|
|
+static int mmci_suspend(struct device *dev)
|
|
|
{
|
|
|
- struct mmc_host *mmc = amba_get_drvdata(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 == 0)
|
|
|
+ if (ret == 0) {
|
|
|
+ pm_runtime_get_sync(dev);
|
|
|
writel(0, host->base + MMCIMASK0);
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
return ret;
|
|
|
}
|
|
|
|
|
|
-static int mmci_resume(struct amba_device *dev)
|
|
|
+static int mmci_resume(struct device *dev)
|
|
|
{
|
|
|
- struct mmc_host *mmc = amba_get_drvdata(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);
|
|
|
|
|
|
writel(MCI_IRQENABLE, host->base + MMCIMASK0);
|
|
|
+ pm_runtime_put(dev);
|
|
|
|
|
|
ret = mmc_resume_host(mmc);
|
|
|
}
|
|
|
|
|
|
return ret;
|
|
|
}
|
|
|
-#else
|
|
|
-#define mmci_suspend NULL
|
|
|
-#define mmci_resume NULL
|
|
|
#endif
|
|
|
|
|
|
+static const struct dev_pm_ops mmci_dev_pm_ops = {
|
|
|
+ SET_SYSTEM_SLEEP_PM_OPS(mmci_suspend, mmci_resume)
|
|
|
+};
|
|
|
+
|
|
|
static struct amba_id mmci_ids[] = {
|
|
|
{
|
|
|
.id = 0x00041180,
|
|
@@ -1511,11 +1588,10 @@ MODULE_DEVICE_TABLE(amba, mmci_ids);
|
|
|
static struct amba_driver mmci_driver = {
|
|
|
.drv = {
|
|
|
.name = DRIVER_NAME,
|
|
|
+ .pm = &mmci_dev_pm_ops,
|
|
|
},
|
|
|
.probe = mmci_probe,
|
|
|
.remove = __devexit_p(mmci_remove),
|
|
|
- .suspend = mmci_suspend,
|
|
|
- .resume = mmci_resume,
|
|
|
.id_table = mmci_ids,
|
|
|
};
|
|
|
|