msm: msm_sdcc: Add device tree support for SDCC controllers
Change-Id: Ic0d784eef2439ff9e10baf64499bb1be71bfb57d
Signed-off-by: Sujit Reddy Thumma <sthumma@codeaurora.org>
diff --git a/drivers/mmc/host/msm_sdcc.c b/drivers/mmc/host/msm_sdcc.c
index 4a475c6..4c6c8b8 100644
--- a/drivers/mmc/host/msm_sdcc.c
+++ b/drivers/mmc/host/msm_sdcc.c
@@ -19,6 +19,7 @@
#include <linux/moduleparam.h>
#include <linux/init.h>
#include <linux/ioport.h>
+#include <linux/of.h>
#include <linux/device.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
@@ -3508,10 +3509,112 @@
spin_unlock_irqrestore(&host->lock, flags);
}
+static struct mmc_platform_data *msmsdcc_populate_pdata(struct device *dev)
+{
+ int i, ret;
+ struct mmc_platform_data *pdata;
+ struct device_node *np = dev->of_node;
+ u32 bus_width = 0;
+ u32 *clk_table;
+ int clk_table_len;
+ u32 *sup_voltages;
+ int sup_volt_len;
+
+ pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);
+ if (!pdata) {
+ dev_err(dev, "could not allocate memory for platform data\n");
+ goto err;
+ }
+
+ of_property_read_u32(np, "qcom,sdcc-bus-width", &bus_width);
+ if (bus_width == 8) {
+ pdata->mmc_bus_width = MMC_CAP_8_BIT_DATA;
+ } else if (bus_width == 4) {
+ pdata->mmc_bus_width = MMC_CAP_4_BIT_DATA;
+ } else {
+ dev_notice(dev, "Invalid bus width, default to 1 bit mode\n");
+ pdata->mmc_bus_width = 0;
+ }
+
+ if (of_get_property(np, "qcom,sdcc-sup-voltages", &sup_volt_len)) {
+ size_t sz;
+ sz = sup_volt_len / sizeof(*sup_voltages);
+ if (sz > 0) {
+ sup_voltages = devm_kzalloc(dev,
+ sz * sizeof(*sup_voltages), GFP_KERNEL);
+ if (!sup_voltages) {
+ dev_err(dev, "No memory for supported voltage\n");
+ goto err;
+ }
+
+ ret = of_property_read_u32_array(np,
+ "qcom,sdcc-sup-voltages", sup_voltages, sz);
+ if (ret < 0) {
+ dev_err(dev, "error while reading voltage"
+ "ranges %d\n", ret);
+ goto err;
+ }
+ } else {
+ dev_err(dev, "No supported voltages\n");
+ goto err;
+ }
+ for (i = 0; i < sz; i += 2) {
+ u32 mask;
+
+ mask = mmc_vddrange_to_ocrmask(sup_voltages[i],
+ sup_voltages[i + 1]);
+ if (!mask)
+ dev_err(dev, "Invalide voltage range %d\n", i);
+ pdata->ocr_mask |= mask;
+ }
+ dev_dbg(dev, "OCR mask=0x%x\n", pdata->ocr_mask);
+ } else {
+ dev_err(dev, "Supported voltage range not specified\n");
+ }
+
+ if (of_get_property(np, "qcom,sdcc-clk-rates", &clk_table_len)) {
+ size_t sz;
+ sz = clk_table_len / sizeof(*clk_table);
+
+ if (sz > 0) {
+ clk_table = devm_kzalloc(dev, sz * sizeof(*clk_table),
+ GFP_KERNEL);
+ if (!clk_table) {
+ dev_err(dev, "No memory for clock table\n");
+ goto err;
+ }
+
+ ret = of_property_read_u32_array(np,
+ "qcom,sdcc-clk-rates", clk_table, sz);
+ if (ret < 0) {
+ dev_err(dev, "error while reading clk"
+ "table %d\n", ret);
+ goto err;
+ }
+ } else {
+ dev_err(dev, "clk_table not specified\n");
+ goto err;
+ }
+ pdata->sup_clk_table = clk_table;
+ pdata->sup_clk_cnt = sz;
+ } else {
+ dev_err(dev, "Supported clock rates not specified\n");
+ }
+
+ if (of_get_property(np, "qcom,sdcc-nonremovable", NULL))
+ pdata->nonremovable = true;
+ if (of_get_property(np, "qcom,sdcc-disable_cmd23", NULL))
+ pdata->disable_cmd23 = true;
+
+ return pdata;
+err:
+ return NULL;
+}
+
static int
msmsdcc_probe(struct platform_device *pdev)
{
- struct mmc_platform_data *plat = pdev->dev.platform_data;
+ struct mmc_platform_data *plat;
struct msmsdcc_host *host;
struct mmc_host *mmc;
unsigned long flags;
@@ -3525,6 +3628,14 @@
int ret = 0;
int i;
+ if (pdev->dev.of_node) {
+ plat = msmsdcc_populate_pdata(&pdev->dev);
+ of_property_read_u32((&pdev->dev)->of_node,
+ "cell-index", &pdev->id);
+ } else {
+ plat = pdev->dev.platform_data;
+ }
+
/* must have platform data */
if (!plat) {
pr_err("%s: Platform data not available\n", __func__);
@@ -3544,35 +3655,54 @@
pr_err("%s: Invalid resource\n", __func__);
return -ENXIO;
}
+ if (pdev->dev.of_node) {
+ /*
+ * Device tree iomem resources are only accessible by index.
+ * index = 0 -> SDCC register interface
+ * index = 1 -> DML register interface
+ * index = 2 -> BAM register interface
+ * IRQ resources:
+ * index = 0 -> SDCC IRQ
+ * index = 1 -> BAM IRQ
+ */
+ core_memres = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ dml_memres = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+ bam_memres = platform_get_resource(pdev, IORESOURCE_MEM, 2);
+ core_irqres = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+ bam_irqres = platform_get_resource(pdev, IORESOURCE_IRQ, 1);
+ } else {
+ for (i = 0; i < pdev->num_resources; i++) {
+ if (pdev->resource[i].flags & IORESOURCE_MEM) {
+ if (!strncmp(pdev->resource[i].name,
+ "sdcc_dml_addr",
+ sizeof("sdcc_dml_addr")))
+ dml_memres = &pdev->resource[i];
+ else if (!strncmp(pdev->resource[i].name,
+ "sdcc_bam_addr",
+ sizeof("sdcc_bam_addr")))
+ bam_memres = &pdev->resource[i];
+ else
+ core_memres = &pdev->resource[i];
- for (i = 0; i < pdev->num_resources; i++) {
- if (pdev->resource[i].flags & IORESOURCE_MEM) {
- if (!strcmp(pdev->resource[i].name,
- "sdcc_dml_addr"))
- dml_memres = &pdev->resource[i];
- else if (!strcmp(pdev->resource[i].name,
- "sdcc_bam_addr"))
- bam_memres = &pdev->resource[i];
- else
- core_memres = &pdev->resource[i];
-
- }
- if (pdev->resource[i].flags & IORESOURCE_IRQ) {
- if (!strcmp(pdev->resource[i].name,
- "sdcc_bam_irq"))
- bam_irqres = &pdev->resource[i];
- else
- core_irqres = &pdev->resource[i];
- }
- if (pdev->resource[i].flags & IORESOURCE_DMA) {
- if (!strncmp(pdev->resource[i].name,
- "sdcc_dma_chnl",
- sizeof("sdcc_dma_chnl")))
- dmares = &pdev->resource[i];
- else if (!strncmp(pdev->resource[i].name,
- "sdcc_dma_crci",
- sizeof("sdcc_dma_crci")))
- dma_crci_res = &pdev->resource[i];
+ }
+ if (pdev->resource[i].flags & IORESOURCE_IRQ) {
+ if (!strncmp(pdev->resource[i].name,
+ "sdcc_bam_irq",
+ sizeof("sdcc_bam_irq")))
+ bam_irqres = &pdev->resource[i];
+ else
+ core_irqres = &pdev->resource[i];
+ }
+ if (pdev->resource[i].flags & IORESOURCE_DMA) {
+ if (!strncmp(pdev->resource[i].name,
+ "sdcc_dma_chnl",
+ sizeof("sdcc_dma_chnl")))
+ dmares = &pdev->resource[i];
+ else if (!strncmp(pdev->resource[i].name,
+ "sdcc_dma_crci",
+ sizeof("sdcc_dma_crci")))
+ dma_crci_res = &pdev->resource[i];
+ }
}
}
@@ -4381,12 +4511,19 @@
.resume = msmsdcc_pm_resume,
};
+static const struct of_device_id msmsdcc_dt_match[] = {
+ {.compatible = "qcom,msm-sdcc"},
+
+};
+MODULE_DEVICE_TABLE(of, msmsdcc_dt_match);
+
static struct platform_driver msmsdcc_driver = {
.probe = msmsdcc_probe,
.remove = msmsdcc_remove,
.driver = {
.name = "msm_sdcc",
.pm = &msmsdcc_dev_pm_ops,
+ .of_match_table = msmsdcc_dt_match,
},
};