diff -drupN a/drivers/watchdog/ingenic_wdt.c b/drivers/watchdog/ingenic_wdt.c --- a/drivers/watchdog/ingenic_wdt.c 1970-01-01 03:00:00.000000000 +0300 +++ b/drivers/watchdog/ingenic_wdt.c 2022-06-09 05:02:34.000000000 +0300 @@ -0,0 +1,580 @@ +/* + * Copyright (C) 2017, bo.liu + * INGENIC Watchdog driver + * + * 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. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define DEFAULT_HEARTBEAT 5 +#define MAX_HEARTBEAT 2048 + + +static bool nowayout = WATCHDOG_NOWAYOUT; +module_param(nowayout, bool, 0); +MODULE_PARM_DESC(nowayout, + "Watchdog cannot be stopped once started (default=" + __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); + +static unsigned int heartbeat = DEFAULT_HEARTBEAT; +module_param(heartbeat, uint, 0); +MODULE_PARM_DESC(heartbeat, + "Watchdog heartbeat period in seconds from 1 to " + __MODULE_STRING(MAX_HEARTBEAT) ", default " + __MODULE_STRING(DEFAULT_HEARTBEAT)); + +struct ingenic_wdt_drvdata { + struct watchdog_device wdt; + struct platform_device *pdev; + const struct mfd_cell *cell; + + struct clk *ext_clk; + struct notifier_block restart_handler; +}; + +static int ingenic_wdt_ping(struct watchdog_device *wdt_dev) +{ + ingenic_watchdog_set_count(0); + return 0; +} + +static inline int get_clk_div(unsigned int *timeout_value) +{ + int clk_div = TCU_PRESCALE_1; + + while (*timeout_value > 0xffff) { + if (clk_div == TCU_PRESCALE_1024) { + /* Requested timeout too high; + * use highest possible value. */ + *timeout_value = 0xffff; + clk_div = -1; + break; + } + *timeout_value >>= 2; + clk_div += 1; + } + + return clk_div; +} +static void wdt_config(struct ingenic_wdt_drvdata *drvdata, + unsigned int new_timeout) +{ + unsigned int rtc_clk_rate; + //unsigned int ext_clk_rate; + unsigned int timeout_value; + unsigned int clk_src; + int clk_div = 0; + + rtc_clk_rate = 24000000 / 512; + clk_src = TCU_CLKSRC_RTC; + //ext_clk_rate = clk_get_rate(drvdata->ext_clk); + //timeout_value = ext_clk_rate * new_timeout; + timeout_value = rtc_clk_rate * new_timeout; + clk_div = get_clk_div(&timeout_value); + if(clk_div < 0){ + printk("Requested timeout too high, use highest possible value\n"); + clk_div = TCU_PRESCALE_1024; + timeout_value = 0xffff; + } + + ingenic_watchdog_config((clk_div << 3) | clk_src, timeout_value); +} + +static int ingenic_wdt_set_timeout(struct watchdog_device *wdt_dev, + unsigned int new_timeout) +{ + struct ingenic_wdt_drvdata *drvdata = watchdog_get_drvdata(wdt_dev); + + wdt_config(drvdata, new_timeout); + wdt_dev->timeout = new_timeout; + return 0; +} + +static int ingenic_wdt_start(struct watchdog_device *wdt_dev) +{ + struct ingenic_wdt_drvdata *drvdata = watchdog_get_drvdata(wdt_dev); + + drvdata->cell->enable(drvdata->pdev); + ingenic_wdt_set_timeout(wdt_dev, wdt_dev->timeout); + return 0; +} + +static int ingenic_wdt_stop(struct watchdog_device *wdt_dev) +{ + struct ingenic_wdt_drvdata *drvdata = watchdog_get_drvdata(wdt_dev); + drvdata->cell->disable(drvdata->pdev); + return 0; +} + +static const struct watchdog_info ingenic_wdt_info = { + .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE, + .identity = "ingenic Watchdog", +}; + +static const struct watchdog_ops ingenic_wdt_ops = { + .owner = THIS_MODULE, + .start = ingenic_wdt_start, + .stop = ingenic_wdt_stop, + .ping = ingenic_wdt_ping, + .set_timeout = ingenic_wdt_set_timeout, +}; + +static int ingenic_reset_handler(struct notifier_block *this, unsigned long mode, + void *cmd) +{ + struct ingenic_wdt_drvdata *drvdata = container_of(this, + struct ingenic_wdt_drvdata, restart_handler); + struct watchdog_device *wdd = &drvdata->wdt; + + if (cmd && !strcmp(cmd, REBOOT_CMD_RECOVERY)) + ingenic_recovery_sign(); + else + ingenic_reboot_sign(); + + if (cmd && !strcmp(cmd, REBOOT_CMD_SOFTBURN)) + ingenic_softburn_sign(); + + mutex_lock(&wdd->lock); + + drvdata->cell->enable(drvdata->pdev); + + ingenic_watchdog_config((3 << 3)|TCU_CLKSRC_RTC, 4); + while (1) { + mdelay(500); + pr_err("wdt reset failed, Never be here\n"); + } + mutex_unlock(&wdd->lock); + return NOTIFY_DONE; +} + +#ifdef CONFIG_OF +static const struct of_device_id ingenic_wdt_of_matches[] = { + { .compatible = "ingenic,watchdog", }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, ingenic_wdt_of_matches) +#endif + +static ssize_t watchdog_cmd_set(struct file *file, const char __user *buffer, size_t count, loff_t *f_pos); +static int watchdog_cmd_open(struct inode *inode, struct file *file); +static const struct file_operations watchdog_cmd_fops ={ + .read = seq_read, + .open = watchdog_cmd_open, + .llseek = seq_lseek, + .release = single_release, + .write = watchdog_cmd_set, +}; + + +static struct watchdog_device *m_wdt; +static int ingenic_wdt_probe(struct platform_device *pdev) +{ + struct ingenic_wdt_drvdata *drvdata; + struct watchdog_device *ingenic_wdt; + int ret; + struct proc_dir_entry *proc; + + drvdata = devm_kzalloc(&pdev->dev, sizeof(struct ingenic_wdt_drvdata), + GFP_KERNEL); + if (!drvdata) { + dev_err(&pdev->dev, "Unable to alloacate watchdog device\n"); + return -ENOMEM; + } + + drvdata->cell = mfd_get_cell(pdev); + if (!drvdata->cell) { + dev_err(&pdev->dev, "Failed to get mfd cell\n"); + return -ENOMEM; + } + drvdata->pdev = pdev; + if (heartbeat < 1 || heartbeat > MAX_HEARTBEAT) + heartbeat = DEFAULT_HEARTBEAT; + + m_wdt = ingenic_wdt = &drvdata->wdt; + ingenic_wdt->info = &ingenic_wdt_info; + ingenic_wdt->ops = &ingenic_wdt_ops; + ingenic_wdt->timeout = heartbeat; + ingenic_wdt->min_timeout = 1; + ingenic_wdt->max_timeout = MAX_HEARTBEAT; + ingenic_wdt->parent = &pdev->dev; + watchdog_set_nowayout(ingenic_wdt, nowayout); + watchdog_set_drvdata(ingenic_wdt, drvdata); + + drvdata->ext_clk = devm_clk_get(&pdev->dev, "ext"); + if (IS_ERR(drvdata->ext_clk)) { + dev_err(&pdev->dev, "cannot find EXT clock\n"); + ret = PTR_ERR(drvdata->ext_clk); + goto err_out; + } + + ret = watchdog_register_device(&drvdata->wdt); + if (ret < 0) + goto err_disable_ext_clk; + + platform_set_drvdata(pdev, drvdata); + + drvdata->restart_handler.notifier_call = ingenic_reset_handler; + drvdata->restart_handler.priority = WDT_RESET_PROR; + ret = register_restart_handler(&drvdata->restart_handler); + if (ret) + dev_warn(&pdev->dev, + "cannot register restart handler (err=%d)\n", ret); + + /* proc info */ + proc = jz_proc_mkdir("watchdog"); + if (!proc) { + printk("create mdio info failed!\n"); + } + proc_create_data("reset", S_IRUGO, proc, &watchdog_cmd_fops, NULL); + + return 0; + +err_disable_ext_clk: + clk_put(drvdata->ext_clk); +err_out: + return ret; +} + +static int ingenic_wdt_remove(struct platform_device *pdev) +{ + struct ingenic_wdt_drvdata *drvdata = platform_get_drvdata(pdev); + + ingenic_wdt_stop(&drvdata->wdt); + watchdog_unregister_device(&drvdata->wdt); + clk_put(drvdata->ext_clk); + devm_kfree(&pdev->dev, drvdata); + return 0; +} + +static struct platform_driver ingenic_wdt_driver = { + .probe = ingenic_wdt_probe, + .remove = ingenic_wdt_remove, + .driver = { + .name = "ingenic,watchdog", + .of_match_table = of_match_ptr(ingenic_wdt_of_matches), + }, +}; + +module_platform_driver(ingenic_wdt_driver); + +/**********************ingenic utils(deprecated)*****************************************/ +struct wdt_utils { + struct watchdog_device *wdt; + unsigned msecs; + + unsigned stop; + struct task_struct *task; + unsigned short count; +}; + +static __deprecated int reset_task(void *data) { + struct wdt_utils *wdt_utils = data; + const struct sched_param param = { + .sched_priority = MAX_RT_PRIO-1, + }; + sched_setscheduler(current,SCHED_RR,¶m); + + if (test_and_set_bit(WDOG_DEV_OPEN, &wdt_utils->wdt->status)) { + pr_warn("watchdog conflict\n"); + wdt_utils->stop = 1; + return 0; + } + + mutex_lock(&wdt_utils->wdt->lock); + + ingenic_wdt_set_timeout(wdt_utils->wdt, (wdt_utils->msecs + 1000 - 1)/1000); + ingenic_wdt_start(wdt_utils->wdt); + + mutex_unlock(&wdt_utils->wdt->lock); + + while (1) { + if(kthread_should_stop()) { + clear_bit(WDOG_DEV_OPEN, &wdt_utils->wdt->status); + mutex_lock(&wdt_utils->wdt->lock); + + ingenic_wdt_stop(wdt_utils->wdt); + + mutex_unlock(&wdt_utils->wdt->lock); + break; + } + + mutex_lock(&wdt_utils->wdt->lock); + + ingenic_wdt_ping(wdt_utils->wdt); + + mutex_unlock(&wdt_utils->wdt->lock); + msleep(wdt_utils->wdt->timeout / 3); + } + + return 0; +} + +static __deprecated ssize_t wdt_time_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct wdt_utils *wdt = dev_get_drvdata(dev); + return sprintf(buf, "%d msecs\n", wdt->msecs); +} +static __deprecated ssize_t wdt_time_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct wdt_utils *wdt = dev_get_drvdata(dev); + unsigned int msecs; + + if(!wdt->stop) + return -EBUSY; + + sscanf(buf,"%d\n",&msecs); + + if(msecs < 1000) + msecs = 1000; + if(msecs > 30000) + msecs = 30000; + + wdt->msecs = msecs; + return count; +} +static __deprecated DEVICE_ATTR_RW(wdt_time); + +static __deprecated ssize_t wdt_control_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct wdt_utils *wdt = dev_get_drvdata(dev); + return sprintf(buf, "%s\n", wdt->stop?">offon<\n"); +} + +static __deprecated ssize_t wdt_control_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct wdt_utils *wdt = dev_get_drvdata(dev); + + if(!strncmp(buf,"on",2) && (wdt->stop == 1)) { + wdt->task = kthread_run(reset_task, wdt, "reset_task%d",wdt->count++); + wdt->stop = 0; + } else if(!strncmp(buf,"off",3) && (wdt->stop == 0)) { + kthread_stop(wdt->task); + wdt->stop = 1; + } + return count; +} +static __deprecated DEVICE_ATTR_RW(wdt_control); + +static __deprecated char *reset_command[] = {"wdt", "hibernate","recovery", "burnerboot"}; +static __deprecated ssize_t reset_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + int len = 0, i; + + for(i = 0; i < ARRAY_SIZE(reset_command); i++) + len += sprintf(buf + len, "%s\t", reset_command[i]); + len += sprintf(buf + len, "\n"); + + return len; +} + +static __deprecated ssize_t reset_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + int command_size = 0; + int i; + char *cmd = NULL; + + if(count == 0) + return -EINVAL; + + command_size = ARRAY_SIZE(reset_command); + for(i = 0;i < command_size; i++) { + if(!strncmp(buf, reset_command[i], strlen(reset_command[i]))) + break; + } + + switch(i) { + case 1: + reboot_mode = REBOOT_HARD; + break; + case 2: + cmd = "recovery"; + break; + case 3: + cmd = "softburn"; + break; + default: + case 0: + break; + } + kernel_restart(cmd); + return count; +} +static __deprecated DEVICE_ATTR_RW(reset); + +static __deprecated struct attribute *reset_attrs[] = { + &dev_attr_wdt_time.attr, + &dev_attr_wdt_control.attr, + &dev_attr_reset.attr, + NULL +}; + +static __deprecated const struct attribute_group attr_group = { + .attrs = reset_attrs, +}; + +static __deprecated int wdt_utils_probe(struct platform_device *pdev) +{ + struct wdt_utils *wdt; + int ret; + + wdt = devm_kzalloc(&pdev->dev, sizeof(struct wdt_utils),GFP_KERNEL); + if(!wdt) + return -ENOMEM; + wdt->wdt = m_wdt; + wdt->count = 0; + wdt->stop = 1; + wdt->msecs = heartbeat * 1000; + dev_set_drvdata(&pdev->dev, wdt); + ret = sysfs_create_group(&pdev->dev.kobj, &attr_group); + if (ret) + printk("watchdog utils regist failed\n"); + return ret; +} + +static __deprecated void wdt_utils_shutdown(struct platform_device *pdev) +{ + struct wdt_utils *wdt_utils = dev_get_drvdata(&pdev->dev); + ingenic_wdt_stop(wdt_utils->wdt); +} + +static __deprecated int wdt_utils_suspend(struct platform_device *pdev, pm_message_t state) +{ + struct wdt_utils *wdt = dev_get_drvdata(&pdev->dev); + if (wdt->stop) + return 0; + kthread_stop(wdt->task); + return 0; +} + + +static __deprecated int wdt_utils_resume(struct platform_device *pdev) +{ + struct wdt_utils *wdt = dev_get_drvdata(&pdev->dev); + if(wdt->stop) + return 0; + wdt->task = kthread_run(reset_task, wdt, "reset_task%d",wdt->count++); + return 0; +} + +static __deprecated struct platform_device wdt_utils_pdev = { + .name = "wdt_utils", +}; + +static __deprecated struct platform_driver wdt_utils_pdrv = { + .probe = wdt_utils_probe, + .shutdown = wdt_utils_shutdown, + .suspend = wdt_utils_suspend, + .resume = wdt_utils_resume, + .driver = { + .name = "wdt_utils", + .owner = THIS_MODULE, + }, +}; + +static __deprecated int __init init_reset(void) +{ + platform_driver_register(&wdt_utils_pdrv); + platform_device_register(&wdt_utils_pdev); + return 0; +} +device_initcall_sync(init_reset); +MODULE_AUTHOR("bo.liu "); +MODULE_DESCRIPTION("ingenic Watchdog Driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:ingenic-wdt"); + + +/* cmd */ +#define WATCHDOG_CMD_BUF_SIZE 100 +static uint8_t watchdog_cmd_buf[100]; +static int watchdog_cmd_show(struct seq_file *m, void *v) +{ + int len = 0; + len += seq_printf(m ,"%s\n", watchdog_cmd_buf); + return len; +} + +static ssize_t watchdog_cmd_set(struct file *file, const char __user *buffer, size_t count, loff_t *f_pos) +{ + int cmd_value = 0; + unsigned int reg04 = 0,reglc = 0; + + char *buf = kzalloc((count+1), GFP_KERNEL); + if(!buf) + return -ENOMEM; + if(copy_from_user(buf, buffer, count)) + { + kfree(buf); + return EFAULT; + } + cmd_value = simple_strtoull(buf, NULL, 0); + + mdelay(1000); + if(1 == cmd_value) { + reg04 = inl(TCU_IOBASE + 0x04); + if(reg04 & 0x1) { + outl(0 << 0, TCU_IOBASE + 0x4); + } + + outl(0x1a, TCU_IOBASE + 0xc); + + reglc = inl(TCU_IOBASE + 0x1c); + if(reglc & 0x10000) { + outl(1 << 16, TCU_IOBASE + 0x3c); + outl(1 << 16, TCU_IOBASE + 0x2c); + } + outl(0x0000, TCU_IOBASE + 0x0); + outl(0x0000, TCU_IOBASE + 0x8); + + reg04 = inl(TCU_IOBASE + 0x04); + if(!(reg04 & 0x1)) { + outl(1 << 0, TCU_IOBASE + 0x4); + } + printk("watchdog reboot system!!!\n"); + } + + kfree(buf); + return count; +} +static int watchdog_cmd_open(struct inode *inode, struct file *file) +{ + return single_open_size(file, watchdog_cmd_show, PDE_DATA(inode),8192); +}