mirror of https://github.com/OpenIPC/firmware.git
585 lines
15 KiB
Diff
585 lines
15 KiB
Diff
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 <bo.liu@ingenic.com>
|
|
+ * 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 <linux/module.h>
|
|
+#include <linux/moduleparam.h>
|
|
+#include <linux/types.h>
|
|
+#include <linux/kernel.h>
|
|
+#include <linux/watchdog.h>
|
|
+#include <linux/platform_device.h>
|
|
+#include <linux/io.h>
|
|
+#include <linux/device.h>
|
|
+#include <linux/clk.h>
|
|
+#include <linux/reboot.h>
|
|
+#include <linux/slab.h>
|
|
+#include <linux/err.h>
|
|
+#include <linux/of.h>
|
|
+#include <linux/delay.h>
|
|
+#include <linux/sched.h>
|
|
+#include <linux/proc_fs.h>
|
|
+#include <linux/seq_file.h>
|
|
+#include <linux/delay.h>
|
|
+#include <linux/kthread.h>
|
|
+#include <linux/mfd/core.h>
|
|
+#include <linux/mfd/ingenic-tcu.h>
|
|
+#include <soc/xburst/reboot.h>
|
|
+#include <jz_proc.h>
|
|
+#include <soc/base.h>
|
|
+
|
|
+#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?">off<on\n":"off>on<\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 <bo.liu@ingenic.com>");
|
|
+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);
|
|
+}
|