When running with DT, we no longer have a board file that can set up the
platform data for wlcore. Allow this data to be passed from DT.

Since some platforms use a gpio-irq, add support for passing either the
irq number or the gpio number. For the latter case, the driver will
request the gpio and convert it to the irq number. If an irq is
specified, it'll be used as is.

[Arik - the pdev_data pointer does not belong to us and is freed when
the device is released. Dereference to our private data first.]

Signed-off-by: Ido Yariv <ido@wizery.com>
Signed-off-by: Arik Nemtsov <arik@wizery.com>
---
 drivers/net/wireless/ti/wlcore/sdio.c | 71 ++++++++++++++++++++++++++++++++---
 include/linux/wl12xx.h                |  3 +-
 2 files changed, 67 insertions(+), 7 deletions(-)

--- a/drivers/net/wireless/ti/wlcore/sdio.c
+++ b/drivers/net/wireless/ti/wlcore/sdio.c
@@ -34,6 +34,7 @@
 #include <linux/wl12xx.h>
 #include <linux/pm_runtime.h>
 #include <linux/printk.h>
+#include <linux/of.h>
 
 #include "wlcore.h"
 #include "wl12xx_80211.h"
@@ -214,6 +215,61 @@ static struct wl1271_if_operations sdio_
 	.set_block_size = wl1271_sdio_set_block_size,
 };
 
+static const struct of_device_id wlcore_of_match[] = {
+	{
+		.compatible = "wlcore",
+	},
+	{}
+};
+MODULE_DEVICE_TABLE(of, wlcore_of_match);
+
+static struct wl12xx_platform_data *get_platform_data(struct device *dev)
+{
+	struct wl12xx_platform_data *pdata;
+	struct device_node *np;
+	u32 gpio;
+
+	pdata = wl12xx_get_platform_data();
+	if (!IS_ERR(pdata))
+		return kmemdup(pdata, sizeof(*pdata), GFP_KERNEL);
+
+	np = of_find_matching_node(NULL, wlcore_of_match);
+	if (!np) {
+		dev_err(dev, "No platform data set\n");
+		return NULL;
+	}
+
+	pdata = kzalloc(sizeof(*pdata), GFP_KERNEL);
+	if (!pdata) {
+		dev_err(dev, "Can't allocate platform data\n");
+		return NULL;
+	}
+
+	if (of_property_read_u32(np, "irq", &pdata->irq)) {
+		if (!of_property_read_u32(np, "gpio", &gpio) &&
+		    !gpio_request_one(gpio, GPIOF_IN, "wlcore_irq")) {
+			pdata->gpio = gpio;
+			pdata->irq = gpio_to_irq(gpio);
+		}
+	}
+
+	/* Optional fields */
+	pdata->use_eeprom = of_property_read_bool(np, "use-eeprom");
+	of_property_read_u32(np, "board-ref-clock", &pdata->board_ref_clock);
+	of_property_read_u32(np, "board-tcxo-clock", &pdata->board_tcxo_clock);
+	of_property_read_u32(np, "platform-quirks", &pdata->platform_quirks);
+
+	return pdata;
+}
+
+static void del_platform_data(struct wl12xx_platform_data *pdata)
+{
+	if (pdata->gpio)
+		gpio_free(pdata->gpio);
+
+	kfree(pdata);
+}
+
 static int wl1271_probe(struct sdio_func *func,
 				  const struct sdio_device_id *id)
 {
@@ -245,12 +301,9 @@ static int wl1271_probe(struct sdio_func
 	/* Use block mode for transferring over one block size of data */
 	func->card->quirks |= MMC_QUIRK_BLKSZ_FOR_BYTE_MODE;
 
-	pdev_data.pdata = wl12xx_get_platform_data();
-	if (IS_ERR(pdev_data.pdata)) {
-		ret = PTR_ERR(pdev_data.pdata);
-		dev_err(glue->dev, "missing wlan platform data: %d\n", ret);
+	pdev_data.pdata = get_platform_data(&func->dev);
+	if (!(pdev_data.pdata))
 		goto out_free_glue;
-	}
 
 	/* if sdio can keep power while host is suspended, enable wow */
 	mmcflags = sdio_get_host_pm_caps(func);
@@ -279,7 +332,7 @@ static int wl1271_probe(struct sdio_func
 	if (!glue->core) {
 		dev_err(glue->dev, "can't allocate platform_device");
 		ret = -ENOMEM;
-		goto out_free_glue;
+		goto out_free_pdata;
 	}
 
 	glue->core->dev.parent = &func->dev;
@@ -313,6 +366,9 @@ static int wl1271_probe(struct sdio_func
 out_dev_put:
 	platform_device_put(glue->core);
 
+out_free_pdata:
+	del_platform_data(pdev_data.pdata);
+
 out_free_glue:
 	kfree(glue);
 
@@ -323,11 +379,14 @@ out:
 static void wl1271_remove(struct sdio_func *func)
 {
 	struct wl12xx_sdio_glue *glue = sdio_get_drvdata(func);
+	struct wlcore_platdev_data *pdev_data = glue->core->dev.platform_data;
+	struct wl12xx_platform_data *pdata = pdev_data.pdata;
 
 	/* Undo decrement done above in wl1271_probe */
 	pm_runtime_get_noresume(&func->dev);
 
 	platform_device_unregister(glue->core);
+	del_platform_data(pdata);
 	kfree(glue);
 }
 
--- a/include/linux/wl12xx.h
+++ b/include/linux/wl12xx.h
@@ -51,6 +51,7 @@ enum {
 struct wl1251_platform_data {
 	int power_gpio;
 	/* SDIO only: IRQ number if WLAN_IRQ line is used, 0 for SDIO IRQs */
+	int gpio;
 	int irq;
 	bool use_eeprom;
 };
@@ -59,7 +60,7 @@ struct wl12xx_platform_data {
 	int irq;
 	int board_ref_clock;
 	int board_tcxo_clock;
-	unsigned long platform_quirks;
+	u32 platform_quirks;
 	bool pwr_in_suspend;
 };
 
