1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * Watchdog driver for Technologic Systems TS-72xx based SBCs |
4 | * (TS-7200, TS-7250 and TS-7260). These boards have external |
5 | * glue logic CPLD chip, which includes programmable watchdog |
6 | * timer. |
7 | * |
8 | * Copyright (c) 2009 Mika Westerberg <mika.westerberg@iki.fi> |
9 | * |
10 | * This driver is based on ep93xx_wdt and wm831x_wdt drivers. |
11 | * |
12 | */ |
13 | |
14 | #include <linux/platform_device.h> |
15 | #include <linux/module.h> |
16 | #include <linux/watchdog.h> |
17 | #include <linux/io.h> |
18 | |
19 | #define TS72XX_WDT_DEFAULT_TIMEOUT 30 |
20 | |
21 | static int timeout; |
22 | module_param(timeout, int, 0); |
23 | MODULE_PARM_DESC(timeout, "Watchdog timeout in seconds." ); |
24 | |
25 | static bool nowayout = WATCHDOG_NOWAYOUT; |
26 | module_param(nowayout, bool, 0); |
27 | MODULE_PARM_DESC(nowayout, "Disable watchdog shutdown on close" ); |
28 | |
29 | /* priv->control_reg */ |
30 | #define TS72XX_WDT_CTRL_DISABLE 0x00 |
31 | #define TS72XX_WDT_CTRL_250MS 0x01 |
32 | #define TS72XX_WDT_CTRL_500MS 0x02 |
33 | #define TS72XX_WDT_CTRL_1SEC 0x03 |
34 | #define TS72XX_WDT_CTRL_RESERVED 0x04 |
35 | #define TS72XX_WDT_CTRL_2SEC 0x05 |
36 | #define TS72XX_WDT_CTRL_4SEC 0x06 |
37 | #define TS72XX_WDT_CTRL_8SEC 0x07 |
38 | |
39 | /* priv->feed_reg */ |
40 | #define TS72XX_WDT_FEED_VAL 0x05 |
41 | |
42 | struct ts72xx_wdt_priv { |
43 | void __iomem *control_reg; |
44 | void __iomem *feed_reg; |
45 | struct watchdog_device wdd; |
46 | unsigned char regval; |
47 | }; |
48 | |
49 | static int ts72xx_wdt_start(struct watchdog_device *wdd) |
50 | { |
51 | struct ts72xx_wdt_priv *priv = watchdog_get_drvdata(wdd); |
52 | |
53 | writeb(TS72XX_WDT_FEED_VAL, addr: priv->feed_reg); |
54 | writeb(val: priv->regval, addr: priv->control_reg); |
55 | |
56 | return 0; |
57 | } |
58 | |
59 | static int ts72xx_wdt_stop(struct watchdog_device *wdd) |
60 | { |
61 | struct ts72xx_wdt_priv *priv = watchdog_get_drvdata(wdd); |
62 | |
63 | writeb(TS72XX_WDT_FEED_VAL, addr: priv->feed_reg); |
64 | writeb(TS72XX_WDT_CTRL_DISABLE, addr: priv->control_reg); |
65 | |
66 | return 0; |
67 | } |
68 | |
69 | static int ts72xx_wdt_ping(struct watchdog_device *wdd) |
70 | { |
71 | struct ts72xx_wdt_priv *priv = watchdog_get_drvdata(wdd); |
72 | |
73 | writeb(TS72XX_WDT_FEED_VAL, addr: priv->feed_reg); |
74 | |
75 | return 0; |
76 | } |
77 | |
78 | static int ts72xx_wdt_settimeout(struct watchdog_device *wdd, unsigned int to) |
79 | { |
80 | struct ts72xx_wdt_priv *priv = watchdog_get_drvdata(wdd); |
81 | |
82 | if (to == 1) { |
83 | priv->regval = TS72XX_WDT_CTRL_1SEC; |
84 | } else if (to == 2) { |
85 | priv->regval = TS72XX_WDT_CTRL_2SEC; |
86 | } else if (to <= 4) { |
87 | priv->regval = TS72XX_WDT_CTRL_4SEC; |
88 | to = 4; |
89 | } else { |
90 | priv->regval = TS72XX_WDT_CTRL_8SEC; |
91 | if (to <= 8) |
92 | to = 8; |
93 | } |
94 | |
95 | wdd->timeout = to; |
96 | |
97 | if (watchdog_active(wdd)) { |
98 | ts72xx_wdt_stop(wdd); |
99 | ts72xx_wdt_start(wdd); |
100 | } |
101 | |
102 | return 0; |
103 | } |
104 | |
105 | static const struct watchdog_info ts72xx_wdt_ident = { |
106 | .options = WDIOF_KEEPALIVEPING | |
107 | WDIOF_SETTIMEOUT | |
108 | WDIOF_MAGICCLOSE, |
109 | .firmware_version = 1, |
110 | .identity = "TS-72XX WDT" , |
111 | }; |
112 | |
113 | static const struct watchdog_ops ts72xx_wdt_ops = { |
114 | .owner = THIS_MODULE, |
115 | .start = ts72xx_wdt_start, |
116 | .stop = ts72xx_wdt_stop, |
117 | .ping = ts72xx_wdt_ping, |
118 | .set_timeout = ts72xx_wdt_settimeout, |
119 | }; |
120 | |
121 | static int ts72xx_wdt_probe(struct platform_device *pdev) |
122 | { |
123 | struct device *dev = &pdev->dev; |
124 | struct ts72xx_wdt_priv *priv; |
125 | struct watchdog_device *wdd; |
126 | int ret; |
127 | |
128 | priv = devm_kzalloc(dev, size: sizeof(*priv), GFP_KERNEL); |
129 | if (!priv) |
130 | return -ENOMEM; |
131 | |
132 | priv->control_reg = devm_platform_ioremap_resource(pdev, index: 0); |
133 | if (IS_ERR(ptr: priv->control_reg)) |
134 | return PTR_ERR(ptr: priv->control_reg); |
135 | |
136 | priv->feed_reg = devm_platform_ioremap_resource(pdev, index: 1); |
137 | if (IS_ERR(ptr: priv->feed_reg)) |
138 | return PTR_ERR(ptr: priv->feed_reg); |
139 | |
140 | wdd = &priv->wdd; |
141 | wdd->info = &ts72xx_wdt_ident; |
142 | wdd->ops = &ts72xx_wdt_ops; |
143 | wdd->min_timeout = 1; |
144 | wdd->max_hw_heartbeat_ms = 8000; |
145 | wdd->parent = dev; |
146 | |
147 | watchdog_set_nowayout(wdd, nowayout); |
148 | |
149 | wdd->timeout = TS72XX_WDT_DEFAULT_TIMEOUT; |
150 | watchdog_init_timeout(wdd, timeout_parm: timeout, dev); |
151 | |
152 | watchdog_set_drvdata(wdd, data: priv); |
153 | |
154 | ret = devm_watchdog_register_device(dev, wdd); |
155 | if (ret) |
156 | return ret; |
157 | |
158 | dev_info(dev, "TS-72xx Watchdog driver\n" ); |
159 | |
160 | return 0; |
161 | } |
162 | |
163 | static struct platform_driver ts72xx_wdt_driver = { |
164 | .probe = ts72xx_wdt_probe, |
165 | .driver = { |
166 | .name = "ts72xx-wdt" , |
167 | }, |
168 | }; |
169 | |
170 | module_platform_driver(ts72xx_wdt_driver); |
171 | |
172 | MODULE_AUTHOR("Mika Westerberg <mika.westerberg@iki.fi>" ); |
173 | MODULE_DESCRIPTION("TS-72xx SBC Watchdog" ); |
174 | MODULE_LICENSE("GPL" ); |
175 | MODULE_ALIAS("platform:ts72xx-wdt" ); |
176 | |