1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * Copyright 2018-2019 NXP. 4 */ 5 6 #include <linux/arm-smccc.h> 7 #include <linux/firmware/imx/sci.h> 8 #include <linux/io.h> 9 #include <linux/init.h> 10 #include <linux/kernel.h> 11 #include <linux/module.h> 12 #include <linux/moduleparam.h> 13 #include <linux/of.h> 14 #include <linux/platform_device.h> 15 #include <linux/reboot.h> 16 #include <linux/watchdog.h> 17 18 #define DEFAULT_TIMEOUT 60 19 /* 20 * Software timer tick implemented in scfw side, support 10ms to 0xffffffff ms 21 * in theory, but for normal case, 1s~128s is enough, you can change this max 22 * value in case it's not enough. 23 */ 24 #define MAX_TIMEOUT 128 25 26 #define IMX_SIP_TIMER 0xC2000002 27 #define IMX_SIP_TIMER_START_WDOG 0x01 28 #define IMX_SIP_TIMER_STOP_WDOG 0x02 29 #define IMX_SIP_TIMER_SET_WDOG_ACT 0x03 30 #define IMX_SIP_TIMER_PING_WDOG 0x04 31 #define IMX_SIP_TIMER_SET_TIMEOUT_WDOG 0x05 32 #define IMX_SIP_TIMER_GET_WDOG_STAT 0x06 33 #define IMX_SIP_TIMER_SET_PRETIME_WDOG 0x07 34 35 #define SC_TIMER_WDOG_ACTION_PARTITION 0 36 37 #define SC_IRQ_WDOG 1 38 #define SC_IRQ_GROUP_WDOG 1 39 40 static bool nowayout = WATCHDOG_NOWAYOUT; 41 module_param(nowayout, bool, 0000); 42 MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=" 43 __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); 44 45 struct imx_sc_wdt_device { 46 struct watchdog_device wdd; 47 struct notifier_block wdt_notifier; 48 }; 49 50 static int imx_sc_wdt_ping(struct watchdog_device *wdog) 51 { 52 struct arm_smccc_res res; 53 54 arm_smccc_smc(IMX_SIP_TIMER, IMX_SIP_TIMER_PING_WDOG, 55 0, 0, 0, 0, 0, 0, &res); 56 57 return 0; 58 } 59 60 static int imx_sc_wdt_start(struct watchdog_device *wdog) 61 { 62 struct arm_smccc_res res; 63 64 arm_smccc_smc(IMX_SIP_TIMER, IMX_SIP_TIMER_START_WDOG, 65 0, 0, 0, 0, 0, 0, &res); 66 if (res.a0) 67 return -EACCES; 68 69 arm_smccc_smc(IMX_SIP_TIMER, IMX_SIP_TIMER_SET_WDOG_ACT, 70 SC_TIMER_WDOG_ACTION_PARTITION, 71 0, 0, 0, 0, 0, &res); 72 return res.a0 ? -EACCES : 0; 73 } 74 75 static int imx_sc_wdt_stop(struct watchdog_device *wdog) 76 { 77 struct arm_smccc_res res; 78 79 arm_smccc_smc(IMX_SIP_TIMER, IMX_SIP_TIMER_STOP_WDOG, 80 0, 0, 0, 0, 0, 0, &res); 81 82 return res.a0 ? -EACCES : 0; 83 } 84 85 static int imx_sc_wdt_set_timeout(struct watchdog_device *wdog, 86 unsigned int timeout) 87 { 88 struct arm_smccc_res res; 89 90 wdog->timeout = timeout; 91 arm_smccc_smc(IMX_SIP_TIMER, IMX_SIP_TIMER_SET_TIMEOUT_WDOG, 92 timeout * 1000, 0, 0, 0, 0, 0, &res); 93 94 return res.a0 ? -EACCES : 0; 95 } 96 97 static int imx_sc_wdt_set_pretimeout(struct watchdog_device *wdog, 98 unsigned int pretimeout) 99 { 100 struct arm_smccc_res res; 101 102 /* 103 * SCU firmware calculates pretimeout based on current time 104 * stamp instead of watchdog timeout stamp, need to convert 105 * the pretimeout to SCU firmware's timeout value. 106 */ 107 arm_smccc_smc(IMX_SIP_TIMER, IMX_SIP_TIMER_SET_PRETIME_WDOG, 108 (wdog->timeout - pretimeout) * 1000, 0, 0, 0, 109 0, 0, &res); 110 if (res.a0) 111 return -EACCES; 112 113 wdog->pretimeout = pretimeout; 114 115 return 0; 116 } 117 118 static int imx_sc_wdt_notify(struct notifier_block *nb, 119 unsigned long event, void *group) 120 { 121 struct imx_sc_wdt_device *imx_sc_wdd = 122 container_of(nb, 123 struct imx_sc_wdt_device, 124 wdt_notifier); 125 126 if (event & SC_IRQ_WDOG && 127 *(u8 *)group == SC_IRQ_GROUP_WDOG) 128 watchdog_notify_pretimeout(&imx_sc_wdd->wdd); 129 130 return 0; 131 } 132 133 static void imx_sc_wdt_action(void *data) 134 { 135 struct notifier_block *wdt_notifier = data; 136 137 imx_scu_irq_unregister_notifier(wdt_notifier); 138 imx_scu_irq_group_enable(SC_IRQ_GROUP_WDOG, 139 SC_IRQ_WDOG, 140 false); 141 } 142 143 static const struct watchdog_ops imx_sc_wdt_ops = { 144 .owner = THIS_MODULE, 145 .start = imx_sc_wdt_start, 146 .stop = imx_sc_wdt_stop, 147 .ping = imx_sc_wdt_ping, 148 .set_timeout = imx_sc_wdt_set_timeout, 149 .set_pretimeout = imx_sc_wdt_set_pretimeout, 150 }; 151 152 static struct watchdog_info imx_sc_wdt_info = { 153 .identity = "i.MX SC watchdog timer", 154 .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | 155 WDIOF_MAGICCLOSE, 156 }; 157 158 static int imx_sc_wdt_probe(struct platform_device *pdev) 159 { 160 struct imx_sc_wdt_device *imx_sc_wdd; 161 struct watchdog_device *wdog; 162 struct device *dev = &pdev->dev; 163 int ret; 164 165 imx_sc_wdd = devm_kzalloc(dev, sizeof(*imx_sc_wdd), GFP_KERNEL); 166 if (!imx_sc_wdd) 167 return -ENOMEM; 168 169 platform_set_drvdata(pdev, imx_sc_wdd); 170 171 wdog = &imx_sc_wdd->wdd; 172 wdog->info = &imx_sc_wdt_info; 173 wdog->ops = &imx_sc_wdt_ops; 174 wdog->min_timeout = 1; 175 wdog->max_timeout = MAX_TIMEOUT; 176 wdog->parent = dev; 177 wdog->timeout = DEFAULT_TIMEOUT; 178 179 watchdog_init_timeout(wdog, 0, dev); 180 watchdog_stop_on_reboot(wdog); 181 watchdog_stop_on_unregister(wdog); 182 183 ret = devm_watchdog_register_device(dev, wdog); 184 if (ret) 185 return ret; 186 187 ret = imx_scu_irq_group_enable(SC_IRQ_GROUP_WDOG, 188 SC_IRQ_WDOG, 189 true); 190 if (ret) { 191 dev_warn(dev, "Enable irq failed, pretimeout NOT supported\n"); 192 return 0; 193 } 194 195 imx_sc_wdd->wdt_notifier.notifier_call = imx_sc_wdt_notify; 196 ret = imx_scu_irq_register_notifier(&imx_sc_wdd->wdt_notifier); 197 if (ret) { 198 imx_scu_irq_group_enable(SC_IRQ_GROUP_WDOG, 199 SC_IRQ_WDOG, 200 false); 201 dev_warn(dev, 202 "Register irq notifier failed, pretimeout NOT supported\n"); 203 return 0; 204 } 205 206 ret = devm_add_action_or_reset(dev, imx_sc_wdt_action, 207 &imx_sc_wdd->wdt_notifier); 208 if (!ret) 209 imx_sc_wdt_info.options |= WDIOF_PRETIMEOUT; 210 else 211 dev_warn(dev, "Add action failed, pretimeout NOT supported\n"); 212 213 return 0; 214 } 215 216 static int __maybe_unused imx_sc_wdt_suspend(struct device *dev) 217 { 218 struct imx_sc_wdt_device *imx_sc_wdd = dev_get_drvdata(dev); 219 220 if (watchdog_active(&imx_sc_wdd->wdd)) 221 imx_sc_wdt_stop(&imx_sc_wdd->wdd); 222 223 return 0; 224 } 225 226 static int __maybe_unused imx_sc_wdt_resume(struct device *dev) 227 { 228 struct imx_sc_wdt_device *imx_sc_wdd = dev_get_drvdata(dev); 229 230 if (watchdog_active(&imx_sc_wdd->wdd)) 231 imx_sc_wdt_start(&imx_sc_wdd->wdd); 232 233 return 0; 234 } 235 236 static SIMPLE_DEV_PM_OPS(imx_sc_wdt_pm_ops, 237 imx_sc_wdt_suspend, imx_sc_wdt_resume); 238 239 static const struct of_device_id imx_sc_wdt_dt_ids[] = { 240 { .compatible = "fsl,imx-sc-wdt", }, 241 { /* sentinel */ } 242 }; 243 MODULE_DEVICE_TABLE(of, imx_sc_wdt_dt_ids); 244 245 static struct platform_driver imx_sc_wdt_driver = { 246 .probe = imx_sc_wdt_probe, 247 .driver = { 248 .name = "imx-sc-wdt", 249 .of_match_table = imx_sc_wdt_dt_ids, 250 .pm = &imx_sc_wdt_pm_ops, 251 }, 252 }; 253 module_platform_driver(imx_sc_wdt_driver); 254 255 MODULE_AUTHOR("Robin Gong <yibin.gong@nxp.com>"); 256 MODULE_DESCRIPTION("NXP i.MX system controller watchdog driver"); 257 MODULE_LICENSE("GPL v2"); 258