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);