| /* |
| * 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 |
| } |