From 5e4d3011401c3a248851c22b6aa92ee3b2397dfc Mon Sep 17 00:00:00 2001
From: Zhangfei Gao <zhangfei.gao@marvell.com>
Date: Fri, 3 Dec 2010 07:21:15 -0500
Subject: [PATCH] mmc: sdhci support emmc ddr50 mode
1. spec 3.0 does not claim support 1.2v ddr mode
2. Call back function set_power is added, since some controller count on external pmic to provide power
Signed-off-by: Zhangfei Gao <zhangfei.gao@marvell.com>
---
drivers/mmc/core/core.c | 6 ++++++
drivers/mmc/host/sdhci.c | 36 +++++++++++++++++++++++++++++++++++-
drivers/mmc/host/sdhci.h | 14 ++++++++++++--
include/linux/mmc/mmc.h | 1 +
include/linux/mmc/sdhci.h | 2 ++
5 files changed, 56 insertions(+), 3 deletions(-)
@@ -1573,6 +1573,12 @@ void mmc_rescan(struct work_struct *work)
*/
err = mmc_send_op_cond(host, 0, &ocr);
if (!err) {
+ if (ocr & MMC_CARD_1_8V) {
+ mmc_power_off(host);
+ host->ocr = ocr & host->ocr_avail;
+ mmc_power_up(host);
+ }
+
if (mmc_attach_mmc(host, ocr))
mmc_power_off(host);
goto out;
@@ -982,6 +982,22 @@ static void sdhci_finish_command(struct sdhci_host *host)
host->cmd = NULL;
}
+static void sdhci_set_ddr(struct sdhci_host *host, unsigned int ddr)
+{
+ u16 con;
+
+ if (ddr == MMC_SDR_MODE)
+ return;
+
+ con = sdhci_readw(host, SDHCI_HOST_CONTROL2);
+ if (con & SDHCI_CTRL2_1_8V) {
+ con &= ~SDHCI_CTRL2_UHS_MASK;
+ if (ddr & MMC_1_8V_DDR_MODE)
+ con |= SDHCI_CTRL2_DDR50;
+ sdhci_writew(host, con, SDHCI_HOST_CONTROL2);
+ }
+}
+
static void sdhci_set_clock(struct sdhci_host *host, unsigned int clock)
{
int div;
@@ -1080,6 +1096,17 @@ static void sdhci_set_power(struct sdhci_host *host, unsigned short power)
return;
}
+ if (pwr == SDHCI_POWER_180) {
+ u16 con;
+
+ con = sdhci_readw(host, SDHCI_HOST_CONTROL2);
+ con |= SDHCI_CTRL2_1_8V;
+ sdhci_writew(host, con, SDHCI_HOST_CONTROL2);
+
+ if (host->ops->set_power)
+ host->ops->set_power(host, pwr);
+ }
+
/*
* Spec says that we should clear the power reg before setting
* a new value. Some controllers don't seem to like this though.
@@ -1176,6 +1203,7 @@ static void sdhci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
}
sdhci_set_clock(host, ios->clock);
+ sdhci_set_ddr(host, ios->ddr);
if (ios->power_mode == MMC_POWER_OFF)
sdhci_set_power(host, -1);
@@ -1736,7 +1764,7 @@ EXPORT_SYMBOL_GPL(sdhci_alloc_host);
int sdhci_add_host(struct sdhci_host *host)
{
struct mmc_host *mmc;
- unsigned int caps;
+ unsigned int caps, caps_h;
int ret;
WARN_ON(host == NULL);
@@ -1761,6 +1789,12 @@ int sdhci_add_host(struct sdhci_host *host)
caps = (host->quirks & SDHCI_QUIRK_MISSING_CAPS) ? host->caps :
sdhci_readl(host, SDHCI_CAPABILITIES);
+ caps_h = sdhci_readl(host, SDHCI_CAPABILITIES_H);
+
+ if (caps & SDHCI_CAN_VDD_180) {
+ if (caps_h & SDHCI_CAN_SDR50)
+ mmc->caps |= (MMC_CAP_1_8V_DDR);
+ }
if (host->quirks & SDHCI_QUIRK_FORCE_DMA)
host->flags |= SDHCI_USE_SDMA;
@@ -144,7 +144,14 @@
#define SDHCI_ACMD12_ERR 0x3C
-/* 3E-3F reserved */
+#define SDHCI_HOST_CONTROL2 0x3E
+#define SDHCI_CTRL2_UHS_MASK 0x0007
+#define SDHCI_CTRL2_SDR12 0x0000
+#define SDHCI_CTRL2_SDR25 0x0001
+#define SDHCI_CTRL2_SDR50 0x0002
+#define SDHCI_CTRL2_SDR104 0x0003
+#define SDHCI_CTRL2_DDR50 0x0004
+#define SDHCI_CTRL2_1_8V 0x0008
#define SDHCI_CAPABILITIES 0x40
#define SDHCI_TIMEOUT_CLK_MASK 0x0000003F
@@ -165,7 +172,10 @@
#define SDHCI_CAN_VDD_180 0x04000000
#define SDHCI_CAN_64BIT 0x10000000
-/* 44-47 reserved for more caps */
+#define SDHCI_CAPABILITIES_H 0x44
+#define SDHCI_CAN_SDR50 0x00000001
+#define SDHCI_CAN_SDR104 0x00000002
+#define SDHCI_CAN_DDR50 0x00000004
#define SDHCI_MAX_CURRENT 0x48
@@ -202,6 +202,7 @@ struct _mmc_csd {
* OCR bits are mostly in host.h
*/
#define MMC_CARD_BUSY 0x80000000 /* Card Power up status bit */
+#define MMC_CARD_1_8V 0x00000080 /* Card 1.70-1.95V support bit */
/*
* Card Command Classes (CCC)
@@ -163,6 +163,8 @@ struct sdhci_ops {
void (*platform_send_init_74_clocks)(struct sdhci_host *host,
u8 power_mode);
unsigned int (*get_ro)(struct sdhci_host *host);
+ unsigned int (*set_power)(struct sdhci_host *host,
+ unsigned short power);
};
--
1.7.0.4