ASR_BASE

Change-Id: Icf3719cc0afe3eeb3edc7fa80a2eb5199ca9dda1
diff --git a/marvell/linux/drivers/misc/pcie8x_rfkill.c b/marvell/linux/drivers/misc/pcie8x_rfkill.c
new file mode 100644
index 0000000..7ef3dc6
--- /dev/null
+++ b/marvell/linux/drivers/misc/pcie8x_rfkill.c
@@ -0,0 +1,534 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * PCIe rfkill driver for ASR1803 SoCs
+ *
+ * Copyright (c) 2021 ASRMicro Inc.
+ */
+
+#include <linux/debugfs.h>
+#include <linux/device.h>
+
+#include <linux/err.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/platform_device.h>
+#include <linux/pci.h>
+#include <linux/pcie8x_rfkill.h>
+#include <linux/pm.h>
+#include <linux/pm_runtime.h>
+#include <linux/clk.h>
+#include <linux/of_device.h>
+#include <linux/of_gpio.h>
+#include <linux/of_platform.h>
+#include <linux/edge_wakeup_mmp.h>
+#include <linux/regulator/consumer.h>
+
+#define PCIE8X_DEV_NAME "pcie-rfkill"
+
+static DEFINE_MUTEX(pcie8x_pwr_mutex);
+
+static void pcie_rfkill_plat_data_init(
+	struct pcie_rfkill_plat_data *pdata)
+{
+	/* all intems are invalid just after alloc */
+	pdata->gpio_power_down = -1;  /* alias of wlan_en */
+	pdata->gpio_reset = -1;
+	pdata->gpio_reset2 = -1;
+	pdata->gpio_edge_wakeup = -1;
+	pdata->gpio_3v3_en = -1;
+	pdata->gpio_1v8_en = -1;
+	pdata->gpio2_3v3_en = -1;
+	pdata->gpio2_1v8_en = -1;
+	pdata->edge_requested = 0;
+	/* power status, unknown status at first */
+	pdata->is_on = -1;
+	pdata->is_on2 = -1;
+}
+
+static struct pcie_rfkill_plat_data
+	*pcie_rfkill_plat_data_alloc(struct platform_device *pdev)
+{
+	struct pcie_rfkill_plat_data *pdata;
+
+	/* create a new one and init it */
+	pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);
+	if (pdata) {
+		pcie_rfkill_plat_data_init(pdata);
+		return pdata;
+	}
+
+	return NULL;
+}
+
+static int pcie_3v3_1v8_ctrl(int gpio_3v3_en, int gpio_1v8_en, int on)
+{
+	if (gpio_3v3_en >= 0) {
+		if (gpio_request(gpio_3v3_en, "pcie_wlan 3v3 on")) {
+			pr_err("gpio %d request failed\n", gpio_3v3_en);
+			return -1;
+		} else {
+			if (on)
+				gpio_direction_output(gpio_3v3_en, 1);
+			else
+				gpio_direction_output(gpio_3v3_en, 0);
+			gpio_free(gpio_3v3_en);
+		}
+	}
+
+	if (gpio_1v8_en >= 0) {
+		if (gpio_request(gpio_1v8_en, "pcie_wlan 1v8 on")) {
+			pr_err("gpio %d request failed\n", gpio_1v8_en);
+			return -1;
+		} else {
+			if (on)
+				gpio_direction_output(gpio_1v8_en, 1);
+			else
+				gpio_direction_output(gpio_1v8_en, 0);
+			gpio_free(gpio_1v8_en);
+		}
+	}
+	return 0;
+}
+
+static void pcie_regulator(struct pcie_rfkill_plat_data *pdata, int on)
+{
+	if (on) {
+		if (pdata->wib_3v3) {
+			if (regulator_set_voltage(pdata->wib_3v3, 3300000, 3300000))
+				printk(KERN_DEBUG "fail to set regulator wib_3v3 to 3.3v\n");
+			if (!regulator_is_enabled(pdata->wib_3v3)) {
+				if (regulator_enable(pdata->wib_3v3))
+					printk(KERN_DEBUG "fail to enable regulator wib_3v3\n");
+			}
+		}
+	} else {
+		if (pdata->wib_3v3) {
+			if (regulator_is_enabled(pdata->wib_3v3)) {
+				if (regulator_disable(pdata->wib_3v3))
+					printk(KERN_DEBUG "fail to disable regulator wib_3v3\n");
+			}
+		}
+	}
+}
+
+static int slot0_pwr_ctrl(struct pcie_rfkill_plat_data *pdata, int on)
+{
+	int gpio_reset = pdata->gpio_reset;
+
+	if(gpio_reset < 0){
+		return -1;
+	}
+	if (on) {
+		if (!gpio_request(gpio_reset, "pcie_dev reset")) {
+			gpio_direction_output(gpio_reset, 0);
+			gpio_free(gpio_reset);
+		} else {
+			pr_err("pcie slot0 perst request failed %d.\n",
+					gpio_reset);
+		}
+		mdelay(10);
+		pcie_3v3_1v8_ctrl(pdata->gpio_3v3_en, pdata->gpio_1v8_en, 0);
+		mdelay(50);
+		pcie_3v3_1v8_ctrl(pdata->gpio_3v3_en, pdata->gpio_1v8_en, 1);
+		mdelay(10);
+	} else {
+		pcie_3v3_1v8_ctrl(pdata->gpio_3v3_en, pdata->gpio_1v8_en, 0);
+		if (!gpio_request(gpio_reset, "pcie_dev reset")) {
+			gpio_direction_output(gpio_reset, 0);
+			gpio_free(gpio_reset);
+		} else {
+			pr_err("pcie slot0 perst request failed %d.\n",
+					gpio_reset);
+		}
+		msleep(500); /*Toff-time, min: 500*/
+	}
+
+	return 0;
+}
+
+static int slot1_pwr_ctrl(struct pcie_rfkill_plat_data *pdata, int on)
+{
+	int gpio_reset2 = pdata->gpio_reset2;
+
+	if(gpio_reset2 < 0){
+		return -1;
+	}
+	if (on) {
+		if (!gpio_request(gpio_reset2, "pcie_dev reset")) {
+			gpio_direction_output(gpio_reset2, 0);
+			gpio_free(gpio_reset2);
+		} else {
+			pr_err("pcie slot1 perst request failed %d.\n",
+					gpio_reset2);
+		}
+		mdelay(10);
+		pcie_3v3_1v8_ctrl(pdata->gpio2_3v3_en, pdata->gpio2_1v8_en, 0);
+		mdelay(50);
+		pcie_3v3_1v8_ctrl(pdata->gpio2_3v3_en, pdata->gpio2_1v8_en, 1);
+		mdelay(10);
+	} else {
+		pcie_3v3_1v8_ctrl(pdata->gpio2_3v3_en, pdata->gpio2_1v8_en, 0);
+		if (!gpio_request(gpio_reset2, "pcie_dev reset")) {
+			gpio_direction_output(gpio_reset2, 0);
+			gpio_free(gpio_reset2);
+		} else {
+			pr_err("pcie slot1 perst gpio request failed %d.\n",
+					gpio_reset2);
+		}
+		msleep(500); /*Toff-time, min: 500*/
+	}
+
+	return 0;
+}
+
+static int pcie_slot1_pwr_on(struct pcie_rfkill_plat_data *pdata)
+{
+	int ret = 0;
+	
+	if (pdata->is_on2)
+		return 0;
+
+	pr_info("%s: on=%d\n", __func__, 1);
+	pcie_regulator(pdata, 1);
+	slot1_pwr_ctrl(pdata, 1);
+	pdata->is_on2 = 1;
+	pr_info("pcie_dev set_power on end\n");
+	
+	return ret;
+}
+
+static int pcie_slot1_pwr_off(struct pcie_rfkill_plat_data *pdata)
+{
+	int ret = 0;
+
+	if (!pdata->is_on2)
+		return 0;
+
+	pr_info("pcie slot1 set_power off start\n");
+	slot1_pwr_ctrl(pdata, 0);
+	pcie_regulator(pdata, 0);
+	pdata->is_on2 = 0;
+	pr_info("pcie_dev set_power off end\n");
+	
+	return ret;
+}
+
+static int pcie_slot0_pwr_on(struct pcie_rfkill_plat_data *pdata)
+{
+	int ret = 0;
+	
+	if (pdata->is_on)
+		return 0;
+
+	pr_info("%s: on=%d\n", __func__, 1);
+	pcie_regulator(pdata, 1);
+	slot0_pwr_ctrl(pdata, 1);
+	pdata->is_on = 1;
+	pr_info("pcie_dev set_power on end\n");
+
+	return ret;
+}
+
+static int pcie_slot0_pwr_off(struct pcie_rfkill_plat_data *pdata)
+{
+	int ret = 0;
+	
+	if (!pdata->is_on)
+		return 0;
+
+	pr_info("pcie slot0 set_power off start\n");
+	slot0_pwr_ctrl(pdata, 0);
+	pcie_regulator(pdata, 0);
+	pdata->is_on = 0;
+	pr_info("pcie_dev set_power off end\n");
+
+	return ret;
+}
+
+static ssize_t pcie_slot0_pwr_show(struct device *dev,
+	struct device_attribute *attr, char *buf)
+{
+	int len = 0;
+	struct pcie_rfkill_plat_data *pdata = dev->platform_data;
+
+	mutex_lock(&pcie8x_pwr_mutex);
+	len = sprintf(buf, "PCIe Device is power %s\n",
+		pdata->is_on ? "on" : "off");
+	mutex_unlock(&pcie8x_pwr_mutex);
+
+	return (ssize_t)len;
+}
+
+static ssize_t pcie_slot0_pwr_store(struct device *dev,
+	struct device_attribute *attr, const char *buf, size_t size)
+{
+	int pwr_ctrl;
+	int valid_ctrl = 0;
+	struct pcie_rfkill_plat_data *pdata = dev->platform_data;
+
+	mutex_lock(&pcie8x_pwr_mutex);
+	if (sscanf(buf, "%d", &pwr_ctrl) == 1) {
+		if ((pwr_ctrl == 1) || (pwr_ctrl == 0))
+			valid_ctrl = 1;
+	}
+
+	if (valid_ctrl != 1) {
+		pr_err("Please input valid ctrl: 0: Close, 1: Open\n");
+		mutex_unlock(&pcie8x_pwr_mutex);
+		return size;
+	}
+	if (pwr_ctrl)
+		pcie_slot0_pwr_on(pdata);
+	else
+		pcie_slot0_pwr_off(pdata);
+
+	pr_info("Now PCIe Device is power %s\n", pdata->is_on ? "on" : "off");
+	mutex_unlock(&pcie8x_pwr_mutex);
+
+	return size;
+}
+
+static ssize_t pcie_slot1_pwr_show(struct device *dev,
+	struct device_attribute *attr, char *buf)
+{
+	int len = 0;
+	struct pcie_rfkill_plat_data *pdata = dev->platform_data;
+
+	mutex_lock(&pcie8x_pwr_mutex);
+	len = sprintf(buf, "PCIe Device is power %s\n",
+		pdata->is_on2 ? "on" : "off");
+	mutex_unlock(&pcie8x_pwr_mutex);
+
+	return (ssize_t)len;
+}
+
+static ssize_t pcie_slot1_pwr_store(struct device *dev,
+	struct device_attribute *attr, const char *buf, size_t size)
+{
+	int pwr_ctrl;
+	int valid_ctrl = 0;
+	struct pcie_rfkill_plat_data *pdata = dev->platform_data;
+
+	mutex_lock(&pcie8x_pwr_mutex);
+	if (sscanf(buf, "%d", &pwr_ctrl) == 1) {
+		if ((pwr_ctrl == 1) || (pwr_ctrl == 0))
+			valid_ctrl = 1;
+	}
+
+	if (valid_ctrl != 1) {
+		pr_err("Please input valid ctrl: 0: Close, 1: Open\n");
+		mutex_unlock(&pcie8x_pwr_mutex);
+		return size;
+	}
+	if (pwr_ctrl)
+		pcie_slot1_pwr_on(pdata);
+	else
+		pcie_slot1_pwr_off(pdata);
+
+	pr_info("Now PCIe Device is power %s\n", pdata->is_on2 ? "on" : "off");
+	mutex_unlock(&pcie8x_pwr_mutex);
+
+	return size;
+}
+
+static DEVICE_ATTR(pwr_ctrl2, 0660,
+	pcie_slot1_pwr_show, pcie_slot1_pwr_store);
+static DEVICE_ATTR(pwr_ctrl, 0660,
+	pcie_slot0_pwr_show, pcie_slot0_pwr_store);
+
+#ifdef CONFIG_OF
+static const struct of_device_id pcie8x_rfkill_of_match[] = {
+	{
+		.compatible = "mrvl,pcie-rfkill",
+	},
+	{},
+};
+
+MODULE_DEVICE_TABLE(of, pcie8x_rfkill_of_match);
+
+static int pcie8x_rfkill_probe_dt(struct platform_device *pdev)
+{
+	struct device_node *np = pdev->dev.of_node;
+	struct pcie_rfkill_plat_data *pdata = pdev->dev.platform_data;
+	const char *wifi_device;
+	struct regulator *wib_3v3;
+	int gpio;
+
+	/* Get PD/RST pins status */
+	pdata->pinctrl = devm_pinctrl_get(&pdev->dev);
+	if (IS_ERR(pdata->pinctrl)) {
+		pdata->pinctrl = NULL;
+		dev_warn(&pdev->dev, "could not get PD/RST pinctrl.\n");
+	} else {
+		pdata->pin_off = pinctrl_lookup_state(pdata->pinctrl, "off");
+		if (IS_ERR(pdata->pin_off)) {
+			pdata->pin_off = NULL;
+			dev_err(&pdev->dev, "could not get off pinstate.\n");
+		}
+
+		pdata->pin_on = pinctrl_lookup_state(pdata->pinctrl, "on");
+		if (IS_ERR(pdata->pin_on)) {
+			pdata->pin_on = NULL;
+			dev_err(&pdev->dev, "could not get on pinstate.\n");
+		}
+	}
+
+	if (pdata->pinctrl && pdata->pin_off)
+		pinctrl_select_state(pdata->pinctrl, pdata->pin_off);
+
+	gpio = of_get_named_gpio(np, "rst-gpio", 0);
+	if (unlikely(gpio < 0)) {
+		pr_debug("pcie rfkill: rst-gpio undefined\n");
+		pdata->gpio_reset = -1;
+	} else {
+		pdata->gpio_reset = gpio;
+	}
+	gpio = of_get_named_gpio(np, "rst-gpio2", 0);
+	if (unlikely(gpio < 0)) {
+		pr_debug("pcie rfkill: rst-gpio2 undefined\n");
+		pdata->gpio_reset2 = -1;
+	} else {
+		pdata->gpio_reset2 = gpio;
+	}
+	
+	gpio = of_get_named_gpio(np, "3v3-ldo-gpio", 0);
+	if (unlikely(gpio < 0)) {
+		pr_debug("pcie rfkill: 3v3-ldo-gpio undefined\n");
+		pdata->gpio_3v3_en = -1;
+	} else {
+		pdata->gpio_3v3_en = gpio;
+	}
+	gpio = of_get_named_gpio(np, "3v3-ldo-gpio2", 0);
+	if (unlikely(gpio < 0)) {
+		pr_debug("pcie rfkill: 3v3-ldo-gpio2 undefined\n");
+		pdata->gpio2_3v3_en = -1;
+	} else {
+		pdata->gpio2_3v3_en = gpio;
+	}
+
+	gpio = of_get_named_gpio(np, "1v8-ldo-gpio", 0);
+	if (unlikely(gpio < 0)) {
+		pr_debug("pcie rfkill: 1v8-ldo-gpio undefined\n");
+		pdata->gpio_1v8_en = -1;
+	} else {
+		pdata->gpio_1v8_en = gpio;
+	}
+	gpio = of_get_named_gpio(np, "1v8-ldo-gpio2", 0);
+	if (unlikely(gpio < 0)) {
+		pr_debug("pcie rfkill: 1v8-ldo-gpio2 undefined\n");
+		pdata->gpio2_1v8_en = -1;
+	} else {
+		pdata->gpio2_1v8_en = gpio;
+	}
+
+	if (of_property_read_string(np,	"wifi-device", &wifi_device)) {
+		wifi_device = "dummy";
+	}
+
+	if (strcmp(wifi_device, "slot1") == 0)
+		pdata->pwr_ctrl = slot1_pwr_ctrl;
+	else if (strcmp(wifi_device, "slot0") == 0)
+		pdata->pwr_ctrl = slot0_pwr_ctrl;
+	else
+		pdata->pwr_ctrl = slot0_pwr_ctrl;
+
+	/* get regulators from dt */
+	wib_3v3 = regulator_get_optional(&pdev->dev, "wib_3v3");
+	if (IS_ERR_OR_NULL(wib_3v3)) {
+		printk(KERN_DEBUG "%s: the regulator for wib_3v3 not found\n",
+				__func__);
+	} else {
+		pdata->wib_3v3 = wib_3v3;
+	}
+	return 0;
+}
+#else
+static int pcie8x_rfkill_probe_dt(struct platform_device *pdev)
+{
+	return 0;
+}
+#endif
+
+static int pcie8x_rfkill_probe(struct platform_device *pdev)
+{
+	struct pcie_rfkill_plat_data *pdata = NULL;
+	/* flag: whether pdata is passed from platfrom_data */
+	int pdata_passed = 1;
+	const struct of_device_id *match = NULL;
+	int ret = -1;
+
+	/* make sure pcie_rfkill_plat_data is valid */
+	pdata = pdev->dev.platform_data;
+	if (!pdata) {
+		/* if platfrom data do not pass the struct to us */
+		pdata_passed = 0;
+		pdata = pcie_rfkill_plat_data_alloc(pdev);
+
+		if (!pdata) {
+			pr_err("can't get pcie_rfkill_plat_data struct during probe\n");
+			goto err_pdata;
+		}
+		pdev->dev.platform_data = pdata;
+	}
+
+	/* set value to pcie_rfkill_plat_data if DT pass them to us */
+#ifdef CONFIG_OF
+	match = of_match_device(of_match_ptr(pcie8x_rfkill_of_match),
+				&pdev->dev);
+#endif
+	if (match) {
+		ret = pcie8x_rfkill_probe_dt(pdev);
+		if (ret)
+			goto err_dt;
+	}
+
+	pdata->is_on = 0;
+	pdata->is_on2 = 0;
+
+	pcie_slot0_pwr_off(pdata);
+	pcie_slot1_pwr_off(pdata);
+	mdelay(100);
+	pcie_slot0_pwr_on(pdata);
+	pcie_slot1_pwr_on(pdata);
+
+	device_create_file(&pdev->dev, &dev_attr_pwr_ctrl);
+	device_create_file(&pdev->dev, &dev_attr_pwr_ctrl2);
+	return 0;
+
+err_dt:
+	if (!pdata_passed)
+		pdev->dev.platform_data = NULL;
+err_pdata:
+
+	return ret;
+}
+
+static int pcie8x_rfkill_suspend(struct platform_device *pdev,
+			       pm_message_t pm_state)
+{
+	return 0;
+}
+
+static int pcie8x_rfkill_resume(struct platform_device *pdev)
+{
+	return 0;
+}
+
+static struct platform_driver pcie8x_rfkill_platform_driver = {
+	.probe = pcie8x_rfkill_probe,
+	.driver = {
+		   .name = PCIE8X_DEV_NAME,
+		   .owner = THIS_MODULE,
+#ifdef CONFIG_OF
+		   .of_match_table = pcie8x_rfkill_of_match,
+#endif
+		   },
+	.suspend = pcie8x_rfkill_suspend,
+	.resume = pcie8x_rfkill_resume,
+};
+
+static int __init pcie8x_rfkill_init(void)
+{
+	return platform_driver_register(&pcie8x_rfkill_platform_driver);
+}
+device_initcall(pcie8x_rfkill_init);