diff options
author | Asutosh Das <asutoshd@codeaurora.org> | 2012-12-21 12:21:42 +0530 |
---|---|---|
committer | Subhash Jadavani <subhashj@codeaurora.org> | 2016-05-27 10:28:24 -0700 |
commit | 1370edc0b20767c949ca77433f21414d0c35b29a (patch) | |
tree | 1d1f5ebfcc0fe3db230702a497199e3bc60c8088 /drivers/mmc | |
parent | 880b6f69f6c5b910cac0b6ad0a6716f00ffc8e19 (diff) |
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>
Diffstat (limited to 'drivers/mmc')
-rw-r--r-- | drivers/mmc/host/sdhci-msm.c | 294 |
1 files changed, 286 insertions, 8 deletions
diff --git a/drivers/mmc/host/sdhci-msm.c b/drivers/mmc/host/sdhci-msm.c index b916373a25bb..6b45dd682760 100644 --- a/drivers/mmc/host/sdhci-msm.c +++ b/drivers/mmc/host/sdhci-msm.c @@ -96,13 +96,43 @@ struct sdhci_msm_gpio_data { 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 { /* * = 1 if controller pins are using gpios * = 0 if controller has dedicated MSM pads */ + u8 is_gpio; bool cfg_sts; struct sdhci_msm_gpio_data *gpio_data; + struct sdhci_msm_pad_data *pad_data; }; struct sdhci_msm_pltfm_data { @@ -180,20 +210,88 @@ free_gpios: 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) { int ret = 0; if (!pdata->pin_data || (pdata->pin_data->cfg_sts == enable)) 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) pdata->pin_data->cfg_sts = enable; 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 static int sdhci_msm_dt_parse_vreg_info(struct device *dev, 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; } +/* 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 static int sdhci_msm_dt_parse_gpio_info(struct device *dev, 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 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); if (cnt > 0) { + pin_data->is_gpio = true; pin_data->gpio_data = devm_kzalloc(dev, sizeof(struct sdhci_msm_gpio_data), GFP_KERNEL); if (!pin_data->gpio_data) { @@ -294,7 +546,6 @@ static int sdhci_msm_dt_parse_gpio_info(struct device *dev, ret = -ENOMEM; goto out; } - for (i = 0; i < cnt; i++) { const char *name = NULL; 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 : "?"); pin_data->gpio_data->gpio[i].name = result; dev_dbg(dev, "%s: gpio[%s] = %d\n", __func__, - pin_data->gpio_data->gpio[i].name, - pin_data->gpio_data->gpio[i].no); - pdata->pin_data = pin_data; + pin_data->gpio_data->gpio[i].name, + pin_data->gpio_data->gpio[i].no); } - } + } 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: if (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)) clk_disable_unprepare(msm_host->bus_clk); if (pdata->pin_data) - sdhci_msm_setup_gpio(pdata, false); + sdhci_msm_setup_pins(pdata, false); return 0; } |