Commit db0fefc5 authored by Adrian Hunter's avatar Adrian Hunter Committed by Tony Lindgren
Browse files

omap_hsmmc: Move gpio and regulator control from board file



This patch moves the setup code for GPIO's and Voltage
Regulators from the board file mmc-twl4030.c to the
driver omap_hsmmc.c.  PBIAS and other system control
configuration remains in the board file.

Moving GPIO code to the driver makes the board initialisation
code independent of when GPIO's are defined.  That makes the
board initialisation now entirely independent of its original
twl4030 roots.

Moving Voltage Regulator code to the driver allows for further
development of regulator support in the core MMC code.  It also
permits the MMC core to be compiled as a module, because the
board code no longer calls MMC core functions.
Signed-off-by: default avatarAdrian Hunter <adrian.hunter@nokia.com>
Signed-off-by: default avatarTony Lindgren <tony@atomide.com>
parent 88c8460a
......@@ -1354,7 +1354,7 @@ CONFIG_USB_OTG_UTILS=y
# CONFIG_USB_GPIO_VBUS is not set
# CONFIG_ISP1301_OMAP is not set
CONFIG_TWL4030_USB=y
CONFIG_MMC=y
CONFIG_MMC=m
# CONFIG_MMC_DEBUG is not set
# CONFIG_MMC_UNSAFE_RESUME is not set
......@@ -1362,7 +1362,7 @@ CONFIG_MMC=y
# MMC/SD/SDIO Card Drivers
#
CONFIG_MMC_BLOCK=m
CONFIG_MMC_BLOCK_BOUNCE=y
# CONFIG_MMC_BLOCK_BOUNCE is not set
# CONFIG_SDIO_UART is not set
# CONFIG_MMC_TEST is not set
......
......@@ -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);
......
......@@ -21,9 +21,7 @@ struct twl4030_hsmmc_info {
int ocr_mask; /* temporary HACK */
};
#if defined(CONFIG_REGULATOR) && \
(defined(CONFIG_MMC_OMAP) || defined(CONFIG_MMC_OMAP_MODULE) || \
defined(CONFIG_MMC_OMAP_HS) || defined(CONFIG_MMC_OMAP_HS_MODULE))
#if defined(CONFIG_MMC_OMAP_HS) || defined(CONFIG_MMC_OMAP_HS_MODULE)
void twl4030_mmc_init(struct twl4030_hsmmc_info *);
......
......@@ -55,12 +55,12 @@ struct omap_mmc_platform_data {
unsigned int max_freq;
/* switch the bus to a new slot */
int (* switch_slot)(struct device *dev, int slot);
int (*switch_slot)(struct device *dev, int slot);
/* initialize board-specific MMC functionality, can be NULL if
* not supported */
int (* init)(struct device *dev);
void (* cleanup)(struct device *dev);
void (* shutdown)(struct device *dev);
int (*init)(struct device *dev);
void (*cleanup)(struct device *dev);
void (*shutdown)(struct device *dev);
/* To handle board related suspend/resume functionality for MMC */
int (*suspend)(struct device *dev, int slot);
......@@ -99,11 +99,18 @@ struct omap_mmc_platform_data {
int switch_pin; /* gpio (card detect) */
int gpio_wp; /* gpio (write protect) */
int (* set_bus_mode)(struct device *dev, int slot, int bus_mode);
int (* set_power)(struct device *dev, int slot, int power_on, int vdd);
int (* get_ro)(struct device *dev, int slot);
int (*set_bus_mode)(struct device *dev, int slot, int bus_mode);
int (*set_power)(struct device *dev, int slot,
int power_on, int vdd);
int (*get_ro)(struct device *dev, int slot);
int (*set_sleep)(struct device *dev, int slot, int sleep,
int vdd, int cardsleep);
/* Call back before enabling / disabling regulators */
void (*before_set_reg)(struct device *dev, int slot,
int power_on, int vdd);
/* Call back after enabling / disabling regulators */
void (*after_set_reg)(struct device *dev, int slot,
int power_on, int vdd);
/* return MMC cover switch state, can be NULL if not supported.
*
......@@ -111,14 +118,14 @@ struct omap_mmc_platform_data {
* 0 - closed
* 1 - open
*/
int (* get_cover_state)(struct device *dev, int slot);
int (*get_cover_state)(struct device *dev, int slot);
const char *name;
u32 ocr_mask;
/* Card detection IRQs */
int card_detect_irq;
int (* card_detect)(int irq);
int (*card_detect)(struct device *dev, int slot);
unsigned int ban_openended:1;
......@@ -126,7 +133,8 @@ struct omap_mmc_platform_data {
};
/* called from board-specific card detection service routine */
extern void omap_mmc_notify_cover_event(struct device *dev, int slot, int is_closed);
extern void omap_mmc_notify_cover_event(struct device *dev, int slot,
int is_closed);
#if defined(CONFIG_MMC_OMAP) || defined(CONFIG_MMC_OMAP_MODULE) || \
defined(CONFIG_MMC_OMAP_HS) || defined(CONFIG_MMC_OMAP_HS_MODULE)
......
......@@ -30,6 +30,8 @@
#include <linux/mmc/core.h>
#include <linux/io.h>
#include <linux/semaphore.h>
#include <linux/gpio.h>
#include <linux/regulator/consumer.h>
#include <plat/dma.h>
#include <mach/hardware.h>
#include <plat/board.h>
......@@ -146,6 +148,15 @@ struct omap_hsmmc_host {
struct clk *fclk;
struct clk *iclk;
struct clk *dbclk;
/*
* vcc == configured supply
* vcc_aux == 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;
struct semaphore sem;
struct work_struct mmc_carddetect_work;
void __iomem *base;
......@@ -171,10 +182,308 @@ struct omap_hsmmc_host {
int vdd;
int protect_card;
int reqs_blocked;
int use_reg;
struct omap_mmc_platform_data *pdata;
};
static int omap_hsmmc_card_detect(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);
}
static int omap_hsmmc_get_wp(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 omap_hsmmc_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);
}
#ifdef CONFIG_PM
static int omap_hsmmc_suspend_cdirq(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 omap_hsmmc_resume_cdirq(struct device *dev, int slot)
<