blob: 5f82f421e2e79d7fdda7734817430cc71dac7454 [file] [log] [blame]
/*
* 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");