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 if (ret) 179 return ret; 180 181 ret = imx_scu_irq_group_enable(SC_IRQ_GROUP_WDOG, 182 SC_IRQ_WDOG, 183 true); 184 if (ret) { 185 dev_warn(dev, "Enable irq failed, pretimeout NOT supported\n"); 186 return 0; 187 } 188 189 imx_sc_wdd->wdt_notifier.notifier_call = imx_sc_wdt_notify; 190 ret = imx_scu_irq_register_notifier(&imx_sc_wdd->wdt_notifier); 191 if (ret) { 192 imx_scu_irq_group_enable(SC_IRQ_GROUP_WDOG, 193 SC_IRQ_WDOG, 194 false); 195 dev_warn(dev, 196 "Register irq notifier failed, pretimeout NOT supported\n"); 197 return 0; 198 } 199 200 ret = devm_add_action_or_reset(dev, imx_sc_wdt_action, 201 &imx_sc_wdd->wdt_notifier); 202 if (!ret) 203 imx_sc_wdt_info.options |= WDIOF_PRETIMEOUT; 204 else 205 dev_warn(dev, "Add action failed, pretimeout NOT supported\n"); 206 207 return 0; 208 } 209 210 static int __maybe_unused imx_sc_wdt_suspend(struct device *dev) 211 { 212 struct imx_sc_wdt_device *imx_sc_wdd = dev_get_drvdata(dev); 213 214 if (watchdog_active(&imx_sc_wdd->wdd)) 215 imx_sc_wdt_stop(&imx_sc_wdd->wdd); 216 217 return 0; 218 } 219 220 static int __maybe_unused imx_sc_wdt_resume(struct device *dev) 221 { 222 struct imx_sc_wdt_device *imx_sc_wdd = dev_get_drvdata(dev); 223 224 if (watchdog_active(&imx_sc_wdd->wdd)) 225 imx_sc_wdt_start(&imx_sc_wdd->wdd); 226 227 return 0; 228 } 229 230 static SIMPLE_DEV_PM_OPS(imx_sc_wdt_pm_ops, 231 imx_sc_wdt_suspend, imx_sc_wdt_resume); 232 233 static const struct of_device_id imx_sc_wdt_dt_ids[] = { 234 { .compatible = "fsl,imx-sc-wdt", }, 235 { /* sentinel */ } 236 }; 237 MODULE_DEVICE_TABLE(of, imx_sc_wdt_dt_ids); 238 239 static struct platform_driver imx_sc_wdt_driver = { 240 .probe = imx_sc_wdt_probe, 241 .driver = { 242 .name = "imx-sc-wdt", 243 .of_match_table = imx_sc_wdt_dt_ids, 244 .pm = &imx_sc_wdt_pm_ops, 245 }, 246 }; 247 module_platform_driver(imx_sc_wdt_driver); 248 249 MODULE_AUTHOR("Robin Gong <yibin.gong@nxp.com>"); 250 MODULE_DESCRIPTION("NXP i.MX system controller watchdog driver"); 251 MODULE_LICENSE("GPL v2"); 252