ASR_BASE

Change-Id: Icf3719cc0afe3eeb3edc7fa80a2eb5199ca9dda1
diff --git a/marvell/linux/drivers/misc/nz3-slic.c b/marvell/linux/drivers/misc/nz3-slic.c
new file mode 100644
index 0000000..5f82f42
--- /dev/null
+++ b/marvell/linux/drivers/misc/nz3-slic.c
@@ -0,0 +1,559 @@
+/*
+ * Marvell SLIC driver
+ *
+ * Copyright (C) 2014 Marvell Technology Group Ltd.
+ *
+ * Author: Yu Zhang <zhangy@marvell.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * 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.
+ */
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/gpio.h>
+#include <linux/of.h>
+#include <linux/of_gpio.h>
+#include <linux/delay.h>
+#include <linux/regulator/machine.h>
+#include <linux/platform_device.h>
+#include <linux/pinctrl/consumer.h>
+#include <linux/edge_wakeup_mmp.h>
+#include <linux/mfd/88pm80x.h>
+#include <soc/asr/addr-map.h>
+
+#define SLIC_STATUS_LENS 8
+
+struct nz3_slic_info {
+	struct device *dev;
+	char chip_status[SLIC_STATUS_LENS];
+	char reset_status[SLIC_STATUS_LENS];
+	int reset_n_gpio;
+	int edge_wakeup_gpio;
+	int vdd_3v3_gpio;
+	struct pinctrl *pinctrl;
+	struct pinctrl_state *pin_lpm_drv_low;
+	struct pinctrl_state *pin_lpm_drv_high;
+	struct regulator *vdd_3v3;
+};
+
+static struct pinctrl *g_nz3_slic_pinctrl = NULL;
+
+static int g_reset_n_gpio = -1;
+static int g_edge_wakeup_gpio = -1;
+static int g_vdd_3v3_gpio = -1;
+
+//#define DEBUG_FALCON_SLIC_RESET
+#ifdef DEBUG_FALCON_SLIC_RESET
+
+#define MFPR_VIRT_BASE	(APB_VIRT_BASE + 0x1e000)
+
+static void __iomem *APBC_AIB_CLK_RST_reg = APB_VIRT_BASE + 0x15000 + 0x3C;/* 0xD4015000+0x3C */
+static void __iomem *GPIO10_MFPR_reg = MFPR_VIRT_BASE + 0x104;/* 0xD401E000+0x104 */
+static void __iomem *APBC_GPIO_CLK_RST_reg = APB_VIRT_BASE + 0x15000 + 0x8;/* 0xD4015008 */
+static void __iomem *GPIO_PDR0_reg = APB_VIRT_BASE + 0x19000 +0xC;/* 0xD4019000+0xC */
+#if 0
+static void __iomem *GPIO_PSR0_reg = APB_VIRT_BASE + 0x19000 +0x18;/* 0xD4019000+0x18 */
+static void __iomem *GPIO_PCR0_reg = APB_VIRT_BASE + 0x19000 +0x24;/* 0xD4019000+0x24 */
+#endif
+
+static void falcon_config_MFPR_GPIO10(void)
+{
+    int reg_value = 0;
+
+    printk(KERN_INFO"%s:L%d, enter.\n", __FUNCTION__, __LINE__);
+
+    if (!APBC_AIB_CLK_RST_reg ||!GPIO10_MFPR_reg ||!APBC_GPIO_CLK_RST_reg || !GPIO_PDR0_reg)
+    {
+        printk(KERN_INFO"%s:L%d, NULL pointer.\n", __FUNCTION__, __LINE__);
+        return;
+    }
+
+    //*(volatile unsigned int *)APBC_AIB_CLK_RST_reg = 0xFFFFFFFB;/* open aib MFPR config clock/rst */
+    __raw_writel(0xFFFFFFFB, APBC_AIB_CLK_RST_reg);
+
+    //*(volatile unsigned int *)GPIO10_MFPR_reg &= ~((0x3<<13) | 0x7);/* use function 0 to select gpio_in[10], and NO Pull up/dn */
+    reg_value = __raw_readl(GPIO10_MFPR_reg);
+    printk(KERN_INFO"%s,GPIO10_MFPR_reg value before= 0x%x\n", __FUNCTION__, reg_value);
+    reg_value &= ~((0x3<<13) | 0x7);
+    __raw_writel(reg_value, GPIO10_MFPR_reg);/* Falcon */
+    printk(KERN_INFO"%s,GPIO10_MFPR_reg value after= 0x%x\n", __FUNCTION__, reg_value);
+
+    //*(volatile unsigned int *)APBC_GPIO_CLK_RST_reg = 0x3;/* gpio clock/rst enable */
+    __raw_writel(0x3, APBC_GPIO_CLK_RST_reg);
+
+    //*(volatile unsigned int *)GPIO_PDR0_reg |= 0x1<<10;/* gpio_in[10] output */
+    reg_value = __raw_readl(GPIO_PDR0_reg);
+    reg_value |= 0x1<<10;
+    __raw_writel(reg_value, GPIO_PDR0_reg);/* Falcon */
+
+    return;
+}
+#endif
+
+static void slic_power_on(struct nz3_slic_info *info)
+{
+	if (!strncmp(info->chip_status, "on", 2)) {
+		dev_info(info->dev, "slic chip already powered on\n");
+		return;
+	}
+
+	if (info->vdd_3v3 > 0) {
+		if (regulator_set_voltage(info->vdd_3v3, 3300000, 3300000))
+			pr_err("fail to set regulator wib_3v3 supply 2 to 3.3v\n");
+		if (regulator_enable(info->vdd_3v3))
+			pr_err("fail to enable regulator vdd_3v3\n");
+	}
+
+	if (info->vdd_3v3_gpio >= 0) {
+		gpio_direction_output(info->vdd_3v3_gpio, 1);
+	}
+
+	usleep_range(65, 70);
+#ifdef DEBUG_FALCON_SLIC_RESET
+	if (cpu_is_asr1803()) {
+		falcon_config_MFPR_GPIO10();
+	}
+#endif
+	if (info->reset_n_gpio >= 0) {
+		gpio_direction_output(info->reset_n_gpio, 1);
+		strncpy(info->reset_status, "high", 5);
+	}
+
+	strncpy(info->chip_status, "on", 3);
+
+	if (info->vdd_3v3 > 0) {
+		buck2_ldo8_sleepmode_control_for_slic(1);
+	}
+
+	dev_info(info->dev, "slic chip powered on\n");
+}
+
+static void slic_power_off(struct nz3_slic_info *info)
+{
+	if (!strncmp(info->chip_status, "off", 3)) {
+		dev_info(info->dev, "slic chip already powered off\n");
+		return;
+	}
+
+	if (info->vdd_3v3 > 0) {
+		if (regulator_disable(info->vdd_3v3))
+			pr_err("fail to disable regulator vdd_3v3\n");
+	}
+
+	if (info->vdd_3v3_gpio >= 0) {
+		gpio_direction_output(info->vdd_3v3_gpio, 0);
+	}
+#ifdef DEBUG_FALCON_SLIC_RESET
+	if (cpu_is_asr1803()) {
+		falcon_config_MFPR_GPIO10();
+	}
+#endif
+	if (info->reset_n_gpio >= 0) {
+		gpio_direction_output(info->reset_n_gpio, 0);
+		strncpy(info->reset_status, "low", 4);
+	}
+
+	strncpy(info->chip_status, "off", 4);
+
+	if (info->vdd_3v3 > 0) {
+		buck2_ldo8_sleepmode_control_for_slic(0);
+	}
+
+	dev_info(info->dev, "slic chip powered off\n");
+}
+
+static void slic_reset(struct nz3_slic_info *info, int flag)
+{
+	printk(KERN_INFO "%s, reset_n_gpio=%d, flag=%d.\n", __FUNCTION__, info->reset_n_gpio, flag);
+#ifdef DEBUG_FALCON_SLIC_RESET
+	if (cpu_is_asr1803()) {
+		falcon_config_MFPR_GPIO10();
+	}
+#endif
+	gpio_direction_output(info->reset_n_gpio, flag);
+
+	sprintf(info->reset_status, "%s", flag ? "high" : "low");
+	dev_info(info->dev, "slic chip reset_n set to %s\n",
+				flag ? "high" : "low");
+}
+
+static ssize_t slic_ctrl(struct device *dev,
+			struct device_attribute *attr,
+			const char *buf, size_t count)
+{
+	static char msg[64];
+	int flag, ret;
+
+	struct nz3_slic_info *info = dev_get_drvdata(dev);
+
+	count = (count > 64) ? 64 : count;
+	memset(msg, 0, count);
+
+	if (!strncmp(buf, "off", 3)) {
+		slic_power_off(info);
+	} else if (!strncmp(buf, "on", 2)) {
+		slic_power_on(info);
+	} else if (!strncmp(buf, "reset", 5)) {
+		ret = sscanf(buf, "%s %d", msg, &flag);
+		if (ret == 2)
+			slic_reset(info, flag);
+	} else
+		dev_info(info->dev, "usage wrong\n");
+
+	return count;
+}
+
+static ssize_t slic_status(struct device *dev,
+		struct device_attribute *attr, char *buf)
+{
+	struct nz3_slic_info *info = dev_get_drvdata(dev);
+
+	return sprintf(buf, "power: %s, reset_n: %s\n", info->chip_status,
+		info->reset_status);
+}
+
+static DEVICE_ATTR(ctrl, S_IWUSR, slic_status, slic_ctrl);
+static DEVICE_ATTR(status, S_IRUSR, slic_status, NULL);
+
+static const struct attribute *slic_attrs[] = {
+	&dev_attr_ctrl.attr,
+	&dev_attr_status.attr,
+	NULL,
+};
+
+static const struct attribute_group slic_attr_group = {
+	.attrs = (struct attribute **)slic_attrs,
+};
+
+#ifdef CONFIG_OF
+static int nz3_slic_dt_init(struct device_node *np,
+			   struct device *dev,
+			   struct nz3_slic_info *info)
+{
+
+	struct regulator *vdd_3v3 = NULL;
+
+	info->reset_n_gpio =
+		of_get_named_gpio(dev->of_node, "rst-gpio", 0);
+	if (info->reset_n_gpio < 0) {
+		dev_err(dev, "%s: of_get_named_gpio failed: %d\n", __func__,
+		       info->reset_n_gpio);
+		info->reset_n_gpio = -1;
+		goto out;
+	}
+
+	info->edge_wakeup_gpio =
+		of_get_named_gpio(dev->of_node, "edge-wakeup-gpio", 0);
+	if (info->edge_wakeup_gpio < 0) {
+		dev_err(dev, "%s: of_get_named_gpio failed: %d\n", __func__,
+		       info->edge_wakeup_gpio);
+		info->edge_wakeup_gpio = -1;
+		goto out;
+	}
+
+	info->vdd_3v3_gpio = of_get_named_gpio(dev->of_node, "vdd-3v3-gpio", 0);
+	if (info->vdd_3v3_gpio < 0) {
+		dev_err(dev, "%s: of_get_named_gpio failed: %d\n", __func__, info->vdd_3v3_gpio);
+		info->vdd_3v3_gpio = -1;
+	}
+
+	g_reset_n_gpio = info->reset_n_gpio;
+	g_edge_wakeup_gpio = info->edge_wakeup_gpio;
+	g_vdd_3v3_gpio = info->vdd_3v3_gpio;
+
+	printk(KERN_INFO"%s: reset_n_gpio=%d, edge_wakeup_gpio=%d, vdd_3v3_gpio=%d, %s.\n",
+                         __FUNCTION__, info->reset_n_gpio, info->edge_wakeup_gpio, info->vdd_3v3_gpio, (info->vdd_3v3_gpio < 0)?"ASR1826 Old DKB":"ASR1826 New DKB");
+
+	printk(KERN_INFO"%s: g_reset_n_gpio=%d, g_edge_wakeup_gpio=%d, g_vdd_3v3_gpio=%d.\n",
+                         __FUNCTION__, g_reset_n_gpio, g_edge_wakeup_gpio, g_vdd_3v3_gpio);
+
+	vdd_3v3 = regulator_get(dev, "vdd33");
+	if (IS_ERR_OR_NULL(vdd_3v3)) {
+		if (PTR_ERR(vdd_3v3) < 0) {
+			pr_info("%s: the regulator for vdd_3v3 not found\n", __func__);
+		}
+	} else {
+		info->vdd_3v3 = vdd_3v3;
+	}
+
+	return 0;
+
+out:
+	return -EINVAL;
+}
+#else
+static int nz3_slic_dt_init(struct device_node *np,
+			   struct device *dev,
+			   struct nz3_slic_info *info)
+{
+	return 0;
+}
+#endif
+
+/* nz3_slic_ctrl */
+static struct dentry *nz3_slic_ctrl = NULL;
+
+static ssize_t nz3_slic_ctrl_read(struct file *file, char __user *user_buf,
+        size_t count, loff_t *ppos)
+{
+
+    printk(KERN_INFO"%s/L%d.\n", __FUNCTION__, __LINE__);
+
+    return 0;
+}
+
+/*
+ *control command:
+ *
+ *Disable VDD 3V3: echo 0 > /sys/kernel/debug/nz3_slic_ctrl
+ *Enable VDD 3V3: echo 1 > /sys/kernel/debug/nz3_slic_ctrl
+ *
+ *
+ */
+static char msg[10];
+
+static ssize_t nz3_slic_ctrl_write(struct file *file,
+        const char __user *user_buf,
+        size_t count, loff_t *ppos)
+{
+    int ret = 0;
+    size_t tmp_count = 0;
+
+    printk(KERN_INFO"%s/L%d.\n", __FUNCTION__, __LINE__);
+
+    memset(msg, 0x00, sizeof(msg));
+    tmp_count = count;
+
+    if (tmp_count >= sizeof(msg)){
+        tmp_count = sizeof(msg) - 1;
+    }
+
+    /* copy the content from user space to kernel space */
+    ret = copy_from_user(msg, user_buf, tmp_count);
+    if (ret){
+        printk(KERN_ALERT"copy from user fail \n");
+        return -EFAULT;
+    }
+
+    switch (msg[0]){
+        case '0':/* input command# echo 0 > /sys/kernel/debug/nz3_slic_ctrl */
+            printk(KERN_INFO "input %c. \n", msg[0]);
+            if (g_vdd_3v3_gpio >= 0) {
+                gpio_direction_output(g_vdd_3v3_gpio, 0);
+            }
+            printk(KERN_INFO "Disable VDD 3V3 for ASR1826 New DKB.\n");
+            break;
+
+        case '1':/* input command# echo 1 > /sys/kernel/debug/nz3_slic_ctrl */
+            printk(KERN_INFO "input %c. \n", msg[0]);
+            if (g_vdd_3v3_gpio >= 0) {
+                gpio_direction_output(g_vdd_3v3_gpio, 1);
+            }
+            printk(KERN_INFO "Enable VDD 3V3 for ASR1826 New DKB.\n");
+            break;
+
+        case '2':/* input command# echo 2 > /sys/kernel/debug/nz3_slic_ctrl */
+            printk(KERN_INFO "input %c. \n", msg[0]);
+#ifdef DEBUG_FALCON_SLIC_RESET
+            if (cpu_is_asr1803()) {
+                falcon_config_MFPR_GPIO10();
+            }
+#endif
+            if (g_reset_n_gpio >= 0) {
+                gpio_direction_output(g_reset_n_gpio, 0);
+                gpio_set_value(g_reset_n_gpio, 0);
+            }
+            printk(KERN_INFO "low reset pin, g_reset_n_gpio=%d.\n", g_reset_n_gpio);
+            break;
+
+        case '3':/* input command# echo 3 > /sys/kernel/debug/nz3_slic_ctrl */
+            printk(KERN_INFO "input %c. \n", msg[0]);
+#ifdef DEBUG_FALCON_SLIC_RESET
+            if (cpu_is_asr1803()) {
+                falcon_config_MFPR_GPIO10();
+            }
+#endif
+            if (g_reset_n_gpio >= 0) {
+                gpio_direction_output(g_reset_n_gpio, 1);
+                gpio_set_value(g_reset_n_gpio, 1);
+            }
+            printk(KERN_INFO "high reset pin, g_reset_n_gpio=%d.\n", g_reset_n_gpio);
+            break;
+
+        default:/* input command#  */
+            printk(KERN_INFO "input invalid. \n");
+            break;
+    }
+
+    return tmp_count;
+}
+
+static const struct file_operations nz3_slic_ctrl_ops = {
+    .owner      = THIS_MODULE,
+    .open       = simple_open,
+    .read       = nz3_slic_ctrl_read,
+    .write      = nz3_slic_ctrl_write,
+};
+
+static inline int nz3_slic_ctrl_debugfs_init(void)
+{
+
+    nz3_slic_ctrl = debugfs_create_file("nz3_slic_ctrl", S_IRUGO | S_IFREG,
+            NULL, NULL, &nz3_slic_ctrl_ops);
+
+    if (nz3_slic_ctrl == NULL) {
+        pr_err("create nz3_slic_ctrl debugfs error!\n");
+        return -ENOENT;
+    } else if (nz3_slic_ctrl == ERR_PTR(-ENODEV)) {
+        pr_err("CONFIG_DEBUG_FS is not enabled!\n");
+        return -ENOENT;
+    }
+
+    return 0;
+}
+
+static void nz3_slic_ctrl_debugfs_remove(void)
+{
+    if (NULL != nz3_slic_ctrl){
+        debugfs_remove_recursive(nz3_slic_ctrl);
+    }
+
+    return;
+}
+
+static int nz3_slic_probe(struct platform_device *pdev)
+{
+	struct nz3_slic_info *info = NULL;
+	struct pinctrl_state *pin_default_nz3_slic = NULL;
+
+	struct device_node *np = pdev->dev.of_node;
+	int ret;
+
+	printk(KERN_INFO "enter %s. \n", __FUNCTION__);
+
+	info = devm_kzalloc(&pdev->dev, sizeof(struct nz3_slic_info),
+						GFP_KERNEL);
+	if (!info)
+		return -ENOMEM;
+
+	memset(info, 0x00, sizeof(struct nz3_slic_info));
+	info->reset_n_gpio = -1;
+	info->edge_wakeup_gpio = -1;
+	info->vdd_3v3_gpio = -1;
+
+	g_nz3_slic_pinctrl = devm_pinctrl_get(&pdev->dev);
+	pin_default_nz3_slic = pinctrl_lookup_state(g_nz3_slic_pinctrl, "default");
+	pinctrl_select_state(g_nz3_slic_pinctrl, pin_default_nz3_slic);
+
+	if (IS_ENABLED(CONFIG_OF)) {
+		ret = nz3_slic_dt_init(np, &pdev->dev, info);
+		if (ret) {
+			dev_err(&pdev->dev, "SLIC probe failed!\n");
+			return ret;
+		}
+	} else {
+		dev_err(&pdev->dev, "SLIC Not support DT, exit!\n");
+		return -EINVAL;
+	}
+
+	info->dev = &pdev->dev;
+
+	if (info->reset_n_gpio >= 0) {
+		ret = devm_gpio_request(info->dev,
+				info->reset_n_gpio, "slic_rst");
+		if (ret) {
+			dev_err(info->dev,
+				"request gpio %d failed\n", info->reset_n_gpio);
+			return ret;
+		}
+	}
+
+	/* use nz3-slic as wakeup source */
+	device_init_wakeup(&pdev->dev, 1);
+
+	if (info->edge_wakeup_gpio >= 0) {
+		ret = request_mfp_edge_wakeup(info->edge_wakeup_gpio,
+					      NULL, info, info->dev);
+		if (ret) {
+			dev_err(info->dev, "failed to request edge wakeup.\n");
+			remove_mfp_edge_wakeup(info->edge_wakeup_gpio);
+			return ret;
+		}
+	}
+
+	if (info->vdd_3v3_gpio >= 0) {
+		ret = devm_gpio_request(info->dev, info->vdd_3v3_gpio, "vdd_3v3");
+		if (ret) {
+			dev_err(info->dev, "request gpio %d failed\n", info->vdd_3v3_gpio);
+			return ret;
+		}
+	}
+
+	slic_power_on(info);
+	strncpy(info->chip_status, "on", 3);
+
+	platform_set_drvdata(pdev, info);
+
+	ret = sysfs_create_group(&pdev->dev.kobj, &slic_attr_group);
+	if (ret) {
+		dev_err(&pdev->dev, "SLIC create sysfs fail!\n");
+		return ret;
+	}
+
+	/* init debug tool */
+	nz3_slic_ctrl_debugfs_init();
+	return 0;
+}
+
+static int nz3_slic_remove(struct platform_device *pdev)
+{
+	printk(KERN_INFO "enter %s. \n", __FUNCTION__);
+
+	sysfs_remove_group(&pdev->dev.kobj, &slic_attr_group);
+
+	nz3_slic_ctrl_debugfs_remove();
+	return 0;
+}
+
+#ifdef CONFIG_OF
+static const struct of_device_id nz3_slic_dt_match[] = {
+	{ .compatible = "asr,nz3-slic", },
+	{},
+};
+#endif
+
+static struct platform_driver nz3_slic_driver = {
+	.driver		= {
+		.name	= "nz3-slic",
+		.owner	= THIS_MODULE,
+#ifdef CONFIG_OF
+		.of_match_table = of_match_ptr(nz3_slic_dt_match),
+#endif
+	},
+	.probe		= nz3_slic_probe,
+	.remove		= nz3_slic_remove,
+};
+
+static int nz3_slic_init(void)
+{
+	return platform_driver_register(&nz3_slic_driver);
+}
+
+static void nz3_slic_exit(void)
+{
+	platform_driver_unregister(&nz3_slic_driver);
+}
+module_init(nz3_slic_init);
+module_exit(nz3_slic_exit);
+
+MODULE_AUTHOR("Yu Zhang <zhangy@marvell.com>");
+MODULE_DESCRIPTION("driver for Marvell PXA1826 SLIC solution");
+MODULE_LICENSE("GPL v2");