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