summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAnson Huang <Anson.Huang@nxp.com>2019-05-27 03:03:17 -0400
committerWim Van Sebroeck <wim@linux-watchdog.org>2019-07-08 13:55:37 -0400
commit15f7d7fc5542f6cb429a069dd77b57ddccbe11e3 (patch)
tree740d3db7dc54ca4a53d5b6a896b460af3630637e
parentb836005b4f95cccdc1f53849a31cac2dc375f4b7 (diff)
watchdog: imx_sc: Add pretimeout support
i.MX system controller watchdog can support pretimeout IRQ via general SCU MU IRQ, it depends on IMX_SCU and driver MUST be probed after SCU IPC ready, then enable corresponding SCU IRQ group and register SCU IRQ notifier, when watchdog pretimeout IRQ fires, SCU MU IRQ will be handled and watchdog pretimeout notifier will be called to handle the event. Signed-off-by: Anson Huang <Anson.Huang@nxp.com> Reviewed-by: Guenter Roeck <linux@roeck-us.net> Signed-off-by: Guenter Roeck <linux@roeck-us.net> Signed-off-by: Wim Van Sebroeck <wim@linux-watchdog.org>
-rw-r--r--drivers/watchdog/Kconfig1
-rw-r--r--drivers/watchdog/imx_sc_wdt.c125
2 files changed, 106 insertions, 20 deletions
diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig
index ffe754539f5a..975e573a6ea5 100644
--- a/drivers/watchdog/Kconfig
+++ b/drivers/watchdog/Kconfig
@@ -717,6 +717,7 @@ config IMX2_WDT
717config IMX_SC_WDT 717config IMX_SC_WDT
718 tristate "IMX SC Watchdog" 718 tristate "IMX SC Watchdog"
719 depends on HAVE_ARM_SMCCC 719 depends on HAVE_ARM_SMCCC
720 depends on IMX_SCU
720 select WATCHDOG_CORE 721 select WATCHDOG_CORE
721 help 722 help
722 This is the driver for the system controller watchdog 723 This is the driver for the system controller watchdog
diff --git a/drivers/watchdog/imx_sc_wdt.c b/drivers/watchdog/imx_sc_wdt.c
index 847aa1c3b5c2..78eaaf75a263 100644
--- a/drivers/watchdog/imx_sc_wdt.c
+++ b/drivers/watchdog/imx_sc_wdt.c
@@ -4,6 +4,7 @@
4 */ 4 */
5 5
6#include <linux/arm-smccc.h> 6#include <linux/arm-smccc.h>
7#include <linux/firmware/imx/sci.h>
7#include <linux/io.h> 8#include <linux/io.h>
8#include <linux/init.h> 9#include <linux/init.h>
9#include <linux/kernel.h> 10#include <linux/kernel.h>
@@ -33,11 +34,19 @@
33 34
34#define SC_TIMER_WDOG_ACTION_PARTITION 0 35#define SC_TIMER_WDOG_ACTION_PARTITION 0
35 36
37#define SC_IRQ_WDOG 1
38#define SC_IRQ_GROUP_WDOG 1
39
36static bool nowayout = WATCHDOG_NOWAYOUT; 40static bool nowayout = WATCHDOG_NOWAYOUT;
37module_param(nowayout, bool, 0000); 41module_param(nowayout, bool, 0000);
38MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=" 42MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default="
39 __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); 43 __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
40 44
45struct imx_sc_wdt_device {
46 struct watchdog_device wdd;
47 struct notifier_block wdt_notifier;
48};
49
41static int imx_sc_wdt_ping(struct watchdog_device *wdog) 50static int imx_sc_wdt_ping(struct watchdog_device *wdog)
42{ 51{
43 struct arm_smccc_res res; 52 struct arm_smccc_res res;
@@ -85,24 +94,66 @@ static int imx_sc_wdt_set_timeout(struct watchdog_device *wdog,
85 return res.a0 ? -EACCES : 0; 94 return res.a0 ? -EACCES : 0;
86} 95}
87 96
97static int imx_sc_wdt_set_pretimeout(struct watchdog_device *wdog,
98 unsigned int pretimeout)
99{
100 struct arm_smccc_res res;
101
102 arm_smccc_smc(IMX_SIP_TIMER, IMX_SIP_TIMER_SET_PRETIME_WDOG,
103 pretimeout * 1000, 0, 0, 0, 0, 0, &res);
104 if (res.a0)
105 return -EACCES;
106
107 wdog->pretimeout = pretimeout;
108
109 return 0;
110}
111
112static int imx_sc_wdt_notify(struct notifier_block *nb,
113 unsigned long event, void *group)
114{
115 struct imx_sc_wdt_device *imx_sc_wdd =
116 container_of(nb,
117 struct imx_sc_wdt_device,
118 wdt_notifier);
119
120 if (event & SC_IRQ_WDOG &&
121 *(u8 *)group == SC_IRQ_GROUP_WDOG)
122 watchdog_notify_pretimeout(&imx_sc_wdd->wdd);
123
124 return 0;
125}
126
127static void imx_sc_wdt_action(void *data)
128{
129 struct notifier_block *wdt_notifier = data;
130
131 imx_scu_irq_unregister_notifier(wdt_notifier);
132 imx_scu_irq_group_enable(SC_IRQ_GROUP_WDOG,
133 SC_IRQ_WDOG,
134 false);
135}
136
88static const struct watchdog_ops imx_sc_wdt_ops = { 137static const struct watchdog_ops imx_sc_wdt_ops = {
89 .owner = THIS_MODULE, 138 .owner = THIS_MODULE,
90 .start = imx_sc_wdt_start, 139 .start = imx_sc_wdt_start,
91 .stop = imx_sc_wdt_stop, 140 .stop = imx_sc_wdt_stop,
92 .ping = imx_sc_wdt_ping, 141 .ping = imx_sc_wdt_ping,
93 .set_timeout = imx_sc_wdt_set_timeout, 142 .set_timeout = imx_sc_wdt_set_timeout,
143 .set_pretimeout = imx_sc_wdt_set_pretimeout,
94}; 144};
95 145
96static const struct watchdog_info imx_sc_wdt_info = { 146static struct watchdog_info imx_sc_wdt_info = {
97 .identity = "i.MX SC watchdog timer", 147 .identity = "i.MX SC watchdog timer",
98 .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | 148 .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING |
99 WDIOF_MAGICCLOSE | WDIOF_PRETIMEOUT, 149 WDIOF_MAGICCLOSE,
100}; 150};
101 151
102static int imx_sc_wdt_probe(struct platform_device *pdev) 152static int imx_sc_wdt_probe(struct platform_device *pdev)
103{ 153{
154 struct imx_sc_wdt_device *imx_sc_wdd;
155 struct watchdog_device *wdog;
104 struct device *dev = &pdev->dev; 156 struct device *dev = &pdev->dev;
105 struct watchdog_device *imx_sc_wdd;
106 int ret; 157 int ret;
107 158
108 imx_sc_wdd = devm_kzalloc(dev, sizeof(*imx_sc_wdd), GFP_KERNEL); 159 imx_sc_wdd = devm_kzalloc(dev, sizeof(*imx_sc_wdd), GFP_KERNEL);
@@ -111,36 +162,70 @@ static int imx_sc_wdt_probe(struct platform_device *pdev)
111 162
112 platform_set_drvdata(pdev, imx_sc_wdd); 163 platform_set_drvdata(pdev, imx_sc_wdd);
113 164
114 imx_sc_wdd->info = &imx_sc_wdt_info; 165 wdog = &imx_sc_wdd->wdd;
115 imx_sc_wdd->ops = &imx_sc_wdt_ops; 166 wdog->info = &imx_sc_wdt_info;
116 imx_sc_wdd->min_timeout = 1; 167 wdog->ops = &imx_sc_wdt_ops;
117 imx_sc_wdd->max_timeout = MAX_TIMEOUT; 168 wdog->min_timeout = 1;
118 imx_sc_wdd->parent = dev; 169 wdog->max_timeout = MAX_TIMEOUT;
119 imx_sc_wdd->timeout = DEFAULT_TIMEOUT; 170 wdog->parent = dev;
120 171 wdog->timeout = DEFAULT_TIMEOUT;
121 watchdog_init_timeout(imx_sc_wdd, 0, dev); 172
122 watchdog_stop_on_reboot(imx_sc_wdd); 173 watchdog_init_timeout(wdog, 0, dev);
123 watchdog_stop_on_unregister(imx_sc_wdd); 174 watchdog_stop_on_reboot(wdog);
175 watchdog_stop_on_unregister(wdog);
176
177 ret = devm_watchdog_register_device(dev, wdog);
178
179 if (ret) {
180 dev_err(dev, "Failed to register watchdog device\n");
181 return ret;
182 }
183
184 ret = imx_scu_irq_group_enable(SC_IRQ_GROUP_WDOG,
185 SC_IRQ_WDOG,
186 true);
187 if (ret) {
188 dev_warn(dev, "Enable irq failed, pretimeout NOT supported\n");
189 return 0;
190 }
191
192 imx_sc_wdd->wdt_notifier.notifier_call = imx_sc_wdt_notify;
193 ret = imx_scu_irq_register_notifier(&imx_sc_wdd->wdt_notifier);
194 if (ret) {
195 imx_scu_irq_group_enable(SC_IRQ_GROUP_WDOG,
196 SC_IRQ_WDOG,
197 false);
198 dev_warn(dev,
199 "Register irq notifier failed, pretimeout NOT supported\n");
200 return 0;
201 }
202
203 ret = devm_add_action_or_reset(dev, imx_sc_wdt_action,
204 &imx_sc_wdd->wdt_notifier);
205 if (!ret)
206 imx_sc_wdt_info.options |= WDIOF_PRETIMEOUT;
207 else
208 dev_warn(dev, "Add action failed, pretimeout NOT supported\n");
124 209
125 return devm_watchdog_register_device(dev, imx_sc_wdd); 210 return 0;
126} 211}
127 212
128static int __maybe_unused imx_sc_wdt_suspend(struct device *dev) 213static int __maybe_unused imx_sc_wdt_suspend(struct device *dev)
129{ 214{
130 struct watchdog_device *imx_sc_wdd = dev_get_drvdata(dev); 215 struct imx_sc_wdt_device *imx_sc_wdd = dev_get_drvdata(dev);
131 216
132 if (watchdog_active(imx_sc_wdd)) 217 if (watchdog_active(&imx_sc_wdd->wdd))
133 imx_sc_wdt_stop(imx_sc_wdd); 218 imx_sc_wdt_stop(&imx_sc_wdd->wdd);
134 219
135 return 0; 220 return 0;
136} 221}
137 222
138static int __maybe_unused imx_sc_wdt_resume(struct device *dev) 223static int __maybe_unused imx_sc_wdt_resume(struct device *dev)
139{ 224{
140 struct watchdog_device *imx_sc_wdd = dev_get_drvdata(dev); 225 struct imx_sc_wdt_device *imx_sc_wdd = dev_get_drvdata(dev);
141 226
142 if (watchdog_active(imx_sc_wdd)) 227 if (watchdog_active(&imx_sc_wdd->wdd))
143 imx_sc_wdt_start(imx_sc_wdd); 228 imx_sc_wdt_start(&imx_sc_wdd->wdd);
144 229
145 return 0; 230 return 0;
146} 231}