]> git.kernelconcepts.de Git - karo-tx-linux.git/blob - drivers/watchdog/orion_wdt.c
watchdog: orion: Add per-compatible clock initialization
[karo-tx-linux.git] / drivers / watchdog / orion_wdt.c
1 /*
2  * drivers/watchdog/orion_wdt.c
3  *
4  * Watchdog driver for Orion/Kirkwood processors
5  *
6  * Author: Sylver Bruneau <sylver.bruneau@googlemail.com>
7  *
8  * This file is licensed under  the terms of the GNU General Public
9  * License version 2. This program is licensed "as is" without any
10  * warranty of any kind, whether express or implied.
11  */
12
13 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
14
15 #include <linux/module.h>
16 #include <linux/moduleparam.h>
17 #include <linux/types.h>
18 #include <linux/kernel.h>
19 #include <linux/platform_device.h>
20 #include <linux/watchdog.h>
21 #include <linux/init.h>
22 #include <linux/interrupt.h>
23 #include <linux/io.h>
24 #include <linux/clk.h>
25 #include <linux/err.h>
26 #include <linux/of.h>
27 #include <linux/of_device.h>
28
29 /* RSTOUT mask register physical address for Orion5x, Kirkwood and Dove */
30 #define ORION_RSTOUT_MASK_OFFSET        0x20108
31
32 /* Internal registers can be configured at any 1 MiB aligned address */
33 #define INTERNAL_REGS_MASK              ~(SZ_1M - 1)
34
35 /*
36  * Watchdog timer block registers.
37  */
38 #define TIMER_CTRL              0x0000
39
40 #define WDT_MAX_CYCLE_COUNT     0xffffffff
41
42 static bool nowayout = WATCHDOG_NOWAYOUT;
43 static int heartbeat = -1;              /* module parameter (seconds) */
44
45 struct orion_watchdog;
46
47 struct orion_watchdog_data {
48         int wdt_counter_offset;
49         int wdt_enable_bit;
50         int rstout_enable_bit;
51         int (*clock_init)(struct platform_device *,
52                           struct orion_watchdog *);
53 };
54
55 struct orion_watchdog {
56         struct watchdog_device wdt;
57         void __iomem *reg;
58         void __iomem *rstout;
59         unsigned long clk_rate;
60         struct clk *clk;
61         const struct orion_watchdog_data *data;
62 };
63
64 static int orion_wdt_clock_init(struct platform_device *pdev,
65                                 struct orion_watchdog *dev)
66 {
67         int ret;
68
69         dev->clk = devm_clk_get(&pdev->dev, NULL);
70         if (IS_ERR(dev->clk))
71                 return PTR_ERR(dev->clk);
72         ret = clk_prepare_enable(dev->clk);
73         if (ret)
74                 return ret;
75
76         dev->clk_rate = clk_get_rate(dev->clk);
77         return 0;
78 }
79
80 static int orion_wdt_ping(struct watchdog_device *wdt_dev)
81 {
82         struct orion_watchdog *dev = watchdog_get_drvdata(wdt_dev);
83         /* Reload watchdog duration */
84         writel(dev->clk_rate * wdt_dev->timeout,
85                dev->reg + dev->data->wdt_counter_offset);
86         return 0;
87 }
88
89 static int orion_wdt_start(struct watchdog_device *wdt_dev)
90 {
91         struct orion_watchdog *dev = watchdog_get_drvdata(wdt_dev);
92
93         /* Set watchdog duration */
94         writel(dev->clk_rate * wdt_dev->timeout,
95                dev->reg + dev->data->wdt_counter_offset);
96
97         /* Enable watchdog timer */
98         atomic_io_modify(dev->reg + TIMER_CTRL, dev->data->wdt_enable_bit,
99                                                 dev->data->wdt_enable_bit);
100
101         /* Enable reset on watchdog */
102         atomic_io_modify(dev->rstout, dev->data->rstout_enable_bit,
103                                       dev->data->rstout_enable_bit);
104
105         return 0;
106 }
107
108 static int orion_wdt_stop(struct watchdog_device *wdt_dev)
109 {
110         struct orion_watchdog *dev = watchdog_get_drvdata(wdt_dev);
111
112         /* Disable reset on watchdog */
113         atomic_io_modify(dev->rstout, dev->data->rstout_enable_bit, 0);
114
115         /* Disable watchdog timer */
116         atomic_io_modify(dev->reg + TIMER_CTRL, dev->data->wdt_enable_bit, 0);
117
118         return 0;
119 }
120
121 static int orion_wdt_enabled(struct orion_watchdog *dev)
122 {
123         bool enabled, running;
124
125         enabled = readl(dev->rstout) & dev->data->rstout_enable_bit;
126         running = readl(dev->reg + TIMER_CTRL) & dev->data->wdt_enable_bit;
127
128         return enabled && running;
129 }
130
131 static unsigned int orion_wdt_get_timeleft(struct watchdog_device *wdt_dev)
132 {
133         struct orion_watchdog *dev = watchdog_get_drvdata(wdt_dev);
134         return readl(dev->reg + dev->data->wdt_counter_offset) / dev->clk_rate;
135 }
136
137 static int orion_wdt_set_timeout(struct watchdog_device *wdt_dev,
138                                  unsigned int timeout)
139 {
140         wdt_dev->timeout = timeout;
141         return 0;
142 }
143
144 static const struct watchdog_info orion_wdt_info = {
145         .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE,
146         .identity = "Orion Watchdog",
147 };
148
149 static const struct watchdog_ops orion_wdt_ops = {
150         .owner = THIS_MODULE,
151         .start = orion_wdt_start,
152         .stop = orion_wdt_stop,
153         .ping = orion_wdt_ping,
154         .set_timeout = orion_wdt_set_timeout,
155         .get_timeleft = orion_wdt_get_timeleft,
156 };
157
158 static irqreturn_t orion_wdt_irq(int irq, void *devid)
159 {
160         panic("Watchdog Timeout");
161         return IRQ_HANDLED;
162 }
163
164 /*
165  * The original devicetree binding for this driver specified only
166  * one memory resource, so in order to keep DT backwards compatibility
167  * we try to fallback to a hardcoded register address, if the resource
168  * is missing from the devicetree.
169  */
170 static void __iomem *orion_wdt_ioremap_rstout(struct platform_device *pdev,
171                                               phys_addr_t internal_regs)
172 {
173         struct resource *res;
174         phys_addr_t rstout;
175
176         res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
177         if (res)
178                 return devm_ioremap(&pdev->dev, res->start,
179                                     resource_size(res));
180
181         /* This workaround works only for "orion-wdt", DT-enabled */
182         if (!of_device_is_compatible(pdev->dev.of_node, "marvell,orion-wdt"))
183                 return NULL;
184
185         rstout = internal_regs + ORION_RSTOUT_MASK_OFFSET;
186
187         WARN(1, FW_BUG "falling back to harcoded RSTOUT reg 0x%x\n", rstout);
188         return devm_ioremap(&pdev->dev, rstout, 0x4);
189 }
190
191 static const struct orion_watchdog_data orion_data = {
192         .rstout_enable_bit = BIT(1),
193         .wdt_enable_bit = BIT(4),
194         .wdt_counter_offset = 0x24,
195         .clock_init = orion_wdt_clock_init,
196 };
197
198 static const struct of_device_id orion_wdt_of_match_table[] = {
199         {
200                 .compatible = "marvell,orion-wdt",
201                 .data = &orion_data,
202         },
203         {},
204 };
205 MODULE_DEVICE_TABLE(of, orion_wdt_of_match_table);
206
207 static int orion_wdt_probe(struct platform_device *pdev)
208 {
209         struct orion_watchdog *dev;
210         const struct of_device_id *match;
211         unsigned int wdt_max_duration;  /* (seconds) */
212         struct resource *res;
213         int ret, irq;
214
215         dev = devm_kzalloc(&pdev->dev, sizeof(struct orion_watchdog),
216                            GFP_KERNEL);
217         if (!dev)
218                 return -ENOMEM;
219
220         match = of_match_device(orion_wdt_of_match_table, &pdev->dev);
221         if (!match)
222                 /* Default legacy match */
223                 match = &orion_wdt_of_match_table[0];
224
225         dev->wdt.info = &orion_wdt_info;
226         dev->wdt.ops = &orion_wdt_ops;
227         dev->wdt.min_timeout = 1;
228         dev->data = match->data;
229
230         res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
231         if (!res)
232                 return -ENODEV;
233
234         dev->reg = devm_ioremap(&pdev->dev, res->start,
235                                resource_size(res));
236         if (!dev->reg)
237                 return -ENOMEM;
238
239         dev->rstout = orion_wdt_ioremap_rstout(pdev, res->start &
240                                                      INTERNAL_REGS_MASK);
241         if (!dev->rstout)
242                 return -ENODEV;
243
244         ret = dev->data->clock_init(pdev, dev);
245         if (ret) {
246                 dev_err(&pdev->dev, "cannot initialize clock\n");
247                 return ret;
248         }
249
250         wdt_max_duration = WDT_MAX_CYCLE_COUNT / dev->clk_rate;
251
252         dev->wdt.timeout = wdt_max_duration;
253         dev->wdt.max_timeout = wdt_max_duration;
254         watchdog_init_timeout(&dev->wdt, heartbeat, &pdev->dev);
255
256         platform_set_drvdata(pdev, &dev->wdt);
257         watchdog_set_drvdata(&dev->wdt, dev);
258
259         /*
260          * Let's make sure the watchdog is fully stopped, unless it's
261          * explicitly enabled. This may be the case if the module was
262          * removed and re-insterted, or if the bootloader explicitly
263          * set a running watchdog before booting the kernel.
264          */
265         if (!orion_wdt_enabled(dev))
266                 orion_wdt_stop(&dev->wdt);
267
268         /* Request the IRQ only after the watchdog is disabled */
269         irq = platform_get_irq(pdev, 0);
270         if (irq > 0) {
271                 /*
272                  * Not all supported platforms specify an interrupt for the
273                  * watchdog, so let's make it optional.
274                  */
275                 ret = devm_request_irq(&pdev->dev, irq, orion_wdt_irq, 0,
276                                        pdev->name, dev);
277                 if (ret < 0) {
278                         dev_err(&pdev->dev, "failed to request IRQ\n");
279                         goto disable_clk;
280                 }
281         }
282
283         watchdog_set_nowayout(&dev->wdt, nowayout);
284         ret = watchdog_register_device(&dev->wdt);
285         if (ret)
286                 goto disable_clk;
287
288         pr_info("Initial timeout %d sec%s\n",
289                 dev->wdt.timeout, nowayout ? ", nowayout" : "");
290         return 0;
291
292 disable_clk:
293         clk_disable_unprepare(dev->clk);
294         return ret;
295 }
296
297 static int orion_wdt_remove(struct platform_device *pdev)
298 {
299         struct watchdog_device *wdt_dev = platform_get_drvdata(pdev);
300         struct orion_watchdog *dev = watchdog_get_drvdata(wdt_dev);
301
302         watchdog_unregister_device(wdt_dev);
303         clk_disable_unprepare(dev->clk);
304         return 0;
305 }
306
307 static void orion_wdt_shutdown(struct platform_device *pdev)
308 {
309         struct watchdog_device *wdt_dev = platform_get_drvdata(pdev);
310         orion_wdt_stop(wdt_dev);
311 }
312
313 static struct platform_driver orion_wdt_driver = {
314         .probe          = orion_wdt_probe,
315         .remove         = orion_wdt_remove,
316         .shutdown       = orion_wdt_shutdown,
317         .driver         = {
318                 .owner  = THIS_MODULE,
319                 .name   = "orion_wdt",
320                 .of_match_table = orion_wdt_of_match_table,
321         },
322 };
323
324 module_platform_driver(orion_wdt_driver);
325
326 MODULE_AUTHOR("Sylver Bruneau <sylver.bruneau@googlemail.com>");
327 MODULE_DESCRIPTION("Orion Processor Watchdog");
328
329 module_param(heartbeat, int, 0);
330 MODULE_PARM_DESC(heartbeat, "Initial watchdog heartbeat in seconds");
331
332 module_param(nowayout, bool, 0);
333 MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default="
334                                 __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
335
336 MODULE_LICENSE("GPL");
337 MODULE_ALIAS("platform:orion_wdt");