mmc: host: add pad and tlmm configuration
This patch adds the pad and tlmm configuration to msm-sdhci driver. Change-Id: Ic2b9beffdb555598bdc15b4b03c8adb78fbd0c2c Signed-off-by: Asutosh Das <asutoshd@codeaurora.org> Signed-off-by: Sahitya Tummala <stummala@codeaurora.org>
This commit is contained in:
parent
880b6f69f6
commit
1370edc0b2
2 changed files with 312 additions and 8 deletions
|
@ -44,10 +44,18 @@ In the following, <supply> can be vdd (flash core voltage) or vdd-io (I/O voltag
|
||||||
- gpios - specifies gpios assigned for sdhc slot.
|
- gpios - specifies gpios assigned for sdhc slot.
|
||||||
- qcom,gpio-names - a list of strings that map in order to the list of gpios
|
- qcom,gpio-names - a list of strings that map in order to the list of gpios
|
||||||
|
|
||||||
|
A slot has either gpios or dedicated tlmm pins as represented below.
|
||||||
|
- qcom,pad-pull-on - Active pull configuration for sdc tlmm pins
|
||||||
|
- qcom,pad-pull-off - Suspend pull configuration for sdc tlmm pins.
|
||||||
|
- qcom,pad-drv-on - Active drive strength configuration for sdc tlmm pins.
|
||||||
|
- qcom,pad-drv-off - Suspend drive strength configuration for sdc tlmm pins.
|
||||||
|
Tlmm pins are specified as <clk cmd data>
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
|
|
||||||
aliases {
|
aliases {
|
||||||
sdhc1 = &sdhc_1;
|
sdhc1 = &sdhc_1;
|
||||||
|
sdhc2 = &sdhc_2;
|
||||||
};
|
};
|
||||||
|
|
||||||
sdhc_1: qcom,sdhc@f9824900 {
|
sdhc_1: qcom,sdhc@f9824900 {
|
||||||
|
@ -79,3 +87,21 @@ Example:
|
||||||
<&msmgpio 35 0>; /* DATA3 */
|
<&msmgpio 35 0>; /* DATA3 */
|
||||||
qcom,gpio-names = "CLK", "CMD", "DAT0", "DAT1", "DAT2", "DAT3";
|
qcom,gpio-names = "CLK", "CMD", "DAT0", "DAT1", "DAT2", "DAT3";
|
||||||
};
|
};
|
||||||
|
|
||||||
|
sdhc_2: qcom,sdhc@f98a4900 {
|
||||||
|
compatible = "qcom,sdhci-msm";
|
||||||
|
reg = <0xf9824900 0x11c>, <0xf9824000 0x800>;
|
||||||
|
reg-names = "hc_mem", "core_mem";
|
||||||
|
interrupts = <0 123 0>, <0 138 0>;
|
||||||
|
interrupt-names = "hc_irq", "pwr_irq";
|
||||||
|
|
||||||
|
vdd-supply = <&pm8941_l21>;
|
||||||
|
vdd-io-supply = <&pm8941_l13>;
|
||||||
|
|
||||||
|
qcom,bus-width = <4>;
|
||||||
|
|
||||||
|
qcom,pad-pull-on = <0x0 0x3 0x3>; /* no-pull, pull-up, pull-up */
|
||||||
|
qcom,pad-pull-off = <0x0 0x3 0x3>; /* no-pull, pull-up, pull-up */
|
||||||
|
qcom,pad-drv-on = <0x7 0x4 0x4>; /* 16mA, 10mA, 10mA */
|
||||||
|
qcom,pad-drv-off = <0x0 0x0 0x0>; /* 2mA, 2mA, 2mA */
|
||||||
|
};
|
||||||
|
|
|
@ -96,13 +96,43 @@ struct sdhci_msm_gpio_data {
|
||||||
u8 size;
|
u8 size;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct sdhci_msm_pad_pull {
|
||||||
|
enum msm_tlmm_pull_tgt no;
|
||||||
|
u32 val;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct sdhci_msm_pad_pull_data {
|
||||||
|
struct sdhci_msm_pad_pull *on;
|
||||||
|
struct sdhci_msm_pad_pull *off;
|
||||||
|
u8 size;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct sdhci_msm_pad_drv {
|
||||||
|
enum msm_tlmm_hdrive_tgt no;
|
||||||
|
u32 val;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct sdhci_msm_pad_drv_data {
|
||||||
|
struct sdhci_msm_pad_drv *on;
|
||||||
|
struct sdhci_msm_pad_drv *off;
|
||||||
|
u8 size;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct sdhci_msm_pad_data {
|
||||||
|
struct sdhci_msm_pad_pull_data *pull;
|
||||||
|
struct sdhci_msm_pad_drv_data *drv;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
struct sdhci_msm_pin_data {
|
struct sdhci_msm_pin_data {
|
||||||
/*
|
/*
|
||||||
* = 1 if controller pins are using gpios
|
* = 1 if controller pins are using gpios
|
||||||
* = 0 if controller has dedicated MSM pads
|
* = 0 if controller has dedicated MSM pads
|
||||||
*/
|
*/
|
||||||
|
u8 is_gpio;
|
||||||
bool cfg_sts;
|
bool cfg_sts;
|
||||||
struct sdhci_msm_gpio_data *gpio_data;
|
struct sdhci_msm_gpio_data *gpio_data;
|
||||||
|
struct sdhci_msm_pad_data *pad_data;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct sdhci_msm_pltfm_data {
|
struct sdhci_msm_pltfm_data {
|
||||||
|
@ -180,20 +210,88 @@ free_gpios:
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int sdhci_msm_setup_pad(struct sdhci_msm_pltfm_data *pdata, bool enable)
|
||||||
|
{
|
||||||
|
struct sdhci_msm_pad_data *curr;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
curr = pdata->pin_data->pad_data;
|
||||||
|
for (i = 0; i < curr->drv->size; i++) {
|
||||||
|
if (enable)
|
||||||
|
msm_tlmm_set_hdrive(curr->drv->on[i].no,
|
||||||
|
curr->drv->on[i].val);
|
||||||
|
else
|
||||||
|
msm_tlmm_set_hdrive(curr->drv->off[i].no,
|
||||||
|
curr->drv->off[i].val);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < curr->pull->size; i++) {
|
||||||
|
if (enable)
|
||||||
|
msm_tlmm_set_pull(curr->pull->on[i].no,
|
||||||
|
curr->pull->on[i].val);
|
||||||
|
else
|
||||||
|
msm_tlmm_set_pull(curr->pull->off[i].no,
|
||||||
|
curr->pull->off[i].val);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static int sdhci_msm_setup_pins(struct sdhci_msm_pltfm_data *pdata, bool enable)
|
static int sdhci_msm_setup_pins(struct sdhci_msm_pltfm_data *pdata, bool enable)
|
||||||
{
|
{
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
|
|
||||||
if (!pdata->pin_data || (pdata->pin_data->cfg_sts == enable))
|
if (!pdata->pin_data || (pdata->pin_data->cfg_sts == enable))
|
||||||
return 0;
|
return 0;
|
||||||
|
if (pdata->pin_data->is_gpio)
|
||||||
|
ret = sdhci_msm_setup_gpio(pdata, enable);
|
||||||
|
else
|
||||||
|
ret = sdhci_msm_setup_pad(pdata, enable);
|
||||||
|
|
||||||
ret = sdhci_msm_setup_gpio(pdata, enable);
|
|
||||||
if (!ret)
|
if (!ret)
|
||||||
pdata->pin_data->cfg_sts = enable;
|
pdata->pin_data->cfg_sts = enable;
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int sdhci_msm_dt_get_array(struct device *dev, const char *prop_name,
|
||||||
|
u32 **out, int *len, u32 size)
|
||||||
|
{
|
||||||
|
int ret = 0;
|
||||||
|
struct device_node *np = dev->of_node;
|
||||||
|
size_t sz;
|
||||||
|
u32 *arr = NULL;
|
||||||
|
|
||||||
|
if (!of_get_property(np, prop_name, len)) {
|
||||||
|
ret = -EINVAL;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
sz = *len = *len / sizeof(*arr);
|
||||||
|
if (sz <= 0 || (size > 0 && (sz != size))) {
|
||||||
|
dev_err(dev, "%s invalid size\n", prop_name);
|
||||||
|
ret = -EINVAL;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
arr = devm_kzalloc(dev, sz * sizeof(*arr), GFP_KERNEL);
|
||||||
|
if (!arr) {
|
||||||
|
dev_err(dev, "%s failed allocating memory\n", prop_name);
|
||||||
|
ret = -ENOMEM;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = of_property_read_u32_array(np, prop_name, arr, sz);
|
||||||
|
if (ret < 0) {
|
||||||
|
dev_err(dev, "%s failed reading array %d\n", prop_name, ret);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
*out = arr;
|
||||||
|
out:
|
||||||
|
if (ret)
|
||||||
|
*len = 0;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
#define MAX_PROP_SIZE 32
|
#define MAX_PROP_SIZE 32
|
||||||
static int sdhci_msm_dt_parse_vreg_info(struct device *dev,
|
static int sdhci_msm_dt_parse_vreg_info(struct device *dev,
|
||||||
struct sdhci_msm_reg_data **vreg_data, const char *vreg_name)
|
struct sdhci_msm_reg_data **vreg_data, const char *vreg_name)
|
||||||
|
@ -261,11 +359,164 @@ static int sdhci_msm_dt_parse_vreg_info(struct device *dev,
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* GPIO/Pad data extraction */
|
||||||
|
static int sdhci_msm_dt_get_pad_pull_info(struct device *dev, int id,
|
||||||
|
struct sdhci_msm_pad_pull_data **pad_pull_data)
|
||||||
|
{
|
||||||
|
int ret = 0, base = 0, len, i;
|
||||||
|
u32 *tmp;
|
||||||
|
struct sdhci_msm_pad_pull_data *pull_data;
|
||||||
|
struct sdhci_msm_pad_pull *pull;
|
||||||
|
|
||||||
|
switch (id) {
|
||||||
|
case 1:
|
||||||
|
base = TLMM_PULL_SDC1_CLK;
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
base = TLMM_PULL_SDC2_CLK;
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
base = TLMM_PULL_SDC3_CLK;
|
||||||
|
break;
|
||||||
|
case 4:
|
||||||
|
base = TLMM_PULL_SDC4_CLK;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
dev_err(dev, "%s: Invalid slot id\n", __func__);
|
||||||
|
ret = -EINVAL;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
pull_data = devm_kzalloc(dev, sizeof(struct sdhci_msm_pad_pull_data),
|
||||||
|
GFP_KERNEL);
|
||||||
|
if (!pull_data) {
|
||||||
|
dev_err(dev, "No memory for msm_mmc_pad_pull_data\n");
|
||||||
|
ret = -ENOMEM;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
pull_data->size = 3; /* array size for clk, cmd, data */
|
||||||
|
|
||||||
|
/* Allocate on, off configs for clk, cmd, data */
|
||||||
|
pull = devm_kzalloc(dev, 2 * pull_data->size *\
|
||||||
|
sizeof(struct sdhci_msm_pad_pull), GFP_KERNEL);
|
||||||
|
if (!pull) {
|
||||||
|
dev_err(dev, "No memory for msm_mmc_pad_pull\n");
|
||||||
|
ret = -ENOMEM;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
pull_data->on = pull;
|
||||||
|
pull_data->off = pull + pull_data->size;
|
||||||
|
|
||||||
|
ret = sdhci_msm_dt_get_array(dev, "qcom,pad-pull-on",
|
||||||
|
&tmp, &len, pull_data->size);
|
||||||
|
if (ret)
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
for (i = 0; i < len; i++) {
|
||||||
|
pull_data->on[i].no = base + i;
|
||||||
|
pull_data->on[i].val = tmp[i];
|
||||||
|
dev_dbg(dev, "%s: val[%d]=0x%x\n", __func__,
|
||||||
|
i, pull_data->on[i].val);
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = sdhci_msm_dt_get_array(dev, "qcom,pad-pull-off",
|
||||||
|
&tmp, &len, pull_data->size);
|
||||||
|
if (ret)
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
for (i = 0; i < len; i++) {
|
||||||
|
pull_data->off[i].no = base + i;
|
||||||
|
pull_data->off[i].val = tmp[i];
|
||||||
|
dev_dbg(dev, "%s: val[%d]=0x%x\n", __func__,
|
||||||
|
i, pull_data->off[i].val);
|
||||||
|
}
|
||||||
|
|
||||||
|
*pad_pull_data = pull_data;
|
||||||
|
out:
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int sdhci_msm_dt_get_pad_drv_info(struct device *dev, int id,
|
||||||
|
struct sdhci_msm_pad_drv_data **pad_drv_data)
|
||||||
|
{
|
||||||
|
int ret = 0, base = 0, len, i;
|
||||||
|
u32 *tmp;
|
||||||
|
struct sdhci_msm_pad_drv_data *drv_data;
|
||||||
|
struct sdhci_msm_pad_drv *drv;
|
||||||
|
|
||||||
|
switch (id) {
|
||||||
|
case 1:
|
||||||
|
base = TLMM_HDRV_SDC1_CLK;
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
base = TLMM_HDRV_SDC2_CLK;
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
base = TLMM_HDRV_SDC3_CLK;
|
||||||
|
break;
|
||||||
|
case 4:
|
||||||
|
base = TLMM_HDRV_SDC4_CLK;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
dev_err(dev, "%s: Invalid slot id\n", __func__);
|
||||||
|
ret = -EINVAL;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
drv_data = devm_kzalloc(dev, sizeof(struct sdhci_msm_pad_drv_data),
|
||||||
|
GFP_KERNEL);
|
||||||
|
if (!drv_data) {
|
||||||
|
dev_err(dev, "No memory for msm_mmc_pad_drv_data\n");
|
||||||
|
ret = -ENOMEM;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
drv_data->size = 3; /* array size for clk, cmd, data */
|
||||||
|
|
||||||
|
/* Allocate on, off configs for clk, cmd, data */
|
||||||
|
drv = devm_kzalloc(dev, 2 * drv_data->size *\
|
||||||
|
sizeof(struct sdhci_msm_pad_drv), GFP_KERNEL);
|
||||||
|
if (!drv) {
|
||||||
|
dev_err(dev, "No memory msm_mmc_pad_drv\n");
|
||||||
|
ret = -ENOMEM;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
drv_data->on = drv;
|
||||||
|
drv_data->off = drv + drv_data->size;
|
||||||
|
|
||||||
|
ret = sdhci_msm_dt_get_array(dev, "qcom,pad-drv-on",
|
||||||
|
&tmp, &len, drv_data->size);
|
||||||
|
if (ret)
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
for (i = 0; i < len; i++) {
|
||||||
|
drv_data->on[i].no = base + i;
|
||||||
|
drv_data->on[i].val = tmp[i];
|
||||||
|
dev_dbg(dev, "%s: val[%d]=0x%x\n", __func__,
|
||||||
|
i, drv_data->on[i].val);
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = sdhci_msm_dt_get_array(dev, "qcom,pad-drv-off",
|
||||||
|
&tmp, &len, drv_data->size);
|
||||||
|
if (ret)
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
for (i = 0; i < len; i++) {
|
||||||
|
drv_data->off[i].no = base + i;
|
||||||
|
drv_data->off[i].val = tmp[i];
|
||||||
|
dev_dbg(dev, "%s: val[%d]=0x%x\n", __func__,
|
||||||
|
i, drv_data->off[i].val);
|
||||||
|
}
|
||||||
|
|
||||||
|
*pad_drv_data = drv_data;
|
||||||
|
out:
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
#define GPIO_NAME_MAX_LEN 32
|
#define GPIO_NAME_MAX_LEN 32
|
||||||
static int sdhci_msm_dt_parse_gpio_info(struct device *dev,
|
static int sdhci_msm_dt_parse_gpio_info(struct device *dev,
|
||||||
struct sdhci_msm_pltfm_data *pdata)
|
struct sdhci_msm_pltfm_data *pdata)
|
||||||
{
|
{
|
||||||
int ret = 0, cnt, i;
|
int ret = 0, id = 0, cnt, i;
|
||||||
struct sdhci_msm_pin_data *pin_data;
|
struct sdhci_msm_pin_data *pin_data;
|
||||||
struct device_node *np = dev->of_node;
|
struct device_node *np = dev->of_node;
|
||||||
|
|
||||||
|
@ -278,6 +529,7 @@ static int sdhci_msm_dt_parse_gpio_info(struct device *dev,
|
||||||
|
|
||||||
cnt = of_gpio_count(np);
|
cnt = of_gpio_count(np);
|
||||||
if (cnt > 0) {
|
if (cnt > 0) {
|
||||||
|
pin_data->is_gpio = true;
|
||||||
pin_data->gpio_data = devm_kzalloc(dev,
|
pin_data->gpio_data = devm_kzalloc(dev,
|
||||||
sizeof(struct sdhci_msm_gpio_data), GFP_KERNEL);
|
sizeof(struct sdhci_msm_gpio_data), GFP_KERNEL);
|
||||||
if (!pin_data->gpio_data) {
|
if (!pin_data->gpio_data) {
|
||||||
|
@ -294,7 +546,6 @@ static int sdhci_msm_dt_parse_gpio_info(struct device *dev,
|
||||||
ret = -ENOMEM;
|
ret = -ENOMEM;
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (i = 0; i < cnt; i++) {
|
for (i = 0; i < cnt; i++) {
|
||||||
const char *name = NULL;
|
const char *name = NULL;
|
||||||
char result[GPIO_NAME_MAX_LEN];
|
char result[GPIO_NAME_MAX_LEN];
|
||||||
|
@ -306,12 +557,39 @@ static int sdhci_msm_dt_parse_gpio_info(struct device *dev,
|
||||||
dev_name(dev), name ? name : "?");
|
dev_name(dev), name ? name : "?");
|
||||||
pin_data->gpio_data->gpio[i].name = result;
|
pin_data->gpio_data->gpio[i].name = result;
|
||||||
dev_dbg(dev, "%s: gpio[%s] = %d\n", __func__,
|
dev_dbg(dev, "%s: gpio[%s] = %d\n", __func__,
|
||||||
pin_data->gpio_data->gpio[i].name,
|
pin_data->gpio_data->gpio[i].name,
|
||||||
pin_data->gpio_data->gpio[i].no);
|
pin_data->gpio_data->gpio[i].no);
|
||||||
pdata->pin_data = pin_data;
|
}
|
||||||
|
} else {
|
||||||
|
pin_data->pad_data =
|
||||||
|
devm_kzalloc(dev,
|
||||||
|
sizeof(struct sdhci_msm_pad_data),
|
||||||
|
GFP_KERNEL);
|
||||||
|
if (!pin_data->pad_data) {
|
||||||
|
dev_err(dev,
|
||||||
|
"No memory for pin_data->pad_data\n");
|
||||||
|
ret = -ENOMEM;
|
||||||
|
goto out;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
|
ret = of_alias_get_id(np, "sdhc");
|
||||||
|
if (ret < 0) {
|
||||||
|
dev_err(dev, "Failed to get slot index %d\n", ret);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
id = ret;
|
||||||
|
|
||||||
|
ret = sdhci_msm_dt_get_pad_pull_info(
|
||||||
|
dev, id, &pin_data->pad_data->pull);
|
||||||
|
if (ret)
|
||||||
|
goto out;
|
||||||
|
ret = sdhci_msm_dt_get_pad_drv_info(
|
||||||
|
dev, id, &pin_data->pad_data->drv);
|
||||||
|
if (ret)
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
}
|
||||||
|
pdata->pin_data = pin_data;
|
||||||
out:
|
out:
|
||||||
if (ret)
|
if (ret)
|
||||||
dev_err(dev, "%s failed with err %d\n", __func__, ret);
|
dev_err(dev, "%s failed with err %d\n", __func__, ret);
|
||||||
|
@ -960,7 +1238,7 @@ static int sdhci_msm_remove(struct platform_device *pdev)
|
||||||
if (!IS_ERR_OR_NULL(msm_host->bus_clk))
|
if (!IS_ERR_OR_NULL(msm_host->bus_clk))
|
||||||
clk_disable_unprepare(msm_host->bus_clk);
|
clk_disable_unprepare(msm_host->bus_clk);
|
||||||
if (pdata->pin_data)
|
if (pdata->pin_data)
|
||||||
sdhci_msm_setup_gpio(pdata, false);
|
sdhci_msm_setup_pins(pdata, false);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue