0001
0002
0003
0004
0005
0006
0007
0008 #include <linux/device.h>
0009 #include <linux/mfd/rn5t618.h>
0010 #include <linux/module.h>
0011 #include <linux/platform_device.h>
0012 #include <linux/watchdog.h>
0013
0014 #define DRIVER_NAME "rn5t618-wdt"
0015
0016 static bool nowayout = WATCHDOG_NOWAYOUT;
0017 static unsigned int timeout;
0018
0019 module_param(timeout, uint, 0);
0020 MODULE_PARM_DESC(timeout, "Initial watchdog timeout in seconds");
0021
0022 module_param(nowayout, bool, 0);
0023 MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default="
0024 __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
0025
0026 struct rn5t618_wdt {
0027 struct watchdog_device wdt_dev;
0028 struct rn5t618 *rn5t618;
0029 };
0030
0031
0032
0033
0034
0035
0036
0037 static const struct {
0038 u8 reg_val;
0039 unsigned int time;
0040 } rn5t618_wdt_map[] = {
0041 { 0, 1 },
0042 { 1, 8 },
0043 { 2, 32 },
0044 { 3, 128 },
0045 };
0046
0047 static int rn5t618_wdt_set_timeout(struct watchdog_device *wdt_dev,
0048 unsigned int t)
0049 {
0050 struct rn5t618_wdt *wdt = watchdog_get_drvdata(wdt_dev);
0051 int ret, i;
0052
0053 for (i = 0; i < ARRAY_SIZE(rn5t618_wdt_map); i++) {
0054 if (rn5t618_wdt_map[i].time + 1 >= t)
0055 break;
0056 }
0057
0058 if (i == ARRAY_SIZE(rn5t618_wdt_map))
0059 return -EINVAL;
0060
0061 ret = regmap_update_bits(wdt->rn5t618->regmap, RN5T618_WATCHDOG,
0062 RN5T618_WATCHDOG_WDOGTIM_M,
0063 rn5t618_wdt_map[i].reg_val);
0064 if (!ret)
0065 wdt_dev->timeout = rn5t618_wdt_map[i].time;
0066
0067 return ret;
0068 }
0069
0070 static int rn5t618_wdt_start(struct watchdog_device *wdt_dev)
0071 {
0072 struct rn5t618_wdt *wdt = watchdog_get_drvdata(wdt_dev);
0073 int ret;
0074
0075 ret = rn5t618_wdt_set_timeout(wdt_dev, wdt_dev->timeout);
0076 if (ret)
0077 return ret;
0078
0079
0080 ret = regmap_update_bits(wdt->rn5t618->regmap, RN5T618_REPCNT,
0081 RN5T618_REPCNT_REPWRON,
0082 RN5T618_REPCNT_REPWRON);
0083 if (ret)
0084 return ret;
0085
0086
0087 ret = regmap_update_bits(wdt->rn5t618->regmap, RN5T618_WATCHDOG,
0088 RN5T618_WATCHDOG_WDOGEN,
0089 RN5T618_WATCHDOG_WDOGEN);
0090 if (ret)
0091 return ret;
0092
0093
0094 return regmap_update_bits(wdt->rn5t618->regmap, RN5T618_PWRIREN,
0095 RN5T618_PWRIRQ_IR_WDOG,
0096 RN5T618_PWRIRQ_IR_WDOG);
0097 }
0098
0099 static int rn5t618_wdt_stop(struct watchdog_device *wdt_dev)
0100 {
0101 struct rn5t618_wdt *wdt = watchdog_get_drvdata(wdt_dev);
0102
0103 return regmap_update_bits(wdt->rn5t618->regmap, RN5T618_WATCHDOG,
0104 RN5T618_WATCHDOG_WDOGEN, 0);
0105 }
0106
0107 static int rn5t618_wdt_ping(struct watchdog_device *wdt_dev)
0108 {
0109 struct rn5t618_wdt *wdt = watchdog_get_drvdata(wdt_dev);
0110 unsigned int val;
0111 int ret;
0112
0113
0114 ret = regmap_read(wdt->rn5t618->regmap, RN5T618_WATCHDOG, &val);
0115 if (ret)
0116 return ret;
0117
0118 ret = regmap_write(wdt->rn5t618->regmap, RN5T618_WATCHDOG, val);
0119 if (ret)
0120 return ret;
0121
0122
0123 return regmap_update_bits(wdt->rn5t618->regmap, RN5T618_PWRIRQ,
0124 RN5T618_PWRIRQ_IR_WDOG, 0);
0125 }
0126
0127 static const struct watchdog_info rn5t618_wdt_info = {
0128 .options = WDIOF_SETTIMEOUT | WDIOF_MAGICCLOSE |
0129 WDIOF_KEEPALIVEPING,
0130 .identity = DRIVER_NAME,
0131 };
0132
0133 static const struct watchdog_ops rn5t618_wdt_ops = {
0134 .owner = THIS_MODULE,
0135 .start = rn5t618_wdt_start,
0136 .stop = rn5t618_wdt_stop,
0137 .ping = rn5t618_wdt_ping,
0138 .set_timeout = rn5t618_wdt_set_timeout,
0139 };
0140
0141 static int rn5t618_wdt_probe(struct platform_device *pdev)
0142 {
0143 struct device *dev = &pdev->dev;
0144 struct rn5t618 *rn5t618 = dev_get_drvdata(dev->parent);
0145 struct rn5t618_wdt *wdt;
0146 int min_timeout, max_timeout;
0147
0148 wdt = devm_kzalloc(dev, sizeof(struct rn5t618_wdt), GFP_KERNEL);
0149 if (!wdt)
0150 return -ENOMEM;
0151
0152 min_timeout = rn5t618_wdt_map[0].time;
0153 max_timeout = rn5t618_wdt_map[ARRAY_SIZE(rn5t618_wdt_map) - 1].time;
0154
0155 wdt->rn5t618 = rn5t618;
0156 wdt->wdt_dev.info = &rn5t618_wdt_info;
0157 wdt->wdt_dev.ops = &rn5t618_wdt_ops;
0158 wdt->wdt_dev.min_timeout = min_timeout;
0159 wdt->wdt_dev.max_timeout = max_timeout;
0160 wdt->wdt_dev.timeout = max_timeout;
0161 wdt->wdt_dev.parent = dev;
0162
0163 watchdog_set_drvdata(&wdt->wdt_dev, wdt);
0164 watchdog_init_timeout(&wdt->wdt_dev, timeout, dev);
0165 watchdog_set_nowayout(&wdt->wdt_dev, nowayout);
0166
0167 platform_set_drvdata(pdev, wdt);
0168
0169 return watchdog_register_device(&wdt->wdt_dev);
0170 }
0171
0172 static int rn5t618_wdt_remove(struct platform_device *pdev)
0173 {
0174 struct rn5t618_wdt *wdt = platform_get_drvdata(pdev);
0175
0176 watchdog_unregister_device(&wdt->wdt_dev);
0177
0178 return 0;
0179 }
0180
0181 static struct platform_driver rn5t618_wdt_driver = {
0182 .probe = rn5t618_wdt_probe,
0183 .remove = rn5t618_wdt_remove,
0184 .driver = {
0185 .name = DRIVER_NAME,
0186 },
0187 };
0188
0189 module_platform_driver(rn5t618_wdt_driver);
0190
0191 MODULE_ALIAS("platform:rn5t618-wdt");
0192 MODULE_AUTHOR("Beniamino Galvani <b.galvani@gmail.com>");
0193 MODULE_DESCRIPTION("RN5T618 watchdog driver");
0194 MODULE_LICENSE("GPL v2");