0001
0002
0003
0004
0005
0006
0007
0008 #include <linux/kernel.h>
0009 #include <linux/mod_devicetable.h>
0010 #include <linux/module.h>
0011 #include <linux/platform_device.h>
0012 #include <linux/property.h>
0013 #include <linux/regmap.h>
0014 #include <linux/watchdog.h>
0015
0016
0017
0018
0019 #define WDT_CTRL 0x00
0020 #define WDT_CTRL_EN BIT(0)
0021 #define WDT_CTRL_LOCK BIT(2)
0022 #define WDT_CTRL_ASSERT_SYS_RESET BIT(6)
0023 #define WDT_CTRL_ASSERT_WDT_TIMEOUT BIT(7)
0024 #define WDT_TIMEOUT 0x01
0025 #define WDT_KICK 0x02
0026 #define WDT_KICK_VALUE 0x6b
0027 #define WDT_COUNT 0x03
0028
0029 #define WDT_DEFAULT_TIMEOUT 10
0030
0031 static bool nowayout = WATCHDOG_NOWAYOUT;
0032 module_param(nowayout, bool, 0);
0033 MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default="
0034 __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
0035
0036 static int timeout;
0037 module_param(timeout, int, 0);
0038 MODULE_PARM_DESC(timeout, "Initial watchdog timeout in seconds");
0039
0040 struct sl28cpld_wdt {
0041 struct watchdog_device wdd;
0042 struct regmap *regmap;
0043 u32 offset;
0044 bool assert_wdt_timeout;
0045 };
0046
0047 static int sl28cpld_wdt_ping(struct watchdog_device *wdd)
0048 {
0049 struct sl28cpld_wdt *wdt = watchdog_get_drvdata(wdd);
0050
0051 return regmap_write(wdt->regmap, wdt->offset + WDT_KICK,
0052 WDT_KICK_VALUE);
0053 }
0054
0055 static int sl28cpld_wdt_start(struct watchdog_device *wdd)
0056 {
0057 struct sl28cpld_wdt *wdt = watchdog_get_drvdata(wdd);
0058 unsigned int val;
0059
0060 val = WDT_CTRL_EN | WDT_CTRL_ASSERT_SYS_RESET;
0061 if (wdt->assert_wdt_timeout)
0062 val |= WDT_CTRL_ASSERT_WDT_TIMEOUT;
0063 if (nowayout)
0064 val |= WDT_CTRL_LOCK;
0065
0066 return regmap_update_bits(wdt->regmap, wdt->offset + WDT_CTRL,
0067 val, val);
0068 }
0069
0070 static int sl28cpld_wdt_stop(struct watchdog_device *wdd)
0071 {
0072 struct sl28cpld_wdt *wdt = watchdog_get_drvdata(wdd);
0073
0074 return regmap_update_bits(wdt->regmap, wdt->offset + WDT_CTRL,
0075 WDT_CTRL_EN, 0);
0076 }
0077
0078 static unsigned int sl28cpld_wdt_get_timeleft(struct watchdog_device *wdd)
0079 {
0080 struct sl28cpld_wdt *wdt = watchdog_get_drvdata(wdd);
0081 unsigned int val;
0082 int ret;
0083
0084 ret = regmap_read(wdt->regmap, wdt->offset + WDT_COUNT, &val);
0085 if (ret)
0086 return 0;
0087
0088 return val;
0089 }
0090
0091 static int sl28cpld_wdt_set_timeout(struct watchdog_device *wdd,
0092 unsigned int timeout)
0093 {
0094 struct sl28cpld_wdt *wdt = watchdog_get_drvdata(wdd);
0095 int ret;
0096
0097 ret = regmap_write(wdt->regmap, wdt->offset + WDT_TIMEOUT, timeout);
0098 if (ret)
0099 return ret;
0100
0101 wdd->timeout = timeout;
0102
0103 return 0;
0104 }
0105
0106 static const struct watchdog_info sl28cpld_wdt_info = {
0107 .options = WDIOF_MAGICCLOSE | WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING,
0108 .identity = "sl28cpld watchdog",
0109 };
0110
0111 static const struct watchdog_ops sl28cpld_wdt_ops = {
0112 .owner = THIS_MODULE,
0113 .start = sl28cpld_wdt_start,
0114 .stop = sl28cpld_wdt_stop,
0115 .ping = sl28cpld_wdt_ping,
0116 .set_timeout = sl28cpld_wdt_set_timeout,
0117 .get_timeleft = sl28cpld_wdt_get_timeleft,
0118 };
0119
0120 static int sl28cpld_wdt_probe(struct platform_device *pdev)
0121 {
0122 struct watchdog_device *wdd;
0123 struct sl28cpld_wdt *wdt;
0124 unsigned int status;
0125 unsigned int val;
0126 int ret;
0127
0128 if (!pdev->dev.parent)
0129 return -ENODEV;
0130
0131 wdt = devm_kzalloc(&pdev->dev, sizeof(*wdt), GFP_KERNEL);
0132 if (!wdt)
0133 return -ENOMEM;
0134
0135 wdt->regmap = dev_get_regmap(pdev->dev.parent, NULL);
0136 if (!wdt->regmap)
0137 return -ENODEV;
0138
0139 ret = device_property_read_u32(&pdev->dev, "reg", &wdt->offset);
0140 if (ret)
0141 return -EINVAL;
0142
0143 wdt->assert_wdt_timeout = device_property_read_bool(&pdev->dev,
0144 "kontron,assert-wdt-timeout-pin");
0145
0146
0147 wdd = &wdt->wdd;
0148 wdd->parent = &pdev->dev;
0149 wdd->info = &sl28cpld_wdt_info;
0150 wdd->ops = &sl28cpld_wdt_ops;
0151 wdd->min_timeout = 1;
0152 wdd->max_timeout = 255;
0153
0154 watchdog_set_drvdata(wdd, wdt);
0155 watchdog_stop_on_reboot(wdd);
0156
0157
0158
0159
0160
0161 ret = regmap_read(wdt->regmap, wdt->offset + WDT_CTRL, &status);
0162 if (ret)
0163 return ret;
0164
0165
0166
0167
0168
0169
0170
0171
0172
0173
0174 ret = regmap_read(wdt->regmap, wdt->offset + WDT_TIMEOUT, &val);
0175 if (ret)
0176 return ret;
0177
0178 if (val)
0179 wdd->timeout = val;
0180 else
0181 wdd->timeout = WDT_DEFAULT_TIMEOUT;
0182
0183 watchdog_init_timeout(wdd, timeout, &pdev->dev);
0184 sl28cpld_wdt_set_timeout(wdd, wdd->timeout);
0185
0186
0187 if (status & WDT_CTRL_LOCK)
0188 nowayout = true;
0189 watchdog_set_nowayout(wdd, nowayout);
0190
0191
0192
0193
0194
0195 if (status & WDT_CTRL_EN) {
0196 sl28cpld_wdt_start(wdd);
0197 set_bit(WDOG_HW_RUNNING, &wdd->status);
0198 }
0199
0200 ret = devm_watchdog_register_device(&pdev->dev, wdd);
0201 if (ret < 0) {
0202 dev_err(&pdev->dev, "failed to register watchdog device\n");
0203 return ret;
0204 }
0205
0206 dev_info(&pdev->dev, "initial timeout %d sec%s\n",
0207 wdd->timeout, nowayout ? ", nowayout" : "");
0208
0209 return 0;
0210 }
0211
0212 static const struct of_device_id sl28cpld_wdt_of_match[] = {
0213 { .compatible = "kontron,sl28cpld-wdt" },
0214 {}
0215 };
0216 MODULE_DEVICE_TABLE(of, sl28cpld_wdt_of_match);
0217
0218 static struct platform_driver sl28cpld_wdt_driver = {
0219 .probe = sl28cpld_wdt_probe,
0220 .driver = {
0221 .name = "sl28cpld-wdt",
0222 .of_match_table = sl28cpld_wdt_of_match,
0223 },
0224 };
0225 module_platform_driver(sl28cpld_wdt_driver);
0226
0227 MODULE_DESCRIPTION("sl28cpld Watchdog Driver");
0228 MODULE_AUTHOR("Michael Walle <michael@walle.cc>");
0229 MODULE_LICENSE("GPL");