|
@@ -9,26 +9,17 @@
|
|
|
* it under the terms of the GNU General Public License version 2 as
|
|
|
* published by the Free Software Foundation.
|
|
|
*/
|
|
|
-#include <linux/err.h>
|
|
|
-#include <linux/io.h>
|
|
|
-#include <linux/module.h>
|
|
|
-#include <linux/platform_device.h>
|
|
|
-#include <linux/interrupt.h>
|
|
|
+#include <linux/kernel.h>
|
|
|
+#include <linux/slab.h>
|
|
|
+#include <linux/string.h>
|
|
|
#include <linux/delay.h>
|
|
|
-#include <linux/gpio.h>
|
|
|
-#include <linux/mmc/host.h>
|
|
|
-#include <linux/regulator/consumer.h>
|
|
|
-
|
|
|
#include <mach/hardware.h>
|
|
|
#include <plat/control.h>
|
|
|
#include <plat/mmc.h>
|
|
|
-#include <plat/board.h>
|
|
|
|
|
|
#include "mmc-twl4030.h"
|
|
|
|
|
|
-
|
|
|
-#if defined(CONFIG_REGULATOR) && \
|
|
|
- (defined(CONFIG_MMC_OMAP_HS) || defined(CONFIG_MMC_OMAP_HS_MODULE))
|
|
|
+#if defined(CONFIG_MMC_OMAP_HS) || defined(CONFIG_MMC_OMAP_HS_MODULE)
|
|
|
|
|
|
static u16 control_pbias_offset;
|
|
|
static u16 control_devconf1_offset;
|
|
@@ -36,168 +27,9 @@ static u16 control_devconf1_offset;
|
|
|
#define HSMMC_NAME_LEN 9
|
|
|
|
|
|
static struct twl_mmc_controller {
|
|
|
- struct omap_mmc_platform_data *mmc;
|
|
|
- /* Vcc == configured supply
|
|
|
- * Vcc_alt == optional
|
|
|
- * - MMC1, supply for DAT4..DAT7
|
|
|
- * - MMC2/MMC2, external level shifter voltage supply, for
|
|
|
- * chip (SDIO, eMMC, etc) or transceiver (MMC2 only)
|
|
|
- */
|
|
|
- struct regulator *vcc;
|
|
|
- struct regulator *vcc_aux;
|
|
|
char name[HSMMC_NAME_LEN + 1];
|
|
|
} hsmmc[OMAP34XX_NR_MMC];
|
|
|
|
|
|
-static int twl_mmc_card_detect(int irq)
|
|
|
-{
|
|
|
- unsigned i;
|
|
|
-
|
|
|
- for (i = 0; i < ARRAY_SIZE(hsmmc); i++) {
|
|
|
- struct omap_mmc_platform_data *mmc;
|
|
|
-
|
|
|
- mmc = hsmmc[i].mmc;
|
|
|
- if (!mmc)
|
|
|
- continue;
|
|
|
- if (irq != mmc->slots[0].card_detect_irq)
|
|
|
- continue;
|
|
|
-
|
|
|
- /* NOTE: assumes card detect signal is active-low */
|
|
|
- return !gpio_get_value_cansleep(mmc->slots[0].switch_pin);
|
|
|
- }
|
|
|
- return -ENOSYS;
|
|
|
-}
|
|
|
-
|
|
|
-static int twl_mmc_get_ro(struct device *dev, int slot)
|
|
|
-{
|
|
|
- struct omap_mmc_platform_data *mmc = dev->platform_data;
|
|
|
-
|
|
|
- /* NOTE: assumes write protect signal is active-high */
|
|
|
- return gpio_get_value_cansleep(mmc->slots[0].gpio_wp);
|
|
|
-}
|
|
|
-
|
|
|
-static int twl_mmc_get_cover_state(struct device *dev, int slot)
|
|
|
-{
|
|
|
- struct omap_mmc_platform_data *mmc = dev->platform_data;
|
|
|
-
|
|
|
- /* NOTE: assumes card detect signal is active-low */
|
|
|
- return !gpio_get_value_cansleep(mmc->slots[0].switch_pin);
|
|
|
-}
|
|
|
-
|
|
|
-/*
|
|
|
- * MMC Slot Initialization.
|
|
|
- */
|
|
|
-static int twl_mmc_late_init(struct device *dev)
|
|
|
-{
|
|
|
- struct omap_mmc_platform_data *mmc = dev->platform_data;
|
|
|
- int ret = 0;
|
|
|
- int i;
|
|
|
-
|
|
|
- /* MMC/SD/SDIO doesn't require a card detect switch */
|
|
|
- if (gpio_is_valid(mmc->slots[0].switch_pin)) {
|
|
|
- ret = gpio_request(mmc->slots[0].switch_pin, "mmc_cd");
|
|
|
- if (ret)
|
|
|
- goto done;
|
|
|
- ret = gpio_direction_input(mmc->slots[0].switch_pin);
|
|
|
- if (ret)
|
|
|
- goto err;
|
|
|
- }
|
|
|
-
|
|
|
- /* require at least main regulator */
|
|
|
- for (i = 0; i < ARRAY_SIZE(hsmmc); i++) {
|
|
|
- if (hsmmc[i].name == mmc->slots[0].name) {
|
|
|
- struct regulator *reg;
|
|
|
-
|
|
|
- hsmmc[i].mmc = mmc;
|
|
|
-
|
|
|
- reg = regulator_get(dev, "vmmc");
|
|
|
- if (IS_ERR(reg)) {
|
|
|
- dev_dbg(dev, "vmmc regulator missing\n");
|
|
|
- /* HACK: until fixed.c regulator is usable,
|
|
|
- * we don't require a main regulator
|
|
|
- * for MMC2 or MMC3
|
|
|
- */
|
|
|
- if (i != 0)
|
|
|
- break;
|
|
|
- ret = PTR_ERR(reg);
|
|
|
- hsmmc[i].vcc = NULL;
|
|
|
- goto err;
|
|
|
- }
|
|
|
- hsmmc[i].vcc = reg;
|
|
|
- mmc->slots[0].ocr_mask = mmc_regulator_get_ocrmask(reg);
|
|
|
-
|
|
|
- /* allow an aux regulator */
|
|
|
- reg = regulator_get(dev, "vmmc_aux");
|
|
|
- hsmmc[i].vcc_aux = IS_ERR(reg) ? NULL : reg;
|
|
|
-
|
|
|
- /* UGLY HACK: workaround regulator framework bugs.
|
|
|
- * When the bootloader leaves a supply active, it's
|
|
|
- * initialized with zero usecount ... and we can't
|
|
|
- * disable it without first enabling it. Until the
|
|
|
- * framework is fixed, we need a workaround like this
|
|
|
- * (which is safe for MMC, but not in general).
|
|
|
- */
|
|
|
- if (regulator_is_enabled(hsmmc[i].vcc) > 0) {
|
|
|
- regulator_enable(hsmmc[i].vcc);
|
|
|
- regulator_disable(hsmmc[i].vcc);
|
|
|
- }
|
|
|
- if (hsmmc[i].vcc_aux) {
|
|
|
- if (regulator_is_enabled(reg) > 0) {
|
|
|
- regulator_enable(reg);
|
|
|
- regulator_disable(reg);
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- break;
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- return 0;
|
|
|
-
|
|
|
-err:
|
|
|
- gpio_free(mmc->slots[0].switch_pin);
|
|
|
-done:
|
|
|
- mmc->slots[0].card_detect_irq = 0;
|
|
|
- mmc->slots[0].card_detect = NULL;
|
|
|
-
|
|
|
- dev_err(dev, "err %d configuring card detect\n", ret);
|
|
|
- return ret;
|
|
|
-}
|
|
|
-
|
|
|
-static void twl_mmc_cleanup(struct device *dev)
|
|
|
-{
|
|
|
- struct omap_mmc_platform_data *mmc = dev->platform_data;
|
|
|
- int i;
|
|
|
-
|
|
|
- gpio_free(mmc->slots[0].switch_pin);
|
|
|
- for(i = 0; i < ARRAY_SIZE(hsmmc); i++) {
|
|
|
- regulator_put(hsmmc[i].vcc);
|
|
|
- regulator_put(hsmmc[i].vcc_aux);
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-#ifdef CONFIG_PM
|
|
|
-
|
|
|
-static int twl_mmc_suspend(struct device *dev, int slot)
|
|
|
-{
|
|
|
- struct omap_mmc_platform_data *mmc = dev->platform_data;
|
|
|
-
|
|
|
- disable_irq(mmc->slots[0].card_detect_irq);
|
|
|
- return 0;
|
|
|
-}
|
|
|
-
|
|
|
-static int twl_mmc_resume(struct device *dev, int slot)
|
|
|
-{
|
|
|
- struct omap_mmc_platform_data *mmc = dev->platform_data;
|
|
|
-
|
|
|
- enable_irq(mmc->slots[0].card_detect_irq);
|
|
|
- return 0;
|
|
|
-}
|
|
|
-
|
|
|
-#else
|
|
|
-#define twl_mmc_suspend NULL
|
|
|
-#define twl_mmc_resume NULL
|
|
|
-#endif
|
|
|
-
|
|
|
#if defined(CONFIG_ARCH_OMAP3) && defined(CONFIG_PM)
|
|
|
|
|
|
static int twl4030_mmc_get_context_loss(struct device *dev)
|
|
@@ -210,12 +42,10 @@ static int twl4030_mmc_get_context_loss(struct device *dev)
|
|
|
#define twl4030_mmc_get_context_loss NULL
|
|
|
#endif
|
|
|
|
|
|
-static int twl_mmc1_set_power(struct device *dev, int slot, int power_on,
|
|
|
- int vdd)
|
|
|
+static void hsmmc1_before_set_reg(struct device *dev, int slot,
|
|
|
+ int power_on, int vdd)
|
|
|
{
|
|
|
u32 reg, prog_io;
|
|
|
- int ret = 0;
|
|
|
- struct twl_mmc_controller *c = &hsmmc[0];
|
|
|
struct omap_mmc_platform_data *mmc = dev->platform_data;
|
|
|
|
|
|
/*
|
|
@@ -255,11 +85,22 @@ static int twl_mmc1_set_power(struct device *dev, int slot, int power_on,
|
|
|
}
|
|
|
reg &= ~OMAP2_PBIASLITEPWRDNZ0;
|
|
|
omap_ctrl_writel(reg, control_pbias_offset);
|
|
|
+ } else {
|
|
|
+ reg = omap_ctrl_readl(control_pbias_offset);
|
|
|
+ reg &= ~OMAP2_PBIASLITEPWRDNZ0;
|
|
|
+ omap_ctrl_writel(reg, control_pbias_offset);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+static void hsmmc1_after_set_reg(struct device *dev, int slot,
|
|
|
+ int power_on, int vdd)
|
|
|
+{
|
|
|
+ u32 reg;
|
|
|
|
|
|
- ret = mmc_regulator_set_ocr(c->vcc, vdd);
|
|
|
+ /* 100ms delay required for PBIAS configuration */
|
|
|
+ msleep(100);
|
|
|
|
|
|
- /* 100ms delay required for PBIAS configuration */
|
|
|
- msleep(100);
|
|
|
+ if (power_on) {
|
|
|
reg = omap_ctrl_readl(control_pbias_offset);
|
|
|
reg |= (OMAP2_PBIASLITEPWRDNZ0 | OMAP2_PBIASSPEEDCTRL0);
|
|
|
if ((1 << vdd) <= MMC_VDD_165_195)
|
|
@@ -268,61 +109,20 @@ static int twl_mmc1_set_power(struct device *dev, int slot, int power_on,
|
|
|
reg |= OMAP2_PBIASLITEVMODE0;
|
|
|
omap_ctrl_writel(reg, control_pbias_offset);
|
|
|
} else {
|
|
|
- reg = omap_ctrl_readl(control_pbias_offset);
|
|
|
- reg &= ~OMAP2_PBIASLITEPWRDNZ0;
|
|
|
- omap_ctrl_writel(reg, control_pbias_offset);
|
|
|
-
|
|
|
- ret = mmc_regulator_set_ocr(c->vcc, 0);
|
|
|
-
|
|
|
- /* 100ms delay required for PBIAS configuration */
|
|
|
- msleep(100);
|
|
|
reg = omap_ctrl_readl(control_pbias_offset);
|
|
|
reg |= (OMAP2_PBIASSPEEDCTRL0 | OMAP2_PBIASLITEPWRDNZ0 |
|
|
|
OMAP2_PBIASLITEVMODE0);
|
|
|
omap_ctrl_writel(reg, control_pbias_offset);
|
|
|
}
|
|
|
-
|
|
|
- return ret;
|
|
|
}
|
|
|
|
|
|
-static int twl_mmc23_set_power(struct device *dev, int slot, int power_on, int vdd)
|
|
|
+static void hsmmc23_before_set_reg(struct device *dev, int slot,
|
|
|
+ int power_on, int vdd)
|
|
|
{
|
|
|
- int ret = 0;
|
|
|
- struct twl_mmc_controller *c = NULL;
|
|
|
struct omap_mmc_platform_data *mmc = dev->platform_data;
|
|
|
- int i;
|
|
|
-
|
|
|
- for (i = 1; i < ARRAY_SIZE(hsmmc); i++) {
|
|
|
- if (mmc == hsmmc[i].mmc) {
|
|
|
- c = &hsmmc[i];
|
|
|
- break;
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- if (c == NULL)
|
|
|
- return -ENODEV;
|
|
|
-
|
|
|
- /* If we don't see a Vcc regulator, assume it's a fixed
|
|
|
- * voltage always-on regulator.
|
|
|
- */
|
|
|
- if (!c->vcc)
|
|
|
- return 0;
|
|
|
|
|
|
- /*
|
|
|
- * Assume Vcc regulator is used only to power the card ... OMAP
|
|
|
- * VDDS is used to power the pins, optionally with a transceiver to
|
|
|
- * support cards using voltages other than VDDS (1.8V nominal). When a
|
|
|
- * transceiver is used, DAT3..7 are muxed as transceiver control pins.
|
|
|
- *
|
|
|
- * In some cases this regulator won't support enable/disable;
|
|
|
- * e.g. it's a fixed rail for a WLAN chip.
|
|
|
- *
|
|
|
- * In other cases vcc_aux switches interface power. Example, for
|
|
|
- * eMMC cards it represents VccQ. Sometimes transceivers or SDIO
|
|
|
- * chips/cards need an interface voltage rail too.
|
|
|
- */
|
|
|
if (power_on) {
|
|
|
- /* only MMC2 supports a CLKIN */
|
|
|
+ /* Only MMC2 supports a CLKIN */
|
|
|
if (mmc->slots[0].internal_clock) {
|
|
|
u32 reg;
|
|
|
|
|
@@ -330,76 +130,7 @@ static int twl_mmc23_set_power(struct device *dev, int slot, int power_on, int v
|
|
|
reg |= OMAP2_MMCSDIO2ADPCLKISEL;
|
|
|
omap_ctrl_writel(reg, control_devconf1_offset);
|
|
|
}
|
|
|
- ret = mmc_regulator_set_ocr(c->vcc, vdd);
|
|
|
- /* enable interface voltage rail, if needed */
|
|
|
- if (ret == 0 && c->vcc_aux) {
|
|
|
- ret = regulator_enable(c->vcc_aux);
|
|
|
- if (ret < 0)
|
|
|
- ret = mmc_regulator_set_ocr(c->vcc, 0);
|
|
|
- }
|
|
|
- } else {
|
|
|
- if (c->vcc_aux && (ret = regulator_is_enabled(c->vcc_aux)) > 0)
|
|
|
- ret = regulator_disable(c->vcc_aux);
|
|
|
- if (ret == 0)
|
|
|
- ret = mmc_regulator_set_ocr(c->vcc, 0);
|
|
|
- }
|
|
|
-
|
|
|
- return ret;
|
|
|
-}
|
|
|
-
|
|
|
-static int twl_mmc1_set_sleep(struct device *dev, int slot, int sleep, int vdd,
|
|
|
- int cardsleep)
|
|
|
-{
|
|
|
- struct twl_mmc_controller *c = &hsmmc[0];
|
|
|
- int mode = sleep ? REGULATOR_MODE_STANDBY : REGULATOR_MODE_NORMAL;
|
|
|
-
|
|
|
- return regulator_set_mode(c->vcc, mode);
|
|
|
-}
|
|
|
-
|
|
|
-static int twl_mmc23_set_sleep(struct device *dev, int slot, int sleep, int vdd,
|
|
|
- int cardsleep)
|
|
|
-{
|
|
|
- struct twl_mmc_controller *c = NULL;
|
|
|
- struct omap_mmc_platform_data *mmc = dev->platform_data;
|
|
|
- int i, err, mode;
|
|
|
-
|
|
|
- for (i = 1; i < ARRAY_SIZE(hsmmc); i++) {
|
|
|
- if (mmc == hsmmc[i].mmc) {
|
|
|
- c = &hsmmc[i];
|
|
|
- break;
|
|
|
- }
|
|
|
}
|
|
|
-
|
|
|
- if (c == NULL)
|
|
|
- return -ENODEV;
|
|
|
-
|
|
|
- /*
|
|
|
- * If we don't see a Vcc regulator, assume it's a fixed
|
|
|
- * voltage always-on regulator.
|
|
|
- */
|
|
|
- if (!c->vcc)
|
|
|
- return 0;
|
|
|
-
|
|
|
- mode = sleep ? REGULATOR_MODE_STANDBY : REGULATOR_MODE_NORMAL;
|
|
|
-
|
|
|
- if (!c->vcc_aux)
|
|
|
- return regulator_set_mode(c->vcc, mode);
|
|
|
-
|
|
|
- if (cardsleep) {
|
|
|
- /* VCC can be turned off if card is asleep */
|
|
|
- struct regulator *vcc_aux = c->vcc_aux;
|
|
|
-
|
|
|
- c->vcc_aux = NULL;
|
|
|
- if (sleep)
|
|
|
- err = twl_mmc23_set_power(dev, slot, 0, 0);
|
|
|
- else
|
|
|
- err = twl_mmc23_set_power(dev, slot, 1, vdd);
|
|
|
- c->vcc_aux = vcc_aux;
|
|
|
- } else
|
|
|
- err = regulator_set_mode(c->vcc, mode);
|
|
|
- if (err)
|
|
|
- return err;
|
|
|
- return regulator_set_mode(c->vcc_aux, mode);
|
|
|
}
|
|
|
|
|
|
static struct omap_mmc_platform_data *hsmmc_data[OMAP34XX_NR_MMC] __initdata;
|
|
@@ -413,7 +144,6 @@ void __init twl4030_mmc_init(struct twl4030_hsmmc_info *controllers)
|
|
|
if (cpu_is_omap2430()) {
|
|
|
control_pbias_offset = OMAP243X_CONTROL_PBIAS_LITE;
|
|
|
control_devconf1_offset = OMAP243X_CONTROL_DEVCONF1;
|
|
|
- nr_hsmmc = 2;
|
|
|
} else {
|
|
|
control_pbias_offset = OMAP343X_CONTROL_PBIAS_LITE;
|
|
|
control_devconf1_offset = OMAP343X_CONTROL_DEVCONF1;
|
|
@@ -448,35 +178,15 @@ void __init twl4030_mmc_init(struct twl4030_hsmmc_info *controllers)
|
|
|
mmc->slots[0].wires = c->wires;
|
|
|
mmc->slots[0].internal_clock = !c->ext_clock;
|
|
|
mmc->dma_mask = 0xffffffff;
|
|
|
- mmc->init = twl_mmc_late_init;
|
|
|
-
|
|
|
- /* note: twl4030 card detect GPIOs can disable VMMCx ... */
|
|
|
- if (gpio_is_valid(c->gpio_cd)) {
|
|
|
- mmc->cleanup = twl_mmc_cleanup;
|
|
|
- mmc->suspend = twl_mmc_suspend;
|
|
|
- mmc->resume = twl_mmc_resume;
|
|
|
-
|
|
|
- mmc->slots[0].switch_pin = c->gpio_cd;
|
|
|
- mmc->slots[0].card_detect_irq = gpio_to_irq(c->gpio_cd);
|
|
|
- if (c->cover_only)
|
|
|
- mmc->slots[0].get_cover_state = twl_mmc_get_cover_state;
|
|
|
- else
|
|
|
- mmc->slots[0].card_detect = twl_mmc_card_detect;
|
|
|
- } else
|
|
|
- mmc->slots[0].switch_pin = -EINVAL;
|
|
|
|
|
|
mmc->get_context_loss_count =
|
|
|
twl4030_mmc_get_context_loss;
|
|
|
|
|
|
- /* write protect normally uses an OMAP gpio */
|
|
|
- if (gpio_is_valid(c->gpio_wp)) {
|
|
|
- gpio_request(c->gpio_wp, "mmc_wp");
|
|
|
- gpio_direction_input(c->gpio_wp);
|
|
|
+ mmc->slots[0].switch_pin = c->gpio_cd;
|
|
|
+ mmc->slots[0].gpio_wp = c->gpio_wp;
|
|
|
|
|
|
- mmc->slots[0].gpio_wp = c->gpio_wp;
|
|
|
- mmc->slots[0].get_ro = twl_mmc_get_ro;
|
|
|
- } else
|
|
|
- mmc->slots[0].gpio_wp = -EINVAL;
|
|
|
+ if (c->cover_only)
|
|
|
+ mmc->slots[0].cover = 1;
|
|
|
|
|
|
if (c->nonremovable)
|
|
|
mmc->slots[0].nonremovable = 1;
|
|
@@ -495,8 +205,8 @@ void __init twl4030_mmc_init(struct twl4030_hsmmc_info *controllers)
|
|
|
switch (c->mmc) {
|
|
|
case 1:
|
|
|
/* on-chip level shifting via PBIAS0/PBIAS1 */
|
|
|
- mmc->slots[0].set_power = twl_mmc1_set_power;
|
|
|
- mmc->slots[0].set_sleep = twl_mmc1_set_sleep;
|
|
|
+ mmc->slots[0].before_set_reg = hsmmc1_before_set_reg;
|
|
|
+ mmc->slots[0].after_set_reg = hsmmc1_after_set_reg;
|
|
|
|
|
|
/* Omap3630 HSMMC1 supports only 4-bit */
|
|
|
if (cpu_is_omap3630() && c->wires > 4) {
|
|
@@ -512,8 +222,8 @@ void __init twl4030_mmc_init(struct twl4030_hsmmc_info *controllers)
|
|
|
/* FALLTHROUGH */
|
|
|
case 3:
|
|
|
/* off-chip level shifting, or none */
|
|
|
- mmc->slots[0].set_power = twl_mmc23_set_power;
|
|
|
- mmc->slots[0].set_sleep = twl_mmc23_set_sleep;
|
|
|
+ mmc->slots[0].before_set_reg = hsmmc23_before_set_reg;
|
|
|
+ mmc->slots[0].after_set_reg = NULL;
|
|
|
break;
|
|
|
default:
|
|
|
pr_err("MMC%d configuration not supported!\n", c->mmc);
|