414 lines
12 KiB
C
414 lines
12 KiB
C
|
/* Copyright (c) 2011-2012, The Linux Foundation. All rights reserved.
|
||
|
*
|
||
|
* This program is free software; you can redistribute it and/or modify
|
||
|
* it under the terms of the GNU General Public License version 2 and
|
||
|
* only version 2 as published by the Free Software Foundation.
|
||
|
*
|
||
|
* This program is distributed in the hope that it will be useful,
|
||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||
|
* GNU General Public License for more details.
|
||
|
*
|
||
|
*/
|
||
|
|
||
|
#include <linux/kernel.h>
|
||
|
#include <linux/regulator/consumer.h>
|
||
|
#include <linux/gpio.h>
|
||
|
#include <asm/mach-types.h>
|
||
|
#include <asm/mach/mmc.h>
|
||
|
#include <mach/gpiomux.h>
|
||
|
#include <mach/board.h>
|
||
|
#include "devices.h"
|
||
|
#include "pm.h"
|
||
|
#include "board-msm7627a.h"
|
||
|
|
||
|
#if (defined(CONFIG_MMC_MSM_SDC1_SUPPORT)\
|
||
|
|| defined(CONFIG_MMC_MSM_SDC2_SUPPORT)\
|
||
|
|| defined(CONFIG_MMC_MSM_SDC3_SUPPORT)\
|
||
|
|| defined(CONFIG_MMC_MSM_SDC4_SUPPORT))
|
||
|
|
||
|
#define MAX_SDCC_CONTROLLER 4
|
||
|
static unsigned long vreg_sts, gpio_sts;
|
||
|
|
||
|
struct sdcc_gpio {
|
||
|
struct msm_gpio *cfg_data;
|
||
|
uint32_t size;
|
||
|
struct msm_gpio *sleep_cfg_data;
|
||
|
};
|
||
|
|
||
|
/**
|
||
|
* Due to insufficient drive strengths for SDC GPIO lines some old versioned
|
||
|
* SD/MMC cards may cause data CRC errors. Hence, set optimal values
|
||
|
* for SDC slots based on timing closure and marginality. SDC1 slot
|
||
|
* require higher value since it should handle bad signal quality due
|
||
|
* to size of T-flash adapters.
|
||
|
*/
|
||
|
static struct msm_gpio sdc1_cfg_data[] = {
|
||
|
{GPIO_CFG(51, 1, GPIO_CFG_OUTPUT, GPIO_CFG_PULL_UP, GPIO_CFG_14MA),
|
||
|
"sdc1_dat_3"},
|
||
|
{GPIO_CFG(52, 1, GPIO_CFG_OUTPUT, GPIO_CFG_PULL_UP, GPIO_CFG_14MA),
|
||
|
"sdc1_dat_2"},
|
||
|
{GPIO_CFG(53, 1, GPIO_CFG_OUTPUT, GPIO_CFG_PULL_UP, GPIO_CFG_14MA),
|
||
|
"sdc1_dat_1"},
|
||
|
{GPIO_CFG(54, 1, GPIO_CFG_OUTPUT, GPIO_CFG_PULL_UP, GPIO_CFG_14MA),
|
||
|
"sdc1_dat_0"},
|
||
|
{GPIO_CFG(55, 1, GPIO_CFG_OUTPUT, GPIO_CFG_PULL_UP, GPIO_CFG_14MA),
|
||
|
"sdc1_cmd"},
|
||
|
{GPIO_CFG(56, 1, GPIO_CFG_OUTPUT, GPIO_CFG_NO_PULL, GPIO_CFG_14MA),
|
||
|
"sdc1_clk"},
|
||
|
};
|
||
|
|
||
|
static struct msm_gpio sdc2_cfg_data[] = {
|
||
|
{GPIO_CFG(62, 2, GPIO_CFG_OUTPUT, GPIO_CFG_NO_PULL, GPIO_CFG_8MA),
|
||
|
"sdc2_clk"},
|
||
|
{GPIO_CFG(63, 2, GPIO_CFG_OUTPUT, GPIO_CFG_PULL_UP, GPIO_CFG_10MA),
|
||
|
"sdc2_cmd"},
|
||
|
{GPIO_CFG(64, 2, GPIO_CFG_OUTPUT, GPIO_CFG_PULL_UP, GPIO_CFG_10MA),
|
||
|
"sdc2_dat_3"},
|
||
|
{GPIO_CFG(65, 2, GPIO_CFG_OUTPUT, GPIO_CFG_PULL_UP, GPIO_CFG_10MA),
|
||
|
"sdc2_dat_2"},
|
||
|
{GPIO_CFG(66, 2, GPIO_CFG_OUTPUT, GPIO_CFG_PULL_UP, GPIO_CFG_10MA),
|
||
|
"sdc2_dat_1"},
|
||
|
{GPIO_CFG(67, 2, GPIO_CFG_OUTPUT, GPIO_CFG_PULL_UP, GPIO_CFG_10MA),
|
||
|
"sdc2_dat_0"},
|
||
|
};
|
||
|
|
||
|
static struct msm_gpio sdc2_sleep_cfg_data[] = {
|
||
|
{GPIO_CFG(62, 0, GPIO_CFG_INPUT, GPIO_CFG_NO_PULL, GPIO_CFG_2MA),
|
||
|
"sdc2_clk"},
|
||
|
{GPIO_CFG(63, 0, GPIO_CFG_INPUT, GPIO_CFG_PULL_UP, GPIO_CFG_2MA),
|
||
|
"sdc2_cmd"},
|
||
|
{GPIO_CFG(64, 0, GPIO_CFG_INPUT, GPIO_CFG_PULL_UP, GPIO_CFG_2MA),
|
||
|
"sdc2_dat_3"},
|
||
|
{GPIO_CFG(65, 0, GPIO_CFG_INPUT, GPIO_CFG_PULL_UP, GPIO_CFG_2MA),
|
||
|
"sdc2_dat_2"},
|
||
|
{GPIO_CFG(66, 0, GPIO_CFG_INPUT, GPIO_CFG_PULL_UP, GPIO_CFG_2MA),
|
||
|
"sdc2_dat_1"},
|
||
|
{GPIO_CFG(67, 0, GPIO_CFG_INPUT, GPIO_CFG_PULL_UP, GPIO_CFG_2MA),
|
||
|
"sdc2_dat_0"},
|
||
|
};
|
||
|
static struct msm_gpio sdc3_cfg_data[] = {
|
||
|
{GPIO_CFG(88, 1, GPIO_CFG_OUTPUT, GPIO_CFG_NO_PULL, GPIO_CFG_8MA),
|
||
|
"sdc3_clk"},
|
||
|
{GPIO_CFG(89, 1, GPIO_CFG_OUTPUT, GPIO_CFG_PULL_UP, GPIO_CFG_10MA),
|
||
|
"sdc3_cmd"},
|
||
|
{GPIO_CFG(90, 1, GPIO_CFG_OUTPUT, GPIO_CFG_PULL_UP, GPIO_CFG_10MA),
|
||
|
"sdc3_dat_3"},
|
||
|
{GPIO_CFG(91, 1, GPIO_CFG_OUTPUT, GPIO_CFG_PULL_UP, GPIO_CFG_10MA),
|
||
|
"sdc3_dat_2"},
|
||
|
{GPIO_CFG(92, 1, GPIO_CFG_OUTPUT, GPIO_CFG_PULL_UP, GPIO_CFG_10MA),
|
||
|
"sdc3_dat_1"},
|
||
|
{GPIO_CFG(93, 1, GPIO_CFG_OUTPUT, GPIO_CFG_PULL_UP, GPIO_CFG_10MA),
|
||
|
"sdc3_dat_0"},
|
||
|
#ifdef CONFIG_MMC_MSM_SDC3_8_BIT_SUPPORT
|
||
|
{GPIO_CFG(19, 3, GPIO_CFG_OUTPUT, GPIO_CFG_PULL_UP, GPIO_CFG_10MA),
|
||
|
"sdc3_dat_7"},
|
||
|
{GPIO_CFG(20, 3, GPIO_CFG_OUTPUT, GPIO_CFG_PULL_UP, GPIO_CFG_10MA),
|
||
|
"sdc3_dat_6"},
|
||
|
{GPIO_CFG(21, 3, GPIO_CFG_OUTPUT, GPIO_CFG_PULL_UP, GPIO_CFG_10MA),
|
||
|
"sdc3_dat_5"},
|
||
|
{GPIO_CFG(108, 3, GPIO_CFG_OUTPUT, GPIO_CFG_PULL_UP, GPIO_CFG_10MA),
|
||
|
"sdc3_dat_4"},
|
||
|
#endif
|
||
|
};
|
||
|
|
||
|
static struct msm_gpio sdc4_cfg_data[] = {
|
||
|
{GPIO_CFG(19, 1, GPIO_CFG_OUTPUT, GPIO_CFG_PULL_UP, GPIO_CFG_10MA),
|
||
|
"sdc4_dat_3"},
|
||
|
{GPIO_CFG(20, 1, GPIO_CFG_OUTPUT, GPIO_CFG_PULL_UP, GPIO_CFG_10MA),
|
||
|
"sdc4_dat_2"},
|
||
|
{GPIO_CFG(21, 1, GPIO_CFG_OUTPUT, GPIO_CFG_PULL_UP, GPIO_CFG_10MA),
|
||
|
"sdc4_dat_1"},
|
||
|
{GPIO_CFG(107, 1, GPIO_CFG_OUTPUT, GPIO_CFG_PULL_UP, GPIO_CFG_10MA),
|
||
|
"sdc4_cmd"},
|
||
|
{GPIO_CFG(108, 1, GPIO_CFG_OUTPUT, GPIO_CFG_PULL_UP, GPIO_CFG_10MA),
|
||
|
"sdc4_dat_0"},
|
||
|
{GPIO_CFG(109, 1, GPIO_CFG_OUTPUT, GPIO_CFG_NO_PULL, GPIO_CFG_8MA),
|
||
|
"sdc4_clk"},
|
||
|
};
|
||
|
|
||
|
static struct sdcc_gpio sdcc_cfg_data[] = {
|
||
|
{
|
||
|
.cfg_data = sdc1_cfg_data,
|
||
|
.size = ARRAY_SIZE(sdc1_cfg_data),
|
||
|
},
|
||
|
{
|
||
|
.cfg_data = sdc2_cfg_data,
|
||
|
.size = ARRAY_SIZE(sdc2_cfg_data),
|
||
|
.sleep_cfg_data = sdc2_sleep_cfg_data,
|
||
|
},
|
||
|
{
|
||
|
.cfg_data = sdc3_cfg_data,
|
||
|
.size = ARRAY_SIZE(sdc3_cfg_data),
|
||
|
},
|
||
|
{
|
||
|
.cfg_data = sdc4_cfg_data,
|
||
|
.size = ARRAY_SIZE(sdc4_cfg_data),
|
||
|
},
|
||
|
};
|
||
|
|
||
|
static int gpio_sdc1_hw_det = 85;
|
||
|
static void gpio_sdc1_config(void)
|
||
|
{
|
||
|
if (machine_is_msm7627a_qrd1() || machine_is_msm7627a_evb()
|
||
|
|| machine_is_msm8625_evb()
|
||
|
|| machine_is_msm7627a_qrd3()
|
||
|
|| machine_is_msm8625_qrd7())
|
||
|
gpio_sdc1_hw_det = 42;
|
||
|
}
|
||
|
|
||
|
static struct regulator *sdcc_vreg_data[MAX_SDCC_CONTROLLER];
|
||
|
static int msm_sdcc_setup_gpio(int dev_id, unsigned int enable)
|
||
|
{
|
||
|
int rc = 0;
|
||
|
struct sdcc_gpio *curr;
|
||
|
|
||
|
curr = &sdcc_cfg_data[dev_id - 1];
|
||
|
if (!(test_bit(dev_id, &gpio_sts)^enable))
|
||
|
return rc;
|
||
|
|
||
|
if (enable) {
|
||
|
set_bit(dev_id, &gpio_sts);
|
||
|
rc = msm_gpios_request_enable(curr->cfg_data, curr->size);
|
||
|
if (rc)
|
||
|
pr_err("%s: Failed to turn on GPIOs for slot %d\n",
|
||
|
__func__, dev_id);
|
||
|
} else {
|
||
|
clear_bit(dev_id, &gpio_sts);
|
||
|
if (curr->sleep_cfg_data) {
|
||
|
rc = msm_gpios_enable(curr->sleep_cfg_data, curr->size);
|
||
|
msm_gpios_free(curr->sleep_cfg_data, curr->size);
|
||
|
return rc;
|
||
|
}
|
||
|
msm_gpios_disable_free(curr->cfg_data, curr->size);
|
||
|
}
|
||
|
return rc;
|
||
|
}
|
||
|
|
||
|
static int msm_sdcc_setup_vreg(int dev_id, unsigned int enable)
|
||
|
{
|
||
|
int rc = 0;
|
||
|
struct regulator *curr = sdcc_vreg_data[dev_id - 1];
|
||
|
|
||
|
if (test_bit(dev_id, &vreg_sts) == enable)
|
||
|
return 0;
|
||
|
|
||
|
if (!curr)
|
||
|
return -ENODEV;
|
||
|
|
||
|
if (IS_ERR(curr))
|
||
|
return PTR_ERR(curr);
|
||
|
|
||
|
if (enable) {
|
||
|
set_bit(dev_id, &vreg_sts);
|
||
|
|
||
|
rc = regulator_enable(curr);
|
||
|
if (rc)
|
||
|
pr_err("%s: could not enable regulator: %d\n",
|
||
|
__func__, rc);
|
||
|
} else {
|
||
|
clear_bit(dev_id, &vreg_sts);
|
||
|
|
||
|
rc = regulator_disable(curr);
|
||
|
if (rc)
|
||
|
pr_err("%s: could not disable regulator: %d\n",
|
||
|
__func__, rc);
|
||
|
}
|
||
|
return rc;
|
||
|
}
|
||
|
|
||
|
static uint32_t msm_sdcc_setup_power(struct device *dv, unsigned int vdd)
|
||
|
{
|
||
|
int rc = 0;
|
||
|
struct platform_device *pdev;
|
||
|
|
||
|
pdev = container_of(dv, struct platform_device, dev);
|
||
|
|
||
|
rc = msm_sdcc_setup_gpio(pdev->id, !!vdd);
|
||
|
if (rc)
|
||
|
goto out;
|
||
|
|
||
|
rc = msm_sdcc_setup_vreg(pdev->id, !!vdd);
|
||
|
out:
|
||
|
return rc;
|
||
|
}
|
||
|
|
||
|
#ifdef CONFIG_MMC_MSM_SDC1_SUPPORT
|
||
|
static unsigned int msm7627a_sdcc_slot_status(struct device *dev)
|
||
|
{
|
||
|
int status;
|
||
|
|
||
|
status = gpio_tlmm_config(GPIO_CFG(gpio_sdc1_hw_det, 2, GPIO_CFG_INPUT,
|
||
|
GPIO_CFG_PULL_UP, GPIO_CFG_8MA),
|
||
|
GPIO_CFG_ENABLE);
|
||
|
if (status)
|
||
|
pr_err("%s:Failed to configure tlmm for GPIO %d\n", __func__,
|
||
|
gpio_sdc1_hw_det);
|
||
|
|
||
|
status = gpio_request(gpio_sdc1_hw_det, "SD_HW_Detect");
|
||
|
if (status) {
|
||
|
pr_err("%s:Failed to request GPIO %d\n", __func__,
|
||
|
gpio_sdc1_hw_det);
|
||
|
} else {
|
||
|
status = gpio_direction_input(gpio_sdc1_hw_det);
|
||
|
if (!status) {
|
||
|
if (machine_is_msm7627a_qrd1() ||
|
||
|
machine_is_msm7627a_evb() ||
|
||
|
machine_is_msm8625_evb() ||
|
||
|
machine_is_msm7627a_qrd3() ||
|
||
|
machine_is_msm8625_qrd7())
|
||
|
status = !gpio_get_value(gpio_sdc1_hw_det);
|
||
|
else
|
||
|
status = gpio_get_value(gpio_sdc1_hw_det);
|
||
|
}
|
||
|
gpio_free(gpio_sdc1_hw_det);
|
||
|
}
|
||
|
return status;
|
||
|
}
|
||
|
|
||
|
static struct mmc_platform_data sdc1_plat_data = {
|
||
|
.ocr_mask = MMC_VDD_28_29,
|
||
|
.translate_vdd = msm_sdcc_setup_power,
|
||
|
.mmc_bus_width = MMC_CAP_4_BIT_DATA,
|
||
|
.msmsdcc_fmin = 144000,
|
||
|
.msmsdcc_fmid = 24576000,
|
||
|
.msmsdcc_fmax = 49152000,
|
||
|
.status = msm7627a_sdcc_slot_status,
|
||
|
.irq_flags = IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
|
||
|
};
|
||
|
#endif
|
||
|
|
||
|
#ifdef CONFIG_MMC_MSM_SDC2_SUPPORT
|
||
|
static struct mmc_platform_data sdc2_plat_data = {
|
||
|
/*
|
||
|
* SDC2 supports only 1.8V, claim for 2.85V range is just
|
||
|
* for allowing buggy cards who advertise 2.8V even though
|
||
|
* they can operate at 1.8V supply.
|
||
|
*/
|
||
|
.ocr_mask = MMC_VDD_28_29 | MMC_VDD_165_195,
|
||
|
.translate_vdd = msm_sdcc_setup_power,
|
||
|
.mmc_bus_width = MMC_CAP_4_BIT_DATA,
|
||
|
.sdiowakeup_irq = MSM_GPIO_TO_INT(66),
|
||
|
.msmsdcc_fmin = 144000,
|
||
|
.msmsdcc_fmid = 24576000,
|
||
|
.msmsdcc_fmax = 49152000,
|
||
|
#ifdef CONFIG_MMC_MSM_SDC2_DUMMY52_REQUIRED
|
||
|
.dummy52_required = 1,
|
||
|
#endif
|
||
|
};
|
||
|
#endif
|
||
|
|
||
|
#ifdef CONFIG_MMC_MSM_SDC3_SUPPORT
|
||
|
static struct mmc_platform_data sdc3_plat_data = {
|
||
|
.ocr_mask = MMC_VDD_28_29,
|
||
|
.translate_vdd = msm_sdcc_setup_power,
|
||
|
#ifdef CONFIG_MMC_MSM_SDC3_8_BIT_SUPPORT
|
||
|
.mmc_bus_width = MMC_CAP_8_BIT_DATA,
|
||
|
#else
|
||
|
.mmc_bus_width = MMC_CAP_4_BIT_DATA,
|
||
|
#endif
|
||
|
.msmsdcc_fmin = 144000,
|
||
|
.msmsdcc_fmid = 24576000,
|
||
|
.msmsdcc_fmax = 49152000,
|
||
|
.nonremovable = 1,
|
||
|
};
|
||
|
#endif
|
||
|
|
||
|
#if (defined(CONFIG_MMC_MSM_SDC4_SUPPORT)\
|
||
|
&& !defined(CONFIG_MMC_MSM_SDC3_8_BIT_SUPPORT))
|
||
|
static struct mmc_platform_data sdc4_plat_data = {
|
||
|
.ocr_mask = MMC_VDD_28_29,
|
||
|
.translate_vdd = msm_sdcc_setup_power,
|
||
|
.mmc_bus_width = MMC_CAP_4_BIT_DATA,
|
||
|
.msmsdcc_fmin = 144000,
|
||
|
.msmsdcc_fmid = 24576000,
|
||
|
.msmsdcc_fmax = 49152000,
|
||
|
};
|
||
|
#endif
|
||
|
|
||
|
static int __init mmc_regulator_init(int sdcc_no, const char *supply, int uV)
|
||
|
{
|
||
|
int rc;
|
||
|
|
||
|
BUG_ON(sdcc_no < 1 || sdcc_no > 4);
|
||
|
|
||
|
sdcc_no--;
|
||
|
|
||
|
sdcc_vreg_data[sdcc_no] = regulator_get(NULL, supply);
|
||
|
|
||
|
if (IS_ERR(sdcc_vreg_data[sdcc_no])) {
|
||
|
rc = PTR_ERR(sdcc_vreg_data[sdcc_no]);
|
||
|
pr_err("%s: could not get regulator \"%s\": %d\n",
|
||
|
__func__, supply, rc);
|
||
|
goto out;
|
||
|
}
|
||
|
|
||
|
rc = regulator_set_voltage(sdcc_vreg_data[sdcc_no], uV, uV);
|
||
|
|
||
|
if (rc) {
|
||
|
pr_err("%s: could not set voltage for \"%s\" to %d uV: %d\n",
|
||
|
__func__, supply, uV, rc);
|
||
|
goto reg_free;
|
||
|
}
|
||
|
|
||
|
return rc;
|
||
|
|
||
|
reg_free:
|
||
|
regulator_put(sdcc_vreg_data[sdcc_no]);
|
||
|
out:
|
||
|
sdcc_vreg_data[sdcc_no] = NULL;
|
||
|
return rc;
|
||
|
}
|
||
|
|
||
|
void __init msm7627a_init_mmc(void)
|
||
|
{
|
||
|
/* eMMC slot */
|
||
|
#ifdef CONFIG_MMC_MSM_SDC3_SUPPORT
|
||
|
|
||
|
/* There is no eMMC on SDC3 for QRD3 based devices */
|
||
|
if (!(machine_is_msm7627a_qrd3() || machine_is_msm8625_qrd7())) {
|
||
|
if (mmc_regulator_init(3, "emmc", 3000000))
|
||
|
return;
|
||
|
/*
|
||
|
* On 7x25A FFA data CRC errors are seen, which are
|
||
|
* probably due to the proximity of SIM card and eMMC.
|
||
|
* Hence, reducing the clock to 24.7Mhz from 49Mhz.
|
||
|
*/
|
||
|
if (machine_is_msm7625a_ffa())
|
||
|
sdc3_plat_data.msmsdcc_fmax =
|
||
|
sdc3_plat_data.msmsdcc_fmid;
|
||
|
msm_add_sdcc(3, &sdc3_plat_data);
|
||
|
}
|
||
|
#endif
|
||
|
/* Micro-SD slot */
|
||
|
#ifdef CONFIG_MMC_MSM_SDC1_SUPPORT
|
||
|
gpio_sdc1_config();
|
||
|
if (mmc_regulator_init(1, "mmc", 2850000))
|
||
|
return;
|
||
|
/* 8x25 EVT do not use hw detector */
|
||
|
if (!((machine_is_msm8625_evt() || machine_is_qrd_skud_prime())))
|
||
|
sdc1_plat_data.status_irq = MSM_GPIO_TO_INT(gpio_sdc1_hw_det);
|
||
|
if (machine_is_msm8625_evt() || machine_is_qrd_skud_prime())
|
||
|
sdc1_plat_data.status = NULL;
|
||
|
|
||
|
msm_add_sdcc(1, &sdc1_plat_data);
|
||
|
#endif
|
||
|
/* SDIO WLAN slot */
|
||
|
#ifdef CONFIG_MMC_MSM_SDC2_SUPPORT
|
||
|
if (mmc_regulator_init(2, "smps3", 1800000))
|
||
|
return;
|
||
|
msm_add_sdcc(2, &sdc2_plat_data);
|
||
|
#endif
|
||
|
/* Not Used */
|
||
|
#if (defined(CONFIG_MMC_MSM_SDC4_SUPPORT)\
|
||
|
&& !defined(CONFIG_MMC_MSM_SDC3_8_BIT_SUPPORT))
|
||
|
/* There is no SDC4 for QRD3/7 based devices */
|
||
|
if (!(machine_is_msm7627a_qrd3() || machine_is_msm8625_qrd7())) {
|
||
|
if (mmc_regulator_init(4, "smps3", 1800000))
|
||
|
return;
|
||
|
msm_add_sdcc(4, &sdc4_plat_data);
|
||
|
}
|
||
|
#endif
|
||
|
}
|
||
|
#endif
|