0001
0002
0003
0004
0005
0006
0007
0008
0009
0010 #include <linux/arm-smccc.h>
0011 #include <linux/err.h>
0012 #include <linux/module.h>
0013 #include <linux/moduleparam.h>
0014 #include <linux/of.h>
0015 #include <linux/platform_device.h>
0016 #include <linux/types.h>
0017 #include <linux/watchdog.h>
0018 #include <uapi/linux/psci.h>
0019
0020 #define DRV_NAME "arm_smc_wdt"
0021 #define DRV_VERSION "1.0"
0022
0023 enum smcwd_call {
0024 SMCWD_INIT = 0,
0025 SMCWD_SET_TIMEOUT = 1,
0026 SMCWD_ENABLE = 2,
0027 SMCWD_PET = 3,
0028 SMCWD_GET_TIMELEFT = 4,
0029 };
0030
0031 static bool nowayout = WATCHDOG_NOWAYOUT;
0032 static unsigned int timeout;
0033
0034 static int smcwd_call(struct watchdog_device *wdd, enum smcwd_call call,
0035 unsigned long arg, struct arm_smccc_res *res)
0036 {
0037 struct arm_smccc_res local_res;
0038
0039 if (!res)
0040 res = &local_res;
0041
0042 arm_smccc_smc((u32)(uintptr_t)watchdog_get_drvdata(wdd), call, arg, 0,
0043 0, 0, 0, 0, res);
0044
0045 if (res->a0 == PSCI_RET_NOT_SUPPORTED)
0046 return -ENODEV;
0047 if (res->a0 == PSCI_RET_INVALID_PARAMS)
0048 return -EINVAL;
0049 if (res->a0 != PSCI_RET_SUCCESS)
0050 return -EIO;
0051 return 0;
0052 }
0053
0054 static int smcwd_ping(struct watchdog_device *wdd)
0055 {
0056 return smcwd_call(wdd, SMCWD_PET, 0, NULL);
0057 }
0058
0059 static unsigned int smcwd_get_timeleft(struct watchdog_device *wdd)
0060 {
0061 struct arm_smccc_res res;
0062
0063 smcwd_call(wdd, SMCWD_GET_TIMELEFT, 0, &res);
0064 if (res.a0)
0065 return 0;
0066 return res.a1;
0067 }
0068
0069 static int smcwd_set_timeout(struct watchdog_device *wdd, unsigned int timeout)
0070 {
0071 int res;
0072
0073 res = smcwd_call(wdd, SMCWD_SET_TIMEOUT, timeout, NULL);
0074 if (!res)
0075 wdd->timeout = timeout;
0076 return res;
0077 }
0078
0079 static int smcwd_stop(struct watchdog_device *wdd)
0080 {
0081 return smcwd_call(wdd, SMCWD_ENABLE, 0, NULL);
0082 }
0083
0084 static int smcwd_start(struct watchdog_device *wdd)
0085 {
0086 return smcwd_call(wdd, SMCWD_ENABLE, 1, NULL);
0087 }
0088
0089 static const struct watchdog_info smcwd_info = {
0090 .identity = DRV_NAME,
0091 .options = WDIOF_SETTIMEOUT |
0092 WDIOF_KEEPALIVEPING |
0093 WDIOF_MAGICCLOSE,
0094 };
0095
0096 static const struct watchdog_ops smcwd_ops = {
0097 .start = smcwd_start,
0098 .stop = smcwd_stop,
0099 .ping = smcwd_ping,
0100 .set_timeout = smcwd_set_timeout,
0101 };
0102
0103 static const struct watchdog_ops smcwd_timeleft_ops = {
0104 .start = smcwd_start,
0105 .stop = smcwd_stop,
0106 .ping = smcwd_ping,
0107 .set_timeout = smcwd_set_timeout,
0108 .get_timeleft = smcwd_get_timeleft,
0109 };
0110
0111 static int smcwd_probe(struct platform_device *pdev)
0112 {
0113 struct watchdog_device *wdd;
0114 int err;
0115 struct arm_smccc_res res;
0116 u32 smc_func_id;
0117
0118 wdd = devm_kzalloc(&pdev->dev, sizeof(*wdd), GFP_KERNEL);
0119 if (!wdd)
0120 return -ENOMEM;
0121 platform_set_drvdata(pdev, wdd);
0122
0123 if (of_property_read_u32(pdev->dev.of_node, "arm,smc-id",
0124 &smc_func_id))
0125 smc_func_id = 0x82003D06;
0126 watchdog_set_drvdata(wdd, (void *)(uintptr_t)smc_func_id);
0127
0128 err = smcwd_call(wdd, SMCWD_INIT, 0, &res);
0129 if (err < 0)
0130 return err;
0131
0132 wdd->info = &smcwd_info;
0133
0134 if (smcwd_call(wdd, SMCWD_GET_TIMELEFT, 0, NULL))
0135 wdd->ops = &smcwd_ops;
0136 else
0137 wdd->ops = &smcwd_timeleft_ops;
0138 wdd->timeout = res.a2;
0139 wdd->max_timeout = res.a2;
0140 wdd->min_timeout = res.a1;
0141 wdd->parent = &pdev->dev;
0142
0143 watchdog_stop_on_reboot(wdd);
0144 watchdog_stop_on_unregister(wdd);
0145 watchdog_set_nowayout(wdd, nowayout);
0146 watchdog_init_timeout(wdd, timeout, &pdev->dev);
0147 err = smcwd_set_timeout(wdd, wdd->timeout);
0148 if (err)
0149 return err;
0150
0151 err = devm_watchdog_register_device(&pdev->dev, wdd);
0152 if (err)
0153 return err;
0154
0155 dev_info(&pdev->dev,
0156 "Watchdog registered (timeout=%d sec, nowayout=%d)\n",
0157 wdd->timeout, nowayout);
0158
0159 return 0;
0160 }
0161
0162 static const struct of_device_id smcwd_dt_ids[] = {
0163 { .compatible = "arm,smc-wdt" },
0164 {}
0165 };
0166 MODULE_DEVICE_TABLE(of, smcwd_dt_ids);
0167
0168 static struct platform_driver smcwd_driver = {
0169 .probe = smcwd_probe,
0170 .driver = {
0171 .name = DRV_NAME,
0172 .of_match_table = smcwd_dt_ids,
0173 },
0174 };
0175
0176 module_platform_driver(smcwd_driver);
0177
0178 module_param(timeout, uint, 0);
0179 MODULE_PARM_DESC(timeout, "Watchdog heartbeat in seconds");
0180
0181 module_param(nowayout, bool, 0);
0182 MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default="
0183 __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
0184
0185 MODULE_LICENSE("GPL");
0186 MODULE_AUTHOR("Julius Werner <jwerner@chromium.org>");
0187 MODULE_DESCRIPTION("ARM Secure Monitor Call Watchdog Driver");
0188 MODULE_VERSION(DRV_VERSION);