Commit | Line | Data |
---|---|---|
0376148f | 1 | // SPDX-License-Identifier: GPL-2.0-only |
f0e5bd41 FB |
2 | /* |
3 | * Copyright (C) ST-Ericsson SA 2011-2013 | |
4 | * | |
f0e5bd41 FB |
5 | * Author: Mathieu Poirier <mathieu.poirier@linaro.org> for ST-Ericsson |
6 | * Author: Jonas Aaberg <jonas.aberg@stericsson.com> for ST-Ericsson | |
7 | */ | |
8 | ||
9 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt | |
10 | ||
11 | #include <linux/module.h> | |
12 | #include <linux/kernel.h> | |
13 | #include <linux/moduleparam.h> | |
f0e5bd41 FB |
14 | #include <linux/err.h> |
15 | #include <linux/uaccess.h> | |
16 | #include <linux/watchdog.h> | |
17 | #include <linux/platform_device.h> | |
f0e5bd41 FB |
18 | |
19 | #include <linux/mfd/dbx500-prcmu.h> | |
20 | ||
21 | #define WATCHDOG_TIMEOUT 600 /* 10 minutes */ | |
22 | ||
23 | #define WATCHDOG_MIN 0 | |
24 | #define WATCHDOG_MAX28 268435 /* 28 bit resolution in ms == 268435.455 s */ | |
f0e5bd41 FB |
25 | |
26 | static unsigned int timeout = WATCHDOG_TIMEOUT; | |
27 | module_param(timeout, uint, 0); | |
28 | MODULE_PARM_DESC(timeout, | |
29 | "Watchdog timeout in seconds. default=" | |
30 | __MODULE_STRING(WATCHDOG_TIMEOUT) "."); | |
31 | ||
32 | static bool nowayout = WATCHDOG_NOWAYOUT; | |
33 | module_param(nowayout, bool, 0); | |
34 | MODULE_PARM_DESC(nowayout, | |
35 | "Watchdog cannot be stopped once started (default=" | |
36 | __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); | |
37 | ||
c7388880 | 38 | static int db8500_wdt_start(struct watchdog_device *wdd) |
f0e5bd41 FB |
39 | { |
40 | return prcmu_enable_a9wdog(PRCMU_WDOG_ALL); | |
41 | } | |
42 | ||
c7388880 | 43 | static int db8500_wdt_stop(struct watchdog_device *wdd) |
f0e5bd41 FB |
44 | { |
45 | return prcmu_disable_a9wdog(PRCMU_WDOG_ALL); | |
46 | } | |
47 | ||
c7388880 | 48 | static int db8500_wdt_keepalive(struct watchdog_device *wdd) |
f0e5bd41 FB |
49 | { |
50 | return prcmu_kick_a9wdog(PRCMU_WDOG_ALL); | |
51 | } | |
52 | ||
c7388880 | 53 | static int db8500_wdt_set_timeout(struct watchdog_device *wdd, |
f0e5bd41 FB |
54 | unsigned int timeout) |
55 | { | |
c7388880 | 56 | db8500_wdt_stop(wdd); |
f0e5bd41 | 57 | prcmu_load_a9wdog(PRCMU_WDOG_ALL, timeout * 1000); |
c7388880 | 58 | db8500_wdt_start(wdd); |
f0e5bd41 FB |
59 | |
60 | return 0; | |
61 | } | |
62 | ||
c7388880 | 63 | static const struct watchdog_info db8500_wdt_info = { |
f0e5bd41 | 64 | .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE, |
c7388880 | 65 | .identity = "DB8500 WDT", |
f0e5bd41 FB |
66 | .firmware_version = 1, |
67 | }; | |
68 | ||
c7388880 | 69 | static const struct watchdog_ops db8500_wdt_ops = { |
f0e5bd41 | 70 | .owner = THIS_MODULE, |
c7388880 LW |
71 | .start = db8500_wdt_start, |
72 | .stop = db8500_wdt_stop, | |
73 | .ping = db8500_wdt_keepalive, | |
74 | .set_timeout = db8500_wdt_set_timeout, | |
f0e5bd41 FB |
75 | }; |
76 | ||
c7388880 LW |
77 | static struct watchdog_device db8500_wdt = { |
78 | .info = &db8500_wdt_info, | |
79 | .ops = &db8500_wdt_ops, | |
f0e5bd41 | 80 | .min_timeout = WATCHDOG_MIN, |
74128d80 | 81 | .max_timeout = WATCHDOG_MAX28, |
f0e5bd41 FB |
82 | }; |
83 | ||
c7388880 | 84 | static int db8500_wdt_probe(struct platform_device *pdev) |
f0e5bd41 | 85 | { |
89e5b7ac | 86 | struct device *dev = &pdev->dev; |
f0e5bd41 | 87 | int ret; |
f0e5bd41 | 88 | |
74128d80 | 89 | timeout = 600; /* Default to 10 minutes */ |
c7388880 LW |
90 | db8500_wdt.parent = dev; |
91 | watchdog_set_nowayout(&db8500_wdt, nowayout); | |
f0e5bd41 FB |
92 | |
93 | /* disable auto off on sleep */ | |
94 | prcmu_config_a9wdog(PRCMU_WDOG_CPU1, false); | |
95 | ||
96 | /* set HW initial value */ | |
97 | prcmu_load_a9wdog(PRCMU_WDOG_ALL, timeout * 1000); | |
98 | ||
c7388880 | 99 | ret = devm_watchdog_register_device(dev, &db8500_wdt); |
f0e5bd41 FB |
100 | if (ret) |
101 | return ret; | |
102 | ||
89e5b7ac | 103 | dev_info(dev, "initialized\n"); |
f0e5bd41 FB |
104 | |
105 | return 0; | |
106 | } | |
107 | ||
108 | #ifdef CONFIG_PM | |
c7388880 | 109 | static int db8500_wdt_suspend(struct platform_device *pdev, |
f0e5bd41 FB |
110 | pm_message_t state) |
111 | { | |
c7388880 LW |
112 | if (watchdog_active(&db8500_wdt)) { |
113 | db8500_wdt_stop(&db8500_wdt); | |
f0e5bd41 FB |
114 | prcmu_config_a9wdog(PRCMU_WDOG_CPU1, true); |
115 | ||
116 | prcmu_load_a9wdog(PRCMU_WDOG_ALL, timeout * 1000); | |
c7388880 | 117 | db8500_wdt_start(&db8500_wdt); |
f0e5bd41 FB |
118 | } |
119 | return 0; | |
120 | } | |
121 | ||
c7388880 | 122 | static int db8500_wdt_resume(struct platform_device *pdev) |
f0e5bd41 | 123 | { |
c7388880 LW |
124 | if (watchdog_active(&db8500_wdt)) { |
125 | db8500_wdt_stop(&db8500_wdt); | |
f0e5bd41 FB |
126 | prcmu_config_a9wdog(PRCMU_WDOG_CPU1, false); |
127 | ||
128 | prcmu_load_a9wdog(PRCMU_WDOG_ALL, timeout * 1000); | |
c7388880 | 129 | db8500_wdt_start(&db8500_wdt); |
f0e5bd41 FB |
130 | } |
131 | return 0; | |
132 | } | |
133 | #else | |
c7388880 LW |
134 | #define db8500_wdt_suspend NULL |
135 | #define db8500_wdt_resume NULL | |
f0e5bd41 FB |
136 | #endif |
137 | ||
c7388880 LW |
138 | static struct platform_driver db8500_wdt_driver = { |
139 | .probe = db8500_wdt_probe, | |
140 | .suspend = db8500_wdt_suspend, | |
141 | .resume = db8500_wdt_resume, | |
f0e5bd41 | 142 | .driver = { |
c7388880 | 143 | .name = "db8500_wdt", |
f0e5bd41 FB |
144 | }, |
145 | }; | |
146 | ||
c7388880 | 147 | module_platform_driver(db8500_wdt_driver); |
f0e5bd41 FB |
148 | |
149 | MODULE_AUTHOR("Jonas Aaberg <jonas.aberg@stericsson.com>"); | |
c7388880 | 150 | MODULE_DESCRIPTION("DB8500 Watchdog Driver"); |
f0e5bd41 | 151 | MODULE_LICENSE("GPL"); |
c7388880 | 152 | MODULE_ALIAS("platform:db8500_wdt"); |