1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * Copyright 2018-2019 NXP. 4 */ 5 6 #include <linux/arm-smccc.h> 7 #include <linux/io.h> 8 #include <linux/init.h> 9 #include <linux/kernel.h> 10 #include <linux/module.h> 11 #include <linux/moduleparam.h> 12 #include <linux/of.h> 13 #include <linux/platform_device.h> 14 #include <linux/reboot.h> 15 #include <linux/watchdog.h> 16 17 #define DEFAULT_TIMEOUT 60 18 /* 19 * Software timer tick implemented in scfw side, support 10ms to 0xffffffff ms 20 * in theory, but for normal case, 1s~128s is enough, you can change this max 21 * value in case it's not enough. 22 */ 23 #define MAX_TIMEOUT 128 24 25 #define IMX_SIP_TIMER 0xC2000002 26 #define IMX_SIP_TIMER_START_WDOG 0x01 27 #define IMX_SIP_TIMER_STOP_WDOG 0x02 28 #define IMX_SIP_TIMER_SET_WDOG_ACT 0x03 29 #define IMX_SIP_TIMER_PING_WDOG 0x04 30 #define IMX_SIP_TIMER_SET_TIMEOUT_WDOG 0x05 31 #define IMX_SIP_TIMER_GET_WDOG_STAT 0x06 32 #define IMX_SIP_TIMER_SET_PRETIME_WDOG 0x07 33 34 #define SC_TIMER_WDOG_ACTION_PARTITION 0 35 36 static bool nowayout = WATCHDOG_NOWAYOUT; 37 module_param(nowayout, bool, 0000); 38 MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=" 39 __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); 40 41 static int imx_sc_wdt_ping(struct watchdog_device *wdog) 42 { 43 struct arm_smccc_res res; 44 45 arm_smccc_smc(IMX_SIP_TIMER, IMX_SIP_TIMER_PING_WDOG, 46 0, 0, 0, 0, 0, 0, &res); 47 48 return 0; 49 } 50 51 static int imx_sc_wdt_start(struct watchdog_device *wdog) 52 { 53 struct arm_smccc_res res; 54 55 arm_smccc_smc(IMX_SIP_TIMER, IMX_SIP_TIMER_START_WDOG, 56 0, 0, 0, 0, 0, 0, &res); 57 if (res.a0) 58 return -EACCES; 59 60 arm_smccc_smc(IMX_SIP_TIMER, IMX_SIP_TIMER_SET_WDOG_ACT, 61 SC_TIMER_WDOG_ACTION_PARTITION, 62 0, 0, 0, 0, 0, &res); 63 return res.a0 ? -EACCES : 0; 64 } 65 66 static int imx_sc_wdt_stop(struct watchdog_device *wdog) 67 { 68 struct arm_smccc_res res; 69 70 arm_smccc_smc(IMX_SIP_TIMER, IMX_SIP_TIMER_STOP_WDOG, 71 0, 0, 0, 0, 0, 0, &res); 72 73 return res.a0 ? -EACCES : 0; 74 } 75 76 static int imx_sc_wdt_set_timeout(struct watchdog_device *wdog, 77 unsigned int timeout) 78 { 79 struct arm_smccc_res res; 80 81 wdog->timeout = timeout; 82 arm_smccc_smc(IMX_SIP_TIMER, IMX_SIP_TIMER_SET_TIMEOUT_WDOG, 83 timeout * 1000, 0, 0, 0, 0, 0, &res); 84 85 return res.a0 ? -EACCES : 0; 86 } 87 88 static const struct watchdog_ops imx_sc_wdt_ops = { 89 .owner = THIS_MODULE, 90 .start = imx_sc_wdt_start, 91 .stop = imx_sc_wdt_stop, 92 .ping = imx_sc_wdt_ping, 93 .set_timeout = imx_sc_wdt_set_timeout, 94 }; 95 96 static const struct watchdog_info imx_sc_wdt_info = { 97 .identity = "i.MX SC watchdog timer", 98 .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | 99 WDIOF_MAGICCLOSE | WDIOF_PRETIMEOUT, 100 }; 101 102 static int imx_sc_wdt_probe(struct platform_device *pdev) 103 { 104 struct device *dev = &pdev->dev; 105 struct watchdog_device *imx_sc_wdd; 106 int ret; 107 108 imx_sc_wdd = devm_kzalloc(dev, sizeof(*imx_sc_wdd), GFP_KERNEL); 109 if (!imx_sc_wdd) 110 return -ENOMEM; 111 112 platform_set_drvdata(pdev, imx_sc_wdd); 113 114 imx_sc_wdd->info = &imx_sc_wdt_info; 115 imx_sc_wdd->ops = &imx_sc_wdt_ops; 116 imx_sc_wdd->min_timeout = 1; 117 imx_sc_wdd->max_timeout = MAX_TIMEOUT; 118 imx_sc_wdd->parent = dev; 119 imx_sc_wdd->timeout = DEFAULT_TIMEOUT; 120 121 watchdog_init_timeout(imx_sc_wdd, 0, dev); 122 watchdog_stop_on_reboot(imx_sc_wdd); 123 watchdog_stop_on_unregister(imx_sc_wdd); 124 125 ret = devm_watchdog_register_device(dev, imx_sc_wdd); 126 if (ret) { 127 dev_err(dev, "Failed to register watchdog device\n"); 128 return ret; 129 } 130 131 return 0; 132 } 133 134 static int __maybe_unused imx_sc_wdt_suspend(struct device *dev) 135 { 136 struct watchdog_device *imx_sc_wdd = dev_get_drvdata(dev); 137 138 if (watchdog_active(imx_sc_wdd)) 139 imx_sc_wdt_stop(imx_sc_wdd); 140 141 return 0; 142 } 143 144 static int __maybe_unused imx_sc_wdt_resume(struct device *dev) 145 { 146 struct watchdog_device *imx_sc_wdd = dev_get_drvdata(dev); 147 148 if (watchdog_active(imx_sc_wdd)) 149 imx_sc_wdt_start(imx_sc_wdd); 150 151 return 0; 152 } 153 154 static SIMPLE_DEV_PM_OPS(imx_sc_wdt_pm_ops, 155 imx_sc_wdt_suspend, imx_sc_wdt_resume); 156 157 static const struct of_device_id imx_sc_wdt_dt_ids[] = { 158 { .compatible = "fsl,imx-sc-wdt", }, 159 { /* sentinel */ } 160 }; 161 MODULE_DEVICE_TABLE(of, imx_sc_wdt_dt_ids); 162 163 static struct platform_driver imx_sc_wdt_driver = { 164 .probe = imx_sc_wdt_probe, 165 .driver = { 166 .name = "imx-sc-wdt", 167 .of_match_table = imx_sc_wdt_dt_ids, 168 .pm = &imx_sc_wdt_pm_ops, 169 }, 170 }; 171 module_platform_driver(imx_sc_wdt_driver); 172 173 MODULE_AUTHOR("Robin Gong <yibin.gong@nxp.com>"); 174 MODULE_DESCRIPTION("NXP i.MX system controller watchdog driver"); 175 MODULE_LICENSE("GPL v2"); 176