diff options
Diffstat (limited to 'drivers/watchdog/orion_wdt.c')
-rw-r--r-- | drivers/watchdog/orion_wdt.c | 213 |
1 files changed, 190 insertions, 23 deletions
diff --git a/drivers/watchdog/orion_wdt.c b/drivers/watchdog/orion_wdt.c index 9b3c41d18703..00d0741228fc 100644 --- a/drivers/watchdog/orion_wdt.c +++ b/drivers/watchdog/orion_wdt.c | |||
@@ -55,15 +55,19 @@ struct orion_watchdog_data { | |||
55 | int wdt_counter_offset; | 55 | int wdt_counter_offset; |
56 | int wdt_enable_bit; | 56 | int wdt_enable_bit; |
57 | int rstout_enable_bit; | 57 | int rstout_enable_bit; |
58 | int rstout_mask_bit; | ||
58 | int (*clock_init)(struct platform_device *, | 59 | int (*clock_init)(struct platform_device *, |
59 | struct orion_watchdog *); | 60 | struct orion_watchdog *); |
61 | int (*enabled)(struct orion_watchdog *); | ||
60 | int (*start)(struct watchdog_device *); | 62 | int (*start)(struct watchdog_device *); |
63 | int (*stop)(struct watchdog_device *); | ||
61 | }; | 64 | }; |
62 | 65 | ||
63 | struct orion_watchdog { | 66 | struct orion_watchdog { |
64 | struct watchdog_device wdt; | 67 | struct watchdog_device wdt; |
65 | void __iomem *reg; | 68 | void __iomem *reg; |
66 | void __iomem *rstout; | 69 | void __iomem *rstout; |
70 | void __iomem *rstout_mask; | ||
67 | unsigned long clk_rate; | 71 | unsigned long clk_rate; |
68 | struct clk *clk; | 72 | struct clk *clk; |
69 | const struct orion_watchdog_data *data; | 73 | const struct orion_watchdog_data *data; |
@@ -142,9 +146,35 @@ static int orion_wdt_ping(struct watchdog_device *wdt_dev) | |||
142 | return 0; | 146 | return 0; |
143 | } | 147 | } |
144 | 148 | ||
149 | static int armada375_start(struct watchdog_device *wdt_dev) | ||
150 | { | ||
151 | struct orion_watchdog *dev = watchdog_get_drvdata(wdt_dev); | ||
152 | u32 reg; | ||
153 | |||
154 | /* Set watchdog duration */ | ||
155 | writel(dev->clk_rate * wdt_dev->timeout, | ||
156 | dev->reg + dev->data->wdt_counter_offset); | ||
157 | |||
158 | /* Clear the watchdog expiration bit */ | ||
159 | atomic_io_modify(dev->reg + TIMER_A370_STATUS, WDT_A370_EXPIRED, 0); | ||
160 | |||
161 | /* Enable watchdog timer */ | ||
162 | atomic_io_modify(dev->reg + TIMER_CTRL, dev->data->wdt_enable_bit, | ||
163 | dev->data->wdt_enable_bit); | ||
164 | |||
165 | /* Enable reset on watchdog */ | ||
166 | reg = readl(dev->rstout); | ||
167 | reg |= dev->data->rstout_enable_bit; | ||
168 | writel(reg, dev->rstout); | ||
169 | |||
170 | atomic_io_modify(dev->rstout_mask, dev->data->rstout_mask_bit, 0); | ||
171 | return 0; | ||
172 | } | ||
173 | |||
145 | static int armada370_start(struct watchdog_device *wdt_dev) | 174 | static int armada370_start(struct watchdog_device *wdt_dev) |
146 | { | 175 | { |
147 | struct orion_watchdog *dev = watchdog_get_drvdata(wdt_dev); | 176 | struct orion_watchdog *dev = watchdog_get_drvdata(wdt_dev); |
177 | u32 reg; | ||
148 | 178 | ||
149 | /* Set watchdog duration */ | 179 | /* Set watchdog duration */ |
150 | writel(dev->clk_rate * wdt_dev->timeout, | 180 | writel(dev->clk_rate * wdt_dev->timeout, |
@@ -157,8 +187,10 @@ static int armada370_start(struct watchdog_device *wdt_dev) | |||
157 | atomic_io_modify(dev->reg + TIMER_CTRL, dev->data->wdt_enable_bit, | 187 | atomic_io_modify(dev->reg + TIMER_CTRL, dev->data->wdt_enable_bit, |
158 | dev->data->wdt_enable_bit); | 188 | dev->data->wdt_enable_bit); |
159 | 189 | ||
160 | atomic_io_modify(dev->rstout, dev->data->rstout_enable_bit, | 190 | /* Enable reset on watchdog */ |
161 | dev->data->rstout_enable_bit); | 191 | reg = readl(dev->rstout); |
192 | reg |= dev->data->rstout_enable_bit; | ||
193 | writel(reg, dev->rstout); | ||
162 | return 0; | 194 | return 0; |
163 | } | 195 | } |
164 | 196 | ||
@@ -189,7 +221,7 @@ static int orion_wdt_start(struct watchdog_device *wdt_dev) | |||
189 | return dev->data->start(wdt_dev); | 221 | return dev->data->start(wdt_dev); |
190 | } | 222 | } |
191 | 223 | ||
192 | static int orion_wdt_stop(struct watchdog_device *wdt_dev) | 224 | static int orion_stop(struct watchdog_device *wdt_dev) |
193 | { | 225 | { |
194 | struct orion_watchdog *dev = watchdog_get_drvdata(wdt_dev); | 226 | struct orion_watchdog *dev = watchdog_get_drvdata(wdt_dev); |
195 | 227 | ||
@@ -202,7 +234,48 @@ static int orion_wdt_stop(struct watchdog_device *wdt_dev) | |||
202 | return 0; | 234 | return 0; |
203 | } | 235 | } |
204 | 236 | ||
205 | static int orion_wdt_enabled(struct orion_watchdog *dev) | 237 | static int armada375_stop(struct watchdog_device *wdt_dev) |
238 | { | ||
239 | struct orion_watchdog *dev = watchdog_get_drvdata(wdt_dev); | ||
240 | u32 reg; | ||
241 | |||
242 | /* Disable reset on watchdog */ | ||
243 | atomic_io_modify(dev->rstout_mask, dev->data->rstout_mask_bit, | ||
244 | dev->data->rstout_mask_bit); | ||
245 | reg = readl(dev->rstout); | ||
246 | reg &= ~dev->data->rstout_enable_bit; | ||
247 | writel(reg, dev->rstout); | ||
248 | |||
249 | /* Disable watchdog timer */ | ||
250 | atomic_io_modify(dev->reg + TIMER_CTRL, dev->data->wdt_enable_bit, 0); | ||
251 | |||
252 | return 0; | ||
253 | } | ||
254 | |||
255 | static int armada370_stop(struct watchdog_device *wdt_dev) | ||
256 | { | ||
257 | struct orion_watchdog *dev = watchdog_get_drvdata(wdt_dev); | ||
258 | u32 reg; | ||
259 | |||
260 | /* Disable reset on watchdog */ | ||
261 | reg = readl(dev->rstout); | ||
262 | reg &= ~dev->data->rstout_enable_bit; | ||
263 | writel(reg, dev->rstout); | ||
264 | |||
265 | /* Disable watchdog timer */ | ||
266 | atomic_io_modify(dev->reg + TIMER_CTRL, dev->data->wdt_enable_bit, 0); | ||
267 | |||
268 | return 0; | ||
269 | } | ||
270 | |||
271 | static int orion_wdt_stop(struct watchdog_device *wdt_dev) | ||
272 | { | ||
273 | struct orion_watchdog *dev = watchdog_get_drvdata(wdt_dev); | ||
274 | |||
275 | return dev->data->stop(wdt_dev); | ||
276 | } | ||
277 | |||
278 | static int orion_enabled(struct orion_watchdog *dev) | ||
206 | { | 279 | { |
207 | bool enabled, running; | 280 | bool enabled, running; |
208 | 281 | ||
@@ -212,6 +285,24 @@ static int orion_wdt_enabled(struct orion_watchdog *dev) | |||
212 | return enabled && running; | 285 | return enabled && running; |
213 | } | 286 | } |
214 | 287 | ||
288 | static int armada375_enabled(struct orion_watchdog *dev) | ||
289 | { | ||
290 | bool masked, enabled, running; | ||
291 | |||
292 | masked = readl(dev->rstout_mask) & dev->data->rstout_mask_bit; | ||
293 | enabled = readl(dev->rstout) & dev->data->rstout_enable_bit; | ||
294 | running = readl(dev->reg + TIMER_CTRL) & dev->data->wdt_enable_bit; | ||
295 | |||
296 | return !masked && enabled && running; | ||
297 | } | ||
298 | |||
299 | static int orion_wdt_enabled(struct watchdog_device *wdt_dev) | ||
300 | { | ||
301 | struct orion_watchdog *dev = watchdog_get_drvdata(wdt_dev); | ||
302 | |||
303 | return dev->data->enabled(dev); | ||
304 | } | ||
305 | |||
215 | static unsigned int orion_wdt_get_timeleft(struct watchdog_device *wdt_dev) | 306 | static unsigned int orion_wdt_get_timeleft(struct watchdog_device *wdt_dev) |
216 | { | 307 | { |
217 | struct orion_watchdog *dev = watchdog_get_drvdata(wdt_dev); | 308 | struct orion_watchdog *dev = watchdog_get_drvdata(wdt_dev); |
@@ -262,10 +353,6 @@ static void __iomem *orion_wdt_ioremap_rstout(struct platform_device *pdev, | |||
262 | return devm_ioremap(&pdev->dev, res->start, | 353 | return devm_ioremap(&pdev->dev, res->start, |
263 | resource_size(res)); | 354 | resource_size(res)); |
264 | 355 | ||
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; | 356 | rstout = internal_regs + ORION_RSTOUT_MASK_OFFSET; |
270 | 357 | ||
271 | WARN(1, FW_BUG "falling back to harcoded RSTOUT reg %pa\n", &rstout); | 358 | WARN(1, FW_BUG "falling back to harcoded RSTOUT reg %pa\n", &rstout); |
@@ -277,7 +364,9 @@ static const struct orion_watchdog_data orion_data = { | |||
277 | .wdt_enable_bit = BIT(4), | 364 | .wdt_enable_bit = BIT(4), |
278 | .wdt_counter_offset = 0x24, | 365 | .wdt_counter_offset = 0x24, |
279 | .clock_init = orion_wdt_clock_init, | 366 | .clock_init = orion_wdt_clock_init, |
367 | .enabled = orion_enabled, | ||
280 | .start = orion_start, | 368 | .start = orion_start, |
369 | .stop = orion_stop, | ||
281 | }; | 370 | }; |
282 | 371 | ||
283 | static const struct orion_watchdog_data armada370_data = { | 372 | static const struct orion_watchdog_data armada370_data = { |
@@ -285,7 +374,9 @@ static const struct orion_watchdog_data armada370_data = { | |||
285 | .wdt_enable_bit = BIT(8), | 374 | .wdt_enable_bit = BIT(8), |
286 | .wdt_counter_offset = 0x34, | 375 | .wdt_counter_offset = 0x34, |
287 | .clock_init = armada370_wdt_clock_init, | 376 | .clock_init = armada370_wdt_clock_init, |
377 | .enabled = orion_enabled, | ||
288 | .start = armada370_start, | 378 | .start = armada370_start, |
379 | .stop = armada370_stop, | ||
289 | }; | 380 | }; |
290 | 381 | ||
291 | static const struct orion_watchdog_data armadaxp_data = { | 382 | static const struct orion_watchdog_data armadaxp_data = { |
@@ -293,7 +384,31 @@ static const struct orion_watchdog_data armadaxp_data = { | |||
293 | .wdt_enable_bit = BIT(8), | 384 | .wdt_enable_bit = BIT(8), |
294 | .wdt_counter_offset = 0x34, | 385 | .wdt_counter_offset = 0x34, |
295 | .clock_init = armadaxp_wdt_clock_init, | 386 | .clock_init = armadaxp_wdt_clock_init, |
387 | .enabled = orion_enabled, | ||
296 | .start = armada370_start, | 388 | .start = armada370_start, |
389 | .stop = armada370_stop, | ||
390 | }; | ||
391 | |||
392 | static const struct orion_watchdog_data armada375_data = { | ||
393 | .rstout_enable_bit = BIT(8), | ||
394 | .rstout_mask_bit = BIT(10), | ||
395 | .wdt_enable_bit = BIT(8), | ||
396 | .wdt_counter_offset = 0x34, | ||
397 | .clock_init = armada370_wdt_clock_init, | ||
398 | .enabled = armada375_enabled, | ||
399 | .start = armada375_start, | ||
400 | .stop = armada375_stop, | ||
401 | }; | ||
402 | |||
403 | static const struct orion_watchdog_data armada380_data = { | ||
404 | .rstout_enable_bit = BIT(8), | ||
405 | .rstout_mask_bit = BIT(10), | ||
406 | .wdt_enable_bit = BIT(8), | ||
407 | .wdt_counter_offset = 0x34, | ||
408 | .clock_init = armadaxp_wdt_clock_init, | ||
409 | .enabled = armada375_enabled, | ||
410 | .start = armada375_start, | ||
411 | .stop = armada375_stop, | ||
297 | }; | 412 | }; |
298 | 413 | ||
299 | static const struct of_device_id orion_wdt_of_match_table[] = { | 414 | static const struct of_device_id orion_wdt_of_match_table[] = { |
@@ -309,16 +424,78 @@ static const struct of_device_id orion_wdt_of_match_table[] = { | |||
309 | .compatible = "marvell,armada-xp-wdt", | 424 | .compatible = "marvell,armada-xp-wdt", |
310 | .data = &armadaxp_data, | 425 | .data = &armadaxp_data, |
311 | }, | 426 | }, |
427 | { | ||
428 | .compatible = "marvell,armada-375-wdt", | ||
429 | .data = &armada375_data, | ||
430 | }, | ||
431 | { | ||
432 | .compatible = "marvell,armada-380-wdt", | ||
433 | .data = &armada380_data, | ||
434 | }, | ||
312 | {}, | 435 | {}, |
313 | }; | 436 | }; |
314 | MODULE_DEVICE_TABLE(of, orion_wdt_of_match_table); | 437 | MODULE_DEVICE_TABLE(of, orion_wdt_of_match_table); |
315 | 438 | ||
439 | static int orion_wdt_get_regs(struct platform_device *pdev, | ||
440 | struct orion_watchdog *dev) | ||
441 | { | ||
442 | struct device_node *node = pdev->dev.of_node; | ||
443 | struct resource *res; | ||
444 | |||
445 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||
446 | if (!res) | ||
447 | return -ENODEV; | ||
448 | dev->reg = devm_ioremap(&pdev->dev, res->start, | ||
449 | resource_size(res)); | ||
450 | if (!dev->reg) | ||
451 | return -ENOMEM; | ||
452 | |||
453 | /* Each supported compatible has some RSTOUT register quirk */ | ||
454 | if (of_device_is_compatible(node, "marvell,orion-wdt")) { | ||
455 | |||
456 | dev->rstout = orion_wdt_ioremap_rstout(pdev, res->start & | ||
457 | INTERNAL_REGS_MASK); | ||
458 | if (!dev->rstout) | ||
459 | return -ENODEV; | ||
460 | |||
461 | } else if (of_device_is_compatible(node, "marvell,armada-370-wdt") || | ||
462 | of_device_is_compatible(node, "marvell,armada-xp-wdt")) { | ||
463 | |||
464 | /* Dedicated RSTOUT register, can be requested. */ | ||
465 | res = platform_get_resource(pdev, IORESOURCE_MEM, 1); | ||
466 | dev->rstout = devm_ioremap_resource(&pdev->dev, res); | ||
467 | if (IS_ERR(dev->rstout)) | ||
468 | return PTR_ERR(dev->rstout); | ||
469 | |||
470 | } else if (of_device_is_compatible(node, "marvell,armada-375-wdt") || | ||
471 | of_device_is_compatible(node, "marvell,armada-380-wdt")) { | ||
472 | |||
473 | /* Dedicated RSTOUT register, can be requested. */ | ||
474 | res = platform_get_resource(pdev, IORESOURCE_MEM, 1); | ||
475 | dev->rstout = devm_ioremap_resource(&pdev->dev, res); | ||
476 | if (IS_ERR(dev->rstout)) | ||
477 | return PTR_ERR(dev->rstout); | ||
478 | |||
479 | res = platform_get_resource(pdev, IORESOURCE_MEM, 2); | ||
480 | if (!res) | ||
481 | return -ENODEV; | ||
482 | dev->rstout_mask = devm_ioremap(&pdev->dev, res->start, | ||
483 | resource_size(res)); | ||
484 | if (!dev->rstout_mask) | ||
485 | return -ENOMEM; | ||
486 | |||
487 | } else { | ||
488 | return -ENODEV; | ||
489 | } | ||
490 | |||
491 | return 0; | ||
492 | } | ||
493 | |||
316 | static int orion_wdt_probe(struct platform_device *pdev) | 494 | static int orion_wdt_probe(struct platform_device *pdev) |
317 | { | 495 | { |
318 | struct orion_watchdog *dev; | 496 | struct orion_watchdog *dev; |
319 | const struct of_device_id *match; | 497 | const struct of_device_id *match; |
320 | unsigned int wdt_max_duration; /* (seconds) */ | 498 | unsigned int wdt_max_duration; /* (seconds) */ |
321 | struct resource *res; | ||
322 | int ret, irq; | 499 | int ret, irq; |
323 | 500 | ||
324 | dev = devm_kzalloc(&pdev->dev, sizeof(struct orion_watchdog), | 501 | dev = devm_kzalloc(&pdev->dev, sizeof(struct orion_watchdog), |
@@ -336,19 +513,9 @@ static int orion_wdt_probe(struct platform_device *pdev) | |||
336 | dev->wdt.min_timeout = 1; | 513 | dev->wdt.min_timeout = 1; |
337 | dev->data = match->data; | 514 | dev->data = match->data; |
338 | 515 | ||
339 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | 516 | ret = orion_wdt_get_regs(pdev, dev); |
340 | if (!res) | 517 | if (ret) |
341 | return -ENODEV; | 518 | return ret; |
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 | 519 | ||
353 | ret = dev->data->clock_init(pdev, dev); | 520 | ret = dev->data->clock_init(pdev, dev); |
354 | if (ret) { | 521 | if (ret) { |
@@ -371,7 +538,7 @@ static int orion_wdt_probe(struct platform_device *pdev) | |||
371 | * removed and re-insterted, or if the bootloader explicitly | 538 | * removed and re-insterted, or if the bootloader explicitly |
372 | * set a running watchdog before booting the kernel. | 539 | * set a running watchdog before booting the kernel. |
373 | */ | 540 | */ |
374 | if (!orion_wdt_enabled(dev)) | 541 | if (!orion_wdt_enabled(&dev->wdt)) |
375 | orion_wdt_stop(&dev->wdt); | 542 | orion_wdt_stop(&dev->wdt); |
376 | 543 | ||
377 | /* Request the IRQ only after the watchdog is disabled */ | 544 | /* Request the IRQ only after the watchdog is disabled */ |