Files
android_kernel_xiaomi_sm8450/arch/arm/mach-omap2/hsmmc.c
Tony Lindgren 3b972bf06c ARM: OMAP2+: Split omap2_hsmmc_init() to properly support I2C GPIO pins
Otherwise omap_device_build() and omap_mux related functions
can't be marked as __init when twl is build as a module.

If a board is using GPIO pins or regulators configured by an
external chip, such as TWL PMIC on I2C bus, the board must
mark those MMC controllers as deferred. Additionally both
omap_hsmmc_init() and omap_hsmmc_late_init() must be called
by the board.

For MMC controllers using internal GPIO pins for card
detect and regulators the slots don't need to be marked
deferred. In this case calling omap_hsmmc_init() is sufficient.

Only mark the MMC slots using gpio_cd or gpio_wd as deferred
as noted by Igor Grinberg <grinberg@compulab.co.il>.

Note that this patch does not change the behaviour for
board-4430sdp.c board-omap4panda.c. These boards wrongly
rely on the omap_hsmmc.c init function callback to configure
the PMIC GPIO interrupt lines on external chip. If the PMIC
interrupt lines are not configured during init, they will
fail.

Reported-by: Russell King <rmk+kernel@arm.linux.org.uk>
Signed-off-by: Rajendra Nayak <rnayak@ti.com>
Signed-off-by: Tony Lindgren <tony@atomide.com>
2012-02-20 10:00:39 -08:00

594 lines
15 KiB
C

/*
* linux/arch/arm/mach-omap2/hsmmc.c
*
* Copyright (C) 2007-2008 Texas Instruments
* Copyright (C) 2008 Nokia Corporation
* Author: Texas Instruments
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/string.h>
#include <linux/delay.h>
#include <linux/gpio.h>
#include <mach/hardware.h>
#include <plat/mmc.h>
#include <plat/omap-pm.h>
#include <plat/mux.h>
#include <plat/omap_device.h>
#include "mux.h"
#include "hsmmc.h"
#include "control.h"
#if defined(CONFIG_MMC_OMAP_HS) || defined(CONFIG_MMC_OMAP_HS_MODULE)
static u16 control_pbias_offset;
static u16 control_devconf1_offset;
static u16 control_mmc1;
#define HSMMC_NAME_LEN 9
#if defined(CONFIG_ARCH_OMAP3) && defined(CONFIG_PM)
static int hsmmc_get_context_loss(struct device *dev)
{
return omap_pm_get_dev_context_loss_count(dev);
}
#else
#define hsmmc_get_context_loss NULL
#endif
static void omap_hsmmc1_before_set_reg(struct device *dev, int slot,
int power_on, int vdd)
{
u32 reg, prog_io;
struct omap_mmc_platform_data *mmc = dev->platform_data;
if (mmc->slots[0].remux)
mmc->slots[0].remux(dev, slot, power_on);
/*
* Assume we power both OMAP VMMC1 (for CMD, CLK, DAT0..3) and the
* card with Vcc regulator (from twl4030 or whatever). OMAP has both
* 1.8V and 3.0V modes, controlled by the PBIAS register.
*
* In 8-bit modes, OMAP VMMC1A (for DAT4..7) needs a supply, which
* is most naturally TWL VSIM; those pins also use PBIAS.
*
* FIXME handle VMMC1A as needed ...
*/
if (power_on) {
if (cpu_is_omap2430()) {
reg = omap_ctrl_readl(OMAP243X_CONTROL_DEVCONF1);
if ((1 << vdd) >= MMC_VDD_30_31)
reg |= OMAP243X_MMC1_ACTIVE_OVERWRITE;
else
reg &= ~OMAP243X_MMC1_ACTIVE_OVERWRITE;
omap_ctrl_writel(reg, OMAP243X_CONTROL_DEVCONF1);
}
if (mmc->slots[0].internal_clock) {
reg = omap_ctrl_readl(OMAP2_CONTROL_DEVCONF0);
reg |= OMAP2_MMCSDIO1ADPCLKISEL;
omap_ctrl_writel(reg, OMAP2_CONTROL_DEVCONF0);
}
reg = omap_ctrl_readl(control_pbias_offset);
if (cpu_is_omap3630()) {
/* Set MMC I/O to 52Mhz */
prog_io = omap_ctrl_readl(OMAP343X_CONTROL_PROG_IO1);
prog_io |= OMAP3630_PRG_SDMMC1_SPEEDCTRL;
omap_ctrl_writel(prog_io, OMAP343X_CONTROL_PROG_IO1);
} else {
reg |= OMAP2_PBIASSPEEDCTRL0;
}
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 omap_hsmmc1_after_set_reg(struct device *dev, int slot,
int power_on, int vdd)
{
u32 reg;
/* 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)
reg &= ~OMAP2_PBIASLITEVMODE0;
else
reg |= OMAP2_PBIASLITEVMODE0;
omap_ctrl_writel(reg, control_pbias_offset);
} else {
reg = omap_ctrl_readl(control_pbias_offset);
reg |= (OMAP2_PBIASSPEEDCTRL0 | OMAP2_PBIASLITEPWRDNZ0 |
OMAP2_PBIASLITEVMODE0);
omap_ctrl_writel(reg, control_pbias_offset);
}
}
static void omap4_hsmmc1_before_set_reg(struct device *dev, int slot,
int power_on, int vdd)
{
u32 reg;
/*
* Assume we power both OMAP VMMC1 (for CMD, CLK, DAT0..3) and the
* card with Vcc regulator (from twl4030 or whatever). OMAP has both
* 1.8V and 3.0V modes, controlled by the PBIAS register.
*/
reg = omap4_ctrl_pad_readl(control_pbias_offset);
reg &= ~(OMAP4_MMC1_PBIASLITE_PWRDNZ_MASK |
OMAP4_MMC1_PWRDNZ_MASK |
OMAP4_MMC1_PBIASLITE_VMODE_MASK);
omap4_ctrl_pad_writel(reg, control_pbias_offset);
}
static void omap4_hsmmc1_after_set_reg(struct device *dev, int slot,
int power_on, int vdd)
{
u32 reg;
unsigned long timeout;
if (power_on) {
reg = omap4_ctrl_pad_readl(control_pbias_offset);
reg |= OMAP4_MMC1_PBIASLITE_PWRDNZ_MASK;
if ((1 << vdd) <= MMC_VDD_165_195)
reg &= ~OMAP4_MMC1_PBIASLITE_VMODE_MASK;
else
reg |= OMAP4_MMC1_PBIASLITE_VMODE_MASK;
reg |= (OMAP4_MMC1_PBIASLITE_PWRDNZ_MASK |
OMAP4_MMC1_PWRDNZ_MASK);
omap4_ctrl_pad_writel(reg, control_pbias_offset);
timeout = jiffies + msecs_to_jiffies(5);
do {
reg = omap4_ctrl_pad_readl(control_pbias_offset);
if (!(reg & OMAP4_MMC1_PBIASLITE_VMODE_ERROR_MASK))
break;
usleep_range(100, 200);
} while (!time_after(jiffies, timeout));
if (reg & OMAP4_MMC1_PBIASLITE_VMODE_ERROR_MASK) {
pr_err("Pbias Voltage is not same as LDO\n");
/* Caution : On VMODE_ERROR Power Down MMC IO */
reg &= ~(OMAP4_MMC1_PWRDNZ_MASK);
omap4_ctrl_pad_writel(reg, control_pbias_offset);
}
}
}
static void hsmmc2_select_input_clk_src(struct omap_mmc_platform_data *mmc)
{
u32 reg;
reg = omap_ctrl_readl(control_devconf1_offset);
if (mmc->slots[0].internal_clock)
reg |= OMAP2_MMCSDIO2ADPCLKISEL;
else
reg &= ~OMAP2_MMCSDIO2ADPCLKISEL;
omap_ctrl_writel(reg, control_devconf1_offset);
}
static void hsmmc2_before_set_reg(struct device *dev, int slot,
int power_on, int vdd)
{
struct omap_mmc_platform_data *mmc = dev->platform_data;
if (mmc->slots[0].remux)
mmc->slots[0].remux(dev, slot, power_on);
if (power_on)
hsmmc2_select_input_clk_src(mmc);
}
static int am35x_hsmmc2_set_power(struct device *dev, int slot,
int power_on, int vdd)
{
struct omap_mmc_platform_data *mmc = dev->platform_data;
if (power_on)
hsmmc2_select_input_clk_src(mmc);
return 0;
}
static int nop_mmc_set_power(struct device *dev, int slot, int power_on,
int vdd)
{
return 0;
}
static inline void omap_hsmmc_mux(struct omap_mmc_platform_data *mmc_controller,
int controller_nr)
{
if (gpio_is_valid(mmc_controller->slots[0].switch_pin) &&
(mmc_controller->slots[0].switch_pin < OMAP_MAX_GPIO_LINES))
omap_mux_init_gpio(mmc_controller->slots[0].switch_pin,
OMAP_PIN_INPUT_PULLUP);
if (gpio_is_valid(mmc_controller->slots[0].gpio_wp) &&
(mmc_controller->slots[0].gpio_wp < OMAP_MAX_GPIO_LINES))
omap_mux_init_gpio(mmc_controller->slots[0].gpio_wp,
OMAP_PIN_INPUT_PULLUP);
if (cpu_is_omap34xx()) {
if (controller_nr == 0) {
omap_mux_init_signal("sdmmc1_clk",
OMAP_PIN_INPUT_PULLUP);
omap_mux_init_signal("sdmmc1_cmd",
OMAP_PIN_INPUT_PULLUP);
omap_mux_init_signal("sdmmc1_dat0",
OMAP_PIN_INPUT_PULLUP);
if (mmc_controller->slots[0].caps &
(MMC_CAP_4_BIT_DATA | MMC_CAP_8_BIT_DATA)) {
omap_mux_init_signal("sdmmc1_dat1",
OMAP_PIN_INPUT_PULLUP);
omap_mux_init_signal("sdmmc1_dat2",
OMAP_PIN_INPUT_PULLUP);
omap_mux_init_signal("sdmmc1_dat3",
OMAP_PIN_INPUT_PULLUP);
}
if (mmc_controller->slots[0].caps &
MMC_CAP_8_BIT_DATA) {
omap_mux_init_signal("sdmmc1_dat4",
OMAP_PIN_INPUT_PULLUP);
omap_mux_init_signal("sdmmc1_dat5",
OMAP_PIN_INPUT_PULLUP);
omap_mux_init_signal("sdmmc1_dat6",
OMAP_PIN_INPUT_PULLUP);
omap_mux_init_signal("sdmmc1_dat7",
OMAP_PIN_INPUT_PULLUP);
}
}
if (controller_nr == 1) {
/* MMC2 */
omap_mux_init_signal("sdmmc2_clk",
OMAP_PIN_INPUT_PULLUP);
omap_mux_init_signal("sdmmc2_cmd",
OMAP_PIN_INPUT_PULLUP);
omap_mux_init_signal("sdmmc2_dat0",
OMAP_PIN_INPUT_PULLUP);
/*
* For 8 wire configurations, Lines DAT4, 5, 6 and 7
* need to be muxed in the board-*.c files
*/
if (mmc_controller->slots[0].caps &
(MMC_CAP_4_BIT_DATA | MMC_CAP_8_BIT_DATA)) {
omap_mux_init_signal("sdmmc2_dat1",
OMAP_PIN_INPUT_PULLUP);
omap_mux_init_signal("sdmmc2_dat2",
OMAP_PIN_INPUT_PULLUP);
omap_mux_init_signal("sdmmc2_dat3",
OMAP_PIN_INPUT_PULLUP);
}
if (mmc_controller->slots[0].caps &
MMC_CAP_8_BIT_DATA) {
omap_mux_init_signal("sdmmc2_dat4.sdmmc2_dat4",
OMAP_PIN_INPUT_PULLUP);
omap_mux_init_signal("sdmmc2_dat5.sdmmc2_dat5",
OMAP_PIN_INPUT_PULLUP);
omap_mux_init_signal("sdmmc2_dat6.sdmmc2_dat6",
OMAP_PIN_INPUT_PULLUP);
omap_mux_init_signal("sdmmc2_dat7.sdmmc2_dat7",
OMAP_PIN_INPUT_PULLUP);
}
}
/*
* For MMC3 the pins need to be muxed in the board-*.c files
*/
}
}
static int omap_hsmmc_pdata_init(struct omap2_hsmmc_info *c,
struct omap_mmc_platform_data *mmc)
{
char *hc_name;
hc_name = kzalloc(sizeof(char) * (HSMMC_NAME_LEN + 1), GFP_KERNEL);
if (!hc_name) {
pr_err("Cannot allocate memory for controller slot name\n");
kfree(hc_name);
return -ENOMEM;
}
if (c->name)
strncpy(hc_name, c->name, HSMMC_NAME_LEN);
else
snprintf(hc_name, (HSMMC_NAME_LEN + 1), "mmc%islot%i",
c->mmc, 1);
mmc->slots[0].name = hc_name;
mmc->nr_slots = 1;
mmc->slots[0].caps = c->caps;
mmc->slots[0].pm_caps = c->pm_caps;
mmc->slots[0].internal_clock = !c->ext_clock;
mmc->dma_mask = 0xffffffff;
if (cpu_is_omap44xx())
mmc->reg_offset = OMAP4_MMC_REG_OFFSET;
else
mmc->reg_offset = 0;
mmc->get_context_loss_count = hsmmc_get_context_loss;
mmc->slots[0].switch_pin = c->gpio_cd;
mmc->slots[0].gpio_wp = c->gpio_wp;
mmc->slots[0].remux = c->remux;
mmc->slots[0].init_card = c->init_card;
if (c->cover_only)
mmc->slots[0].cover = 1;
if (c->nonremovable)
mmc->slots[0].nonremovable = 1;
if (c->power_saving)
mmc->slots[0].power_saving = 1;
if (c->no_off)
mmc->slots[0].no_off = 1;
if (c->no_off_init)
mmc->slots[0].no_regulator_off_init = c->no_off_init;
if (c->vcc_aux_disable_is_sleep)
mmc->slots[0].vcc_aux_disable_is_sleep = 1;
/*
* NOTE: MMC slots should have a Vcc regulator set up.
* This may be from a TWL4030-family chip, another
* controllable regulator, or a fixed supply.
*
* temporary HACK: ocr_mask instead of fixed supply
*/
if (cpu_is_omap3505() || cpu_is_omap3517())
mmc->slots[0].ocr_mask = MMC_VDD_165_195 |
MMC_VDD_26_27 |
MMC_VDD_27_28 |
MMC_VDD_29_30 |
MMC_VDD_30_31 |
MMC_VDD_31_32;
else
mmc->slots[0].ocr_mask = c->ocr_mask;
if (!cpu_is_omap3517() && !cpu_is_omap3505())
mmc->slots[0].features |= HSMMC_HAS_PBIAS;
if (cpu_is_omap44xx() && (omap_rev() > OMAP4430_REV_ES1_0))
mmc->slots[0].features |= HSMMC_HAS_UPDATED_RESET;
switch (c->mmc) {
case 1:
if (mmc->slots[0].features & HSMMC_HAS_PBIAS) {
/* on-chip level shifting via PBIAS0/PBIAS1 */
if (cpu_is_omap44xx()) {
mmc->slots[0].before_set_reg =
omap4_hsmmc1_before_set_reg;
mmc->slots[0].after_set_reg =
omap4_hsmmc1_after_set_reg;
} else {
mmc->slots[0].before_set_reg =
omap_hsmmc1_before_set_reg;
mmc->slots[0].after_set_reg =
omap_hsmmc1_after_set_reg;
}
}
if (cpu_is_omap3517() || cpu_is_omap3505())
mmc->slots[0].set_power = nop_mmc_set_power;
/* OMAP3630 HSMMC1 supports only 4-bit */
if (cpu_is_omap3630() &&
(c->caps & MMC_CAP_8_BIT_DATA)) {
c->caps &= ~MMC_CAP_8_BIT_DATA;
c->caps |= MMC_CAP_4_BIT_DATA;
mmc->slots[0].caps = c->caps;
}
break;
case 2:
if (cpu_is_omap3517() || cpu_is_omap3505())
mmc->slots[0].set_power = am35x_hsmmc2_set_power;
if (c->ext_clock)
c->transceiver = 1;
if (c->transceiver && (c->caps & MMC_CAP_8_BIT_DATA)) {
c->caps &= ~MMC_CAP_8_BIT_DATA;
c->caps |= MMC_CAP_4_BIT_DATA;
}
if (mmc->slots[0].features & HSMMC_HAS_PBIAS) {
/* off-chip level shifting, or none */
mmc->slots[0].before_set_reg = hsmmc2_before_set_reg;
mmc->slots[0].after_set_reg = NULL;
}
break;
case 3:
case 4:
case 5:
mmc->slots[0].before_set_reg = NULL;
mmc->slots[0].after_set_reg = NULL;
break;
default:
pr_err("MMC%d configuration not supported!\n", c->mmc);
kfree(hc_name);
return -ENODEV;
}
return 0;
}
static int omap_hsmmc_done;
void omap_hsmmc_late_init(struct omap2_hsmmc_info *c)
{
struct platform_device *pdev;
struct omap_mmc_platform_data *mmc_pdata;
int res;
if (omap_hsmmc_done != 1)
return;
omap_hsmmc_done++;
for (; c->mmc; c++) {
if (!c->deferred)
continue;
pdev = c->pdev;
if (!pdev)
continue;
mmc_pdata = pdev->dev.platform_data;
if (!mmc_pdata)
continue;
mmc_pdata->slots[0].switch_pin = c->gpio_cd;
mmc_pdata->slots[0].gpio_wp = c->gpio_wp;
res = omap_device_register(pdev);
if (res)
pr_err("Could not late init MMC %s\n",
c->name);
}
}
#define MAX_OMAP_MMC_HWMOD_NAME_LEN 16
static void omap_hsmmc_init_one(struct omap2_hsmmc_info *hsmmcinfo,
int ctrl_nr)
{
struct omap_hwmod *oh;
struct omap_hwmod *ohs[1];
struct omap_device *od;
struct platform_device *pdev;
char oh_name[MAX_OMAP_MMC_HWMOD_NAME_LEN];
struct omap_mmc_platform_data *mmc_data;
struct omap_mmc_dev_attr *mmc_dev_attr;
char *name;
int res;
mmc_data = kzalloc(sizeof(struct omap_mmc_platform_data), GFP_KERNEL);
if (!mmc_data) {
pr_err("Cannot allocate memory for mmc device!\n");
return;
}
res = omap_hsmmc_pdata_init(hsmmcinfo, mmc_data);
if (res < 0)
goto free_mmc;
omap_hsmmc_mux(mmc_data, (ctrl_nr - 1));
name = "omap_hsmmc";
res = snprintf(oh_name, MAX_OMAP_MMC_HWMOD_NAME_LEN,
"mmc%d", ctrl_nr);
WARN(res >= MAX_OMAP_MMC_HWMOD_NAME_LEN,
"String buffer overflow in MMC%d device setup\n", ctrl_nr);
oh = omap_hwmod_lookup(oh_name);
if (!oh) {
pr_err("Could not look up %s\n", oh_name);
goto free_name;
}
ohs[0] = oh;
if (oh->dev_attr != NULL) {
mmc_dev_attr = oh->dev_attr;
mmc_data->controller_flags = mmc_dev_attr->flags;
}
pdev = platform_device_alloc(name, ctrl_nr - 1);
if (!pdev) {
pr_err("Could not allocate pdev for %s\n", name);
goto free_name;
}
dev_set_name(&pdev->dev, "%s.%d", pdev->name, pdev->id);
od = omap_device_alloc(pdev, ohs, 1, NULL, 0);
if (!od) {
pr_err("Could not allocate od for %s\n", name);
goto put_pdev;
}
res = platform_device_add_data(pdev, mmc_data,
sizeof(struct omap_mmc_platform_data));
if (res) {
pr_err("Could not add pdata for %s\n", name);
goto put_pdev;
}
hsmmcinfo->pdev = pdev;
if (hsmmcinfo->deferred)
goto free_mmc;
res = omap_device_register(pdev);
if (res) {
pr_err("Could not register od for %s\n", name);
goto free_od;
}
goto free_mmc;
free_od:
omap_device_delete(od);
put_pdev:
platform_device_put(pdev);
free_name:
kfree(mmc_data->slots[0].name);
free_mmc:
kfree(mmc_data);
}
void omap_hsmmc_init(struct omap2_hsmmc_info *controllers)
{
u32 reg;
if (omap_hsmmc_done)
return;
omap_hsmmc_done = 1;
if (!cpu_is_omap44xx()) {
if (cpu_is_omap2430()) {
control_pbias_offset = OMAP243X_CONTROL_PBIAS_LITE;
control_devconf1_offset = OMAP243X_CONTROL_DEVCONF1;
} else {
control_pbias_offset = OMAP343X_CONTROL_PBIAS_LITE;
control_devconf1_offset = OMAP343X_CONTROL_DEVCONF1;
}
} else {
control_pbias_offset =
OMAP4_CTRL_MODULE_PAD_CORE_CONTROL_PBIASLITE;
control_mmc1 = OMAP4_CTRL_MODULE_PAD_CORE_CONTROL_MMC1;
reg = omap4_ctrl_pad_readl(control_mmc1);
reg |= (OMAP4_SDMMC1_PUSTRENGTH_GRP0_MASK |
OMAP4_SDMMC1_PUSTRENGTH_GRP1_MASK);
reg &= ~(OMAP4_SDMMC1_PUSTRENGTH_GRP2_MASK |
OMAP4_SDMMC1_PUSTRENGTH_GRP3_MASK);
reg |= (OMAP4_SDMMC1_DR0_SPEEDCTRL_MASK |
OMAP4_SDMMC1_DR1_SPEEDCTRL_MASK |
OMAP4_SDMMC1_DR2_SPEEDCTRL_MASK);
omap4_ctrl_pad_writel(reg, control_mmc1);
}
for (; controllers->mmc; controllers++)
omap_hsmmc_init_one(controllers, controllers->mmc);
}
#endif