| /* |
| * 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 <dt-bindings/soc/mbtk_plat_irq.h> |
| #include <uapi/linux/bsp_api.h> |
| //#include <linux/soc/sc/common.h> |
| /*#include <linux/soc/sc/pcu.h> |
| #include <linux/soc/sc/spinlock.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 0 |
| |
| #define TRUE 1 |
| #define FALSE 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}; |
| |
| #define MAX_GPIO_NUM 4 |
| |
| struct MbtkPlatIrq_dev |
| { |
| struct device *dev; |
| struct pinctrl *pctrl; |
| int gpio_desc[MAX_GPIO_NUM] ; |
| int release_func_callback[MAX_GPIO_NUM] ; |
| //struct wakeup_source *WakeUp_ws; |
| |
| }; |
| |
| struct MbtkPlatIrq_dev g_MbtkPlatIrq_dev = {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 |
| |
| |
| |
| /*-------------------------------------------------------------------*/ |
| #define MBTK_IRQ_MINOR (170) |
| #define MBTK_LIBIRQ_MAX (4) |
| |
| /* |
| * 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[MBTK_LIBIRQ_MAX]; |
| |
| struct device_node *np; |
| struct device_node *ext8in1_np; |
| struct libirq_info info[MBTK_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 0 //forbid by niening |
| if (line>=4) |
| return EX24_INT + line - 8; |
| else |
| return EX21_INT + line; |
| |
| |
| #else |
| int irq; |
| |
| irq = gpio_to_irq(g_MbtkPlatIrq_dev.gpio_desc[line]); |
| if (irq < 0) |
| { |
| pr_err("%s. gpio %d request irq %d failed\n", __func__,g_MbtkPlatIrq_dev.gpio_desc[line], irq); |
| |
| } |
| |
| return irq; |
| |
| #endif |
| } |
| |
| static unsigned int irq_type_convert(unsigned int req_type) |
| { |
| //printk("--->%s/L%d req_type = %d\n", __FUNCTION__, __LINE__, 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 mbkt_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 mbkt_irq_map(int hirq, unsigned int type, unsigned int line) |
| { |
| struct of_phandle_args args; |
| int ret = 0; |
| #if 0 |
| char name[16]; |
| |
| //if (hirq>=EX8_INT && hirq<=EX15_INT) |
| // return mbkt_irq_map_ext8in1(hirq, type); |
| |
| memset(name, 0, sizeof(name)); |
| sprintf(name, "%s%d", "gpio_irq", line); |
| |
| ret = gpio_request(g_MbtkPlatIrq_dev.gpio_desc[line], name); |
| if(ret < 0) |
| { |
| pr_err("%s. gpio_request for =%d failed\n", __func__,g_MbtkPlatIrq_dev.gpio_desc[line]); |
| } |
| |
| gpio_direction_input(g_MbtkPlatIrq_dev.gpio_desc[line]); |
| |
| hirq = gpio_to_irq(g_MbtkPlatIrq_dev.gpio_desc[line]); |
| |
| |
| pr_err("%s. %s=%d, hirq = %d\n", __func__, name, g_MbtkPlatIrq_dev.gpio_desc[line], hirq); |
| irq_ctx.info[line].hirq = hirq; |
| #endif |
| args.args_count = 3; |
| args.args[0] = 0; |
| args.args[1] = hirq; |
| args.args[2] = type; |
| args.np = irq_ctx.np; |
| |
| return hirq; |
| //return irq_create_of_mapping(&args); |
| } |
| |
| static void mbtk_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 mbtk_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 mbtk_irq_isr(int irq, void *priv) |
| { |
| struct libirq_info *info = (struct libirq_info *)priv; |
| unsigned int line = info->line; |
| int gpio_value = 0; |
| |
| disable_irq_nosync(irq); //add by niening |
| |
| gpio_value = gpio_get_value_cansleep(g_MbtkPlatIrq_dev.gpio_desc[line]) ; |
| |
| printk("%s/L%d, gpio_value = %d \n", __FUNCTION__, __LINE__, gpio_value); |
| if(info->wake) |
| { |
| pm_wakeup_event(g_MbtkPlatIrq_dev.dev, 2000); |
| } |
| |
| |
| if(line_used(line)) { |
| irq_ctx.pending |= BIT(line); |
| |
| mbtk_irq_wakeup(line); |
| } |
| |
| |
| |
| pr_info("%s:eint get = %d\n", __func__, line); |
| |
| enable_irq(irq);//add by niening |
| |
| return IRQ_HANDLED; |
| } |
| |
| static int mbtk_irq_open(struct inode *inode, struct file *filp) |
| { |
| int error = 0; |
| unsigned int line; |
| struct libirq_info *info; |
| |
| line = iminor(inode) - MBTK_IRQ_MINOR; |
| |
| filp->private_data = &(irq_ctx.info[line]); |
| |
| |
| info = (struct libirq_info *)filp->private_data; |
| |
| // add by niening for solve upper app restart send install cmd |
| |
| if (g_MbtkPlatIrq_dev.release_func_callback[info->line] == TRUE) |
| { |
| pr_err("%s/L%d\n", __FUNCTION__, __LINE__); |
| free_irq(info->virq, info); |
| } |
| |
| |
| return error; |
| } |
| |
| static int mbtk_irq_release(struct inode *inode, struct file *filp) |
| { |
| struct libirq_info *info; |
| |
| info = (struct libirq_info *)filp->private_data; |
| |
| pr_err("%s/L%d\n", __FUNCTION__, __LINE__); |
| |
| if(line_used(info->line)) { |
| |
| #if 0 // forbid by niening for upper app goto sleep will callback this function |
| |
| //irq_set_irq_type(info->virq, IRQ_TYPE_NONE); |
| //free_irq(info->virq, info); |
| #endif |
| |
| line_used(info->line) = 0; |
| } |
| |
| filp->private_data = NULL; |
| |
| |
| g_MbtkPlatIrq_dev.release_func_callback[info->line] = TRUE; |
| return 0; |
| } |
| |
| static long mbtk_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 = mbkt_irq_map(info->hirq, type, info->line); |
| if (virq <= 0) { |
| pr_err("%s:mbkt_irq_map %d failed %d(%d)\n", __func__, info->line, virq, type); |
| return -ENOMEM; |
| } |
| /* pr_err("%s:mbkt_irq_map %d %d %d\n", __func__, info->line, virq, info->hirq);*/ |
| |
| #if 1// niening_Test |
| /* 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, mbtk_irq_isr, 0, info->name, info); |
| if(ret<0) { |
| pr_err("%s:request_irq %d failed %d\n", __func__, info->line, type); |
| return ret; |
| } |
| #else |
| ret = devm_request_threaded_irq(g_MbtkPlatIrq_dev.dev, virq, mbtk_irq_isr,mbtk_irq_isr,0 , info->name, info); |
| |
| |
| #endif |
| 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); |
| |
| #if 0 //niening_test |
| ret = irq_set_irq_type(info->virq, type); |
| #else |
| |
| ret = irq_set_irq_type(info->virq, (type | IRQF_ONESHOT)); |
| #endif |
| 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; |
| |
| #if 0 //niening_test |
| |
| ret = irq_set_irq_wake(info->virq, en); |
| if (ret) |
| return ret; |
| #else |
| if(en) |
| enable_irq_wake(info->virq); |
| else |
| disable_irq_wake(info->virq); |
| |
| #endif |
| |
| 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; |
| |
| mbtk_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 |
| mbtk_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 mbtk_irq_ioctl(filp, cmd, (unsigned long)compat_ptr(arg)); |
| } |
| |
| #endif /* CONFIG_COMPAT */ |
| |
| static const struct file_operations mbtk_plat_irq_fops = { |
| .open = mbtk_irq_open, |
| .release = mbtk_irq_release, |
| .llseek = no_llseek, |
| .unlocked_ioctl = mbtk_irq_ioctl, |
| #ifdef CONFIG_COMPAT |
| .compat_ioctl = mbtk_irq_compat_ioctl, |
| #endif |
| }; |
| |
| static struct miscdevice mbtk_irq_device[MBTK_LIBIRQ_MAX] = {0}; |
| static int mbtk_plat_irq_device_init(struct platform_device *pdev) |
| { |
| int i; |
| int ret = 0; |
| char name[16]; |
| struct miscdevice *misc_dev = NULL; |
| struct device_node *np; |
| g_MbtkPlatIrq_dev.dev = &pdev->dev; |
| |
| |
| np = of_find_compatible_node(NULL, NULL, "mbtk,plat-irq"); |
| if (NULL == np) { |
| pr_err("Can't find interrupt-controller \n"); |
| return -ENODEV; |
| } |
| irq_ctx.np = np; |
| |
| for(i= 0; i < MAX_GPIO_NUM; i++) |
| { |
| memset(name, 0, sizeof(name)); |
| sprintf(name, "%s%d", "gpio_irq", i); |
| pr_err("%s. parse %s \n", __func__, name); |
| |
| g_MbtkPlatIrq_dev.gpio_desc[i] = of_get_named_gpio(np, name, 0); |
| g_MbtkPlatIrq_dev.release_func_callback[i] = FALSE; |
| |
| if (unlikely(g_MbtkPlatIrq_dev.gpio_desc[i] < 0)) |
| { |
| pr_err("%s. parse %s failed\n", __func__, name); |
| } |
| else |
| { |
| pr_err("%s. %s=%d g_MbtkPlatIrq_dev.release_func_callback[i] =%d\n", __func__, name, g_MbtkPlatIrq_dev.gpio_desc[i], g_MbtkPlatIrq_dev.release_func_callback[i]); |
| } |
| |
| ret = gpio_request(g_MbtkPlatIrq_dev.gpio_desc[i], name); |
| if(ret < 0) |
| { |
| pr_err("%s. gpio_request for =%d failed\n", __func__,g_MbtkPlatIrq_dev.gpio_desc[i]); |
| return ret; |
| } |
| |
| gpio_direction_input(g_MbtkPlatIrq_dev.gpio_desc[i]); |
| } |
| |
| /* |
| irq_ctx.pctrl = devm_pinctrl_get(&pdev->dev); |
| if (IS_ERR(irq_ctx.pctrl)) { |
| dev_warn(&pdev->dev, "Failed to get sc_irq pins"); |
| printk("--->%s/L%d\n", __FUNCTION__, __LINE__); |
| irq_ctx.pctrl = NULL; |
| return -ENODEV; |
| } |
| */ |
| for (i=0; i<MBTK_LIBIRQ_MAX; i++) { |
| misc_dev = &mbtk_irq_device[i]; |
| misc_dev->mode = 0666, |
| misc_dev->minor = MBTK_IRQ_MINOR + i, |
| memset(name, 0, sizeof(name)); |
| sprintf(name, "%s%d", "sc_irq", i); |
| misc_dev->name = name; |
| misc_dev->fops = &mbtk_plat_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); |
| |
| while ((ret = misc_register(misc_dev)) < 0) { |
| if (ret != -EINTR) |
| break; |
| } |
| if (ret < 0) { |
| |
| printk(KERN_ERR "misc_register failed with error %d\n", ret); |
| } |
| |
| |
| /* ret = misc_register(misc_dev); |
| if (ret) { |
| pr_err("%s:register dev(%d) failed:%d \n", __func__, i, ret); |
| return ret; |
| }*/ |
| |
| #if 0 //niening_Test |
| 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; |
| } |
| #endif |
| } |
| |
| |
| spin_lock_init(&irq_ctx.lock); |
| |
| return ret; |
| }; |
| |
| static int mbtk_plat_irq_probe(struct platform_device *pdev) |
| { |
| int ret = 0; |
| |
| //ret = mbtk_plat_irq_device_init(); |
| //if (ret) |
| // return ret; |
| |
| ret = mbtk_plat_irq_device_init(pdev); |
| |
| device_init_wakeup(&pdev->dev, true); |
| |
| return ret; |
| } |
| |
| static int mbtk_plat_irq_remove(struct platform_device *pdev) |
| { |
| return 0; |
| } |
| |
| static const struct of_device_id mbtk_plat_irq_match[] = { |
| { .compatible = "mbtk,plat-irq", }, |
| { } |
| }; |
| |
| static struct platform_driver zx_bsp_driver = { |
| .probe = mbtk_plat_irq_probe, |
| .remove = mbtk_plat_irq_remove, |
| .driver = { |
| .name = "mbtk_PlatIrq", |
| .of_match_table = mbtk_plat_irq_match, |
| }, |
| }; |
| builtin_platform_driver(zx_bsp_driver) |
| |
| /*---------------------------------------------------------------*/ |
| #if 0 |
| |
| static struct reset_control *reboot_rst; |
| static int zx_restart(struct notifier_block *this, |
| unsigned long mode, void *cmd) |
| { |
| 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 |
| } |
| |
| #endif |