ASR_BASE

Change-Id: Icf3719cc0afe3eeb3edc7fa80a2eb5199ca9dda1
diff --git a/marvell/linux/drivers/misc/asr_gps_ctrl.c b/marvell/linux/drivers/misc/asr_gps_ctrl.c
new file mode 100755
index 0000000..b553dc0
--- /dev/null
+++ b/marvell/linux/drivers/misc/asr_gps_ctrl.c
@@ -0,0 +1,359 @@
+/*
+ * rfkill power contorl for wlan/bt on ASR platform
+ *
+ * Copyright (C) 2018 ASR, Inc.
+ * 
+  * Authors: Yi Zhang <yizhang@marvell.com>
+
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#include <linux/debugfs.h>
+#include <linux/device.h>
+#include <linux/module.h>
+#include <linux/err.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/suspend.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>
+
+#define ASR_DEV_NAME "asr-gnss"
+
+#define GPS_STATUS_LENS 16
+struct asr_gnss_platform_data {
+	char chip_status[GPS_STATUS_LENS];
+	int gpio_reset;
+	int gpio_edge_wakeup;
+	int gpio_wakeup_device;
+	struct clk *clk_26m_out;
+};
+
+
+
+static DEFINE_MUTEX(asr_gnss_opt_mutex);
+
+static void gnss_edge_wakeup(int irq, void *p_rsv)
+{
+	pr_debug("gnss_edge_wakeup event +++++\n");
+}
+
+static void asr_gnss_platform_data_init(
+	struct asr_gnss_platform_data *pdata)
+{
+	/* all intems are invalid just after alloc */
+
+	pdata->gpio_reset = -1;
+	pdata->gpio_edge_wakeup = -1;
+	pdata->gpio_wakeup_device = -1;
+
+	memset(pdata->chip_status, 0, GPS_STATUS_LENS);
+}
+
+static struct asr_gnss_platform_data
+	*asr_gnss_platform_data_alloc(struct platform_device *pdev)
+{
+	struct asr_gnss_platform_data *pdata;
+
+	/* create a new one and init it */
+	pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);
+	if (pdata) {
+		asr_gnss_platform_data_init(pdata);
+		return pdata;
+	}
+
+	return NULL;
+}
+
+/* GPS: power on/off control */
+static void gps_power_on(struct device *dev)
+{
+	struct asr_gnss_platform_data *info = dev->platform_data;
+	if (info->gpio_edge_wakeup >= 0) {
+		request_mfp_edge_wakeup(info->gpio_edge_wakeup,
+			gnss_edge_wakeup, NULL, dev);
+	}
+
+	pr_debug("gps chip powered on\n");
+	return;
+}
+
+static void gps_power_off(struct asr_gnss_platform_data *info)
+{
+	pr_debug("gps chip powered off\n");
+	return;
+}
+
+static int gps_reset(struct asr_gnss_platform_data *info, int flag)
+{
+	int gpio_reset = info->gpio_reset;
+
+	if (gpio_reset < 0)
+		return -1;
+
+	if (gpio_request(gpio_reset, "asr5311 rst")) {
+		pr_info("gpio %d request failed\n", gpio_reset);
+		return -1;
+	}
+
+	gpio_direction_output(gpio_reset, flag);
+
+	gpio_free(gpio_reset);
+
+	return 0;
+}
+
+static int gps_wakeup(struct asr_gnss_platform_data *info, int flag)
+{
+	int host_wakeup = info->gpio_wakeup_device;
+
+	if (host_wakeup < 0)
+		return -1;
+
+	if (gpio_request(host_wakeup, "host wakeup asr5311")) {
+		pr_info("gpio %d request failed\n", host_wakeup);
+		return -1;
+	}
+
+	gpio_direction_output(host_wakeup, flag);
+
+	gpio_free(host_wakeup);
+
+	return 0;
+}
+
+static ssize_t gps_ctrl(struct device *dev,
+			struct device_attribute *attr,
+			const char *buf, size_t count)
+{
+	static char msg[256];
+	int flag, ret;
+
+	struct asr_gnss_platform_data *info = dev->platform_data;
+
+	count = (count > 255) ? 255 : count;
+	memset(msg, 0, count);
+
+	sscanf(buf, "%s", info->chip_status);
+	if (!strncmp(buf, "off", 3)) {
+		strncpy(info->chip_status, "off", 4);
+		gps_power_off(info);
+	} else if (!strncmp(buf, "on", 2)) {
+		strncpy(info->chip_status, "on", 3);
+		gps_power_on(dev);
+	} else if (!strncmp(buf, "reset", 5)) {
+		strncpy(info->chip_status, "reset", 6);
+		ret = sscanf(buf, "%s %d", msg, &flag);
+		if (ret == 2)
+			gps_reset(info, flag);
+	} else if (!strncmp(buf, "wakeup", 6)) {
+		strncpy(info->chip_status, "wakeup", 7);
+		ret = sscanf(buf, "%s %d", msg, &flag);
+		if (ret == 2)
+			gps_wakeup(info, flag);
+	} else
+		pr_err("usage wrong\n");
+
+	return count;
+}
+
+static ssize_t gps_status(struct device *dev,
+		struct device_attribute *attr, char *buf)
+{
+	struct asr_gnss_platform_data *info = dev->platform_data;
+
+	return sprintf(buf, "status: %s\n", info->chip_status);
+}
+
+static DEVICE_ATTR(ctrl, S_IWUSR, gps_status, gps_ctrl);
+static DEVICE_ATTR(status, S_IRUSR, gps_status, NULL);
+
+static const struct attribute *gps_attrs[] = {
+	&dev_attr_ctrl.attr,
+	&dev_attr_status.attr,
+	NULL,
+};
+
+static const struct attribute_group gps_attr_group = {
+	.attrs = (struct attribute **)gps_attrs,
+};
+
+
+#ifdef CONFIG_OF
+static const struct of_device_id asr_gnss_of_match[] = {
+	{
+		.compatible = "asr,asr-gnss",
+	},
+	{},
+};
+
+MODULE_DEVICE_TABLE(of, asr_gnss_of_match);
+
+static int asr_gnss_probe_dt(struct platform_device *pdev)
+{
+	struct device_node *np = pdev->dev.of_node;
+	int gpio = 0;
+	struct asr_gnss_platform_data *pdata = pdev->dev.platform_data;
+
+	/* get gpios from dt */
+	gpio = of_get_named_gpio(np, "edge-wakeup-gpio", 0);
+	if (unlikely(gpio < 0)) {
+		dev_warn(&pdev->dev, "edge-wakeup-gpio undefined\n");
+		pdata->gpio_edge_wakeup = -1;
+	} else {
+		pdata->gpio_edge_wakeup = gpio;
+	}
+
+	gpio = of_get_named_gpio(np, "rst-gpio", 0);
+	if (unlikely(gpio < 0)) {
+		dev_err(&pdev->dev, "rst-gpio undefined\n");
+		pdata->gpio_reset = -1;
+	} else {
+		pdata->gpio_reset = gpio;
+	}
+
+	gpio = of_get_named_gpio(np, "host-wakeup-gnss-gpio", 0);
+	if (unlikely(gpio < 0)) {
+		dev_err(&pdev->dev, "host-wakeup-wlan-gpio undefined\n");
+	} else {
+		pdata->gpio_wakeup_device = gpio;
+		pr_info("%s: host_wakeup_wlan_gpio %d\n", __func__, gpio);
+	}
+
+	return 0;
+}
+#else
+static int asr_gnss_probe_dt(struct platform_device *pdev)
+{
+	return 0;
+}
+#endif
+
+static int asr_gnss_probe(struct platform_device *pdev)
+{
+	struct asr_gnss_platform_data *pdata = NULL;
+	/* flag: whether pdata is passed from platfrom_data */
+	int pdata_passed = 1;
+	struct device *dev = &pdev->dev;
+	const struct of_device_id *match = NULL;
+	int ret = -1;
+
+	pr_err("asr_gnss_probe!!!!");
+	/* make sure asr_rfkill_platform_data is valid */
+	pdata = pdev->dev.platform_data;
+	if (!pdata) {
+		/* if platfrom data do not pass the struct to us */
+		pdata_passed = 0;
+
+		pdata = asr_gnss_platform_data_alloc(pdev);
+		if (!pdata) {
+			pr_err("can't get asr_gnss_platform_data struct during probe\n");
+			goto err_pdata;
+		}
+
+		pdev->dev.platform_data = pdata;
+	}
+	
+	pdata->clk_26m_out = devm_clk_get(dev, "gnss_26m");
+	if (!IS_ERR(pdata->clk_26m_out)) {
+		dev_info(dev, "enable 26M output for wifi\n");
+		clk_prepare_enable(pdata->clk_26m_out);
+	}
+	/* set value to asr_rfkill_platform_data if DT pass them to us */
+#ifdef CONFIG_OF
+	match = of_match_device(of_match_ptr(asr_gnss_of_match),
+				&pdev->dev);
+#endif
+	if (match) {
+		ret = asr_gnss_probe_dt(pdev);
+		if (ret)
+			goto err_dt;
+	}
+
+	ret = sysfs_create_group(&pdev->dev.kobj, &gps_attr_group);
+	if (ret) {
+		dev_err(&pdev->dev, "GPS create sysfs fail!\n");
+		return ret;
+	}
+	return 0;
+
+err_dt:
+	if (!pdata_passed)
+		pdev->dev.platform_data = NULL;
+err_pdata:
+
+	return ret;
+}
+
+static int asr_gnss_remove(struct platform_device *pdev)
+{
+	struct asr_gnss_platform_data *pdata;
+	pdata = pdev->dev.platform_data;
+
+	if (!IS_ERR(pdata->clk_26m_out))
+		clk_disable_unprepare(pdata->clk_26m_out);
+
+	return 0;
+}
+
+static int asr_gnss_suspend(struct platform_device *pdev,
+			       pm_message_t pm_state)
+{
+	return 0;
+}
+
+static int asr_gnss_resume(struct platform_device *pdev)
+{
+	return 0;
+}
+
+static struct platform_driver asr_gnss_platform_driver = {
+	.probe = asr_gnss_probe,
+	.remove = asr_gnss_remove,
+	.driver = {
+		   .name = ASR_DEV_NAME,
+		   .owner = THIS_MODULE,
+#ifdef CONFIG_OF
+		   .of_match_table = asr_gnss_of_match,
+#endif
+
+		   },
+	.suspend = asr_gnss_suspend,
+	.resume  = asr_gnss_resume,
+};
+
+static int __init asr_gnss_init(void)
+{
+	return platform_driver_register(&asr_gnss_platform_driver);
+}
+
+static void __exit asr_gnss_exit(void)
+{
+	platform_driver_unregister(&asr_gnss_platform_driver);
+}
+
+module_init(asr_gnss_init);
+module_exit(asr_gnss_exit);
+
+MODULE_ALIAS("platform:asr_gps");
+MODULE_DESCRIPTION("asr_gps");
+MODULE_AUTHOR("ASR");
+MODULE_LICENSE("GPL V2");