tspp: support device tree mechanism in the driver

Update the TSPP driver to support using the device tree mechanism
for getting platform-dependent data.

Change-Id: I5ac0f90266c3f30621865cf097db4dba1ee07fc6
Signed-off-by: Liron Kuch <lkuch@codeaurora.org>
diff --git a/drivers/misc/tspp.c b/drivers/misc/tspp.c
index 0715d6d..cfd9eae 100644
--- a/drivers/misc/tspp.c
+++ b/drivers/misc/tspp.c
@@ -41,6 +41,8 @@
 #include <mach/dma.h>
 #include <mach/msm_tspp.h>
 #include <linux/debugfs.h>
+#include <linux/of.h>
+#include <linux/of_gpio.h>
 
 /*
  * General defines
@@ -647,45 +649,19 @@
 }
 
 /*** GPIO functions ***/
-static void tspp_gpios_free(const struct msm_gpio *table, int size)
-{
-	int i;
-	const struct msm_gpio *g;
-	for (i = size-1; i >= 0; i--) {
-		g = table + i;
-		gpio_free(GPIO_PIN(g->gpio_cfg));
-	}
-}
-
-static int tspp_gpios_request(const struct msm_gpio *table, int size)
-{
-	int rc;
-	int i;
-	const struct msm_gpio *g;
-	for (i = 0; i < size; i++) {
-		g = table + i;
-		rc = gpio_request(GPIO_PIN(g->gpio_cfg), g->label);
-		if (rc) {
-			pr_err("tspp: gpio_request(%d) <%s> failed: %d\n",
-			       GPIO_PIN(g->gpio_cfg), g->label ?: "?", rc);
-			goto err;
-		}
-	}
-	return 0;
-err:
-	tspp_gpios_free(table, i);
-	return rc;
-}
-
 static int tspp_gpios_disable(const struct msm_gpio *table, int size)
 {
 	int rc = 0;
 	int i;
 	const struct msm_gpio *g;
+
 	for (i = size-1; i >= 0; i--) {
 		int tmp;
 		g = table + i;
-		tmp = gpio_tlmm_config(g->gpio_cfg, GPIO_CFG_DISABLE);
+
+		tmp = gpio_tlmm_config(GPIO_CFG(GPIO_PIN(g->gpio_cfg),
+			0, GPIO_CFG_INPUT, GPIO_CFG_PULL_DOWN, GPIO_CFG_2MA),
+			GPIO_CFG_DISABLE);
 		if (tmp) {
 			pr_err("tspp_gpios_disable(0x%08x, GPIO_CFG_DISABLE) <%s> failed: %d\n",
 			       g->gpio_cfg, g->label ?: "?", rc);
@@ -704,8 +680,9 @@
 static int tspp_gpios_enable(const struct msm_gpio *table, int size)
 {
 	int rc;
-	int i;
+	int i, j;
 	const struct msm_gpio *g;
+
 	for (i = 0; i < size; i++) {
 		g = table + i;
 		rc = gpio_tlmm_config(g->gpio_cfg, GPIO_CFG_ENABLE);
@@ -721,39 +698,26 @@
 	}
 	return 0;
 err:
-	tspp_gpios_disable(table, i);
-	return rc;
-}
+	for (j = 0; j < i; j++)
+		tspp_gpios_disable(table, j);
 
-static int tspp_gpios_request_enable(const struct msm_gpio *table, int size)
-{
-	int rc = tspp_gpios_request(table, size);
-	if (rc)
-		return rc;
-	rc = tspp_gpios_enable(table, size);
-	if (rc)
-		tspp_gpios_free(table, size);
 	return rc;
 }
 
-static void tspp_gpios_disable_free(const struct msm_gpio *table, int size)
-{
-	tspp_gpios_disable(table, size);
-	tspp_gpios_free(table, size);
-}
-
 static int tspp_start_gpios(struct tspp_device *device)
 {
 	struct msm_tspp_platform_data *pdata =
 		device->pdev->dev.platform_data;
-	return tspp_gpios_request_enable(pdata->gpios, pdata->num_gpios);
+
+	return tspp_gpios_enable(pdata->gpios, pdata->num_gpios);
 }
 
 static void tspp_stop_gpios(struct tspp_device *device)
 {
 	struct msm_tspp_platform_data *pdata =
 		device->pdev->dev.platform_data;
-	tspp_gpios_disable_free(pdata->gpios, pdata->num_gpios);
+
+	tspp_gpios_disable(pdata->gpios, pdata->num_gpios);
 }
 
 /*** Clock functions ***/
@@ -1427,9 +1391,10 @@
 	event = &channel->event;
 
 	/* start the clocks if needed */
-	tspp_clock_start(pdev);
-	if (tspp_channels_in_use(pdev) == 0)
+	if (tspp_channels_in_use(pdev) == 0) {
+		tspp_clock_start(pdev);
 		wake_lock(&pdev->wake_lock);
+	}
 
 	/* mark it as used */
 	channel->used = 1;
@@ -1604,9 +1569,10 @@
 	channel->locked = NULL;
 	channel->used = 0;
 
-	if (tspp_channels_in_use(pdev) == 0)
+	if (tspp_channels_in_use(pdev) == 0) {
 		wake_unlock(&pdev->wake_lock);
-	tspp_clock_stop(pdev);
+		tspp_clock_stop(pdev);
+	}
 
 	return 0;
 }
@@ -2603,6 +2569,137 @@
 	}
 }
 
+/* copy device-tree data to platfrom data struct */
+static __devinit struct msm_tspp_platform_data *
+msm_tspp_dt_to_pdata(struct platform_device *pdev)
+{
+	struct device_node *node = pdev->dev.of_node;
+	struct msm_tspp_platform_data *data;
+	struct msm_gpio *gpios;
+	int i, rc;
+	int gpio;
+	u32 gpio_func;
+
+	/* Note: memory allocated by devm_kzalloc is freed automatically */
+	data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
+	if (!data) {
+		pr_err("tspp: Unable to allocate platform data\n");
+		return NULL;
+	}
+	rc = of_property_read_string(node, "qcom,tsif-pclk", &data->tsif_pclk);
+	if (rc) {
+		pr_err("tspp: Could not find tsif-pclk property, err = %d\n",
+			rc);
+		return NULL;
+	}
+	rc = of_property_read_string(node, "qcom,tsif-ref-clk",
+			&data->tsif_ref_clk);
+	if (rc) {
+		pr_err("tspp: Could not find tsif-ref-clk property, err = %d\n",
+			rc);
+		return NULL;
+	}
+
+	data->num_gpios = of_gpio_count(node);
+	if (data->num_gpios == 0) {
+		pr_err("tspp: Could not find GPIO definitions\n");
+		return NULL;
+	}
+	gpios = devm_kzalloc(&pdev->dev,
+			(data->num_gpios * sizeof(struct msm_gpio)),
+			GFP_KERNEL);
+	if (!gpios) {
+		pr_err("tspp: Unable to allocate memory for GPIOs table\n");
+		return NULL;
+	}
+	/* Assuming GPIO FUNC property is the same for all GPIOs */
+	if (of_property_read_u32(node, "qcom,gpios-func", &gpio_func)) {
+		pr_err("tspp: Could not find gpios-func property\n");
+		return NULL;
+	}
+	for (i = 0; i < data->num_gpios; i++) {
+		gpio = of_get_gpio(node, i);
+		gpios[i].gpio_cfg = GPIO_CFG(gpio, gpio_func,
+						GPIO_CFG_INPUT,
+						GPIO_CFG_PULL_DOWN,
+						GPIO_CFG_2MA);
+		rc = of_property_read_string_index(node, "qcom,gpio-names",
+						i, &gpios[i].label);
+		if (rc)
+			pr_warn("tspp: Could not find gpio-names property\n");
+	}
+
+	data->gpios = gpios;
+
+	return data;
+}
+
+static int msm_tspp_map_irqs(struct platform_device *pdev,
+				struct tspp_device *device)
+{
+	int rc;
+	int i;
+
+	/* get IRQ numbers from platform information */
+
+	/* map TSPP IRQ */
+	rc = platform_get_irq_byname(pdev, "TSIF_TSPP_IRQ");
+	if (rc > 0) {
+		device->tspp_irq = rc;
+		rc = request_irq(device->tspp_irq, tspp_isr, IRQF_SHARED,
+				 dev_name(&pdev->dev), device);
+		if (rc) {
+			dev_err(&pdev->dev,
+				"failed to request TSPP IRQ %d : %d",
+				device->tspp_irq, rc);
+			device->tspp_irq = 0;
+			return -EINVAL;
+		}
+	} else {
+		dev_err(&pdev->dev, "failed to get TSPP IRQ");
+		return -EINVAL;
+	}
+
+	/* map TSIF IRQs */
+	rc = platform_get_irq_byname(pdev, "TSIF0_IRQ");
+	if (rc > 0) {
+		device->tsif[0].tsif_irq = rc;
+	} else {
+		dev_err(&pdev->dev, "failed to get TSIF0 IRQ");
+		return -EINVAL;
+	}
+
+	rc = platform_get_irq_byname(pdev, "TSIF1_IRQ");
+	if (rc > 0) {
+		device->tsif[1].tsif_irq = rc;
+	} else {
+		dev_err(&pdev->dev, "failed to get TSIF1 IRQ");
+		return -EINVAL;
+	}
+
+	for (i = 0; i < TSPP_TSIF_INSTANCES; i++) {
+		rc = request_irq(device->tsif[i].tsif_irq,
+				tsif_isr, IRQF_SHARED,
+				dev_name(&pdev->dev), &device->tsif[i]);
+		if (rc) {
+			dev_warn(&pdev->dev, "failed to request TSIF%d IRQ: %d",
+				i, rc);
+			device->tsif[i].tsif_irq = 0;
+		}
+	}
+
+	/* map BAM IRQ */
+	rc = platform_get_irq_byname(pdev, "TSIF_BAM_IRQ");
+	if (rc > 0) {
+		device->bam_irq = rc;
+	} else {
+		dev_err(&pdev->dev, "failed to get TSPP BAM IRQ");
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
 static int __devinit msm_tspp_probe(struct platform_device *pdev)
 {
 	int rc = -ENODEV;
@@ -2616,8 +2713,20 @@
 	struct resource *mem_bam;
 	struct tspp_channel *channel;
 
-	/* must have platform data */
-	data = pdev->dev.platform_data;
+	if (pdev->dev.of_node) {
+		/* get information from device tree */
+		data = msm_tspp_dt_to_pdata(pdev);
+		/* get device ID */
+		rc = of_property_read_u32(pdev->dev.of_node,
+					"cell-index", &pdev->id);
+		if (rc)
+			pdev->id = -1;
+
+		pdev->dev.platform_data = data;
+	} else {
+		/* must have platform data */
+		data = pdev->dev.platform_data;
+	}
 	if (!data) {
 		pr_err("tspp: Platform data not available");
 		rc = -EINVAL;
@@ -2666,7 +2775,8 @@
 	}
 
 	/* map I/O memory */
-	mem_tsif0 = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	mem_tsif0 = platform_get_resource_byname(pdev,
+				IORESOURCE_MEM, "MSM_TSIF0_PHYS");
 	if (!mem_tsif0) {
 		pr_err("tspp: Missing tsif0 MEM resource");
 		rc = -ENXIO;
@@ -2679,7 +2789,8 @@
 		goto err_map_tsif0;
 	}
 
-	mem_tsif1 = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+	mem_tsif1 = platform_get_resource_byname(pdev,
+				IORESOURCE_MEM, "MSM_TSIF1_PHYS");
 	if (!mem_tsif1) {
 		dev_err(&pdev->dev, "Missing tsif1 MEM resource");
 		rc = -ENXIO;
@@ -2692,7 +2803,8 @@
 		goto err_map_tsif1;
 	}
 
-	mem_tspp = platform_get_resource(pdev, IORESOURCE_MEM, 2);
+	mem_tspp = platform_get_resource_byname(pdev,
+				IORESOURCE_MEM, "MSM_TSPP_PHYS");
 	if (!mem_tspp) {
 		dev_err(&pdev->dev, "Missing MEM resource");
 		rc = -ENXIO;
@@ -2704,7 +2816,8 @@
 		goto err_map_dev;
 	}
 
-	mem_bam = platform_get_resource(pdev, IORESOURCE_MEM, 3);
+	mem_bam = platform_get_resource_byname(pdev,
+				IORESOURCE_MEM, "MSM_TSPP_BAM_PHYS");
 	if (!mem_bam) {
 		pr_err("tspp: Missing bam MEM resource");
 		rc = -ENXIO;
@@ -2719,39 +2832,8 @@
 		goto err_map_bam;
 	}
 
-	/* map TSPP IRQ */
-	rc = platform_get_irq(pdev, 0);
-	if (rc > 0) {
-		device->tspp_irq = rc;
-		rc = request_irq(device->tspp_irq, tspp_isr, IRQF_SHARED,
-				 dev_name(&pdev->dev), device);
-		if (rc) {
-			dev_err(&pdev->dev, "failed to request IRQ %d : %d",
-				device->tspp_irq, rc);
-			goto err_irq;
-		}
-	} else {
-		dev_err(&pdev->dev, "failed to get tspp IRQ");
+	if (msm_tspp_map_irqs(pdev, device))
 		goto err_irq;
-	}
-
-	/* map TSIF IRQs */
-	device->tsif[0].tsif_irq = TSIF1_IRQ;
-	device->tsif[1].tsif_irq = TSIF2_IRQ;
-
-	for (i = 0; i < TSPP_TSIF_INSTANCES; i++) {
-		rc = request_irq(device->tsif[i].tsif_irq,
-				tsif_isr, IRQF_SHARED,
-				dev_name(&pdev->dev), &device->tsif[i]);
-		if (rc) {
-			dev_warn(&pdev->dev, "failed to request TSIF%d IRQ: %d",
-				i, rc);
-			device->tsif[i].tsif_irq = 0;
-		}
-	}
-
-	/* BAM IRQ */
-	device->bam_irq = TSIF_BAM_IRQ;
 
 	/* GPIOs */
 	rc = tspp_start_gpios(device);
@@ -2786,17 +2868,17 @@
 	device->bam_props.irq = device->bam_irq;
 	device->bam_props.manage = SPS_BAM_MGR_LOCAL;
 
+	if (tspp_clock_start(device) != 0) {
+		dev_err(&pdev->dev, "Can't start clocks");
+		goto err_clock;
+	}
+
 	if (sps_register_bam_device(&device->bam_props,
 		&device->bam_handle) != 0) {
 		pr_err("tspp: failed to register bam");
 		goto err_bam;
 	}
 
-	if (tspp_clock_start(device) != 0) {
-		dev_err(&pdev->dev, "Can't start clocks");
-		goto err_clock;
-	}
-
 	spin_lock_init(&device->spinlock);
 	tasklet_init(&device->tlet, tspp_sps_complete_tlet,
 			(unsigned long)device);
@@ -2805,7 +2887,11 @@
 	tspp_global_reset(device);
 
 	version = readl_relaxed(device->base + TSPP_VERSION);
-	if (version != 1)
+	/*
+	 * TSPP version can be bits [7:0] or alternatively,
+	 * TSPP major version is bits [31:28].
+	 */
+	if ((version != 0x1) && (((version >> 28) & 0xF) != 0x1))
 		pr_warn("tspp: unrecognized hw version=%i", version);
 
 	/* initialize the channels */
@@ -2825,21 +2911,30 @@
 	return 0;
 
 err_channel:
-	/* uninitialize channels */
+	/* un-initialize channels */
 	for (j = 0; j < i; j++) {
 		channel = &(device->channels[i]);
 		device_destroy(tspp_class, channel->cdev.dev);
 		cdev_del(&channel->cdev);
 	}
-err_clock:
+
 	sps_deregister_bam_device(device->bam_handle);
+err_clock:
 err_bam:
 	tspp_debugfs_exit(device);
 	for (i = 0; i < TSPP_TSIF_INSTANCES; i++)
 		tsif_debugfs_exit(&device->tsif[i]);
+
+	tspp_stop_gpios(device);
 err_gpio:
 err_irq:
-	tspp_stop_gpios(device);
+	for (i = 0; i < TSPP_TSIF_INSTANCES; i++) {
+		if (device->tsif[i].tsif_irq)
+			free_irq(device->tsif[i].tsif_irq,  &device->tsif[i]);
+	}
+	if (device->tspp_irq)
+		free_irq(device->tspp_irq, device);
+
 	iounmap(device->bam_props.virt_addr);
 err_map_bam:
 err_res_bam:
@@ -2879,7 +2974,10 @@
 		cdev_del(&channel->cdev);
 	}
 
+	/* de-registering BAM device requires clocks */
+	tspp_clock_start(device);
 	sps_deregister_bam_device(device->bam_handle);
+	tspp_clock_stop(device);
 
 	for (i = 0; i < TSPP_TSIF_INSTANCES; i++) {
 		tsif_debugfs_exit(&device->tsif[i]);
@@ -2928,12 +3026,18 @@
 	.runtime_resume = tspp_runtime_resume,
 };
 
+static struct of_device_id msm_match_table[] = {
+	{.compatible = "qcom,msm_tspp"},
+	{}
+};
+
 static struct platform_driver msm_tspp_driver = {
 	.probe          = msm_tspp_probe,
 	.remove         = __exit_p(msm_tspp_remove),
 	.driver         = {
 		.name   = "msm_tspp",
 		.pm     = &tspp_dev_pm_ops,
+		.of_match_table = msm_match_table,
 	},
 };