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