0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011 #include <linux/devm-helpers.h>
0012 #include <linux/slab.h>
0013 #include <linux/errno.h>
0014 #include <linux/device.h>
0015 #include <linux/kernel.h>
0016 #include <linux/module.h>
0017 #include <linux/mfd/retu.h>
0018 #include <linux/watchdog.h>
0019 #include <linux/platform_device.h>
0020
0021
0022 #define RETU_WDT_MAX_TIMER 63
0023
0024 struct retu_wdt_dev {
0025 struct retu_dev *rdev;
0026 struct device *dev;
0027 struct delayed_work ping_work;
0028 };
0029
0030
0031
0032
0033
0034
0035 static void retu_wdt_ping_enable(struct retu_wdt_dev *wdev)
0036 {
0037 retu_write(wdev->rdev, RETU_REG_WATCHDOG, RETU_WDT_MAX_TIMER);
0038 schedule_delayed_work(&wdev->ping_work,
0039 round_jiffies_relative(RETU_WDT_MAX_TIMER * HZ / 2));
0040 }
0041
0042 static void retu_wdt_ping_disable(struct retu_wdt_dev *wdev)
0043 {
0044 retu_write(wdev->rdev, RETU_REG_WATCHDOG, RETU_WDT_MAX_TIMER);
0045 cancel_delayed_work_sync(&wdev->ping_work);
0046 }
0047
0048 static void retu_wdt_ping_work(struct work_struct *work)
0049 {
0050 struct retu_wdt_dev *wdev = container_of(to_delayed_work(work),
0051 struct retu_wdt_dev, ping_work);
0052 retu_wdt_ping_enable(wdev);
0053 }
0054
0055 static int retu_wdt_start(struct watchdog_device *wdog)
0056 {
0057 struct retu_wdt_dev *wdev = watchdog_get_drvdata(wdog);
0058
0059 retu_wdt_ping_disable(wdev);
0060
0061 return retu_write(wdev->rdev, RETU_REG_WATCHDOG, wdog->timeout);
0062 }
0063
0064 static int retu_wdt_stop(struct watchdog_device *wdog)
0065 {
0066 struct retu_wdt_dev *wdev = watchdog_get_drvdata(wdog);
0067
0068 retu_wdt_ping_enable(wdev);
0069
0070 return 0;
0071 }
0072
0073 static int retu_wdt_ping(struct watchdog_device *wdog)
0074 {
0075 struct retu_wdt_dev *wdev = watchdog_get_drvdata(wdog);
0076
0077 return retu_write(wdev->rdev, RETU_REG_WATCHDOG, wdog->timeout);
0078 }
0079
0080 static int retu_wdt_set_timeout(struct watchdog_device *wdog,
0081 unsigned int timeout)
0082 {
0083 struct retu_wdt_dev *wdev = watchdog_get_drvdata(wdog);
0084
0085 wdog->timeout = timeout;
0086 return retu_write(wdev->rdev, RETU_REG_WATCHDOG, wdog->timeout);
0087 }
0088
0089 static const struct watchdog_info retu_wdt_info = {
0090 .options = WDIOF_SETTIMEOUT | WDIOF_MAGICCLOSE | WDIOF_KEEPALIVEPING,
0091 .identity = "Retu watchdog",
0092 };
0093
0094 static const struct watchdog_ops retu_wdt_ops = {
0095 .owner = THIS_MODULE,
0096 .start = retu_wdt_start,
0097 .stop = retu_wdt_stop,
0098 .ping = retu_wdt_ping,
0099 .set_timeout = retu_wdt_set_timeout,
0100 };
0101
0102 static int retu_wdt_probe(struct platform_device *pdev)
0103 {
0104 struct retu_dev *rdev = dev_get_drvdata(pdev->dev.parent);
0105 bool nowayout = WATCHDOG_NOWAYOUT;
0106 struct watchdog_device *retu_wdt;
0107 struct retu_wdt_dev *wdev;
0108 int ret;
0109
0110 retu_wdt = devm_kzalloc(&pdev->dev, sizeof(*retu_wdt), GFP_KERNEL);
0111 if (!retu_wdt)
0112 return -ENOMEM;
0113
0114 wdev = devm_kzalloc(&pdev->dev, sizeof(*wdev), GFP_KERNEL);
0115 if (!wdev)
0116 return -ENOMEM;
0117
0118 retu_wdt->info = &retu_wdt_info;
0119 retu_wdt->ops = &retu_wdt_ops;
0120 retu_wdt->timeout = RETU_WDT_MAX_TIMER;
0121 retu_wdt->min_timeout = 0;
0122 retu_wdt->max_timeout = RETU_WDT_MAX_TIMER;
0123 retu_wdt->parent = &pdev->dev;
0124
0125 watchdog_set_drvdata(retu_wdt, wdev);
0126 watchdog_set_nowayout(retu_wdt, nowayout);
0127
0128 wdev->rdev = rdev;
0129 wdev->dev = &pdev->dev;
0130
0131 ret = devm_delayed_work_autocancel(&pdev->dev, &wdev->ping_work,
0132 retu_wdt_ping_work);
0133 if (ret)
0134 return ret;
0135
0136 ret = devm_watchdog_register_device(&pdev->dev, retu_wdt);
0137 if (ret < 0)
0138 return ret;
0139
0140 if (nowayout)
0141 retu_wdt_ping(retu_wdt);
0142 else
0143 retu_wdt_ping_enable(wdev);
0144
0145 return 0;
0146 }
0147
0148 static struct platform_driver retu_wdt_driver = {
0149 .probe = retu_wdt_probe,
0150 .driver = {
0151 .name = "retu-wdt",
0152 },
0153 };
0154 module_platform_driver(retu_wdt_driver);
0155
0156 MODULE_ALIAS("platform:retu-wdt");
0157 MODULE_DESCRIPTION("Retu watchdog");
0158 MODULE_AUTHOR("Amit Kucheria");
0159 MODULE_AUTHOR("Aaro Koskinen <aaro.koskinen@iki.fi>");
0160 MODULE_LICENSE("GPL");