aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/watchdog
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2014-04-05 18:37:40 -0400
committerLinus Torvalds <torvalds@linux-foundation.org>2014-04-05 18:37:40 -0400
commitcbda94e039c3862326a65d1d0506447af8330c3c (patch)
tree1147da54ec6eb7e1081977f07e62d514b981d9a3 /drivers/watchdog
parentf83ccb93585d1f472c30fa2bbb8b56c23dbdb506 (diff)
parentf1d7d8c86bc8ca41c88acf10ce383c5104cf4920 (diff)
Merge tag 'drivers-3.15' of git://git.kernel.org/pub/scm/linux/kernel/git/arm/arm-soc
Pull ARM SoC driver changes from Arnd Bergmann: "These changes are mostly for ARM specific device drivers that either don't have an upstream maintainer, or that had the maintainer ask us to pick up the changes to avoid conflicts. A large chunk of this are clock drivers (bcm281xx, exynos, versatile, shmobile), aside from that, reset controllers for STi as well as a large rework of the Marvell Orion/EBU watchdog driver are notable" * tag 'drivers-3.15' of git://git.kernel.org/pub/scm/linux/kernel/git/arm/arm-soc: (99 commits) Revert "dts: socfpga: Add DTS entry for adding the stmmac glue layer for stmmac." Revert "net: stmmac: Add SOCFPGA glue driver" ARM: shmobile: r8a7791: Fix SCIFA3-5 clocks ARM: STi: Add reset controller support to mach-sti Kconfig drivers: reset: stih416: add softreset controller drivers: reset: stih415: add softreset controller drivers: reset: Reset controller driver for STiH416 drivers: reset: Reset controller driver for STiH415 drivers: reset: STi SoC system configuration reset controller support dts: socfpga: Add sysmgr node so the gmac can use to reference dts: socfpga: Add support for SD/MMC on the SOCFPGA platform reset: Add optional resets and stubs ARM: shmobile: r7s72100: fix bus clock calculation Power: Reset: Generalize qnap-poweroff to work on Synology devices. dts: socfpga: Update clock entry to support multiple parents ARM: socfpga: Update socfpga_defconfig dts: socfpga: Add DTS entry for adding the stmmac glue layer for stmmac. net: stmmac: Add SOCFPGA glue driver watchdog: orion_wdt: Use %pa to print 'phys_addr_t' drivers: cci: Export CCI PMU revision ...
Diffstat (limited to 'drivers/watchdog')
-rw-r--r--drivers/watchdog/Kconfig2
-rw-r--r--drivers/watchdog/orion_wdt.c381
2 files changed, 303 insertions, 80 deletions
diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig
index 0c6048d5c9a3..74ec8fc5cc03 100644
--- a/drivers/watchdog/Kconfig
+++ b/drivers/watchdog/Kconfig
@@ -301,7 +301,7 @@ config DAVINCI_WATCHDOG
301 301
302config ORION_WATCHDOG 302config ORION_WATCHDOG
303 tristate "Orion watchdog" 303 tristate "Orion watchdog"
304 depends on ARCH_ORION5X || ARCH_KIRKWOOD || ARCH_DOVE || MACH_DOVE 304 depends on ARCH_ORION5X || ARCH_KIRKWOOD || ARCH_DOVE || MACH_DOVE || ARCH_MVEBU
305 select WATCHDOG_CORE 305 select WATCHDOG_CORE
306 help 306 help
307 Say Y here if to include support for the watchdog timer 307 Say Y here if to include support for the watchdog timer
diff --git a/drivers/watchdog/orion_wdt.c b/drivers/watchdog/orion_wdt.c
index 498163497c1c..9b3c41d18703 100644
--- a/drivers/watchdog/orion_wdt.c
+++ b/drivers/watchdog/orion_wdt.c
@@ -18,101 +18,204 @@
18#include <linux/kernel.h> 18#include <linux/kernel.h>
19#include <linux/platform_device.h> 19#include <linux/platform_device.h>
20#include <linux/watchdog.h> 20#include <linux/watchdog.h>
21#include <linux/interrupt.h>
21#include <linux/io.h> 22#include <linux/io.h>
22#include <linux/spinlock.h>
23#include <linux/clk.h> 23#include <linux/clk.h>
24#include <linux/err.h> 24#include <linux/err.h>
25#include <linux/of.h> 25#include <linux/of.h>
26#include <mach/bridge-regs.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)
27 33
28/* 34/*
29 * Watchdog timer block registers. 35 * Watchdog timer block registers.
30 */ 36 */
31#define TIMER_CTRL 0x0000 37#define TIMER_CTRL 0x0000
32#define WDT_EN 0x0010 38#define TIMER_A370_STATUS 0x04
33#define WDT_VAL 0x0024
34 39
35#define WDT_MAX_CYCLE_COUNT 0xffffffff 40#define WDT_MAX_CYCLE_COUNT 0xffffffff
36#define WDT_IN_USE 0
37#define WDT_OK_TO_CLOSE 1
38 41
39#define WDT_RESET_OUT_EN BIT(1) 42#define WDT_A370_RATIO_MASK(v) ((v) << 16)
40#define WDT_INT_REQ BIT(3) 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)
41 48
42static bool nowayout = WATCHDOG_NOWAYOUT; 49static bool nowayout = WATCHDOG_NOWAYOUT;
43static int heartbeat = -1; /* module parameter (seconds) */ 50static int heartbeat = -1; /* module parameter (seconds) */
44static unsigned int wdt_max_duration; /* (seconds) */
45static struct clk *clk;
46static unsigned int wdt_tclk;
47static void __iomem *wdt_reg;
48static DEFINE_SPINLOCK(wdt_lock);
49 51
50static int orion_wdt_ping(struct watchdog_device *wdt_dev) 52struct orion_watchdog;
53
54struct 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
63struct 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
72static int orion_wdt_clock_init(struct platform_device *pdev,
73 struct orion_watchdog *dev)
51{ 74{
52 spin_lock(&wdt_lock); 75 int ret;
53 76
54 /* Reload watchdog duration */ 77 dev->clk = clk_get(&pdev->dev, NULL);
55 writel(wdt_tclk * wdt_dev->timeout, wdt_reg + WDT_VAL); 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 }
56 85
57 spin_unlock(&wdt_lock); 86 dev->clk_rate = clk_get_rate(dev->clk);
58 return 0; 87 return 0;
59} 88}
60 89
61static int orion_wdt_start(struct watchdog_device *wdt_dev) 90static int armada370_wdt_clock_init(struct platform_device *pdev,
91 struct orion_watchdog *dev)
62{ 92{
63 u32 reg; 93 int ret;
64 94
65 spin_lock(&wdt_lock); 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
113static 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
136static 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
145static int armada370_start(struct watchdog_device *wdt_dev)
146{
147 struct orion_watchdog *dev = watchdog_get_drvdata(wdt_dev);
66 148
67 /* Set watchdog duration */ 149 /* Set watchdog duration */
68 writel(wdt_tclk * wdt_dev->timeout, wdt_reg + WDT_VAL); 150 writel(dev->clk_rate * wdt_dev->timeout,
151 dev->reg + dev->data->wdt_counter_offset);
69 152
70 /* Clear watchdog timer interrupt */ 153 /* Clear the watchdog expiration bit */
71 writel(~WDT_INT_REQ, BRIDGE_CAUSE); 154 atomic_io_modify(dev->reg + TIMER_A370_STATUS, WDT_A370_EXPIRED, 0);
72 155
73 /* Enable watchdog timer */ 156 /* Enable watchdog timer */
74 reg = readl(wdt_reg + TIMER_CTRL); 157 atomic_io_modify(dev->reg + TIMER_CTRL, dev->data->wdt_enable_bit,
75 reg |= WDT_EN; 158 dev->data->wdt_enable_bit);
76 writel(reg, wdt_reg + TIMER_CTRL); 159
160 atomic_io_modify(dev->rstout, dev->data->rstout_enable_bit,
161 dev->data->rstout_enable_bit);
162 return 0;
163}
164
165static 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);
77 176
78 /* Enable reset on watchdog */ 177 /* Enable reset on watchdog */
79 reg = readl(RSTOUTn_MASK); 178 atomic_io_modify(dev->rstout, dev->data->rstout_enable_bit,
80 reg |= WDT_RESET_OUT_EN; 179 dev->data->rstout_enable_bit);
81 writel(reg, RSTOUTn_MASK);
82 180
83 spin_unlock(&wdt_lock);
84 return 0; 181 return 0;
85} 182}
86 183
87static int orion_wdt_stop(struct watchdog_device *wdt_dev) 184static int orion_wdt_start(struct watchdog_device *wdt_dev)
88{ 185{
89 u32 reg; 186 struct orion_watchdog *dev = watchdog_get_drvdata(wdt_dev);
90 187
91 spin_lock(&wdt_lock); 188 /* There are some per-SoC quirks to handle */
189 return dev->data->start(wdt_dev);
190}
191
192static int orion_wdt_stop(struct watchdog_device *wdt_dev)
193{
194 struct orion_watchdog *dev = watchdog_get_drvdata(wdt_dev);
92 195
93 /* Disable reset on watchdog */ 196 /* Disable reset on watchdog */
94 reg = readl(RSTOUTn_MASK); 197 atomic_io_modify(dev->rstout, dev->data->rstout_enable_bit, 0);
95 reg &= ~WDT_RESET_OUT_EN;
96 writel(reg, RSTOUTn_MASK);
97 198
98 /* Disable watchdog timer */ 199 /* Disable watchdog timer */
99 reg = readl(wdt_reg + TIMER_CTRL); 200 atomic_io_modify(dev->reg + TIMER_CTRL, dev->data->wdt_enable_bit, 0);
100 reg &= ~WDT_EN;
101 writel(reg, wdt_reg + TIMER_CTRL);
102 201
103 spin_unlock(&wdt_lock);
104 return 0; 202 return 0;
105} 203}
106 204
107static unsigned int orion_wdt_get_timeleft(struct watchdog_device *wdt_dev) 205static int orion_wdt_enabled(struct orion_watchdog *dev)
108{ 206{
109 unsigned int time_left; 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;
110 211
111 spin_lock(&wdt_lock); 212 return enabled && running;
112 time_left = readl(wdt_reg + WDT_VAL) / wdt_tclk; 213}
113 spin_unlock(&wdt_lock);
114 214
115 return time_left; 215static 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;
116} 219}
117 220
118static int orion_wdt_set_timeout(struct watchdog_device *wdt_dev, 221static int orion_wdt_set_timeout(struct watchdog_device *wdt_dev,
@@ -136,68 +239,188 @@ static const struct watchdog_ops orion_wdt_ops = {
136 .get_timeleft = orion_wdt_get_timeleft, 239 .get_timeleft = orion_wdt_get_timeleft,
137}; 240};
138 241
139static struct watchdog_device orion_wdt = { 242static irqreturn_t orion_wdt_irq(int irq, void *devid)
140 .info = &orion_wdt_info, 243{
141 .ops = &orion_wdt_ops, 244 panic("Watchdog Timeout");
142 .min_timeout = 1, 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 */
254static 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
275static 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
283static 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,
143}; 289};
144 290
291static 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
299static 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};
314MODULE_DEVICE_TABLE(of, orion_wdt_of_match_table);
315
145static int orion_wdt_probe(struct platform_device *pdev) 316static int orion_wdt_probe(struct platform_device *pdev)
146{ 317{
318 struct orion_watchdog *dev;
319 const struct of_device_id *match;
320 unsigned int wdt_max_duration; /* (seconds) */
147 struct resource *res; 321 struct resource *res;
148 int ret; 322 int ret, irq;
149 323
150 clk = devm_clk_get(&pdev->dev, NULL); 324 dev = devm_kzalloc(&pdev->dev, sizeof(struct orion_watchdog),
151 if (IS_ERR(clk)) { 325 GFP_KERNEL);
152 dev_err(&pdev->dev, "Orion Watchdog missing clock\n"); 326 if (!dev)
153 return -ENODEV; 327 return -ENOMEM;
154 } 328
155 clk_prepare_enable(clk); 329 match = of_match_device(orion_wdt_of_match_table, &pdev->dev);
156 wdt_tclk = clk_get_rate(clk); 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;
157 338
158 res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 339 res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
159 if (!res) 340 if (!res)
160 return -ENODEV; 341 return -ENODEV;
161 wdt_reg = devm_ioremap(&pdev->dev, res->start, resource_size(res));
162 if (!wdt_reg)
163 return -ENOMEM;
164 342
165 wdt_max_duration = WDT_MAX_CYCLE_COUNT / wdt_tclk; 343 dev->reg = devm_ioremap(&pdev->dev, res->start,
344 resource_size(res));
345 if (!dev->reg)
346 return -ENOMEM;
166 347
167 orion_wdt.timeout = wdt_max_duration; 348 dev->rstout = orion_wdt_ioremap_rstout(pdev, res->start &
168 orion_wdt.max_timeout = wdt_max_duration; 349 INTERNAL_REGS_MASK);
169 watchdog_init_timeout(&orion_wdt, heartbeat, &pdev->dev); 350 if (!dev->rstout)
351 return -ENODEV;
170 352
171 watchdog_set_nowayout(&orion_wdt, nowayout); 353 ret = dev->data->clock_init(pdev, dev);
172 ret = watchdog_register_device(&orion_wdt);
173 if (ret) { 354 if (ret) {
174 clk_disable_unprepare(clk); 355 dev_err(&pdev->dev, "cannot initialize clock\n");
175 return ret; 356 return ret;
176 } 357 }
177 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
178 pr_info("Initial timeout %d sec%s\n", 397 pr_info("Initial timeout %d sec%s\n",
179 orion_wdt.timeout, nowayout ? ", nowayout" : ""); 398 dev->wdt.timeout, nowayout ? ", nowayout" : "");
180 return 0; 399 return 0;
400
401disable_clk:
402 clk_disable_unprepare(dev->clk);
403 clk_put(dev->clk);
404 return ret;
181} 405}
182 406
183static int orion_wdt_remove(struct platform_device *pdev) 407static int orion_wdt_remove(struct platform_device *pdev)
184{ 408{
185 watchdog_unregister_device(&orion_wdt); 409 struct watchdog_device *wdt_dev = platform_get_drvdata(pdev);
186 clk_disable_unprepare(clk); 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);
187 return 0; 415 return 0;
188} 416}
189 417
190static void orion_wdt_shutdown(struct platform_device *pdev) 418static void orion_wdt_shutdown(struct platform_device *pdev)
191{ 419{
192 orion_wdt_stop(&orion_wdt); 420 struct watchdog_device *wdt_dev = platform_get_drvdata(pdev);
421 orion_wdt_stop(wdt_dev);
193} 422}
194 423
195static const struct of_device_id orion_wdt_of_match_table[] = {
196 { .compatible = "marvell,orion-wdt", },
197 {},
198};
199MODULE_DEVICE_TABLE(of, orion_wdt_of_match_table);
200
201static struct platform_driver orion_wdt_driver = { 424static struct platform_driver orion_wdt_driver = {
202 .probe = orion_wdt_probe, 425 .probe = orion_wdt_probe,
203 .remove = orion_wdt_remove, 426 .remove = orion_wdt_remove,