blob: 2cf9b674601af69dd644f017b174d46d96fc6757 [file] [log] [blame]
/*
* drivers/soc/zte/plat/plat-zx298501.c
*
* Copyright (C) 2021 ZTE-TSP
*
* 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.
*/
#include <linux/kernel.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/clockchips.h>
#include <linux/clk.h>
#include <linux/clk-provider.h>
#include <linux/module.h>
#include <linux/err.h>
#include <linux/syscore_ops.h>
#include <linux/gpio.h>
#include <linux/of_device.h>
#include <linux/pinctrl/consumer.h>
#include <linux/io.h>
#include <linux/of_gpio.h>
#include <linux/platform_device.h>
#include <linux/pm_domain.h>
#include <linux/pm_runtime.h>
#include <linux/delay.h>
#include <linux/reset.h>
#include <linux/io.h>
#include <linux/amba/serial.h>
#include <linux/serial_reg.h>
#include <linux/of_device.h>
#include <linux/of_irq.h>
#include <linux/of_address.h>
#include <linux/sched/clock.h>
#include <linux/suspend.h>
#include <linux/mfd/syscon.h>
#include <linux/regmap.h>
#include <linux/reboot.h>
#include <linux/miscdevice.h>
#include <linux/soc/sc/common.h>
#include <linux/soc/sc/pcu.h>
#include <linux/soc/sc/spinlock.h>
#include <dt-bindings/soc/zx297520v3-irq.h>
#include <uapi/linux/sc_bsp/bsp_api.h>
/*
* we use sysfs to test&debug some system funcs
*
*/
struct kobject *zx_root_kobj;
struct kobject *zx_test_kobj;
struct kobject *zx_pm_kobj;
extern int __init zx_clk_test_init(void);
extern int __init zx_dma_test_init(void);
extern int __init zx_icp_test_init(void);
extern int __init zx_timer_test_init(void);
#define DOUBLE_EINT_DBG 0
#define EINT_THREAD_TEST 0
#define CONFIG_USE_DEBUG_LED 1
#define ZX_RESET_TEST 1
#define ZX_CLK_TEST 1
#define ZX_PINCTRL_TEST 1
#define ZX_GPIO_TEST 1
#define ZX_EINT_TEST 1
#define ZX_PM_TEST 1
#if ZX_PM_TEST
#define PM_RUNTIME_AUTO_TEST 1
#endif
#define ZX_SPINLOCK_TEST 0
#define ZX_PM_QOS_TEST 1
#define GPIO_HIGH 1
#define GPIO_LOW 0
/*
*
* some test need device probe
*/
struct zx_drv_test
{
struct device *dev;
#if ZX_RESET_TEST
struct reset_control *rst;
#endif
#if ZX_PINCTRL_TEST
struct pinctrl *pctrl;
struct pinctrl_state *state0;
struct pinctrl_state *state1;
struct pinctrl_state *state2;
#endif
#if ZX_GPIO_TEST
int gpio;
int gpio2;
int gpio3;
struct gpio_desc *gd;
#endif
#if ZX_EINT_TEST
int eint_irq;
int eint_irq2;
#endif
#if ZX_CLK_TEST
struct clk *clk;
#endif
};
struct zx_drv_test drv_test = {0};
#if 0//ZX_RESET_TEST
static void drv_reset_test(struct reset_control *rstc)
{
reset_control_assert(rstc);
udelay(10);
reset_control_deassert(rstc);
}
#endif
#if ZX_EINT_TEST
#if EINT_THREAD_TEST
static irqreturn_t test_eint_pri_isr(int irq, void *p)
{
disable_irq_nosync(irq);
return IRQ_WAKE_THREAD;
}
static irqreturn_t test_eint_isr(int irq, void *p)
{
static int eint_cnt = 0;
if ( pinctrl_select_state(drv_test.pctrl, drv_test.state0) < 0) {
pr_err("setting state0 failed\n");
}
irq_set_irq_type(drv_test.eint_irq, gpio_get_value(drv_test.gpio)?IRQ_TYPE_LEVEL_LOW:IRQ_TYPE_LEVEL_HIGH);
if ( pinctrl_select_state(drv_test.pctrl, drv_test.state1) < 0) {
pr_err("setting state0 failed\n");
}
enable_irq(irq);
pr_info("eint9 get = %d\n", ++eint_cnt);
return IRQ_HANDLED;
}
#else
static irqreturn_t test_eint_isr(int irq, void *p)
{
static int eint_cnt = 0;
irq_set_irq_type(drv_test.eint_irq, gpio_get_value(drv_test.gpio)?IRQ_TYPE_LEVEL_LOW:IRQ_TYPE_LEVEL_HIGH);
//pr_info("eint9 get = %d\n", ++eint_cnt);
return IRQ_HANDLED;
}
#endif
static irqreturn_t test_eint_isr2(int irq, void *p)
{
static int eint_cnt1 = 0;
irq_set_irq_type(drv_test.eint_irq2, gpio_get_value(drv_test.gpio2)?IRQ_TYPE_LEVEL_LOW:IRQ_TYPE_LEVEL_HIGH);
pr_info("eint12 get = %d\n", ++eint_cnt1);
return IRQ_HANDLED;
}
#endif
#if ZX_GPIO_TEST
static irqreturn_t gpio_irq_handler(int irq, void *dev_id)
{
static int irq_cnt = 0;
irq_cnt ++;
pr_info("gpio irq_cnt = %d\n", irq_cnt);
return IRQ_HANDLED;
}
#endif
/*
* test led helper interface
*
*/
#if CONFIG_USE_DEBUG_LED
static void test_led_init(void)
{
int ret;
if (!drv_test.dev)
return;
ret = gpio_request(drv_test.gpio, "led_test");
if (ret)
{
pr_info("led_test gpio request error.\n");
return ;
}
gpio_direction_output(drv_test.gpio, 0);
}
static void test_led_on(void)
{
if (!drv_test.dev)
return;
gpio_direction_output(drv_test.gpio, 1);
}
static void test_led_off(void)
{
if (!drv_test.dev)
return;
gpio_direction_output(drv_test.gpio, 0);
}
#else
static void test_led_init(void){}
static void test_led_on(void){}
static void test_led_off(void){}
#endif
#if ZX_PM_TEST
static int zx_drv_test_pm_resume(struct device *dev)
{
pm_stay_awake(dev);
pr_info("zx_drv_test_pm_resume\n");
return 0;
}
static int zx_drv_test_pm_suspend(struct device *dev)
{
pr_info("zx_drv_test_pm_suspend\n");
return 0;
}
static int zx_drv_test_pm_runtime_resume(struct device *dev)
{
/* enable clk and restore regs */
pr_info("zx_drv_test_pm_runtime_resume\n");
return 0;
}
static int zx_drv_test_pm_runtime_suspend(struct device *dev)
{
/* backup regs and disable clk */
pr_info("zx_drv_test_pm_runtime_suspend\n");
return 0;
}
static int zx_drv_test_pm_runtime_idle(struct device *dev)
{
pr_info("zx_drv_test_pm_runtime_idle\n");
return 0;
}
static const struct dev_pm_ops zx_drv_test_pm = {
.resume = zx_drv_test_pm_resume,
.suspend = zx_drv_test_pm_suspend,
.runtime_resume = zx_drv_test_pm_runtime_resume,
.runtime_suspend = zx_drv_test_pm_runtime_suspend,
.runtime_idle = zx_drv_test_pm_runtime_idle
};
#endif
static int zx_drv_test_probe(struct platform_device *pdev)
{
int gpio;
int irq;
enum of_gpio_flags flags;
int ret;
drv_test.dev = &pdev->dev;
/* reset */
#if ZX_RESET_TEST
drv_test.rst = devm_reset_control_get(&pdev->dev, "test_rst");
#endif
/* clk */
#if ZX_CLK_TEST
drv_test.clk = devm_clk_get(&pdev->dev, "test");
if (IS_ERR(drv_test.clk)) {
ret = PTR_ERR(drv_test.clk);
dev_err(&pdev->dev, "failed to get test_clk: %d\n", ret);
return ret;
}
clk_prepare_enable(drv_test.clk);
#endif
#if ZX_GPIO_TEST
drv_test.gd = gpiod_get_index(drv_test.dev, "testtt", 0, GPIOD_OUT_HIGH);
/* gpio test */
gpio = of_get_gpio_flags(pdev->dev.of_node, 0, &flags);
if (!gpio_is_valid(gpio)) {
pr_info("test gpio no found\n");
goto gpio_init_end;
}
/* pr_info("test gpio :%d flag=0x%x\n", gpio, flags); */
drv_test.gpio = gpio;
ret = gpio_request(drv_test.gpio, "gpio119");
if (ret)
{
pr_info("led_test gpio request error.\n");
BUG();
return 0;
}
gpio_direction_input(drv_test.gpio);
#if DOUBLE_EINT_DBG
gpio = of_get_gpio_flags(pdev->dev.of_node, 1, &flags);
if (!gpio_is_valid(gpio)) {
pr_info("test gpio1 no found\n");
goto gpio_init_end;
}
drv_test.gpio2 = gpio;
ret = gpio_request(drv_test.gpio2, "gpio131");
if (ret)
{
pr_info("led_test gpio2 request error.\n");
BUG();
return 0;
}
gpio_direction_input(drv_test.gpio2);
pr_info("test gpio :%d gpio2 : %d\n", drv_test.gpio, drv_test.gpio2);
#endif
gpio = of_get_gpio_flags(pdev->dev.of_node, 2, &flags);
if (!gpio_is_valid(gpio)) {
pr_info("test gpio1 no found\n");
goto gpio_init_end;
}
drv_test.gpio3 = gpio;
ret = gpio_request(drv_test.gpio3, "gpio120");
if (ret)
{
pr_info("led_test gpio3 request error.\n");
BUG();
return 0;
}
gpio_direction_output(drv_test.gpio3, 1);
gpio_init_end:
#endif
/* pinctrl */
#if ZX_PINCTRL_TEST
/*
drv_test.pctrl = devm_pinctrl_get_select_default(&pdev->dev);
*/
drv_test.pctrl = devm_pinctrl_get(&pdev->dev);
if (IS_ERR(drv_test.pctrl)) {
dev_warn(&pdev->dev, "Failed to get test pins");
drv_test.pctrl = NULL;
goto pinctrl_init_end;
}
drv_test.state0 = pinctrl_lookup_state(drv_test.pctrl, "state0");
if (IS_ERR(drv_test.state0)) {
dev_err(&pdev->dev, "TEST: missing state0\n");
}
drv_test.state1 = pinctrl_lookup_state(drv_test.pctrl, "state1"); // int9
if (IS_ERR(drv_test.state1)) {
dev_err(&pdev->dev, "TEST: missing state1\n");
}
drv_test.state2 = pinctrl_lookup_state(drv_test.pctrl, "ext_int5"); // int12
if (IS_ERR(drv_test.state2)) {
dev_err(&pdev->dev, "TEST: missing state2\n");
}
if ( pinctrl_select_state(drv_test.pctrl, drv_test.state1) < 0) {
dev_err(&pdev->dev, "setting state0 failed\n");
}
#if DOUBLE_EINT_DBG
/* eint5 */
if ( pinctrl_select_state(drv_test.pctrl, drv_test.state2) < 0) {
dev_err(&pdev->dev, "setting eint5 failed\n");
}
#endif
pinctrl_init_end:
#endif
#if ZX_PM_TEST
/* just show how a device use wake source */
device_init_wakeup(&pdev->dev, true);
// pm_stay_awake(&pdev->dev);
#endif
/* eint5 irq */
#if ZX_EINT_TEST
drv_test.eint_irq = irq_of_parse_and_map(pdev->dev.of_node, 0);
irq_set_irq_type(drv_test.eint_irq, gpio_get_value(drv_test.gpio)?IRQ_TYPE_LEVEL_LOW:IRQ_TYPE_LEVEL_HIGH);
#if EINT_THREAD_TEST
ret = request_threaded_irq(drv_test.eint_irq, test_eint_pri_isr, test_eint_isr, IRQF_ONESHOT, "test_eint9", &drv_test);
#else
ret = request_irq(drv_test.eint_irq,
test_eint_isr,
0,
"test_eint9",
&drv_test);
#endif
if(ret<0)
BUG();
enable_irq_wake(drv_test.eint_irq);
#if DOUBLE_EINT_DBG
drv_test.eint_irq2 = irq_of_parse_and_map(pdev->dev.of_node, 1);
irq_set_irq_type(drv_test.eint_irq2, gpio_get_value(drv_test.gpio2)?IRQ_TYPE_LEVEL_LOW:IRQ_TYPE_LEVEL_HIGH);
ret = request_irq(drv_test.eint_irq2,
test_eint_isr2,
0,
"test_eint12",
&drv_test);
if(ret<0)
BUG();
enable_irq_wake(drv_test.eint_irq2);
#endif
#endif
#if ZX_PM_TEST
#if PM_RUNTIME_AUTO_TEST
pm_runtime_set_autosuspend_delay(&pdev->dev, 3000 /*ms*/);
pm_runtime_use_autosuspend(&pdev->dev);
pm_runtime_enable(&pdev->dev);
pm_runtime_get_sync(&pdev->dev);
if (!pm_runtime_enabled(&pdev->dev)) {
zx_drv_test_pm_runtime_resume(&pdev->dev);
}
/* put to suspend 3s later */
pm_runtime_mark_last_busy(&pdev->dev);
pm_runtime_put_sync_autosuspend(&pdev->dev);
#else
if (pdev->dev.pm_domain) {
pm_runtime_set_active(&pdev->dev);
pm_runtime_enable(&pdev->dev);
}
if (pm_runtime_enabled(&pdev->dev))
pm_runtime_get_sync(&pdev->dev);
#endif
#endif
return 0;
}
static const struct of_device_id zx297520v3_drv_test_match[] = {
{ .compatible = "zte,drv-test", },
{ }
};
static struct platform_driver zx_test_driver = {
.probe = zx_drv_test_probe,
.driver = {
.name = "zx297520v3_drv_test",
#if ZX_PM_TEST
.pm = &zx_drv_test_pm,
#endif
.of_match_table = zx297520v3_drv_test_match,
},
};
/*sys fs*/
#define zte_attr(_name) \
static struct kobj_attribute _name##_attr = \
{ \
.attr = \
{ \
.name = __stringify(_name), \
.mode = 0644, \
}, \
.show = _name##_show, \
.store = _name##_store, \
}
/*=============================================================================
*======== /sys/zte/test/os_timer ==============================================
*=============================================================================
*/
static ssize_t os_timer_show(struct kobject *kobj, struct kobj_attribute *attr,
char *buf)
{
char *s = buf;
s += sprintf(s, "%s\n", "[TEST]Test will light on/off led every 5s~");
return (s - buf);
}
/*echo 1 > /sys/zte/test/os_timer*/
static struct timer_list test_timer;
static unsigned long test_timer_count = 0;
static unsigned int os_timer_timeout = 5*1000;
static void test_timer_expired(struct timer_list *unused)
{
mod_timer(&test_timer, jiffies + msecs_to_jiffies(os_timer_timeout));
// gpio_set_value(drv_test.gpio3, gpio_get_value(drv_test.gpio3)^1);
pr_info("[TEST]Test timer arrived:%lu \n",
++test_timer_count);
/*
if(test_timer_count&1)
test_led_on();
else
test_led_off();
*/
}
static ssize_t os_timer_store(struct kobject *kobj, struct kobj_attribute *attr,
const char *buf, size_t n)
{
int error = 0;
unsigned int temp;
// if(strict_strtol(buf, 0, &temp))
if(sscanf(buf, "%u", &temp) != 1)
error = -EINVAL;
pr_info("temp=%d", temp);
if(temp == 1)
{
mod_timer(&test_timer, jiffies + msecs_to_jiffies(os_timer_timeout));
}
else
{
del_timer(&test_timer);
test_timer_count = 0;
}
return error ? error : n;
}
zte_attr(os_timer);
/*=============================================================================
*======== /sys/zte/test/timer ==============================================
*=============================================================================
*/
/*echo 0xXXXXXXXX > /sys/zte/test/reg_read*/
#if ZX_PM_TEST
static ssize_t wake_show(struct kobject *kobj, struct kobj_attribute *attr,
char *buf)
{
char *s = buf;
s += sprintf(s, "0x%x\n", 0xaa55aa55);
return (s - buf);
}
static ssize_t wake_store(struct kobject *kobj, struct kobj_attribute *attr,
const char *buf, size_t n)
{
int error = 0;
unsigned int temp;
if(sscanf(buf, "%u", &temp) != 1)
error = -EINVAL;
pr_info("temp=%d", temp);
if(temp == 1)
{
pm_stay_awake(drv_test.dev);
}
else if(temp == 2)
{
pm_relax(drv_test.dev);
}
#if 0
if(sscanf(buf, "%08x", &addr) != 1)
error = -EINVAL;
reg_vir_addr = ioremap(addr, 0x1000);
pr_info("reg[%08x]=%08x\n", addr, ioread32((void __iomem *)reg_vir_addr));
iounmap(reg_vir_addr);
#endif
return error ? error : n;
}
zte_attr(wake);
#endif
/*=============================================================================
*======== /sys/zte/test/spinlock ==============================================
*=============================================================================
*/
/*echo 0xXXXXXXXX > /sys/zte/test/spinlock*/
#if ZX_SPINLOCK_TEST
void hw_spin_lock(u32 hwid);
void hw_spin_unlock(u32 hwid);
static ssize_t spinlock_show(struct kobject *kobj, struct kobj_attribute *attr,
char *buf)
{
char *s = buf;
// s += sprintf(s, "%s\n", "[TEST]Read register[0xXXXXXXXX] value~");
return (s - buf);
}
static ssize_t spinlock_store(struct kobject *kobj, struct kobj_attribute *attr,
const char *buf, size_t n)
{
int error = 0;
u32 temp;
{
#if 0
int irq_base ;
pr_info("current irq=%d\n", irq);
irq_base = irq_alloc_descs(-1, 0, 11, 0);
pr_info("next irq=%d\n", irq_base);
#endif
struct of_phandle_args out_irq;
int rc;
rc = of_irq_parse_one(drv_test.dev->of_node, 0, &out_irq);
pr_info("pcie irq=%d\n", rc);
}
sscanf(buf, "%u", &temp);
pr_info("spinlock store:%d\n", temp);
#if 0
/* 1--lock 2--unlock */
if(temp == 1)
{
hw_spin_lock(7);
pr_info("spinlock lock ok!\n");
}
else if(temp == 2)
{
hw_spin_unlock(7);
pr_info("spinlock unlock ok!\n");
}
#endif
return error ? error : n;
}
zte_attr(spinlock);
#endif
/*=============================================================================
*======== /sys/zte/test/reset ==============================================
*=============================================================================
*/
/* echo 1/0 > /sys/zte/test/reset */
#if ZX_RESET_TEST
static ssize_t reset_show(struct kobject *kobj, struct kobj_attribute *attr,
char *buf)
{
char *s = buf;
s += sprintf(s, "%s %d\n", "reset signal status:", reset_control_status(drv_test.rst));
return (s - buf);
}
static ssize_t reset_store(struct kobject *kobj, struct kobj_attribute *attr,
const char *buf, size_t n)
{
int error = 0;
u32 temp;
sscanf(buf, "%u", &temp);
/* 1--assert 0--deassert */
if(temp == 1)
{
reset_control_deassert(drv_test.rst);
pr_info("reset signal assert!\n");
}
else if(temp == 0)
{
reset_control_deassert(drv_test.rst);
pr_info("reset signal release!\n");
}
return error ? error : n;
}
zte_attr(reset);
#endif
/*=============================================================================
*======== /sys/zte/test/gpio ============================================
*=============================================================================
*/
/* echo 1/0 > /sys/zte/test/gpio */
#if ZX_PINCTRL_TEST
static ssize_t gpio_show(struct kobject *kobj, struct kobj_attribute *attr,
char *buf)
{
char *s = buf;
return (s - buf);
}
static ssize_t gpio_store(struct kobject *kobj, struct kobj_attribute *attr,
const char *buf, size_t n)
{
int error = 0;
u32 temp;
sscanf(buf, "%u", &temp);
if (!drv_test.dev)
return error;
/* 0-out_l 1-out_h 2-in and get */
if(temp == 0) {
gpio_direction_output(drv_test.gpio, 0);
pr_info("gpio out low");
}
else if(temp == 1) {
gpio_direction_output(drv_test.gpio, 1);
pr_info("gpio out high");
}
else if(temp == 2) {
gpio_direction_input(drv_test.gpio);
pr_info("gpio get value(%d) !\n",__gpio_get_value(drv_test.gpio));
}
return error ? error : n;
}
zte_attr(gpio);
#endif
/*=============================================================================
*======== /sys/zte/test/pinctrl ============================================
*=============================================================================
*/
/* echo 1/0 > /sys/zte/test/pinctrl */
#if ZX_PINCTRL_TEST
static ssize_t pinctrl_show(struct kobject *kobj, struct kobj_attribute *attr,
char *buf)
{
char *s = buf;
int i;
for (i=0; i<16; i++)
printk("gpio_%d mapped irq to %d \n", i, gpio_to_irq(i));
return (s - buf);
}
static ssize_t pinctrl_store(struct kobject *kobj, struct kobj_attribute *attr,
const char *buf, size_t n)
{
int error = 0;
u32 temp;
sscanf(buf, "%u", &temp);
/* temp --> pin state */
if(temp == 1)
{
if ( pinctrl_select_state(drv_test.pctrl, drv_test.state1) < 0) {
dev_err(drv_test.dev, "setting state1 failed\n");
}
pr_info("setting state1 !\n");
}
else if(temp == 0)
{
if ( pinctrl_select_state(drv_test.pctrl, drv_test.state0) < 0) {
dev_err(drv_test.dev, "setting state0 failed\n");
}
pr_info("setting state0 !\n");
}
else if(temp == 2)
{
if ( pinctrl_select_state(drv_test.pctrl, drv_test.state2) < 0) {
dev_err(drv_test.dev, "setting state2 failed\n");
}
pr_info("setting state2 !\n");
}
return error ? error : n;
}
zte_attr(pinctrl);
#endif
/*=============================================================================
*======== /sys/zte/test/pd ==============================================
*=============================================================================
*/
/* echo 1/0 > /sys/zte/test/pd */
#if ZX_PM_TEST
static ssize_t pd_show(struct kobject *kobj, struct kobj_attribute *attr,
char *buf)
{
char *s = buf;
return (s - buf);
}
static ssize_t pd_store(struct kobject *kobj, struct kobj_attribute *attr,
const char *buf, size_t n)
{
int error = 0;
#ifdef CONFIG_PM
u32 temp;
sscanf(buf, "%u", &temp);
/* 1--on 0--off */
if(temp == 1)
{
pm_runtime_get_sync(drv_test.dev);
pr_info("power on!\n");
}
else if(temp == 0)
{
#if PM_RUNTIME_AUTO_TEST
pm_runtime_mark_last_busy(drv_test.dev);
pm_runtime_put_sync_autosuspend(drv_test.dev);
#else
pm_runtime_put_sync(drv_test.dev);
#endif
pr_info("power off!\n");
}
#else
error = -ENXIO;
#endif
return error ? error : n;
}
zte_attr(pd);
#endif
/*=============================================================================
*======== /sys/zte/test/clk ==============================================
*=============================================================================
*/
/* echo 1/0 > /sys/zte/test/clk */
#if ZX_CLK_TEST
static ssize_t clk_show(struct kobject *kobj, struct kobj_attribute *attr,
char *buf)
{
char *s = buf;
s += sprintf(s, "%s %d\n", "clk enable status:", __clk_is_enabled(drv_test.clk));
s += sprintf(s, "%s %d\n", "clk rate:", clk_get_rate(drv_test.clk));
return (s - buf);
}
static ssize_t clk_store(struct kobject *kobj, struct kobj_attribute *attr,
const char *buf, size_t n)
{
int error = 0;
u32 temp;
sscanf(buf, "%u", &temp);
/* 1--on 0--off */
if(temp == 1) {
clk_enable(drv_test.clk);
}
else if(temp == 0) {
clk_disable(drv_test.clk);
} else {
clk_set_rate(drv_test.clk, temp);
}
return error ? error : n;
}
zte_attr(clk);
#endif
/*=============================================================================
*======== /sys/zte/test/pm_qos ==============================================
*=============================================================================
*/
/* echo 1/0 > /sys/zte/test/pm_qos */
#if ZX_PM_QOS_TEST
static unsigned int pm_qos_test = 0;
static ssize_t pm_qos_show(struct kobject *kobj, struct kobj_attribute *attr,
char *buf)
{
char *s = buf;
const char *mode[3] = {"normal", "performance", "powersave"};
s += sprintf(s, "pm_qos mode: %s\n", (pm_qos_test<=2) ? mode[pm_qos_test] : "unknown");
return (s - buf);
}
static ssize_t pm_qos_store(struct kobject *kobj, struct kobj_attribute *attr,
const char *buf, size_t n)
{
int error = 0;
u32 temp;
sscanf(buf, "%u", &temp);
if(temp == 1) {
freq_performance(FREQ_OWNER_MANAGER);
} else if(temp == 2) {
freq_powersave(FREQ_OWNER_MANAGER);
} else if(temp == 0) {
freq_normal(FREQ_OWNER_MANAGER);
} else {
return -EINVAL;
}
pm_qos_test = temp;
return error ? error : n;
}
zte_attr(pm_qos);
#endif
static ssize_t rpmsg_log_show(struct kobject *kobj, struct kobj_attribute *attr,
char *buf);
static ssize_t rpmsg_log_store(struct kobject *kobj, struct kobj_attribute *attr,
const char *buf, size_t n)
{
int error = 0;
return error ? error : n;
}
zte_attr(rpmsg_log);
/*test group*/
static struct attribute * g[] =
{
&os_timer_attr.attr,
#if ZX_PM_TEST
&wake_attr.attr,
#endif
#if ZX_SPINLOCK_TEST
&spinlock_attr.attr,
#endif
#if ZX_RESET_TEST
&reset_attr.attr,
#endif
#if ZX_GPIO_TEST
&gpio_attr.attr,
#endif
#if ZX_PINCTRL_TEST
&pinctrl_attr.attr,
#endif
#if ZX_PM_TEST
&pd_attr.attr,
#endif
#if ZX_CLK_TEST
&clk_attr.attr,
#endif
#if ZX_PM_QOS_TEST
&pm_qos_attr.attr,
#endif
&rpmsg_log_attr.attr,
NULL,
};
static struct attribute_group zte_test_attr_group =
{
.attrs = g,
};
/**
* 1¡¢create sysfs "/sys/zte/test"
* 2¡¢call other debug modules
*/
static int __init zx_test_init(void)
{
int ret;
zx_test_kobj = kobject_create_and_add("test", zx_root_kobj);
if (!zx_test_kobj)
return -ENOMEM;
ret = sysfs_create_group(zx_test_kobj, &zte_test_attr_group);
if (ret)
{
pr_info("[DEBUG] sysfs_create_group ret %d\n", ret);
return ret;
}
timer_setup(&test_timer, test_timer_expired, 0);
pr_info("[DEBUG] create test sysfs interface OK.\n");
return platform_driver_register(&zx_test_driver);
}
/*
#define SC_LIBPM_LPMODE_CPU_HALT (0)
#define SC_LIBPM_LPMODE_CPU_CLKOFF (1)
#define SC_LIBPM_LPMODE_CPU_POWEROFF (2)
*/
static int zx_lp_mode = 2;
int pm_get_lpmode(void)
{
return zx_lp_mode;
}
static ssize_t lp_mode_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
return sprintf(buf, "lp_mode:%d\n", zx_lp_mode);
}
static ssize_t lp_mode_store(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
int error = 0;
unsigned int lp_mode;
if(sscanf(buf, "%u", &lp_mode) != 1)
error = -EINVAL;
// pr_info("%s: lp_mode=%d\n", __func__, lp_mode);
if (lp_mode > 2)
error = -EINVAL;
else
zx_lp_mode = lp_mode;
return error ? error : count;
}
static DEVICE_ATTR(lp_mode, 0600, lp_mode_show, lp_mode_store);
static struct attribute *zx_pm_attributes[] = {
&dev_attr_lp_mode.attr,
NULL,
};
static const struct attribute_group zx_pm_attribute_group = {
.attrs = (struct attribute **) zx_pm_attributes,
};
#ifdef CONFIG_PM_SLEEP_DEBUG
extern bool pm_debug_messages_on;
#endif
/**
* 1¡¢create sysfs "/sys/zte"
* 2¡¢call other debug modules
*/
int __init zx_dma_test_init(void);
static int __init zx_debug_init(void)
{
pr_info("[DEBUG] create zte sysfs interface OK.\n");
zx_root_kobj = kobject_create_and_add("zte", NULL);
if (!zx_root_kobj)
return -ENOMEM;
zx_pm_kobj = kobject_create_and_add("power", zx_root_kobj);
if (!zx_pm_kobj)
return -ENOMEM;
sysfs_create_group(zx_pm_kobj, &zx_pm_attribute_group);
zx_test_init();
zx_dma_test_init();
/* zx_clk_test_init(); */
zx_icp_test_init();
zx_timer_test_init();
#ifdef CONFIG_PM_SLEEP_DEBUG
pm_debug_messages_on = true;
#endif
return 0;
}
late_initcall(zx_debug_init);
void __init zx29_clock_init(void);
struct zx297520v3_chip_info {
void __iomem *stdcrm_base;
void __iomem *socsys_base;
void __iomem *sflock_base;
void __iomem *apcrm_base;
};
static struct zx297520v3_chip_info zx_chip_info;
void __iomem *get_stdcrm_base(void)
{
return zx_chip_info.stdcrm_base;
}
void __iomem *get_socsys_base(void)
{
return zx_chip_info.socsys_base;
}
static int spinlock_init(void)
{
struct device_node *np;
void __iomem *param[2];
np = of_find_compatible_node(NULL, NULL, "zte,zx297520v3-standby");
if (!np)
{
BUG();
return -ENODEV;
}
zx_chip_info.stdcrm_base = of_iomap(np, 0);
WARN(!zx_chip_info.stdcrm_base, "unable to map stdcrm_base registers\n");
np = of_find_compatible_node(NULL, NULL, "zte,zx29_spinlock");
if (!np)
{
BUG();
return -ENODEV;
}
zx_chip_info.sflock_base = of_iomap(np, 0);
WARN(!zx_chip_info.sflock_base, "unable to map sflock_base registers\n");
param[0] = zx_chip_info.stdcrm_base;
param[1] = zx_chip_info.sflock_base;
zx_spinlock_init(param);
return 0;
}
static void socsys_init(void)
{
struct device_node *np;
np = of_find_compatible_node(NULL, NULL, "zte,zx297520v3-socsys");
if (!np)
{
BUG();
}
zx_chip_info.socsys_base = of_iomap(np, 0);
WARN(!zx_chip_info.socsys_base, "unable to map socsys_base registers\n");
}
/*---------------------------------------------------------*/
#define AP_INT_MODE_BASE (zx_chip_info.apcrm_base + 0x70)
#define AP_PPI_MODE_REG (zx_chip_info.apcrm_base + 0xA0)
#define INT_HIGHLEVEL (0x0) /* 00: high level */
#define INT_LOWLEVEL (0x1) /* 01: low level */
#define INT_POSEDGE (0x2) /* 10: raise edge */
#define INT_NEGEDGE (0x3) /* 11: fall edge */
static int zx29_int_set_type(unsigned int hwirq, unsigned int type)
{
unsigned int data_tmp=0;
unsigned int srctype=0;
unsigned int reg_index=0,pos_index=0;
switch (type) {
case IRQ_TYPE_LEVEL_HIGH:
srctype = INT_HIGHLEVEL;
break;
case IRQ_TYPE_EDGE_RISING:
srctype = INT_POSEDGE;
break;
case IRQ_TYPE_LEVEL_LOW:
srctype = INT_LOWLEVEL;
break;
case IRQ_TYPE_EDGE_FALLING:
srctype = INT_NEGEDGE;
break;
default:
return -EINVAL;
}
reg_index=(hwirq)/16;
pos_index=((hwirq)%16)*2;
data_tmp=zx_read_reg(AP_INT_MODE_BASE+reg_index*4);
data_tmp &= ~(3<<pos_index);
data_tmp |= srctype<<pos_index;
zx_write_reg(AP_INT_MODE_BASE+reg_index*4, data_tmp);
return 0;
}
static void int_set_type_default(unsigned int line)
{
unsigned int int_type=0;
switch ( line )
{
case WDT_INT:
case AP_TIMER0_INT:
case GSM_RFSSCR_INT:
case GSM_RFSSCT_INT:
case AP_TIMER3_INT:
case AP_TIMER4_INT:
case SYS_COUNTER_INT:
{
int_type = IRQ_TYPE_EDGE_RISING;
break;
}
case MCU_LCD_INT:
{
int_type = IRQ_TYPE_LEVEL_LOW;
break;
}
default:
{
int_type = IRQ_TYPE_LEVEL_HIGH;
break;
}
}
zx29_int_set_type(line, int_type);
}
static void apcrm_init(void)
{
int i;
struct device_node *np;
np = of_find_compatible_node(NULL, NULL, "zte,zx297520v3-apcrm");
if (!np)
{
BUG();
}
zx_chip_info.apcrm_base = of_iomap(np, 0);
WARN(!zx_chip_info.apcrm_base, "unable to map apcrm_base registers\n");
zx_write_reg(AP_PPI_MODE_REG, 0x55545555);
for (i=0; i<IRQ_ZX297520V3_SPI_NUM; i++)
int_set_type_default(i);
}
void early_drv_init(void)
{
spinlock_init();
socsys_init();
apcrm_init();
}
//early_initcall(early_drv_init);
/*-------------------------------------------------------------------*/
#define ZX_PM_MINOR (235)
static unsigned int pm_wl_mask = 0;
static unsigned int pm_wl_event = 0;
static u64 pm_sleep_time;
static DECLARE_WAIT_QUEUE_HEAD(zx_pm_wait);
static bool zx_pm_wake_flag = false;
static bool zx_in_suspend = false;
void pm_wl_set_event(unsigned int wake_event)
{
if (wake_event >=PM_WL_EVENT_END)
return;
pm_wl_event = BIT(wake_event)&pm_wl_mask;
}
static unsigned int pm_wl_get_event(void)
{
// return pm_wl_event&pm_wl_mask;
return pm_wl_event;
}
void pm_set_sleeptime(u64 sleep_time)
{
pm_sleep_time = sleep_time;
}
static u64 pm_get_sleeptime(void)
{
return pm_sleep_time;
}
static int zx_pm_open(struct inode *inode, struct file *filp)
{
int error = 0;
return error;
}
static int zx_pm_release(struct inode *inode, struct file *filp)
{
return 0;
}
void zx_enter_suspend(void)
{
zx_in_suspend = true;
}
void zx_exit_suspend(void)
{
zx_in_suspend = false;
}
bool zx_suspend_query(void)
{
return zx_in_suspend;
}
void pm_notify_wake_event(void)
{
if (pm_wl_event) {
zx_pm_wake_flag = true;
wake_up_interruptible(&zx_pm_wait);
}
}
static ssize_t zx_pm_read(struct file *filp, char __user *buf,
size_t count, loff_t *offp)
{
ssize_t res = 0;
int ret;
struct sc_pm_info pm_info;
zx_pm_wake_flag = false;
wait_event_freezable(zx_pm_wait, zx_pm_wake_flag);
pm_info.sleep_time = pm_get_sleeptime();
pm_info.wake_event = pm_wl_get_event();
res = sizeof(struct sc_pm_info);
ret = copy_to_user((void __user *)buf, &pm_info, res);
if (ret < 0)
return -EFAULT;
return res;
}
static unsigned int pm_event_convert(unsigned int req_event)
{
return 0;
}
static long zx_pm_ioctl(struct file *filp, unsigned int cmd,
unsigned long arg)
{
int error = 0;
unsigned int wl_event;
switch (cmd) {
case SC_PM_WL_SET:
wl_event = (unsigned int)arg;
if (wl_event >= BIT(PM_WL_EVENT_END)) {
error = -ENOTTY;
break;
}
pm_wl_mask |= wl_event;
/* pr_info("%s:pm_wl_mask(0x%x) user_set(0x%x)\n", __func__, pm_wl_mask, wl_event); */
break;
case SC_PM_WL_CLEAR:
wl_event = (unsigned int)arg;
if (wl_event >= BIT(PM_WL_EVENT_END)) {
error = -ENOTTY;
break;
}
pm_wl_mask &= (~wl_event);
/* pr_info("%s:pm_wl_mask(0x%x) user_clr(0x%x)\n", __func__, pm_wl_mask, wl_event); */
break;
case SC_PM_WL_GET:
/* pr_info("%s:pm_wl_mask(0x%x)\n", __func__, pm_wl_mask); */
error = put_user(pm_wl_mask, (unsigned int __user *)arg);
break;
default:
error = -ENOTTY;
break;
}
return error;
}
#ifdef CONFIG_COMPAT
static long
zx_pm_compat_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
BUILD_BUG_ON(sizeof(loff_t) != sizeof(compat_loff_t));
if (_IOC_TYPE(cmd) != SC_PM_IOC_MAGIC)
return -ENOTTY;
if (_IOC_SIZE(cmd) == sizeof(compat_uptr_t)) {
cmd &= ~IOCSIZE_MASK;
cmd |= sizeof(char *) << IOCSIZE_SHIFT;
}
return zx_pm_ioctl(filp, cmd, (unsigned long)compat_ptr(arg));
}
#endif /* CONFIG_COMPAT */
static const struct file_operations zx_pm_fops = {
.open = zx_pm_open,
.release = zx_pm_release,
.read = zx_pm_read,
/* .write = zx_pm_write,*/
.llseek = no_llseek,
.unlocked_ioctl = zx_pm_ioctl,
#ifdef CONFIG_COMPAT
.compat_ioctl = zx_pm_compat_ioctl,
#endif
};
static struct miscdevice zx_pm_device = {
.minor = ZX_PM_MINOR,
.name = "sc_pm",
.fops = &zx_pm_fops,
};
static int zx_pm_device_init(void)
{
return misc_register(&zx_pm_device);
};
/*-------------------------------------------------------------------*/
#define ZX_IRQ_MINOR (237)
#define SC_LIBIRQ_MAX (16)
/*
* line : request
* type : driver use
* wake_flag : for wait
*
*/
struct libirq_info {
unsigned int line;
unsigned int hirq;
unsigned int virq;
unsigned int type;
int wake;
unsigned int used;
bool wake_flag;
wait_queue_head_t wait;
char name[16];
};
struct libirq_context {
unsigned int pending;
spinlock_t lock;
struct pinctrl *pctrl;
struct pinctrl_state *state[SC_LIBIRQ_MAX];
struct device_node *np;
struct device_node *ext8in1_np;
struct libirq_info info[SC_LIBIRQ_MAX];
};
static struct libirq_context irq_ctx = {0};
#define line_used(l) irq_ctx.info[l].used
static int irq_line_convert(int line)
{
if (line>=8)
return EX8_INT + line - 8;
else
return EX0_INT + line;
}
static unsigned int irq_type_convert(unsigned int req_type)
{
if (req_type == 0)
return IRQ_TYPE_EDGE_RISING;
else if (req_type == 1)
return IRQ_TYPE_EDGE_FALLING;
else if (req_type == 2)
return IRQ_TYPE_LEVEL_HIGH;
else if (req_type == 3)
return IRQ_TYPE_LEVEL_LOW;
else
return IRQ_TYPE_NONE;
}
static int zx_irq_map_ext8in1(int hirq, unsigned int type)
{
struct of_phandle_args args;
args.args_count = 2;
args.args[0] = hirq;
args.args[1] = type;
args.np = irq_ctx.ext8in1_np;
return irq_create_of_mapping(&args);
}
static int zx_irq_map(int hirq, unsigned int type)
{
struct of_phandle_args args;
if (hirq>=EX8_INT && hirq<=EX15_INT)
return zx_irq_map_ext8in1(hirq, type);
args.args_count = 3;
args.args[0] = 0;
args.args[1] = hirq;
args.args[2] = type;
args.np = irq_ctx.np;
return irq_create_of_mapping(&args);
}
static void zx_irq_wait(unsigned int line)
{
struct libirq_info *info = &(irq_ctx.info[line]);
info->wake_flag = false;
wait_event_freezable(info->wait, info->wake_flag);
}
static void zx_irq_wakeup(unsigned int line)
{
struct libirq_info *info = &(irq_ctx.info[line]);
info->wake_flag = true;
wake_up_interruptible(&info->wait);
}
static irqreturn_t zx_irq_isr(int irq, void *p)
{
struct libirq_info *info = (struct libirq_info *)p;
unsigned int line = info->line;
if(line_used(line)) {
irq_ctx.pending |= BIT(line);
zx_irq_wakeup(line);
}
/* pr_info("%s:eint get = %d\n", __func__, line); */
return IRQ_HANDLED;
}
static int zx_irq_open(struct inode *inode, struct file *filp)
{
int error = 0;
unsigned int line;
line = iminor(inode) - ZX_IRQ_MINOR;
filp->private_data = &(irq_ctx.info[line]);
return error;
}
static int zx_irq_release(struct inode *inode, struct file *filp)
{
struct libirq_info *info;
info = (struct libirq_info *)filp->private_data;
if(line_used(info->line)) {
irq_set_irq_type(info->virq, IRQ_TYPE_NONE);
free_irq(info->virq, info);
line_used(info->line) = 0;
}
filp->private_data = NULL;
return 0;
}
static long zx_irq_ioctl(struct file *filp, unsigned int cmd,
unsigned long arg)
{
int error = 0;
struct libirq_info *info;
int virq;
unsigned int type;
unsigned int en;
int ret = 0;
unsigned long flags;
info = (struct libirq_info *)filp->private_data;
switch (cmd) {
case SC_IRQ_INSTALL:
if(line_used(info->line))
return -EEXIST;
type = irq_type_convert((unsigned int)arg);
virq = zx_irq_map(info->hirq, type);
if (virq <= 0) {
pr_err("%s:zx_irq_map %d failed %d(%d)\n", __func__, info->line, virq, type);
return -ENOMEM;
}
/* pr_err("%s:zx_irq_map %d %d %d\n", __func__, info->line, virq, info->hirq);*/
if ( pinctrl_select_state(irq_ctx.pctrl, irq_ctx.state[info->line]) < 0) {
pr_err("%s:setting state%d failed\n", __func__, info->line);
return -ENODEV;
}
ret = request_irq(virq, zx_irq_isr, 0, info->name, info);
if(ret<0) {
pr_err("%s:request_irq %d failed %d\n", __func__, info->line, type);
return ret;
}
info->virq = virq;
info->type = type;
line_used(info->line) = 1;
/* pr_info("%s:install(%d) hirq(%d) virq(%d) type(%d)\n", __func__, info->line, info->hirq, info->virq, info->type);*/
break;
case SC_IRQ_UNINSTALL:
if(!line_used(info->line))
return -ENODEV;
irq_set_irq_type(info->virq, IRQ_TYPE_NONE);
free_irq(info->virq, info);
line_used(info->line) = 0;
/* pr_info("%s:uninstall(%d)\n", __func__, info->line);*/
break;
case SC_IRQ_SET_TYPE:
if(!line_used(info->line))
return -ENODEV;
type = irq_type_convert((unsigned int)arg);
ret = irq_set_irq_type(info->virq, type);
if (ret)
return ret;
info->type = type;
/* pr_info("%s:set_type(%d) virq(%d) type(%d)\n", __func__, info->line, info->virq, info->type); */
break;
case SC_IRQ_SET_WAKE:
if(!line_used(info->line))
return -ENODEV;
en = (unsigned int)arg;
ret = irq_set_irq_wake(info->virq, en);
if (ret)
return ret;
info->wake = en;
/* pr_info("%s:set_wake(%d) virq(%d) wake(%d)\n", __func__, info->line, info->virq, info->wake); */
break;
case SC_IRQ_GET_WAKE:
if(!line_used(info->line))
return -ENODEV;
error = put_user(info->wake, (unsigned int __user *)arg);
/* pr_info("%s:get_wake(%d) virq(%d) wake(%d)\n", __func__, info->line, info->virq, info->wake); */
break;
case SC_IRQ_GET_STATUS:
if(!line_used(info->line))
return -ENODEV;
zx_irq_wait(info->line);
spin_lock_irqsave(&irq_ctx.lock, flags);
error = put_user(irq_ctx.pending, (unsigned int __user *)arg);
spin_unlock_irqrestore(&irq_ctx.lock, flags);
/* pr_debug("%s:get_status(%d) virq(%d) wake(%d) pending(0x%x)\n",
__func__, info->line, info->virq, info->wake, irq_ctx.pending); */
break;
case SC_IRQ_CLEAR_STATUS:
if(!line_used(info->line))
return -ENODEV;
spin_lock_irqsave(&irq_ctx.lock, flags);
irq_ctx.pending &= ~(1 << info->line);
spin_unlock_irqrestore(&irq_ctx.lock, flags);
/* pr_info("%s:clear_status(%d)\n", __func__, info->line); */
break;
default:
error = -ENOTTY;
break;
}
return error;
}
#ifdef CONFIG_COMPAT
static long
zx_irq_compat_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
BUILD_BUG_ON(sizeof(loff_t) != sizeof(compat_loff_t));
if (_IOC_TYPE(cmd) != SC_IRQ_IOC_MAGIC)
return -ENOTTY;
if (_IOC_SIZE(cmd) == sizeof(compat_uptr_t)) {
cmd &= ~IOCSIZE_MASK;
cmd |= sizeof(char *) << IOCSIZE_SHIFT;
}
return zx_irq_ioctl(filp, cmd, (unsigned long)compat_ptr(arg));
}
#endif /* CONFIG_COMPAT */
static const struct file_operations zx_irq_fops = {
.open = zx_irq_open,
.release = zx_irq_release,
.llseek = no_llseek,
.unlocked_ioctl = zx_irq_ioctl,
#ifdef CONFIG_COMPAT
.compat_ioctl = zx_irq_compat_ioctl,
#endif
};
static struct miscdevice zx_irq_device[SC_LIBIRQ_MAX] = {0};
static int zx_irq_device_init(struct platform_device *pdev)
{
int i;
int ret = 0;
char name[16];
struct miscdevice *misc_dev;
struct device_node *np;
irq_ctx.pctrl = devm_pinctrl_get(&pdev->dev);
if (IS_ERR(irq_ctx.pctrl)) {
dev_warn(&pdev->dev, "Failed to get sc_irq pins");
irq_ctx.pctrl = NULL;
return -ENODEV;
}
for (i=0; i<SC_LIBIRQ_MAX; i++) {
misc_dev = &zx_irq_device[i];
misc_dev->minor = ZX_IRQ_MINOR + i,
sprintf(name, "%s%d", "sc_irq", i);
misc_dev->name = name,
misc_dev->fops = &zx_irq_fops,
strcpy(irq_ctx.info[i].name, name);
irq_ctx.info[i].line = i;
irq_ctx.info[i].hirq = irq_line_convert(i);
init_waitqueue_head(&irq_ctx.info[i].wait);
ret = misc_register(misc_dev);
if (ret) {
pr_err("%s:register dev(%d) failed:%d \n", __func__, i, ret);
return ret;
}
irq_ctx.state[i] = pinctrl_lookup_state(irq_ctx.pctrl, name);
if (IS_ERR(irq_ctx.state[i])) {
dev_err(&pdev->dev, "TEST: missing state(%s)\n", name);
return -ENODEV;
}
}
np = of_find_compatible_node(NULL, NULL, "zte,zx297520v3-pcu");
if (NULL == np) {
pr_err("Can't find interrupt-controller \n");
return -ENODEV;
}
irq_ctx.np = np;
np = of_find_compatible_node(NULL, NULL, "zte,zx297520v3-ext8in1");
if (NULL == np) {
pr_err("Can't find ext8in1 interrupt-controller \n");
return -ENODEV;
}
irq_ctx.ext8in1_np = np;
spin_lock_init(&irq_ctx.lock);
return ret;
};
static int zx_bsp_probe(struct platform_device *pdev)
{
int ret = 0;
ret = zx_pm_device_init();
if (ret)
return ret;
ret = zx_irq_device_init(pdev);
device_init_wakeup(&pdev->dev, true);
return ret;
}
static int zx_bsp_remove(struct platform_device *pdev)
{
return 0;
}
static const struct of_device_id zx_bsp_match[] = {
{ .compatible = "sc,sc-bsp", },
{ }
};
static struct platform_driver zx_bsp_driver = {
.probe = zx_bsp_probe,
.remove = zx_bsp_remove,
.driver = {
.name = "sc_bsp",
.of_match_table = zx_bsp_match,
},
};
builtin_platform_driver(zx_bsp_driver)
/*---------------------------------------------------------------*/
static struct reset_control *reboot_rst;
static int zx_restart(struct notifier_block *this,
unsigned long mode, void *cmd)
{
/*reset spinand cs*/
soft_spin_lock_nand_psm(NAND_SFLOCK);
gpio_set_value(86,GPIO_HIGH);
if (reboot_rst) {
reset_control_assert(reboot_rst);
}
return NOTIFY_DONE;
}
static struct notifier_block zx_restart_handler = {
.notifier_call = zx_restart,
.priority = 129,
};
static int zx_reboot_probe(struct platform_device *pdev)
{
int ret;
struct device_node *np = pdev->dev.of_node;
reboot_rst = of_reset_control_get_by_index(np, 0);
if (!reboot_rst) {
dev_err(&pdev->dev, "No reset handler found!");
return -EINVAL;
}
ret = register_restart_handler(&zx_restart_handler);
if (ret)
pr_warn("cannot register restart handler, %d\n", ret);
return 0;
}
static const struct of_device_id zx_reboot_match[] = {
{ .compatible = "zte,reboot", },
{ }
};
static struct platform_driver zx_reboot_driver = {
.probe = zx_reboot_probe,
.driver = {
.name = "zx_reboot",
.of_match_table = zx_reboot_match,
},
};
builtin_platform_driver(zx_reboot_driver)
/*----------------------------------------------------------------*/
#define CONFIG_RPMSG_LOG 1
#ifdef CONFIG_RPMSG_LOG
#define RPMSG_LOG_SIZE (20*1024)
static char rpmsg_printk_buf[RPMSG_LOG_SIZE];
static u32 rpmsg_log_point = 0;
static u32 rpmsg_log_turn = 0;
static u32 rpmsg_sram_inited = 0;
//static char rpmsg_log_temp_buf[512] = {0};
static spinlock_t rpmsg_log_lock;
static void rpmsg_sram_cpy(char *s, unsigned len)
{
unsigned long flags;
spin_lock_irqsave(&rpmsg_log_lock, flags);
if(rpmsg_log_point + len + 2 >= RPMSG_LOG_SIZE) {
rpmsg_log_point = 0;
if (!rpmsg_log_turn)
rpmsg_log_turn = 1;
}
memcpy(rpmsg_printk_buf+rpmsg_log_point, s, len);
rpmsg_log_point += len;
rpmsg_printk_buf[rpmsg_log_point]=0;
spin_unlock_irqrestore(&rpmsg_log_lock, flags);
}
#endif
static ssize_t rpmsg_log_show(struct kobject *kobj, struct kobj_attribute *attr,
char *buf)
{
char *s = buf;
#ifdef CONFIG_RPMSG_LOG
unsigned long flags;
if (rpmsg_sram_inited) {
spin_lock_irqsave(&rpmsg_log_lock, flags);
if (!rpmsg_log_turn) {
s += sprintf(s, "%s", rpmsg_printk_buf);
}
else {
s += sprintf(s, "%s", rpmsg_printk_buf+rpmsg_log_point+2);
s += sprintf(s, "%s", rpmsg_printk_buf);
}
spin_unlock_irqrestore(&rpmsg_log_lock, flags);
}
#endif
return (s - buf);
}
/**
* usage: like printk(...)
*/
void rpmsg_printk(const char *fmt, ...)
{
#ifdef CONFIG_RPMSG_LOG
va_list args;
unsigned long long t;
unsigned long nanosec_rem;
int tlen, len;
char rpmsg_log_temp_buf[512] = {0};
if(!rpmsg_sram_inited)
return;
va_start(args, fmt);
/* add time stamp */
t = cpu_clock(smp_processor_id());
nanosec_rem = do_div(t, 1000000000);
tlen = sprintf(rpmsg_log_temp_buf, ">%5lu.%06lu< ",
(unsigned long) t, nanosec_rem / 1000);
len = vsprintf(rpmsg_log_temp_buf+tlen, fmt, args);
len += tlen;
rpmsg_sram_cpy(rpmsg_log_temp_buf, len);
va_end(args);
#endif
}
void rpmsg_sram_init(void)
{
#ifdef CONFIG_RPMSG_LOG
pr_info("[RPMSG] LOG_INIT \n");
memset(rpmsg_printk_buf, 0, RPMSG_LOG_SIZE);
rpmsg_log_point = 0;
spin_lock_init(&rpmsg_log_lock);
rpmsg_sram_inited = 1;
#endif
}