Version:  2.0.40 2.2.26 2.4.37 2.6.39 3.0 3.1 3.2 3.3 3.4 3.5 3.6 3.7 3.8 3.9 3.10 3.11 3.12 3.13 3.14 3.15

Linux/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/interrupt.h>
 22 #include <linux/io.h>
 23 #include <linux/clk.h>
 24 #include <linux/err.h>
 25 #include <linux/of.h>
 26 #include <linux/of_device.h>
 27 
 28 /* RSTOUT mask register physical address for Orion5x, Kirkwood and Dove */
 29 #define ORION_RSTOUT_MASK_OFFSET        0x20108
 30 
 31 /* Internal registers can be configured at any 1 MiB aligned address */
 32 #define INTERNAL_REGS_MASK              ~(SZ_1M - 1)
 33 
 34 /*
 35  * Watchdog timer block registers.
 36  */
 37 #define TIMER_CTRL              0x0000
 38 #define TIMER_A370_STATUS       0x04
 39 
 40 #define WDT_MAX_CYCLE_COUNT     0xffffffff
 41 
 42 #define WDT_A370_RATIO_MASK(v)  ((v) << 16)
 43 #define WDT_A370_RATIO_SHIFT    5
 44 #define WDT_A370_RATIO          (1 << WDT_A370_RATIO_SHIFT)
 45 
 46 #define WDT_AXP_FIXED_ENABLE_BIT BIT(10)
 47 #define WDT_A370_EXPIRED        BIT(31)
 48 
 49 static bool nowayout = WATCHDOG_NOWAYOUT;
 50 static int heartbeat = -1;              /* module parameter (seconds) */
 51 
 52 struct orion_watchdog;
 53 
 54 struct orion_watchdog_data {
 55         int wdt_counter_offset;
 56         int wdt_enable_bit;
 57         int rstout_enable_bit;
 58         int (*clock_init)(struct platform_device *,
 59                           struct orion_watchdog *);
 60         int (*start)(struct watchdog_device *);
 61 };
 62 
 63 struct orion_watchdog {
 64         struct watchdog_device wdt;
 65         void __iomem *reg;
 66         void __iomem *rstout;
 67         unsigned long clk_rate;
 68         struct clk *clk;
 69         const struct orion_watchdog_data *data;
 70 };
 71 
 72 static int orion_wdt_clock_init(struct platform_device *pdev,
 73                                 struct orion_watchdog *dev)
 74 {
 75         int ret;
 76 
 77         dev->clk = clk_get(&pdev->dev, NULL);
 78         if (IS_ERR(dev->clk))
 79                 return PTR_ERR(dev->clk);
 80         ret = clk_prepare_enable(dev->clk);
 81         if (ret) {
 82                 clk_put(dev->clk);
 83                 return ret;
 84         }
 85 
 86         dev->clk_rate = clk_get_rate(dev->clk);
 87         return 0;
 88 }
 89 
 90 static int armada370_wdt_clock_init(struct platform_device *pdev,
 91                                     struct orion_watchdog *dev)
 92 {
 93         int ret;
 94 
 95         dev->clk = clk_get(&pdev->dev, NULL);
 96         if (IS_ERR(dev->clk))
 97                 return PTR_ERR(dev->clk);
 98         ret = clk_prepare_enable(dev->clk);
 99         if (ret) {
100                 clk_put(dev->clk);
101                 return ret;
102         }
103 
104         /* Setup watchdog input clock */
105         atomic_io_modify(dev->reg + TIMER_CTRL,
106                         WDT_A370_RATIO_MASK(WDT_A370_RATIO_SHIFT),
107                         WDT_A370_RATIO_MASK(WDT_A370_RATIO_SHIFT));
108 
109         dev->clk_rate = clk_get_rate(dev->clk) / WDT_A370_RATIO;
110         return 0;
111 }
112 
113 static int armadaxp_wdt_clock_init(struct platform_device *pdev,
114                                    struct orion_watchdog *dev)
115 {
116         int ret;
117 
118         dev->clk = of_clk_get_by_name(pdev->dev.of_node, "fixed");
119         if (IS_ERR(dev->clk))
120                 return PTR_ERR(dev->clk);
121         ret = clk_prepare_enable(dev->clk);
122         if (ret) {
123                 clk_put(dev->clk);
124                 return ret;
125         }
126 
127         /* Enable the fixed watchdog clock input */
128         atomic_io_modify(dev->reg + TIMER_CTRL,
129                          WDT_AXP_FIXED_ENABLE_BIT,
130                          WDT_AXP_FIXED_ENABLE_BIT);
131 
132         dev->clk_rate = clk_get_rate(dev->clk);
133         return 0;
134 }
135 
136 static int orion_wdt_ping(struct watchdog_device *wdt_dev)
137 {
138         struct orion_watchdog *dev = watchdog_get_drvdata(wdt_dev);
139         /* Reload watchdog duration */
140         writel(dev->clk_rate * wdt_dev->timeout,
141                dev->reg + dev->data->wdt_counter_offset);
142         return 0;
143 }
144 
145 static int armada370_start(struct watchdog_device *wdt_dev)
146 {
147         struct orion_watchdog *dev = watchdog_get_drvdata(wdt_dev);
148 
149         /* Set watchdog duration */
150         writel(dev->clk_rate * wdt_dev->timeout,
151                dev->reg + dev->data->wdt_counter_offset);
152 
153         /* Clear the watchdog expiration bit */
154         atomic_io_modify(dev->reg + TIMER_A370_STATUS, WDT_A370_EXPIRED, 0);
155 
156         /* Enable watchdog timer */
157         atomic_io_modify(dev->reg + TIMER_CTRL, dev->data->wdt_enable_bit,
158                                                 dev->data->wdt_enable_bit);
159 
160         atomic_io_modify(dev->rstout, dev->data->rstout_enable_bit,
161                                       dev->data->rstout_enable_bit);
162         return 0;
163 }
164 
165 static int orion_start(struct watchdog_device *wdt_dev)
166 {
167         struct orion_watchdog *dev = watchdog_get_drvdata(wdt_dev);
168 
169         /* Set watchdog duration */
170         writel(dev->clk_rate * wdt_dev->timeout,
171                dev->reg + dev->data->wdt_counter_offset);
172 
173         /* Enable watchdog timer */
174         atomic_io_modify(dev->reg + TIMER_CTRL, dev->data->wdt_enable_bit,
175                                                 dev->data->wdt_enable_bit);
176 
177         /* Enable reset on watchdog */
178         atomic_io_modify(dev->rstout, dev->data->rstout_enable_bit,
179                                       dev->data->rstout_enable_bit);
180 
181         return 0;
182 }
183 
184 static int orion_wdt_start(struct watchdog_device *wdt_dev)
185 {
186         struct orion_watchdog *dev = watchdog_get_drvdata(wdt_dev);
187 
188         /* There are some per-SoC quirks to handle */
189         return dev->data->start(wdt_dev);
190 }
191 
192 static int orion_wdt_stop(struct watchdog_device *wdt_dev)
193 {
194         struct orion_watchdog *dev = watchdog_get_drvdata(wdt_dev);
195 
196         /* Disable reset on watchdog */
197         atomic_io_modify(dev->rstout, dev->data->rstout_enable_bit, 0);
198 
199         /* Disable watchdog timer */
200         atomic_io_modify(dev->reg + TIMER_CTRL, dev->data->wdt_enable_bit, 0);
201 
202         return 0;
203 }
204 
205 static int orion_wdt_enabled(struct orion_watchdog *dev)
206 {
207         bool enabled, running;
208 
209         enabled = readl(dev->rstout) & dev->data->rstout_enable_bit;
210         running = readl(dev->reg + TIMER_CTRL) & dev->data->wdt_enable_bit;
211 
212         return enabled && running;
213 }
214 
215 static unsigned int orion_wdt_get_timeleft(struct watchdog_device *wdt_dev)
216 {
217         struct orion_watchdog *dev = watchdog_get_drvdata(wdt_dev);
218         return readl(dev->reg + dev->data->wdt_counter_offset) / dev->clk_rate;
219 }
220 
221 static int orion_wdt_set_timeout(struct watchdog_device *wdt_dev,
222                                  unsigned int timeout)
223 {
224         wdt_dev->timeout = timeout;
225         return 0;
226 }
227 
228 static const struct watchdog_info orion_wdt_info = {
229         .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE,
230         .identity = "Orion Watchdog",
231 };
232 
233 static const struct watchdog_ops orion_wdt_ops = {
234         .owner = THIS_MODULE,
235         .start = orion_wdt_start,
236         .stop = orion_wdt_stop,
237         .ping = orion_wdt_ping,
238         .set_timeout = orion_wdt_set_timeout,
239         .get_timeleft = orion_wdt_get_timeleft,
240 };
241 
242 static irqreturn_t orion_wdt_irq(int irq, void *devid)
243 {
244         panic("Watchdog Timeout");
245         return IRQ_HANDLED;
246 }
247 
248 /*
249  * The original devicetree binding for this driver specified only
250  * one memory resource, so in order to keep DT backwards compatibility
251  * we try to fallback to a hardcoded register address, if the resource
252  * is missing from the devicetree.
253  */
254 static void __iomem *orion_wdt_ioremap_rstout(struct platform_device *pdev,
255                                               phys_addr_t internal_regs)
256 {
257         struct resource *res;
258         phys_addr_t rstout;
259 
260         res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
261         if (res)
262                 return devm_ioremap(&pdev->dev, res->start,
263                                     resource_size(res));
264 
265         /* This workaround works only for "orion-wdt", DT-enabled */
266         if (!of_device_is_compatible(pdev->dev.of_node, "marvell,orion-wdt"))
267                 return NULL;
268 
269         rstout = internal_regs + ORION_RSTOUT_MASK_OFFSET;
270 
271         WARN(1, FW_BUG "falling back to harcoded RSTOUT reg %pa\n", &rstout);
272         return devm_ioremap(&pdev->dev, rstout, 0x4);
273 }
274 
275 static const struct orion_watchdog_data orion_data = {
276         .rstout_enable_bit = BIT(1),
277         .wdt_enable_bit = BIT(4),
278         .wdt_counter_offset = 0x24,
279         .clock_init = orion_wdt_clock_init,
280         .start = orion_start,
281 };
282 
283 static const struct orion_watchdog_data armada370_data = {
284         .rstout_enable_bit = BIT(8),
285         .wdt_enable_bit = BIT(8),
286         .wdt_counter_offset = 0x34,
287         .clock_init = armada370_wdt_clock_init,
288         .start = armada370_start,
289 };
290 
291 static const struct orion_watchdog_data armadaxp_data = {
292         .rstout_enable_bit = BIT(8),
293         .wdt_enable_bit = BIT(8),
294         .wdt_counter_offset = 0x34,
295         .clock_init = armadaxp_wdt_clock_init,
296         .start = armada370_start,
297 };
298 
299 static const struct of_device_id orion_wdt_of_match_table[] = {
300         {
301                 .compatible = "marvell,orion-wdt",
302                 .data = &orion_data,
303         },
304         {
305                 .compatible = "marvell,armada-370-wdt",
306                 .data = &armada370_data,
307         },
308         {
309                 .compatible = "marvell,armada-xp-wdt",
310                 .data = &armadaxp_data,
311         },
312         {},
313 };
314 MODULE_DEVICE_TABLE(of, orion_wdt_of_match_table);
315 
316 static int orion_wdt_probe(struct platform_device *pdev)
317 {
318         struct orion_watchdog *dev;
319         const struct of_device_id *match;
320         unsigned int wdt_max_duration;  /* (seconds) */
321         struct resource *res;
322         int ret, irq;
323 
324         dev = devm_kzalloc(&pdev->dev, sizeof(struct orion_watchdog),
325                            GFP_KERNEL);
326         if (!dev)
327                 return -ENOMEM;
328 
329         match = of_match_device(orion_wdt_of_match_table, &pdev->dev);
330         if (!match)
331                 /* Default legacy match */
332                 match = &orion_wdt_of_match_table[0];
333 
334         dev->wdt.info = &orion_wdt_info;
335         dev->wdt.ops = &orion_wdt_ops;
336         dev->wdt.min_timeout = 1;
337         dev->data = match->data;
338 
339         res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
340         if (!res)
341                 return -ENODEV;
342 
343         dev->reg = devm_ioremap(&pdev->dev, res->start,
344                                resource_size(res));
345         if (!dev->reg)
346                 return -ENOMEM;
347 
348         dev->rstout = orion_wdt_ioremap_rstout(pdev, res->start &
349                                                      INTERNAL_REGS_MASK);
350         if (!dev->rstout)
351                 return -ENODEV;
352 
353         ret = dev->data->clock_init(pdev, dev);
354         if (ret) {
355                 dev_err(&pdev->dev, "cannot initialize clock\n");
356                 return ret;
357         }
358 
359         wdt_max_duration = WDT_MAX_CYCLE_COUNT / dev->clk_rate;
360 
361         dev->wdt.timeout = wdt_max_duration;
362         dev->wdt.max_timeout = wdt_max_duration;
363         watchdog_init_timeout(&dev->wdt, heartbeat, &pdev->dev);
364 
365         platform_set_drvdata(pdev, &dev->wdt);
366         watchdog_set_drvdata(&dev->wdt, dev);
367 
368         /*
369          * Let's make sure the watchdog is fully stopped, unless it's
370          * explicitly enabled. This may be the case if the module was
371          * removed and re-insterted, or if the bootloader explicitly
372          * set a running watchdog before booting the kernel.
373          */
374         if (!orion_wdt_enabled(dev))
375                 orion_wdt_stop(&dev->wdt);
376 
377         /* Request the IRQ only after the watchdog is disabled */
378         irq = platform_get_irq(pdev, 0);
379         if (irq > 0) {
380                 /*
381                  * Not all supported platforms specify an interrupt for the
382                  * watchdog, so let's make it optional.
383                  */
384                 ret = devm_request_irq(&pdev->dev, irq, orion_wdt_irq, 0,
385                                        pdev->name, dev);
386                 if (ret < 0) {
387                         dev_err(&pdev->dev, "failed to request IRQ\n");
388                         goto disable_clk;
389                 }
390         }
391 
392         watchdog_set_nowayout(&dev->wdt, nowayout);
393         ret = watchdog_register_device(&dev->wdt);
394         if (ret)
395                 goto disable_clk;
396 
397         pr_info("Initial timeout %d sec%s\n",
398                 dev->wdt.timeout, nowayout ? ", nowayout" : "");
399         return 0;
400 
401 disable_clk:
402         clk_disable_unprepare(dev->clk);
403         clk_put(dev->clk);
404         return ret;
405 }
406 
407 static int orion_wdt_remove(struct platform_device *pdev)
408 {
409         struct watchdog_device *wdt_dev = platform_get_drvdata(pdev);
410         struct orion_watchdog *dev = watchdog_get_drvdata(wdt_dev);
411 
412         watchdog_unregister_device(wdt_dev);
413         clk_disable_unprepare(dev->clk);
414         clk_put(dev->clk);
415         return 0;
416 }
417 
418 static void orion_wdt_shutdown(struct platform_device *pdev)
419 {
420         struct watchdog_device *wdt_dev = platform_get_drvdata(pdev);
421         orion_wdt_stop(wdt_dev);
422 }
423 
424 static struct platform_driver orion_wdt_driver = {
425         .probe          = orion_wdt_probe,
426         .remove         = orion_wdt_remove,
427         .shutdown       = orion_wdt_shutdown,
428         .driver         = {
429                 .owner  = THIS_MODULE,
430                 .name   = "orion_wdt",
431                 .of_match_table = orion_wdt_of_match_table,
432         },
433 };
434 
435 module_platform_driver(orion_wdt_driver);
436 
437 MODULE_AUTHOR("Sylver Bruneau <sylver.bruneau@googlemail.com>");
438 MODULE_DESCRIPTION("Orion Processor Watchdog");
439 
440 module_param(heartbeat, int, 0);
441 MODULE_PARM_DESC(heartbeat, "Initial watchdog heartbeat in seconds");
442 
443 module_param(nowayout, bool, 0);
444 MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default="
445                                 __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
446 
447 MODULE_LICENSE("GPL");
448 MODULE_ALIAS("platform:orion_wdt");
449 

This page was automatically generated by LXR 0.3.1 (source).  •  Linux is a registered trademark of Linus Torvalds  •  Contact us