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