summaryrefslogtreecommitdiff
path: root/drivers/mmc
diff options
context:
space:
mode:
authorAsutosh Das <asutoshd@codeaurora.org>2012-12-21 12:21:42 +0530
committerSubhash Jadavani <subhashj@codeaurora.org>2016-05-27 10:28:24 -0700
commit1370edc0b20767c949ca77433f21414d0c35b29a (patch)
tree1d1f5ebfcc0fe3db230702a497199e3bc60c8088 /drivers/mmc
parent880b6f69f6c5b910cac0b6ad0a6716f00ffc8e19 (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.c294
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;
}