1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
48
49
50#define DRV_NAME "iTCO_wdt"
51#define DRV_VERSION "1.11"
52
53
54#include <linux/module.h>
55#include <linux/moduleparam.h>
56#include <linux/types.h>
57#include <linux/errno.h>
58#include <linux/kernel.h>
59#include <linux/miscdevice.h>
60
61#include <linux/watchdog.h>
62#include <linux/init.h>
63#include <linux/fs.h>
64#include <linux/platform_device.h>
65#include <linux/pci.h>
66#include <linux/ioport.h>
67#include <linux/spinlock.h>
68#include <linux/uaccess.h>
69#include <linux/io.h>
70#include <linux/platform_data/itco_wdt.h>
71
72#include "iTCO_vendor.h"
73
74
75
76#define TCOBASE (iTCO_wdt_private.tco_res->start)
77
78#define SMI_EN (iTCO_wdt_private.smi_res->start)
79
80#define TCO_RLD (TCOBASE + 0x00)
81#define TCOv1_TMR (TCOBASE + 0x01)
82#define TCO_DAT_IN (TCOBASE + 0x02)
83#define TCO_DAT_OUT (TCOBASE + 0x03)
84#define TCO1_STS (TCOBASE + 0x04)
85#define TCO2_STS (TCOBASE + 0x06)
86#define TCO1_CNT (TCOBASE + 0x08)
87#define TCO2_CNT (TCOBASE + 0x0a)
88#define TCOv2_TMR (TCOBASE + 0x12)
89
90
91static struct {
92
93 unsigned int iTCO_version;
94 struct resource *tco_res;
95 struct resource *smi_res;
96
97
98
99
100 struct resource *gcs_pmc_res;
101 unsigned long __iomem *gcs_pmc;
102
103 spinlock_t io_lock;
104 struct platform_device *dev;
105
106 struct pci_dev *pdev;
107} iTCO_wdt_private;
108
109
110#define WATCHDOG_TIMEOUT 30
111static int heartbeat = WATCHDOG_TIMEOUT;
112module_param(heartbeat, int, 0);
113MODULE_PARM_DESC(heartbeat, "Watchdog timeout in seconds. "
114 "5..76 (TCO v1) or 3..614 (TCO v2), default="
115 __MODULE_STRING(WATCHDOG_TIMEOUT) ")");
116
117static bool nowayout = WATCHDOG_NOWAYOUT;
118module_param(nowayout, bool, 0);
119MODULE_PARM_DESC(nowayout,
120 "Watchdog cannot be stopped once started (default="
121 __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
122
123static int turn_SMI_watchdog_clear_off = 1;
124module_param(turn_SMI_watchdog_clear_off, int, 0);
125MODULE_PARM_DESC(turn_SMI_watchdog_clear_off,
126 "Turn off SMI clearing watchdog (depends on TCO-version)(default=1)");
127
128
129
130
131
132
133
134
135
136
137static inline unsigned int seconds_to_ticks(int secs)
138{
139 return iTCO_wdt_private.iTCO_version == 3 ? secs : (secs * 10) / 6;
140}
141
142static inline unsigned int ticks_to_seconds(int ticks)
143{
144 return iTCO_wdt_private.iTCO_version == 3 ? ticks : (ticks * 6) / 10;
145}
146
147static inline u32 no_reboot_bit(void)
148{
149 u32 enable_bit;
150
151 switch (iTCO_wdt_private.iTCO_version) {
152 case 3:
153 enable_bit = 0x00000010;
154 break;
155 case 2:
156 enable_bit = 0x00000020;
157 break;
158 case 4:
159 case 1:
160 default:
161 enable_bit = 0x00000002;
162 break;
163 }
164
165 return enable_bit;
166}
167
168static void iTCO_wdt_set_NO_REBOOT_bit(void)
169{
170 u32 val32;
171
172
173 if (iTCO_wdt_private.iTCO_version >= 2) {
174 val32 = readl(iTCO_wdt_private.gcs_pmc);
175 val32 |= no_reboot_bit();
176 writel(val32, iTCO_wdt_private.gcs_pmc);
177 } else if (iTCO_wdt_private.iTCO_version == 1) {
178 pci_read_config_dword(iTCO_wdt_private.pdev, 0xd4, &val32);
179 val32 |= no_reboot_bit();
180 pci_write_config_dword(iTCO_wdt_private.pdev, 0xd4, val32);
181 }
182}
183
184static int iTCO_wdt_unset_NO_REBOOT_bit(void)
185{
186 u32 enable_bit = no_reboot_bit();
187 u32 val32 = 0;
188
189
190 if (iTCO_wdt_private.iTCO_version >= 2) {
191 val32 = readl(iTCO_wdt_private.gcs_pmc);
192 val32 &= ~enable_bit;
193 writel(val32, iTCO_wdt_private.gcs_pmc);
194
195 val32 = readl(iTCO_wdt_private.gcs_pmc);
196 } else if (iTCO_wdt_private.iTCO_version == 1) {
197 pci_read_config_dword(iTCO_wdt_private.pdev, 0xd4, &val32);
198 val32 &= ~enable_bit;
199 pci_write_config_dword(iTCO_wdt_private.pdev, 0xd4, val32);
200
201 pci_read_config_dword(iTCO_wdt_private.pdev, 0xd4, &val32);
202 }
203
204 if (val32 & enable_bit)
205 return -EIO;
206
207 return 0;
208}
209
210static int iTCO_wdt_start(struct watchdog_device *wd_dev)
211{
212 unsigned int val;
213
214 spin_lock(&iTCO_wdt_private.io_lock);
215
216 iTCO_vendor_pre_start(iTCO_wdt_private.smi_res, wd_dev->timeout);
217
218
219 if (iTCO_wdt_unset_NO_REBOOT_bit()) {
220 spin_unlock(&iTCO_wdt_private.io_lock);
221 pr_err("failed to reset NO_REBOOT flag, reboot disabled by hardware/BIOS\n");
222 return -EIO;
223 }
224
225
226
227 if (iTCO_wdt_private.iTCO_version >= 2)
228 outw(0x01, TCO_RLD);
229 else if (iTCO_wdt_private.iTCO_version == 1)
230 outb(0x01, TCO_RLD);
231
232
233 val = inw(TCO1_CNT);
234 val &= 0xf7ff;
235 outw(val, TCO1_CNT);
236 val = inw(TCO1_CNT);
237 spin_unlock(&iTCO_wdt_private.io_lock);
238
239 if (val & 0x0800)
240 return -1;
241 return 0;
242}
243
244static int iTCO_wdt_stop(struct watchdog_device *wd_dev)
245{
246 unsigned int val;
247
248 spin_lock(&iTCO_wdt_private.io_lock);
249
250 iTCO_vendor_pre_stop(iTCO_wdt_private.smi_res);
251
252
253 val = inw(TCO1_CNT);
254 val |= 0x0800;
255 outw(val, TCO1_CNT);
256 val = inw(TCO1_CNT);
257
258
259 iTCO_wdt_set_NO_REBOOT_bit();
260
261 spin_unlock(&iTCO_wdt_private.io_lock);
262
263 if ((val & 0x0800) == 0)
264 return -1;
265 return 0;
266}
267
268static int iTCO_wdt_ping(struct watchdog_device *wd_dev)
269{
270 spin_lock(&iTCO_wdt_private.io_lock);
271
272 iTCO_vendor_pre_keepalive(iTCO_wdt_private.smi_res, wd_dev->timeout);
273
274
275 if (iTCO_wdt_private.iTCO_version >= 2) {
276 outw(0x01, TCO_RLD);
277 } else if (iTCO_wdt_private.iTCO_version == 1) {
278
279
280 outw(0x0008, TCO1_STS);
281
282 outb(0x01, TCO_RLD);
283 }
284
285 spin_unlock(&iTCO_wdt_private.io_lock);
286 return 0;
287}
288
289static int iTCO_wdt_set_timeout(struct watchdog_device *wd_dev, unsigned int t)
290{
291 unsigned int val16;
292 unsigned char val8;
293 unsigned int tmrval;
294
295 tmrval = seconds_to_ticks(t);
296
297
298 if (iTCO_wdt_private.iTCO_version == 1)
299 tmrval /= 2;
300
301
302
303 if (tmrval < 0x04)
304 return -EINVAL;
305 if (((iTCO_wdt_private.iTCO_version >= 2) && (tmrval > 0x3ff)) ||
306 ((iTCO_wdt_private.iTCO_version == 1) && (tmrval > 0x03f)))
307 return -EINVAL;
308
309 iTCO_vendor_pre_set_heartbeat(tmrval);
310
311
312 if (iTCO_wdt_private.iTCO_version >= 2) {
313 spin_lock(&iTCO_wdt_private.io_lock);
314 val16 = inw(TCOv2_TMR);
315 val16 &= 0xfc00;
316 val16 |= tmrval;
317 outw(val16, TCOv2_TMR);
318 val16 = inw(TCOv2_TMR);
319 spin_unlock(&iTCO_wdt_private.io_lock);
320
321 if ((val16 & 0x3ff) != tmrval)
322 return -EINVAL;
323 } else if (iTCO_wdt_private.iTCO_version == 1) {
324 spin_lock(&iTCO_wdt_private.io_lock);
325 val8 = inb(TCOv1_TMR);
326 val8 &= 0xc0;
327 val8 |= (tmrval & 0xff);
328 outb(val8, TCOv1_TMR);
329 val8 = inb(TCOv1_TMR);
330 spin_unlock(&iTCO_wdt_private.io_lock);
331
332 if ((val8 & 0x3f) != tmrval)
333 return -EINVAL;
334 }
335
336 wd_dev->timeout = t;
337 return 0;
338}
339
340static unsigned int iTCO_wdt_get_timeleft(struct watchdog_device *wd_dev)
341{
342 unsigned int val16;
343 unsigned char val8;
344 unsigned int time_left = 0;
345
346
347 if (iTCO_wdt_private.iTCO_version >= 2) {
348 spin_lock(&iTCO_wdt_private.io_lock);
349 val16 = inw(TCO_RLD);
350 val16 &= 0x3ff;
351 spin_unlock(&iTCO_wdt_private.io_lock);
352
353 time_left = ticks_to_seconds(val16);
354 } else if (iTCO_wdt_private.iTCO_version == 1) {
355 spin_lock(&iTCO_wdt_private.io_lock);
356 val8 = inb(TCO_RLD);
357 val8 &= 0x3f;
358 if (!(inw(TCO1_STS) & 0x0008))
359 val8 += (inb(TCOv1_TMR) & 0x3f);
360 spin_unlock(&iTCO_wdt_private.io_lock);
361
362 time_left = ticks_to_seconds(val8);
363 }
364 return time_left;
365}
366
367
368
369
370
371static const struct watchdog_info ident = {
372 .options = WDIOF_SETTIMEOUT |
373 WDIOF_KEEPALIVEPING |
374 WDIOF_MAGICCLOSE,
375 .firmware_version = 0,
376 .identity = DRV_NAME,
377};
378
379static const struct watchdog_ops iTCO_wdt_ops = {
380 .owner = THIS_MODULE,
381 .start = iTCO_wdt_start,
382 .stop = iTCO_wdt_stop,
383 .ping = iTCO_wdt_ping,
384 .set_timeout = iTCO_wdt_set_timeout,
385 .get_timeleft = iTCO_wdt_get_timeleft,
386};
387
388static struct watchdog_device iTCO_wdt_watchdog_dev = {
389 .info = &ident,
390 .ops = &iTCO_wdt_ops,
391};
392
393
394
395
396
397static void iTCO_wdt_cleanup(void)
398{
399
400 if (!nowayout)
401 iTCO_wdt_stop(&iTCO_wdt_watchdog_dev);
402
403
404 watchdog_unregister_device(&iTCO_wdt_watchdog_dev);
405
406
407 release_region(iTCO_wdt_private.tco_res->start,
408 resource_size(iTCO_wdt_private.tco_res));
409 release_region(iTCO_wdt_private.smi_res->start,
410 resource_size(iTCO_wdt_private.smi_res));
411 if (iTCO_wdt_private.iTCO_version >= 2) {
412 iounmap(iTCO_wdt_private.gcs_pmc);
413 release_mem_region(iTCO_wdt_private.gcs_pmc_res->start,
414 resource_size(iTCO_wdt_private.gcs_pmc_res));
415 }
416
417 iTCO_wdt_private.tco_res = NULL;
418 iTCO_wdt_private.smi_res = NULL;
419 iTCO_wdt_private.gcs_pmc_res = NULL;
420 iTCO_wdt_private.gcs_pmc = NULL;
421}
422
423static int iTCO_wdt_probe(struct platform_device *dev)
424{
425 int ret = -ENODEV;
426 unsigned long val32;
427 struct itco_wdt_platform_data *pdata = dev->dev.platform_data;
428
429 if (!pdata)
430 goto out;
431
432 spin_lock_init(&iTCO_wdt_private.io_lock);
433
434 iTCO_wdt_private.tco_res =
435 platform_get_resource(dev, IORESOURCE_IO, ICH_RES_IO_TCO);
436 if (!iTCO_wdt_private.tco_res)
437 goto out;
438
439 iTCO_wdt_private.smi_res =
440 platform_get_resource(dev, IORESOURCE_IO, ICH_RES_IO_SMI);
441 if (!iTCO_wdt_private.smi_res)
442 goto out;
443
444 iTCO_wdt_private.iTCO_version = pdata->version;
445 iTCO_wdt_private.dev = dev;
446 iTCO_wdt_private.pdev = to_pci_dev(dev->dev.parent);
447
448
449
450
451
452 if (iTCO_wdt_private.iTCO_version >= 2) {
453 iTCO_wdt_private.gcs_pmc_res = platform_get_resource(dev,
454 IORESOURCE_MEM,
455 ICH_RES_MEM_GCS_PMC);
456
457 if (!iTCO_wdt_private.gcs_pmc_res)
458 goto out;
459
460 if (!request_mem_region(iTCO_wdt_private.gcs_pmc_res->start,
461 resource_size(iTCO_wdt_private.gcs_pmc_res), dev->name)) {
462 ret = -EBUSY;
463 goto out;
464 }
465 iTCO_wdt_private.gcs_pmc = ioremap(iTCO_wdt_private.gcs_pmc_res->start,
466 resource_size(iTCO_wdt_private.gcs_pmc_res));
467 if (!iTCO_wdt_private.gcs_pmc) {
468 ret = -EIO;
469 goto unreg_gcs_pmc;
470 }
471 }
472
473
474 if (iTCO_wdt_unset_NO_REBOOT_bit() && iTCO_vendor_check_noreboot_on()) {
475 pr_info("unable to reset NO_REBOOT flag, device disabled by hardware/BIOS\n");
476 ret = -ENODEV;
477 goto unmap_gcs_pmc;
478 }
479
480
481 iTCO_wdt_set_NO_REBOOT_bit();
482
483
484 if (!request_region(iTCO_wdt_private.smi_res->start,
485 resource_size(iTCO_wdt_private.smi_res), dev->name)) {
486 pr_err("I/O address 0x%04llx already in use, device disabled\n",
487 (u64)SMI_EN);
488 ret = -EBUSY;
489 goto unmap_gcs_pmc;
490 }
491 if (turn_SMI_watchdog_clear_off >= iTCO_wdt_private.iTCO_version) {
492
493
494
495
496 val32 = inl(SMI_EN);
497 val32 &= 0xffffdfff;
498 outl(val32, SMI_EN);
499 }
500
501 if (!request_region(iTCO_wdt_private.tco_res->start,
502 resource_size(iTCO_wdt_private.tco_res), dev->name)) {
503 pr_err("I/O address 0x%04llx already in use, device disabled\n",
504 (u64)TCOBASE);
505 ret = -EBUSY;
506 goto unreg_smi;
507 }
508
509 pr_info("Found a %s TCO device (Version=%d, TCOBASE=0x%04llx)\n",
510 pdata->name, pdata->version, (u64)TCOBASE);
511
512
513 switch (iTCO_wdt_private.iTCO_version) {
514 case 4:
515 outw(0x0008, TCO1_STS);
516 outw(0x0002, TCO2_STS);
517 break;
518 case 3:
519 outl(0x20008, TCO1_STS);
520 break;
521 case 2:
522 case 1:
523 default:
524 outw(0x0008, TCO1_STS);
525 outw(0x0002, TCO2_STS);
526 outw(0x0004, TCO2_STS);
527 break;
528 }
529
530 iTCO_wdt_watchdog_dev.bootstatus = 0;
531 iTCO_wdt_watchdog_dev.timeout = WATCHDOG_TIMEOUT;
532 watchdog_set_nowayout(&iTCO_wdt_watchdog_dev, nowayout);
533 iTCO_wdt_watchdog_dev.parent = &dev->dev;
534
535
536 iTCO_wdt_stop(&iTCO_wdt_watchdog_dev);
537
538
539
540 if (iTCO_wdt_set_timeout(&iTCO_wdt_watchdog_dev, heartbeat)) {
541 iTCO_wdt_set_timeout(&iTCO_wdt_watchdog_dev, WATCHDOG_TIMEOUT);
542 pr_info("timeout value out of range, using %d\n",
543 WATCHDOG_TIMEOUT);
544 }
545
546 ret = watchdog_register_device(&iTCO_wdt_watchdog_dev);
547 if (ret != 0) {
548 pr_err("cannot register watchdog device (err=%d)\n", ret);
549 goto unreg_tco;
550 }
551
552 pr_info("initialized. heartbeat=%d sec (nowayout=%d)\n",
553 heartbeat, nowayout);
554
555 return 0;
556
557unreg_tco:
558 release_region(iTCO_wdt_private.tco_res->start,
559 resource_size(iTCO_wdt_private.tco_res));
560unreg_smi:
561 release_region(iTCO_wdt_private.smi_res->start,
562 resource_size(iTCO_wdt_private.smi_res));
563unmap_gcs_pmc:
564 if (iTCO_wdt_private.iTCO_version >= 2)
565 iounmap(iTCO_wdt_private.gcs_pmc);
566unreg_gcs_pmc:
567 if (iTCO_wdt_private.iTCO_version >= 2)
568 release_mem_region(iTCO_wdt_private.gcs_pmc_res->start,
569 resource_size(iTCO_wdt_private.gcs_pmc_res));
570out:
571 iTCO_wdt_private.tco_res = NULL;
572 iTCO_wdt_private.smi_res = NULL;
573 iTCO_wdt_private.gcs_pmc_res = NULL;
574 iTCO_wdt_private.gcs_pmc = NULL;
575
576 return ret;
577}
578
579static int iTCO_wdt_remove(struct platform_device *dev)
580{
581 if (iTCO_wdt_private.tco_res || iTCO_wdt_private.smi_res)
582 iTCO_wdt_cleanup();
583
584 return 0;
585}
586
587static void iTCO_wdt_shutdown(struct platform_device *dev)
588{
589 iTCO_wdt_stop(NULL);
590}
591
592static struct platform_driver iTCO_wdt_driver = {
593 .probe = iTCO_wdt_probe,
594 .remove = iTCO_wdt_remove,
595 .shutdown = iTCO_wdt_shutdown,
596 .driver = {
597 .owner = THIS_MODULE,
598 .name = DRV_NAME,
599 },
600};
601
602static int __init iTCO_wdt_init_module(void)
603{
604 int err;
605
606 pr_info("Intel TCO WatchDog Timer Driver v%s\n", DRV_VERSION);
607
608 err = platform_driver_register(&iTCO_wdt_driver);
609 if (err)
610 return err;
611
612 return 0;
613}
614
615static void __exit iTCO_wdt_cleanup_module(void)
616{
617 platform_driver_unregister(&iTCO_wdt_driver);
618 pr_info("Watchdog Module Unloaded\n");
619}
620
621module_init(iTCO_wdt_init_module);
622module_exit(iTCO_wdt_cleanup_module);
623
624MODULE_AUTHOR("Wim Van Sebroeck <wim@iguana.be>");
625MODULE_DESCRIPTION("Intel TCO WatchDog Timer Driver");
626MODULE_VERSION(DRV_VERSION);
627MODULE_LICENSE("GPL");
628MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
629MODULE_ALIAS("platform:" DRV_NAME);
630