aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/watchdog
diff options
context:
space:
mode:
authorLeela Krishna Amudala <l.krishna@samsung.com>2013-12-06 00:47:47 -0500
committerWim Van Sebroeck <wim@iguana.be>2014-01-28 15:24:15 -0500
commit4f1f653a68d67ca5732fdd7ee4deb290a3ea5149 (patch)
tree493c871579b06084d9eaf0c2ef19495d1ae3ec85 /drivers/watchdog
parent178624403c04a4af315583eaf75ebb8545474544 (diff)
watchdog: s3c2410_wdt: use syscon regmap interface to configure pmu register
Add device tree support for exynos5250 and 5420 SoCs and use syscon regmap interface to configure AUTOMATIC_WDT_RESET_DISABLE and MASK_WDT_RESET_REQUEST registers of PMU to mask/unmask enable/disable of watchdog in probe and s2r scenarios. Signed-off-by: Leela Krishna Amudala <l.krishna@samsung.com> Signed-off-by: Doug Anderson <dianders@chromium.org> Reviewed-by: Guenter Roeck <linux@roeck-us.net> Signed-off-by: Wim Van Sebroeck <wim@iguana.be>
Diffstat (limited to 'drivers/watchdog')
-rw-r--r--drivers/watchdog/Kconfig1
-rw-r--r--drivers/watchdog/s3c2410_wdt.c154
2 files changed, 146 insertions, 9 deletions
diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig
index 7a2fedab0fb5..c11a3387335d 100644
--- a/drivers/watchdog/Kconfig
+++ b/drivers/watchdog/Kconfig
@@ -196,6 +196,7 @@ config S3C2410_WATCHDOG
196 tristate "S3C2410 Watchdog" 196 tristate "S3C2410 Watchdog"
197 depends on HAVE_S3C2410_WATCHDOG 197 depends on HAVE_S3C2410_WATCHDOG
198 select WATCHDOG_CORE 198 select WATCHDOG_CORE
199 select MFD_SYSCON if ARCH_EXYNOS5
199 help 200 help
200 Watchdog timer block in the Samsung SoCs. This will reboot 201 Watchdog timer block in the Samsung SoCs. This will reboot
201 the system when the timer expires with the watchdog enabled. 202 the system when the timer expires with the watchdog enabled.
diff --git a/drivers/watchdog/s3c2410_wdt.c b/drivers/watchdog/s3c2410_wdt.c
index 8beaa1799df6..64f5470a6a0e 100644
--- a/drivers/watchdog/s3c2410_wdt.c
+++ b/drivers/watchdog/s3c2410_wdt.c
@@ -40,6 +40,8 @@
40#include <linux/slab.h> 40#include <linux/slab.h>
41#include <linux/err.h> 41#include <linux/err.h>
42#include <linux/of.h> 42#include <linux/of.h>
43#include <linux/mfd/syscon.h>
44#include <linux/regmap.h>
43 45
44#define S3C2410_WTCON 0x00 46#define S3C2410_WTCON 0x00
45#define S3C2410_WTDAT 0x04 47#define S3C2410_WTDAT 0x04
@@ -60,6 +62,10 @@
60#define CONFIG_S3C2410_WATCHDOG_ATBOOT (0) 62#define CONFIG_S3C2410_WATCHDOG_ATBOOT (0)
61#define CONFIG_S3C2410_WATCHDOG_DEFAULT_TIME (15) 63#define CONFIG_S3C2410_WATCHDOG_DEFAULT_TIME (15)
62 64
65#define EXYNOS5_WDT_DISABLE_REG_OFFSET 0x0408
66#define EXYNOS5_WDT_MASK_RESET_REG_OFFSET 0x040c
67#define QUIRK_HAS_PMU_CONFIG (1 << 0)
68
63static bool nowayout = WATCHDOG_NOWAYOUT; 69static bool nowayout = WATCHDOG_NOWAYOUT;
64static int tmr_margin; 70static int tmr_margin;
65static int tmr_atboot = CONFIG_S3C2410_WATCHDOG_ATBOOT; 71static int tmr_atboot = CONFIG_S3C2410_WATCHDOG_ATBOOT;
@@ -83,6 +89,25 @@ MODULE_PARM_DESC(soft_noboot, "Watchdog action, set to 1 to ignore reboots, "
83 "0 to reboot (default 0)"); 89 "0 to reboot (default 0)");
84MODULE_PARM_DESC(debug, "Watchdog debug, set to >1 for debug (default 0)"); 90MODULE_PARM_DESC(debug, "Watchdog debug, set to >1 for debug (default 0)");
85 91
92/**
93 * struct s3c2410_wdt_variant - Per-variant config data
94 *
95 * @disable_reg: Offset in pmureg for the register that disables the watchdog
96 * timer reset functionality.
97 * @mask_reset_reg: Offset in pmureg for the register that masks the watchdog
98 * timer reset functionality.
99 * @mask_bit: Bit number for the watchdog timer in the disable register and the
100 * mask reset register.
101 * @quirks: A bitfield of quirks.
102 */
103
104struct s3c2410_wdt_variant {
105 int disable_reg;
106 int mask_reset_reg;
107 int mask_bit;
108 u32 quirks;
109};
110
86struct s3c2410_wdt { 111struct s3c2410_wdt {
87 struct device *dev; 112 struct device *dev;
88 struct clk *clock; 113 struct clk *clock;
@@ -93,7 +118,49 @@ struct s3c2410_wdt {
93 unsigned long wtdat_save; 118 unsigned long wtdat_save;
94 struct watchdog_device wdt_device; 119 struct watchdog_device wdt_device;
95 struct notifier_block freq_transition; 120 struct notifier_block freq_transition;
121 struct s3c2410_wdt_variant *drv_data;
122 struct regmap *pmureg;
123};
124
125static const struct s3c2410_wdt_variant drv_data_s3c2410 = {
126 .quirks = 0
127};
128
129#ifdef CONFIG_OF
130static const struct s3c2410_wdt_variant drv_data_exynos5250 = {
131 .disable_reg = EXYNOS5_WDT_DISABLE_REG_OFFSET,
132 .mask_reset_reg = EXYNOS5_WDT_MASK_RESET_REG_OFFSET,
133 .mask_bit = 20,
134 .quirks = QUIRK_HAS_PMU_CONFIG
135};
136
137static const struct s3c2410_wdt_variant drv_data_exynos5420 = {
138 .disable_reg = EXYNOS5_WDT_DISABLE_REG_OFFSET,
139 .mask_reset_reg = EXYNOS5_WDT_MASK_RESET_REG_OFFSET,
140 .mask_bit = 0,
141 .quirks = QUIRK_HAS_PMU_CONFIG
142};
143
144static const struct of_device_id s3c2410_wdt_match[] = {
145 { .compatible = "samsung,s3c2410-wdt",
146 .data = &drv_data_s3c2410 },
147 { .compatible = "samsung,exynos5250-wdt",
148 .data = &drv_data_exynos5250 },
149 { .compatible = "samsung,exynos5420-wdt",
150 .data = &drv_data_exynos5420 },
151 {},
96}; 152};
153MODULE_DEVICE_TABLE(of, s3c2410_wdt_match);
154#endif
155
156static const struct platform_device_id s3c2410_wdt_ids[] = {
157 {
158 .name = "s3c2410-wdt",
159 .driver_data = (unsigned long)&drv_data_s3c2410,
160 },
161 {}
162};
163MODULE_DEVICE_TABLE(platform, s3c2410_wdt_ids);
97 164
98/* watchdog control routines */ 165/* watchdog control routines */
99 166
@@ -110,6 +177,35 @@ static inline struct s3c2410_wdt *freq_to_wdt(struct notifier_block *nb)
110 return container_of(nb, struct s3c2410_wdt, freq_transition); 177 return container_of(nb, struct s3c2410_wdt, freq_transition);
111} 178}
112 179
180static int s3c2410wdt_mask_and_disable_reset(struct s3c2410_wdt *wdt, bool mask)
181{
182 int ret;
183 u32 mask_val = 1 << wdt->drv_data->mask_bit;
184 u32 val = 0;
185
186 /* No need to do anything if no PMU CONFIG needed */
187 if (!(wdt->drv_data->quirks & QUIRK_HAS_PMU_CONFIG))
188 return 0;
189
190 if (mask)
191 val = mask_val;
192
193 ret = regmap_update_bits(wdt->pmureg,
194 wdt->drv_data->disable_reg,
195 mask_val, val);
196 if (ret < 0)
197 goto error;
198
199 ret = regmap_update_bits(wdt->pmureg,
200 wdt->drv_data->mask_reset_reg,
201 mask_val, val);
202 error:
203 if (ret < 0)
204 dev_err(wdt->dev, "failed to update reg(%d)\n", ret);
205
206 return ret;
207}
208
113static int s3c2410wdt_keepalive(struct watchdog_device *wdd) 209static int s3c2410wdt_keepalive(struct watchdog_device *wdd)
114{ 210{
115 struct s3c2410_wdt *wdt = watchdog_get_drvdata(wdd); 211 struct s3c2410_wdt *wdt = watchdog_get_drvdata(wdd);
@@ -328,6 +424,20 @@ static inline void s3c2410wdt_cpufreq_deregister(struct s3c2410_wdt *wdt)
328} 424}
329#endif 425#endif
330 426
427/* s3c2410_get_wdt_driver_data */
428static inline struct s3c2410_wdt_variant *
429get_wdt_drv_data(struct platform_device *pdev)
430{
431 if (pdev->dev.of_node) {
432 const struct of_device_id *match;
433 match = of_match_node(s3c2410_wdt_match, pdev->dev.of_node);
434 return (struct s3c2410_wdt_variant *)match->data;
435 } else {
436 return (struct s3c2410_wdt_variant *)
437 platform_get_device_id(pdev)->driver_data;
438 }
439}
440
331static int s3c2410wdt_probe(struct platform_device *pdev) 441static int s3c2410wdt_probe(struct platform_device *pdev)
332{ 442{
333 struct device *dev; 443 struct device *dev;
@@ -350,6 +460,16 @@ static int s3c2410wdt_probe(struct platform_device *pdev)
350 spin_lock_init(&wdt->lock); 460 spin_lock_init(&wdt->lock);
351 wdt->wdt_device = s3c2410_wdd; 461 wdt->wdt_device = s3c2410_wdd;
352 462
463 wdt->drv_data = get_wdt_drv_data(pdev);
464 if (wdt->drv_data->quirks & QUIRK_HAS_PMU_CONFIG) {
465 wdt->pmureg = syscon_regmap_lookup_by_phandle(dev->of_node,
466 "samsung,syscon-phandle");
467 if (IS_ERR(wdt->pmureg)) {
468 dev_err(dev, "syscon regmap lookup failed.\n");
469 return PTR_ERR(wdt->pmureg);
470 }
471 }
472
353 wdt_irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0); 473 wdt_irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
354 if (wdt_irq == NULL) { 474 if (wdt_irq == NULL) {
355 dev_err(dev, "no irq resource specified\n"); 475 dev_err(dev, "no irq resource specified\n");
@@ -418,6 +538,10 @@ static int s3c2410wdt_probe(struct platform_device *pdev)
418 goto err_cpufreq; 538 goto err_cpufreq;
419 } 539 }
420 540
541 ret = s3c2410wdt_mask_and_disable_reset(wdt, false);
542 if (ret < 0)
543 goto err_unregister;
544
421 if (tmr_atboot && started == 0) { 545 if (tmr_atboot && started == 0) {
422 dev_info(dev, "starting watchdog timer\n"); 546 dev_info(dev, "starting watchdog timer\n");
423 s3c2410wdt_start(&wdt->wdt_device); 547 s3c2410wdt_start(&wdt->wdt_device);
@@ -442,6 +566,9 @@ static int s3c2410wdt_probe(struct platform_device *pdev)
442 566
443 return 0; 567 return 0;
444 568
569 err_unregister:
570 watchdog_unregister_device(&wdt->wdt_device);
571
445 err_cpufreq: 572 err_cpufreq:
446 s3c2410wdt_cpufreq_deregister(wdt); 573 s3c2410wdt_cpufreq_deregister(wdt);
447 574
@@ -455,8 +582,13 @@ static int s3c2410wdt_probe(struct platform_device *pdev)
455 582
456static int s3c2410wdt_remove(struct platform_device *dev) 583static int s3c2410wdt_remove(struct platform_device *dev)
457{ 584{
585 int ret;
458 struct s3c2410_wdt *wdt = platform_get_drvdata(dev); 586 struct s3c2410_wdt *wdt = platform_get_drvdata(dev);
459 587
588 ret = s3c2410wdt_mask_and_disable_reset(wdt, true);
589 if (ret < 0)
590 return ret;
591
460 watchdog_unregister_device(&wdt->wdt_device); 592 watchdog_unregister_device(&wdt->wdt_device);
461 593
462 s3c2410wdt_cpufreq_deregister(wdt); 594 s3c2410wdt_cpufreq_deregister(wdt);
@@ -471,6 +603,8 @@ static void s3c2410wdt_shutdown(struct platform_device *dev)
471{ 603{
472 struct s3c2410_wdt *wdt = platform_get_drvdata(dev); 604 struct s3c2410_wdt *wdt = platform_get_drvdata(dev);
473 605
606 s3c2410wdt_mask_and_disable_reset(wdt, true);
607
474 s3c2410wdt_stop(&wdt->wdt_device); 608 s3c2410wdt_stop(&wdt->wdt_device);
475} 609}
476 610
@@ -478,12 +612,17 @@ static void s3c2410wdt_shutdown(struct platform_device *dev)
478 612
479static int s3c2410wdt_suspend(struct device *dev) 613static int s3c2410wdt_suspend(struct device *dev)
480{ 614{
615 int ret;
481 struct s3c2410_wdt *wdt = dev_get_drvdata(dev); 616 struct s3c2410_wdt *wdt = dev_get_drvdata(dev);
482 617
483 /* Save watchdog state, and turn it off. */ 618 /* Save watchdog state, and turn it off. */
484 wdt->wtcon_save = readl(wdt->reg_base + S3C2410_WTCON); 619 wdt->wtcon_save = readl(wdt->reg_base + S3C2410_WTCON);
485 wdt->wtdat_save = readl(wdt->reg_base + S3C2410_WTDAT); 620 wdt->wtdat_save = readl(wdt->reg_base + S3C2410_WTDAT);
486 621
622 ret = s3c2410wdt_mask_and_disable_reset(wdt, true);
623 if (ret < 0)
624 return ret;
625
487 /* Note that WTCNT doesn't need to be saved. */ 626 /* Note that WTCNT doesn't need to be saved. */
488 s3c2410wdt_stop(&wdt->wdt_device); 627 s3c2410wdt_stop(&wdt->wdt_device);
489 628
@@ -492,6 +631,7 @@ static int s3c2410wdt_suspend(struct device *dev)
492 631
493static int s3c2410wdt_resume(struct device *dev) 632static int s3c2410wdt_resume(struct device *dev)
494{ 633{
634 int ret;
495 struct s3c2410_wdt *wdt = dev_get_drvdata(dev); 635 struct s3c2410_wdt *wdt = dev_get_drvdata(dev);
496 636
497 /* Restore watchdog state. */ 637 /* Restore watchdog state. */
@@ -499,6 +639,10 @@ static int s3c2410wdt_resume(struct device *dev)
499 writel(wdt->wtdat_save, wdt->reg_base + S3C2410_WTCNT);/* Reset count */ 639 writel(wdt->wtdat_save, wdt->reg_base + S3C2410_WTCNT);/* Reset count */
500 writel(wdt->wtcon_save, wdt->reg_base + S3C2410_WTCON); 640 writel(wdt->wtcon_save, wdt->reg_base + S3C2410_WTCON);
501 641
642 ret = s3c2410wdt_mask_and_disable_reset(wdt, false);
643 if (ret < 0)
644 return ret;
645
502 dev_info(dev, "watchdog %sabled\n", 646 dev_info(dev, "watchdog %sabled\n",
503 (wdt->wtcon_save & S3C2410_WTCON_ENABLE) ? "en" : "dis"); 647 (wdt->wtcon_save & S3C2410_WTCON_ENABLE) ? "en" : "dis");
504 648
@@ -509,18 +653,11 @@ static int s3c2410wdt_resume(struct device *dev)
509static SIMPLE_DEV_PM_OPS(s3c2410wdt_pm_ops, s3c2410wdt_suspend, 653static SIMPLE_DEV_PM_OPS(s3c2410wdt_pm_ops, s3c2410wdt_suspend,
510 s3c2410wdt_resume); 654 s3c2410wdt_resume);
511 655
512#ifdef CONFIG_OF
513static const struct of_device_id s3c2410_wdt_match[] = {
514 { .compatible = "samsung,s3c2410-wdt" },
515 {},
516};
517MODULE_DEVICE_TABLE(of, s3c2410_wdt_match);
518#endif
519
520static struct platform_driver s3c2410wdt_driver = { 656static struct platform_driver s3c2410wdt_driver = {
521 .probe = s3c2410wdt_probe, 657 .probe = s3c2410wdt_probe,
522 .remove = s3c2410wdt_remove, 658 .remove = s3c2410wdt_remove,
523 .shutdown = s3c2410wdt_shutdown, 659 .shutdown = s3c2410wdt_shutdown,
660 .id_table = s3c2410_wdt_ids,
524 .driver = { 661 .driver = {
525 .owner = THIS_MODULE, 662 .owner = THIS_MODULE,
526 .name = "s3c2410-wdt", 663 .name = "s3c2410-wdt",
@@ -535,4 +672,3 @@ MODULE_AUTHOR("Ben Dooks <ben@simtec.co.uk>, "
535 "Dimitry Andric <dimitry.andric@tomtom.com>"); 672 "Dimitry Andric <dimitry.andric@tomtom.com>");
536MODULE_DESCRIPTION("S3C2410 Watchdog Device Driver"); 673MODULE_DESCRIPTION("S3C2410 Watchdog Device Driver");
537MODULE_LICENSE("GPL"); 674MODULE_LICENSE("GPL");
538MODULE_ALIAS("platform:s3c2410-wdt");