0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011
0012
0013
0014
0015
0016
0017 #include <linux/err.h>
0018 #include <linux/module.h>
0019 #include <linux/moduleparam.h>
0020 #include <linux/mod_devicetable.h>
0021 #include <linux/types.h>
0022 #include <linux/kernel.h>
0023 #include <linux/watchdog.h>
0024 #include <linux/bitops.h>
0025 #include <linux/platform_device.h>
0026 #include <linux/spinlock.h>
0027 #include <linux/io.h>
0028 #include <linux/slab.h>
0029 #include <linux/property.h>
0030
0031 #define DEFAULT_HEARTBEAT 60
0032 #define MAX_HEARTBEAT 60
0033
0034 static unsigned int heartbeat = DEFAULT_HEARTBEAT;
0035 static bool nowayout = WATCHDOG_NOWAYOUT;
0036
0037
0038
0039
0040
0041 #define MAX6369_WDSET (7 << 0)
0042 #define MAX6369_WDI (1 << 3)
0043
0044 #define MAX6369_WDSET_DISABLED 3
0045
0046 static int nodelay;
0047
0048 struct max63xx_wdt {
0049 struct watchdog_device wdd;
0050 const struct max63xx_timeout *timeout;
0051
0052
0053 void __iomem *base;
0054 spinlock_t lock;
0055
0056
0057 void (*ping)(struct max63xx_wdt *wdt);
0058 void (*set)(struct max63xx_wdt *wdt, u8 set);
0059 };
0060
0061
0062
0063
0064
0065
0066
0067
0068
0069
0070
0071
0072
0073
0074
0075 struct max63xx_timeout {
0076 const u8 wdset;
0077 const u8 tdelay;
0078 const u8 twd;
0079 };
0080
0081 static const struct max63xx_timeout max6369_table[] = {
0082 { 5, 1, 1 },
0083 { 6, 10, 10 },
0084 { 7, 60, 60 },
0085 { },
0086 };
0087
0088 static const struct max63xx_timeout max6371_table[] = {
0089 { 6, 60, 3 },
0090 { 7, 60, 60 },
0091 { },
0092 };
0093
0094 static const struct max63xx_timeout max6373_table[] = {
0095 { 2, 60, 1 },
0096 { 5, 0, 1 },
0097 { 1, 3, 3 },
0098 { 7, 60, 10 },
0099 { 6, 0, 10 },
0100 { },
0101 };
0102
0103 static const struct max63xx_timeout *
0104 max63xx_select_timeout(const struct max63xx_timeout *table, int value)
0105 {
0106 while (table->twd) {
0107 if (value <= table->twd) {
0108 if (nodelay && table->tdelay == 0)
0109 return table;
0110
0111 if (!nodelay)
0112 return table;
0113 }
0114
0115 table++;
0116 }
0117
0118 return NULL;
0119 }
0120
0121 static int max63xx_wdt_ping(struct watchdog_device *wdd)
0122 {
0123 struct max63xx_wdt *wdt = watchdog_get_drvdata(wdd);
0124
0125 wdt->ping(wdt);
0126 return 0;
0127 }
0128
0129 static int max63xx_wdt_start(struct watchdog_device *wdd)
0130 {
0131 struct max63xx_wdt *wdt = watchdog_get_drvdata(wdd);
0132
0133 wdt->set(wdt, wdt->timeout->wdset);
0134
0135
0136 if (wdt->timeout->tdelay == 0)
0137 wdt->ping(wdt);
0138 return 0;
0139 }
0140
0141 static int max63xx_wdt_stop(struct watchdog_device *wdd)
0142 {
0143 struct max63xx_wdt *wdt = watchdog_get_drvdata(wdd);
0144
0145 wdt->set(wdt, MAX6369_WDSET_DISABLED);
0146 return 0;
0147 }
0148
0149 static const struct watchdog_ops max63xx_wdt_ops = {
0150 .owner = THIS_MODULE,
0151 .start = max63xx_wdt_start,
0152 .stop = max63xx_wdt_stop,
0153 .ping = max63xx_wdt_ping,
0154 };
0155
0156 static const struct watchdog_info max63xx_wdt_info = {
0157 .options = WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE,
0158 .identity = "max63xx Watchdog",
0159 };
0160
0161 static void max63xx_mmap_ping(struct max63xx_wdt *wdt)
0162 {
0163 u8 val;
0164
0165 spin_lock(&wdt->lock);
0166
0167 val = __raw_readb(wdt->base);
0168
0169 __raw_writeb(val | MAX6369_WDI, wdt->base);
0170 __raw_writeb(val & ~MAX6369_WDI, wdt->base);
0171
0172 spin_unlock(&wdt->lock);
0173 }
0174
0175 static void max63xx_mmap_set(struct max63xx_wdt *wdt, u8 set)
0176 {
0177 u8 val;
0178
0179 spin_lock(&wdt->lock);
0180
0181 val = __raw_readb(wdt->base);
0182 val &= ~MAX6369_WDSET;
0183 val |= set & MAX6369_WDSET;
0184 __raw_writeb(val, wdt->base);
0185
0186 spin_unlock(&wdt->lock);
0187 }
0188
0189 static int max63xx_mmap_init(struct platform_device *p, struct max63xx_wdt *wdt)
0190 {
0191 wdt->base = devm_platform_ioremap_resource(p, 0);
0192 if (IS_ERR(wdt->base))
0193 return PTR_ERR(wdt->base);
0194
0195 spin_lock_init(&wdt->lock);
0196
0197 wdt->ping = max63xx_mmap_ping;
0198 wdt->set = max63xx_mmap_set;
0199 return 0;
0200 }
0201
0202 static int max63xx_wdt_probe(struct platform_device *pdev)
0203 {
0204 struct device *dev = &pdev->dev;
0205 struct max63xx_wdt *wdt;
0206 const struct max63xx_timeout *table;
0207 int err;
0208
0209 wdt = devm_kzalloc(dev, sizeof(*wdt), GFP_KERNEL);
0210 if (!wdt)
0211 return -ENOMEM;
0212
0213
0214 table = device_get_match_data(dev);
0215 if (!table)
0216 table = (struct max63xx_timeout *)pdev->id_entry->driver_data;
0217
0218 if (heartbeat < 1 || heartbeat > MAX_HEARTBEAT)
0219 heartbeat = DEFAULT_HEARTBEAT;
0220
0221 wdt->timeout = max63xx_select_timeout(table, heartbeat);
0222 if (!wdt->timeout) {
0223 dev_err(dev, "unable to satisfy %ds heartbeat request\n",
0224 heartbeat);
0225 return -EINVAL;
0226 }
0227
0228 err = max63xx_mmap_init(pdev, wdt);
0229 if (err)
0230 return err;
0231
0232 platform_set_drvdata(pdev, &wdt->wdd);
0233 watchdog_set_drvdata(&wdt->wdd, wdt);
0234
0235 wdt->wdd.parent = dev;
0236 wdt->wdd.timeout = wdt->timeout->twd;
0237 wdt->wdd.info = &max63xx_wdt_info;
0238 wdt->wdd.ops = &max63xx_wdt_ops;
0239
0240 watchdog_set_nowayout(&wdt->wdd, nowayout);
0241
0242 err = devm_watchdog_register_device(dev, &wdt->wdd);
0243 if (err)
0244 return err;
0245
0246 dev_info(dev, "using %ds heartbeat with %ds initial delay\n",
0247 wdt->timeout->twd, wdt->timeout->tdelay);
0248 return 0;
0249 }
0250
0251 static const struct platform_device_id max63xx_id_table[] = {
0252 { "max6369_wdt", (kernel_ulong_t)max6369_table, },
0253 { "max6370_wdt", (kernel_ulong_t)max6369_table, },
0254 { "max6371_wdt", (kernel_ulong_t)max6371_table, },
0255 { "max6372_wdt", (kernel_ulong_t)max6371_table, },
0256 { "max6373_wdt", (kernel_ulong_t)max6373_table, },
0257 { "max6374_wdt", (kernel_ulong_t)max6373_table, },
0258 { },
0259 };
0260 MODULE_DEVICE_TABLE(platform, max63xx_id_table);
0261
0262 static const struct of_device_id max63xx_dt_id_table[] = {
0263 { .compatible = "maxim,max6369", .data = max6369_table, },
0264 { .compatible = "maxim,max6370", .data = max6369_table, },
0265 { .compatible = "maxim,max6371", .data = max6371_table, },
0266 { .compatible = "maxim,max6372", .data = max6371_table, },
0267 { .compatible = "maxim,max6373", .data = max6373_table, },
0268 { .compatible = "maxim,max6374", .data = max6373_table, },
0269 { }
0270 };
0271 MODULE_DEVICE_TABLE(of, max63xx_dt_id_table);
0272
0273 static struct platform_driver max63xx_wdt_driver = {
0274 .probe = max63xx_wdt_probe,
0275 .id_table = max63xx_id_table,
0276 .driver = {
0277 .name = "max63xx_wdt",
0278 .of_match_table = max63xx_dt_id_table,
0279 },
0280 };
0281
0282 module_platform_driver(max63xx_wdt_driver);
0283
0284 MODULE_AUTHOR("Marc Zyngier <maz@misterjones.org>");
0285 MODULE_DESCRIPTION("max63xx Watchdog Driver");
0286
0287 module_param(heartbeat, int, 0);
0288 MODULE_PARM_DESC(heartbeat,
0289 "Watchdog heartbeat period in seconds from 1 to "
0290 __MODULE_STRING(MAX_HEARTBEAT) ", default "
0291 __MODULE_STRING(DEFAULT_HEARTBEAT));
0292
0293 module_param(nowayout, bool, 0);
0294 MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default="
0295 __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
0296
0297 module_param(nodelay, int, 0);
0298 MODULE_PARM_DESC(nodelay,
0299 "Force selection of a timeout setting without initial delay "
0300 "(max6373/74 only, default=0)");
0301
0302 MODULE_LICENSE("GPL v2");