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 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 112 static 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 127 static 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 137 static const struct watchdog_ops imx_sc_wdt_ops = { 138 .owner = THIS_MODULE, 139 .start = imx_sc_wdt_start, 140 .stop = imx_sc_wdt_stop, 141 .ping = imx_sc_wdt_ping, 142 .set_timeout = imx_sc_wdt_set_timeout, 143 .set_pretimeout = imx_sc_wdt_set_pretimeout, 144 }; 145 146 static struct watchdog_info imx_sc_wdt_info = { 147 .identity = "i.MX SC watchdog timer", 148 .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | 149 WDIOF_MAGICCLOSE, 150 }; 151 152 static int imx_sc_wdt_probe(struct platform_device *pdev) 153 { 154 struct imx_sc_wdt_device *imx_sc_wdd; 155 struct watchdog_device *wdog; 156 struct device *dev = &pdev->dev; 157 int ret; 158 159 imx_sc_wdd = devm_kzalloc(dev, sizeof(*imx_sc_wdd), GFP_KERNEL); 160 if (!imx_sc_wdd) 161 return -ENOMEM; 162 163 platform_set_drvdata(pdev, imx_sc_wdd); 164 165 wdog = &imx_sc_wdd->wdd; 166 wdog->info = &imx_sc_wdt_info; 167 wdog->ops = &imx_sc_wdt_ops; 168 wdog->min_timeout = 1; 169 wdog->max_timeout = MAX_TIMEOUT; 170 wdog->parent = dev; 171 wdog->timeout = DEFAULT_TIMEOUT; 172 173 watchdog_init_timeout(wdog, 0, dev); 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"); 209 210 return 0; 211 } 212 213 static int __maybe_unused imx_sc_wdt_suspend(struct device *dev) 214 { 215 struct imx_sc_wdt_device *imx_sc_wdd = dev_get_drvdata(dev); 216 217 if (watchdog_active(&imx_sc_wdd->wdd)) 218 imx_sc_wdt_stop(&imx_sc_wdd->wdd); 219 220 return 0; 221 } 222 223 static int __maybe_unused imx_sc_wdt_resume(struct device *dev) 224 { 225 struct imx_sc_wdt_device *imx_sc_wdd = dev_get_drvdata(dev); 226 227 if (watchdog_active(&imx_sc_wdd->wdd)) 228 imx_sc_wdt_start(&imx_sc_wdd->wdd); 229 230 return 0; 231 } 232 233 static SIMPLE_DEV_PM_OPS(imx_sc_wdt_pm_ops, 234 imx_sc_wdt_suspend, imx_sc_wdt_resume); 235 236 static const struct of_device_id imx_sc_wdt_dt_ids[] = { 237 { .compatible = "fsl,imx-sc-wdt", }, 238 { /* sentinel */ } 239 }; 240 MODULE_DEVICE_TABLE(of, imx_sc_wdt_dt_ids); 241 242 static struct platform_driver imx_sc_wdt_driver = { 243 .probe = imx_sc_wdt_probe, 244 .driver = { 245 .name = "imx-sc-wdt", 246 .of_match_table = imx_sc_wdt_dt_ids, 247 .pm = &imx_sc_wdt_pm_ops, 248 }, 249 }; 250 module_platform_driver(imx_sc_wdt_driver); 251 252 MODULE_AUTHOR("Robin Gong <yibin.gong@nxp.com>"); 253 MODULE_DESCRIPTION("NXP i.MX system controller watchdog driver"); 254 MODULE_LICENSE("GPL v2"); 255