Blame view
drivers/watchdog/max63xx_wdt.c
6.99 KB
66aaa7a55 [WATCHDOG] suppor... |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
/* * drivers/char/watchdog/max63xx_wdt.c * * Driver for max63{69,70,71,72,73,74} watchdog timers * * Copyright (C) 2009 Marc Zyngier <maz@misterjones.org> * * This file is licensed under the terms of the GNU General Public * License version 2. This program is licensed "as is" without any * warranty of any kind, whether express or implied. * * This driver assumes the watchdog pins are memory mapped (as it is * the case for the Arcom Zeus). Should it be connected over GPIOs or * another interface, some abstraction will have to be introduced. */ |
4c271bb67 watchdog: Convert... |
16 |
#include <linux/err.h> |
66aaa7a55 [WATCHDOG] suppor... |
17 18 19 20 |
#include <linux/module.h> #include <linux/moduleparam.h> #include <linux/types.h> #include <linux/kernel.h> |
66aaa7a55 [WATCHDOG] suppor... |
21 |
#include <linux/watchdog.h> |
66aaa7a55 [WATCHDOG] suppor... |
22 23 24 |
#include <linux/bitops.h> #include <linux/platform_device.h> #include <linux/spinlock.h> |
66aaa7a55 [WATCHDOG] suppor... |
25 |
#include <linux/io.h> |
5a0e3ad6a include cleanup: ... |
26 |
#include <linux/slab.h> |
66aaa7a55 [WATCHDOG] suppor... |
27 28 29 |
#define DEFAULT_HEARTBEAT 60 #define MAX_HEARTBEAT 60 |
a0f368336 watchdog: Convert... |
30 |
static unsigned int heartbeat = DEFAULT_HEARTBEAT; |
86a1e1896 watchdog: nowayou... |
31 |
static bool nowayout = WATCHDOG_NOWAYOUT; |
66aaa7a55 [WATCHDOG] suppor... |
32 33 34 35 36 37 |
/* * Memory mapping: a single byte, 3 first lower bits to select bit 3 * to ping the watchdog. */ #define MAX6369_WDSET (7 << 0) |
5f3b27569 watchdog: cleanup... |
38 |
#define MAX6369_WDI (1 << 3) |
66aaa7a55 [WATCHDOG] suppor... |
39 |
|
b9be9660b watchdog: max63xx... |
40 |
#define MAX6369_WDSET_DISABLED 3 |
66aaa7a55 [WATCHDOG] suppor... |
41 |
|
66aaa7a55 [WATCHDOG] suppor... |
42 |
static int nodelay; |
b9be9660b watchdog: max63xx... |
43 44 45 46 47 48 49 50 51 52 53 54 55 |
struct max63xx_wdt { struct watchdog_device wdd; const struct max63xx_timeout *timeout; /* memory mapping */ void __iomem *base; spinlock_t lock; /* WDI and WSET bits write access routines */ void (*ping)(struct max63xx_wdt *wdt); void (*set)(struct max63xx_wdt *wdt, u8 set); }; |
66aaa7a55 [WATCHDOG] suppor... |
56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 |
/* * The timeout values used are actually the absolute minimum the chip * offers. Typical values on my board are slightly over twice as long * (10s setting ends up with a 25s timeout), and can be up to 3 times * the nominal setting (according to the datasheet). So please take * these values with a grain of salt. Same goes for the initial delay * "feature". Only max6373/74 have a few settings without this initial * delay (selected with the "nodelay" parameter). * * I also decided to remove from the tables any timeout smaller than a * second, as it looked completly overkill... */ /* Timeouts in second */ struct max63xx_timeout { |
b9be9660b watchdog: max63xx... |
72 73 74 |
const u8 wdset; const u8 tdelay; const u8 twd; |
66aaa7a55 [WATCHDOG] suppor... |
75 |
}; |
b9be9660b watchdog: max63xx... |
76 |
static const struct max63xx_timeout max6369_table[] = { |
66aaa7a55 [WATCHDOG] suppor... |
77 78 79 80 81 |
{ 5, 1, 1 }, { 6, 10, 10 }, { 7, 60, 60 }, { }, }; |
b9be9660b watchdog: max63xx... |
82 |
static const struct max63xx_timeout max6371_table[] = { |
66aaa7a55 [WATCHDOG] suppor... |
83 84 85 86 |
{ 6, 60, 3 }, { 7, 60, 60 }, { }, }; |
b9be9660b watchdog: max63xx... |
87 |
static const struct max63xx_timeout max6373_table[] = { |
66aaa7a55 [WATCHDOG] suppor... |
88 89 90 91 92 93 94 |
{ 2, 60, 1 }, { 5, 0, 1 }, { 1, 3, 3 }, { 7, 60, 10 }, { 6, 0, 10 }, { }, }; |
66aaa7a55 [WATCHDOG] suppor... |
95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 |
static struct max63xx_timeout * max63xx_select_timeout(struct max63xx_timeout *table, int value) { while (table->twd) { if (value <= table->twd) { if (nodelay && table->tdelay == 0) return table; if (!nodelay) return table; } table++; } return NULL; } |
a0f368336 watchdog: Convert... |
112 |
static int max63xx_wdt_ping(struct watchdog_device *wdd) |
66aaa7a55 [WATCHDOG] suppor... |
113 |
{ |
b9be9660b watchdog: max63xx... |
114 |
struct max63xx_wdt *wdt = watchdog_get_drvdata(wdd); |
66aaa7a55 [WATCHDOG] suppor... |
115 |
|
b9be9660b watchdog: max63xx... |
116 |
wdt->ping(wdt); |
a0f368336 watchdog: Convert... |
117 |
return 0; |
66aaa7a55 [WATCHDOG] suppor... |
118 |
} |
a0f368336 watchdog: Convert... |
119 |
static int max63xx_wdt_start(struct watchdog_device *wdd) |
66aaa7a55 [WATCHDOG] suppor... |
120 |
{ |
b9be9660b watchdog: max63xx... |
121 |
struct max63xx_wdt *wdt = watchdog_get_drvdata(wdd); |
66aaa7a55 [WATCHDOG] suppor... |
122 |
|
b9be9660b watchdog: max63xx... |
123 |
wdt->set(wdt, wdt->timeout->wdset); |
66aaa7a55 [WATCHDOG] suppor... |
124 125 |
/* check for a edge triggered startup */ |
b9be9660b watchdog: max63xx... |
126 127 |
if (wdt->timeout->tdelay == 0) wdt->ping(wdt); |
a0f368336 watchdog: Convert... |
128 |
return 0; |
66aaa7a55 [WATCHDOG] suppor... |
129 |
} |
a0f368336 watchdog: Convert... |
130 |
static int max63xx_wdt_stop(struct watchdog_device *wdd) |
66aaa7a55 [WATCHDOG] suppor... |
131 |
{ |
b9be9660b watchdog: max63xx... |
132 |
struct max63xx_wdt *wdt = watchdog_get_drvdata(wdd); |
b1183e064 [WATCHDOG] max63x... |
133 |
|
b9be9660b watchdog: max63xx... |
134 |
wdt->set(wdt, MAX6369_WDSET_DISABLED); |
a0f368336 watchdog: Convert... |
135 |
return 0; |
66aaa7a55 [WATCHDOG] suppor... |
136 |
} |
a0f368336 watchdog: Convert... |
137 138 139 140 141 |
static const struct watchdog_ops max63xx_wdt_ops = { .owner = THIS_MODULE, .start = max63xx_wdt_start, .stop = max63xx_wdt_stop, .ping = max63xx_wdt_ping, |
66aaa7a55 [WATCHDOG] suppor... |
142 |
}; |
b9be9660b watchdog: max63xx... |
143 144 145 |
static const struct watchdog_info max63xx_wdt_info = { .options = WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE, .identity = "max63xx Watchdog", |
66aaa7a55 [WATCHDOG] suppor... |
146 |
}; |
b9be9660b watchdog: max63xx... |
147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 |
static void max63xx_mmap_ping(struct max63xx_wdt *wdt) { u8 val; spin_lock(&wdt->lock); val = __raw_readb(wdt->base); __raw_writeb(val | MAX6369_WDI, wdt->base); __raw_writeb(val & ~MAX6369_WDI, wdt->base); spin_unlock(&wdt->lock); } static void max63xx_mmap_set(struct max63xx_wdt *wdt, u8 set) { u8 val; spin_lock(&wdt->lock); val = __raw_readb(wdt->base); val &= ~MAX6369_WDSET; val |= set & MAX6369_WDSET; __raw_writeb(val, wdt->base); spin_unlock(&wdt->lock); } static int max63xx_mmap_init(struct platform_device *p, struct max63xx_wdt *wdt) { struct resource *mem = platform_get_resource(p, IORESOURCE_MEM, 0); wdt->base = devm_ioremap_resource(&p->dev, mem); if (IS_ERR(wdt->base)) return PTR_ERR(wdt->base); spin_lock_init(&wdt->lock); wdt->ping = max63xx_mmap_ping; wdt->set = max63xx_mmap_set; return 0; } |
2d991a164 watchdog: remove ... |
189 |
static int max63xx_wdt_probe(struct platform_device *pdev) |
66aaa7a55 [WATCHDOG] suppor... |
190 |
{ |
b9be9660b watchdog: max63xx... |
191 |
struct max63xx_wdt *wdt; |
66aaa7a55 [WATCHDOG] suppor... |
192 |
struct max63xx_timeout *table; |
b9be9660b watchdog: max63xx... |
193 194 195 196 197 |
int err; wdt = devm_kzalloc(&pdev->dev, sizeof(*wdt), GFP_KERNEL); if (!wdt) return -ENOMEM; |
66aaa7a55 [WATCHDOG] suppor... |
198 199 200 201 202 |
table = (struct max63xx_timeout *)pdev->id_entry->driver_data; if (heartbeat < 1 || heartbeat > MAX_HEARTBEAT) heartbeat = DEFAULT_HEARTBEAT; |
b9be9660b watchdog: max63xx... |
203 204 205 206 207 |
wdt->timeout = max63xx_select_timeout(table, heartbeat); if (!wdt->timeout) { dev_err(&pdev->dev, "unable to satisfy %ds heartbeat request ", heartbeat); |
66aaa7a55 [WATCHDOG] suppor... |
208 209 |
return -EINVAL; } |
b9be9660b watchdog: max63xx... |
210 211 212 213 214 215 |
err = max63xx_mmap_init(pdev, wdt); if (err) return err; platform_set_drvdata(pdev, &wdt->wdd); watchdog_set_drvdata(&wdt->wdd, wdt); |
66aaa7a55 [WATCHDOG] suppor... |
216 |
|
b9be9660b watchdog: max63xx... |
217 218 219 220 |
wdt->wdd.parent = &pdev->dev; wdt->wdd.timeout = wdt->timeout->twd; wdt->wdd.info = &max63xx_wdt_info; wdt->wdd.ops = &max63xx_wdt_ops; |
66aaa7a55 [WATCHDOG] suppor... |
221 |
|
b9be9660b watchdog: max63xx... |
222 |
watchdog_set_nowayout(&wdt->wdd, nowayout); |
66aaa7a55 [WATCHDOG] suppor... |
223 |
|
b9be9660b watchdog: max63xx... |
224 225 226 |
err = watchdog_register_device(&wdt->wdd); if (err) return err; |
66aaa7a55 [WATCHDOG] suppor... |
227 |
|
b9be9660b watchdog: max63xx... |
228 229 230 231 |
dev_info(&pdev->dev, "using %ds heartbeat with %ds initial delay ", wdt->timeout->twd, wdt->timeout->tdelay); return 0; |
66aaa7a55 [WATCHDOG] suppor... |
232 |
} |
4b12b896c watchdog: remove ... |
233 |
static int max63xx_wdt_remove(struct platform_device *pdev) |
66aaa7a55 [WATCHDOG] suppor... |
234 |
{ |
b9be9660b watchdog: max63xx... |
235 236 237 |
struct watchdog_device *wdd = platform_get_drvdata(pdev); watchdog_unregister_device(wdd); |
66aaa7a55 [WATCHDOG] suppor... |
238 239 |
return 0; } |
8c7c72c9b watchdog: max63xx... |
240 |
static const struct platform_device_id max63xx_id_table[] = { |
66aaa7a55 [WATCHDOG] suppor... |
241 242 243 244 245 246 247 248 249 250 251 252 |
{ "max6369_wdt", (kernel_ulong_t)max6369_table, }, { "max6370_wdt", (kernel_ulong_t)max6369_table, }, { "max6371_wdt", (kernel_ulong_t)max6371_table, }, { "max6372_wdt", (kernel_ulong_t)max6371_table, }, { "max6373_wdt", (kernel_ulong_t)max6373_table, }, { "max6374_wdt", (kernel_ulong_t)max6373_table, }, { }, }; MODULE_DEVICE_TABLE(platform, max63xx_id_table); static struct platform_driver max63xx_wdt_driver = { .probe = max63xx_wdt_probe, |
82268714b watchdog: remove ... |
253 |
.remove = max63xx_wdt_remove, |
66aaa7a55 [WATCHDOG] suppor... |
254 255 256 |
.id_table = max63xx_id_table, .driver = { .name = "max63xx_wdt", |
66aaa7a55 [WATCHDOG] suppor... |
257 258 |
}, }; |
b8ec61189 watchdog: convert... |
259 |
module_platform_driver(max63xx_wdt_driver); |
66aaa7a55 [WATCHDOG] suppor... |
260 261 262 263 264 265 266 267 268 |
MODULE_AUTHOR("Marc Zyngier <maz@misterjones.org>"); MODULE_DESCRIPTION("max63xx Watchdog Driver"); module_param(heartbeat, int, 0); MODULE_PARM_DESC(heartbeat, "Watchdog heartbeat period in seconds from 1 to " __MODULE_STRING(MAX_HEARTBEAT) ", default " __MODULE_STRING(DEFAULT_HEARTBEAT)); |
86a1e1896 watchdog: nowayou... |
269 |
module_param(nowayout, bool, 0); |
66aaa7a55 [WATCHDOG] suppor... |
270 271 272 273 274 275 276 |
MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=" __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); module_param(nodelay, int, 0); MODULE_PARM_DESC(nodelay, "Force selection of a timeout setting without initial delay " "(max6373/74 only, default=0)"); |
29efefb90 watchdog: max63xx... |
277 |
MODULE_LICENSE("GPL v2"); |