0001
0002
0003
0004
0005
0006
0007
0008
0009
0010 #include <linux/bitops.h>
0011 #include <linux/mfd/syscon.h>
0012 #include <linux/module.h>
0013 #include <linux/of.h>
0014 #include <linux/platform_device.h>
0015 #include <linux/regmap.h>
0016 #include <linux/watchdog.h>
0017
0018
0019 #define WDTTIMSET 0x3004
0020 #define WDTTIMSET_PERIOD_MASK (0xf << 0)
0021 #define WDTTIMSET_PERIOD_1_SEC (0x3 << 0)
0022
0023
0024 #define WDTRSTSEL 0x3008
0025 #define WDTRSTSEL_RSTSEL_MASK (0x3 << 0)
0026 #define WDTRSTSEL_RSTSEL_BOTH (0x0 << 0)
0027 #define WDTRSTSEL_RSTSEL_IRQ_ONLY (0x2 << 0)
0028
0029
0030 #define WDTCTRL 0x300c
0031 #define WDTCTRL_STATUS BIT(8)
0032 #define WDTCTRL_CLEAR BIT(1)
0033 #define WDTCTRL_ENABLE BIT(0)
0034
0035 #define SEC_TO_WDTTIMSET_PRD(sec) \
0036 (ilog2(sec) + WDTTIMSET_PERIOD_1_SEC)
0037
0038 #define WDTST_TIMEOUT 1000
0039
0040 #define WDT_DEFAULT_TIMEOUT 64
0041 #define WDT_PERIOD_MIN 1
0042 #define WDT_PERIOD_MAX 128
0043
0044 static unsigned int timeout = 0;
0045 static bool nowayout = WATCHDOG_NOWAYOUT;
0046
0047 struct uniphier_wdt_dev {
0048 struct watchdog_device wdt_dev;
0049 struct regmap *regmap;
0050 };
0051
0052
0053
0054
0055 static int uniphier_watchdog_ping(struct watchdog_device *w)
0056 {
0057 struct uniphier_wdt_dev *wdev = watchdog_get_drvdata(w);
0058 unsigned int val;
0059 int ret;
0060
0061
0062 ret = regmap_write_bits(wdev->regmap, WDTCTRL,
0063 WDTCTRL_CLEAR, WDTCTRL_CLEAR);
0064 if (!ret)
0065
0066
0067
0068
0069 ret = regmap_read_poll_timeout(wdev->regmap, WDTCTRL, val,
0070 (val & WDTCTRL_STATUS),
0071 0, WDTST_TIMEOUT);
0072
0073 return ret;
0074 }
0075
0076 static int __uniphier_watchdog_start(struct regmap *regmap, unsigned int sec)
0077 {
0078 unsigned int val;
0079 int ret;
0080
0081 ret = regmap_read_poll_timeout(regmap, WDTCTRL, val,
0082 !(val & WDTCTRL_STATUS),
0083 0, WDTST_TIMEOUT);
0084 if (ret)
0085 return ret;
0086
0087
0088 ret = regmap_write(regmap, WDTTIMSET,
0089 SEC_TO_WDTTIMSET_PRD(sec));
0090 if (ret)
0091 return ret;
0092
0093
0094 ret = regmap_write(regmap, WDTCTRL, WDTCTRL_ENABLE | WDTCTRL_CLEAR);
0095 if (!ret)
0096
0097
0098
0099
0100 ret = regmap_read_poll_timeout(regmap, WDTCTRL, val,
0101 (val & WDTCTRL_STATUS),
0102 0, WDTST_TIMEOUT);
0103
0104 return ret;
0105 }
0106
0107 static int __uniphier_watchdog_stop(struct regmap *regmap)
0108 {
0109
0110 return regmap_write_bits(regmap, WDTCTRL, WDTCTRL_ENABLE, 0);
0111 }
0112
0113 static int __uniphier_watchdog_restart(struct regmap *regmap, unsigned int sec)
0114 {
0115 int ret;
0116
0117 ret = __uniphier_watchdog_stop(regmap);
0118 if (ret)
0119 return ret;
0120
0121 return __uniphier_watchdog_start(regmap, sec);
0122 }
0123
0124 static int uniphier_watchdog_start(struct watchdog_device *w)
0125 {
0126 struct uniphier_wdt_dev *wdev = watchdog_get_drvdata(w);
0127 unsigned int tmp_timeout;
0128
0129 tmp_timeout = roundup_pow_of_two(w->timeout);
0130
0131 return __uniphier_watchdog_start(wdev->regmap, tmp_timeout);
0132 }
0133
0134 static int uniphier_watchdog_stop(struct watchdog_device *w)
0135 {
0136 struct uniphier_wdt_dev *wdev = watchdog_get_drvdata(w);
0137
0138 return __uniphier_watchdog_stop(wdev->regmap);
0139 }
0140
0141 static int uniphier_watchdog_set_timeout(struct watchdog_device *w,
0142 unsigned int t)
0143 {
0144 struct uniphier_wdt_dev *wdev = watchdog_get_drvdata(w);
0145 unsigned int tmp_timeout;
0146 int ret;
0147
0148 tmp_timeout = roundup_pow_of_two(t);
0149 if (tmp_timeout == w->timeout)
0150 return 0;
0151
0152 if (watchdog_active(w)) {
0153 ret = __uniphier_watchdog_restart(wdev->regmap, tmp_timeout);
0154 if (ret)
0155 return ret;
0156 }
0157
0158 w->timeout = tmp_timeout;
0159
0160 return 0;
0161 }
0162
0163
0164
0165
0166 static const struct watchdog_info uniphier_wdt_info = {
0167 .identity = "uniphier-wdt",
0168 .options = WDIOF_SETTIMEOUT |
0169 WDIOF_KEEPALIVEPING |
0170 WDIOF_MAGICCLOSE |
0171 WDIOF_OVERHEAT,
0172 };
0173
0174 static const struct watchdog_ops uniphier_wdt_ops = {
0175 .owner = THIS_MODULE,
0176 .start = uniphier_watchdog_start,
0177 .stop = uniphier_watchdog_stop,
0178 .ping = uniphier_watchdog_ping,
0179 .set_timeout = uniphier_watchdog_set_timeout,
0180 };
0181
0182 static int uniphier_wdt_probe(struct platform_device *pdev)
0183 {
0184 struct device *dev = &pdev->dev;
0185 struct uniphier_wdt_dev *wdev;
0186 struct regmap *regmap;
0187 struct device_node *parent;
0188 int ret;
0189
0190 wdev = devm_kzalloc(dev, sizeof(*wdev), GFP_KERNEL);
0191 if (!wdev)
0192 return -ENOMEM;
0193
0194 parent = of_get_parent(dev->of_node);
0195 regmap = syscon_node_to_regmap(parent);
0196 of_node_put(parent);
0197 if (IS_ERR(regmap))
0198 return PTR_ERR(regmap);
0199
0200 wdev->regmap = regmap;
0201 wdev->wdt_dev.info = &uniphier_wdt_info;
0202 wdev->wdt_dev.ops = &uniphier_wdt_ops;
0203 wdev->wdt_dev.max_timeout = WDT_PERIOD_MAX;
0204 wdev->wdt_dev.min_timeout = WDT_PERIOD_MIN;
0205 wdev->wdt_dev.timeout = WDT_DEFAULT_TIMEOUT;
0206 wdev->wdt_dev.parent = dev;
0207
0208 watchdog_init_timeout(&wdev->wdt_dev, timeout, dev);
0209 watchdog_set_nowayout(&wdev->wdt_dev, nowayout);
0210 watchdog_stop_on_reboot(&wdev->wdt_dev);
0211
0212 watchdog_set_drvdata(&wdev->wdt_dev, wdev);
0213
0214 uniphier_watchdog_stop(&wdev->wdt_dev);
0215 ret = regmap_write(wdev->regmap, WDTRSTSEL, WDTRSTSEL_RSTSEL_BOTH);
0216 if (ret)
0217 return ret;
0218
0219 ret = devm_watchdog_register_device(dev, &wdev->wdt_dev);
0220 if (ret)
0221 return ret;
0222
0223 dev_info(dev, "watchdog driver (timeout=%d sec, nowayout=%d)\n",
0224 wdev->wdt_dev.timeout, nowayout);
0225
0226 return 0;
0227 }
0228
0229 static const struct of_device_id uniphier_wdt_dt_ids[] = {
0230 { .compatible = "socionext,uniphier-wdt" },
0231 { }
0232 };
0233 MODULE_DEVICE_TABLE(of, uniphier_wdt_dt_ids);
0234
0235 static struct platform_driver uniphier_wdt_driver = {
0236 .probe = uniphier_wdt_probe,
0237 .driver = {
0238 .name = "uniphier-wdt",
0239 .of_match_table = uniphier_wdt_dt_ids,
0240 },
0241 };
0242
0243 module_platform_driver(uniphier_wdt_driver);
0244
0245 module_param(timeout, uint, 0000);
0246 MODULE_PARM_DESC(timeout,
0247 "Watchdog timeout seconds in power of 2. (0 < timeout < 128, default="
0248 __MODULE_STRING(WDT_DEFAULT_TIMEOUT) ")");
0249
0250 module_param(nowayout, bool, 0000);
0251 MODULE_PARM_DESC(nowayout,
0252 "Watchdog cannot be stopped once started (default="
0253 __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
0254
0255 MODULE_AUTHOR("Keiji Hayashibara <hayashibara.keiji@socionext.com>");
0256 MODULE_DESCRIPTION("UniPhier Watchdog Device Driver");
0257 MODULE_LICENSE("GPL v2");