aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTakahisa Tanaka <mc74hc00@gmail.com>2012-12-02 00:33:18 -0500
committerWim Van Sebroeck <wim@iguana.be>2012-12-19 16:25:09 -0500
commit740fbddf5c3f9ad8b23c5d917ba1cc7e376a5104 (patch)
tree5257b9a01867322f80bfa019ff71b093255bfa8b
parent902e2e7d482c55395652ff78cb3457fc390b101d (diff)
watchdog: sp5100_tco: Add SB8x0 chipset support
The current sp5100_tco driver only supports SP5100/SB7x0 chipset, doesn't support SB8x0 chipset, because current sp5100_tco driver doesn't know that the offset address for watchdog timer was changed from SB8x0 chipset. The offset address of SP5100 and SB7x0 chipsets are as follows, quote from the AMD SB700/710/750 Register Reference Guide (Page 164) and the AMD SP5100 Register Reference Guide (Page 166). WatchDogTimerControl 69h WatchDogTimerBase0 6Ch WatchDogTimerBase1 6Dh WatchDogTimerBase2 6Eh WatchDogTimerBase3 6Fh In contrast, the offset address of SB8x0 chipset is as follows, quote from AMD SB800-Series Southbridges Register Reference Guide (Page 147). WatchDogTimerEn 48h WatchDogTimerConfig 4Ch So, In the case of SB8x0 chipset, sp5100_tco reads meaningless MMIO address (for example, 0xbafe00) from wrong offset address, and the following message is logged. SP5100 TCO timer: mmio address 0xbafe00 already in use With this patch, sp5100_tco driver supports SB8x0 chipset, and can avoid iomem resource conflict. The processing of this patch is as follows. Step 1) Attempt to get the watchdog base address from indirect I/O (0xCD6 and 0xCD7). - Go to the step 7 if obtained address hasn't conflicted with other resource. But, currently, the address (0xfec000f0) conflicts with the IOAPIC MMIO address, and the following message is logged. SP5100 TCO timer: mmio address 0xfec000f0 already in use 0xfec000f0 is recommended by AMD BIOS Developer's Guide. So, go to the next step. Step 2) Attempt to get the SBResource_MMIO base address from AcpiMmioEN (for SB8x0, PM_Reg:24h) or SBResource_MMIO (SP5100/SB7x0, PCI_Reg:9Ch) register. - Go to the step 7 if these register has enabled by BIOS, and obtained address hasn't conflicted with other resource. - If above condition isn't true, go to the next step. Step 3) Attempt to get the free MMIO address from allocate_resource(). - Go to the step 7 if these register has enabled by BIOS, and obtained address hasn't conflicted with other resource. - Driver initialization has failed if obtained address has conflicted with other resource, and no 'force_addr' parameter is specified. Step 4) Use the specified address If 'force_addr' parameter is specified. - allocate_resource() function may fail, when the PCI bridge device occupies iomem resource from 0xf0000000 to 0xffffffff. To handle such a case, I added 'force_addr' parameter to sp5100_tco driver. With 'force_addr' parameter, sp5100_tco driver directly can assign MMIO address for watchdog timer from free iomem region. Note that It's dangerous to specify wrong address in the 'force_addr' parameter. Example of force_addr parameter use # cat /proc/iomem ...snip... fec00000-fec003ff : IOAPIC 0 <--- free MMIO region fec10000-fec1001f : pnp 00:0b fec20000-fec203ff : IOAPIC 1 ...snip... # cat /etc/modprobe.d/sp5100_tco.conf options sp5100_tco force_addr=0xfec00800 # modprobe sp5100_tco # cat /proc/iomem ...snip... fec00000-fec003ff : IOAPIC 0 fec00800-fec00807 : SP5100 TCO <--- watchdog timer MMIO address fec10000-fec1001f : pnp 00:0b fec20000-fec203ff : IOAPIC 1 ...snip... # - Driver initialization has failed if specified address has conflicted with other resource. Step 5) Disable the watchdog timer - To rewrite the watchdog timer register of the chipset, absolutely guarantee that the watchdog timer is disabled. Step 6) Re-program the watchdog timer MMIO address to chipset. - Re-program the obtained MMIO address in Step 3 or Step 4 to chipset via indirect I/O (0xCD6 and 0xCD7). Step 7) Enable and setup the watchdog timer This patch has worked fine on my test environment (ASUS M4A89GTD-PRO/USB3 and DL165G7). therefore I believe that it's no problem to re-program the MMIO address for watchdog timer to chipset during disabled watchdog. However, I'm not sure about it, because I don't know much about chipset programming. So, any comments will be welcome. Bugzilla: https://bugzilla.kernel.org/show_bug.cgi?id=43176 Tested-by: Arkadiusz Miskiewicz <arekm@maven.pl> Tested-by: Paul Menzel <paulepanter@users.sourceforge.net> Signed-off-by: Takahisa Tanaka <mc74hc00@gmail.com> Signed-off-by: Wim Van Sebroeck <wim@iguana.be>
-rw-r--r--drivers/watchdog/sp5100_tco.c321
-rw-r--r--drivers/watchdog/sp5100_tco.h46
2 files changed, 306 insertions, 61 deletions
diff --git a/drivers/watchdog/sp5100_tco.c b/drivers/watchdog/sp5100_tco.c
index b3876812ff07..2b0e000d4377 100644
--- a/drivers/watchdog/sp5100_tco.c
+++ b/drivers/watchdog/sp5100_tco.c
@@ -13,7 +13,9 @@
13 * as published by the Free Software Foundation; either version 13 * as published by the Free Software Foundation; either version
14 * 2 of the License, or (at your option) any later version. 14 * 2 of the License, or (at your option) any later version.
15 * 15 *
16 * See AMD Publication 43009 "AMD SB700/710/750 Register Reference Guide" 16 * See AMD Publication 43009 "AMD SB700/710/750 Register Reference Guide",
17 * AMD Publication 45482 "AMD SB800-Series Southbridges Register
18 * Reference Guide"
17 */ 19 */
18 20
19/* 21/*
@@ -38,18 +40,24 @@
38#include "sp5100_tco.h" 40#include "sp5100_tco.h"
39 41
40/* Module and version information */ 42/* Module and version information */
41#define TCO_VERSION "0.01" 43#define TCO_VERSION "0.03"
42#define TCO_MODULE_NAME "SP5100 TCO timer" 44#define TCO_MODULE_NAME "SP5100 TCO timer"
43#define TCO_DRIVER_NAME TCO_MODULE_NAME ", v" TCO_VERSION 45#define TCO_DRIVER_NAME TCO_MODULE_NAME ", v" TCO_VERSION
44 46
45/* internal variables */ 47/* internal variables */
46static u32 tcobase_phys; 48static u32 tcobase_phys;
49static u32 resbase_phys;
50static u32 tco_wdt_fired;
47static void __iomem *tcobase; 51static void __iomem *tcobase;
48static unsigned int pm_iobase; 52static unsigned int pm_iobase;
49static DEFINE_SPINLOCK(tco_lock); /* Guards the hardware */ 53static DEFINE_SPINLOCK(tco_lock); /* Guards the hardware */
50static unsigned long timer_alive; 54static unsigned long timer_alive;
51static char tco_expect_close; 55static char tco_expect_close;
52static struct pci_dev *sp5100_tco_pci; 56static struct pci_dev *sp5100_tco_pci;
57static struct resource wdt_res = {
58 .name = "Watchdog Timer",
59 .flags = IORESOURCE_MEM,
60};
53 61
54/* the watchdog platform device */ 62/* the watchdog platform device */
55static struct platform_device *sp5100_tco_platform_device; 63static struct platform_device *sp5100_tco_platform_device;
@@ -64,9 +72,15 @@ MODULE_PARM_DESC(heartbeat, "Watchdog heartbeat in seconds. (default="
64 72
65static bool nowayout = WATCHDOG_NOWAYOUT; 73static bool nowayout = WATCHDOG_NOWAYOUT;
66module_param(nowayout, bool, 0); 74module_param(nowayout, bool, 0);
67MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started" 75MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started."
68 " (default=" __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); 76 " (default=" __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
69 77
78static unsigned int force_addr;
79module_param(force_addr, uint, 0);
80MODULE_PARM_DESC(force_addr, "Force the use of specified MMIO address."
81 " ONLY USE THIS PARAMETER IF YOU REALLY KNOW"
82 " WHAT YOU ARE DOING (default=none)");
83
70/* 84/*
71 * Some TCO specific functions 85 * Some TCO specific functions
72 */ 86 */
@@ -122,6 +136,79 @@ static int tco_timer_set_heartbeat(int t)
122 return 0; 136 return 0;
123} 137}
124 138
139static void tco_timer_enable(void)
140{
141 int val;
142
143 if (sp5100_tco_pci->revision >= 0x40) {
144 /* For SB800 or later */
145 /* Set the Watchdog timer resolution to 1 sec */
146 outb(SB800_PM_WATCHDOG_CONFIG, SB800_IO_PM_INDEX_REG);
147 val = inb(SB800_IO_PM_DATA_REG);
148 val |= SB800_PM_WATCHDOG_SECOND_RES;
149 outb(val, SB800_IO_PM_DATA_REG);
150
151 /* Enable watchdog decode bit and watchdog timer */
152 outb(SB800_PM_WATCHDOG_CONTROL, SB800_IO_PM_INDEX_REG);
153 val = inb(SB800_IO_PM_DATA_REG);
154 val |= SB800_PCI_WATCHDOG_DECODE_EN;
155 val &= ~SB800_PM_WATCHDOG_DISABLE;
156 outb(val, SB800_IO_PM_DATA_REG);
157 } else {
158 /* For SP5100 or SB7x0 */
159 /* Enable watchdog decode bit */
160 pci_read_config_dword(sp5100_tco_pci,
161 SP5100_PCI_WATCHDOG_MISC_REG,
162 &val);
163
164 val |= SP5100_PCI_WATCHDOG_DECODE_EN;
165
166 pci_write_config_dword(sp5100_tco_pci,
167 SP5100_PCI_WATCHDOG_MISC_REG,
168 val);
169
170 /* Enable Watchdog timer and set the resolution to 1 sec */
171 outb(SP5100_PM_WATCHDOG_CONTROL, SP5100_IO_PM_INDEX_REG);
172 val = inb(SP5100_IO_PM_DATA_REG);
173 val |= SP5100_PM_WATCHDOG_SECOND_RES;
174 val &= ~SP5100_PM_WATCHDOG_DISABLE;
175 outb(val, SP5100_IO_PM_DATA_REG);
176 }
177}
178
179static void tco_timer_disable(void)
180{
181 int val;
182
183 if (sp5100_tco_pci->revision >= 0x40) {
184 /* For SB800 or later */
185 /* Enable watchdog decode bit and Disable watchdog timer */
186 outb(SB800_PM_WATCHDOG_CONTROL, SB800_IO_PM_INDEX_REG);
187 val = inb(SB800_IO_PM_DATA_REG);
188 val |= SB800_PCI_WATCHDOG_DECODE_EN;
189 val |= SB800_PM_WATCHDOG_DISABLE;
190 outb(val, SB800_IO_PM_DATA_REG);
191 } else {
192 /* For SP5100 or SB7x0 */
193 /* Enable watchdog decode bit */
194 pci_read_config_dword(sp5100_tco_pci,
195 SP5100_PCI_WATCHDOG_MISC_REG,
196 &val);
197
198 val |= SP5100_PCI_WATCHDOG_DECODE_EN;
199
200 pci_write_config_dword(sp5100_tco_pci,
201 SP5100_PCI_WATCHDOG_MISC_REG,
202 val);
203
204 /* Disable Watchdog timer */
205 outb(SP5100_PM_WATCHDOG_CONTROL, SP5100_IO_PM_INDEX_REG);
206 val = inb(SP5100_IO_PM_DATA_REG);
207 val |= SP5100_PM_WATCHDOG_DISABLE;
208 outb(val, SP5100_IO_PM_DATA_REG);
209 }
210}
211
125/* 212/*
126 * /dev/watchdog handling 213 * /dev/watchdog handling
127 */ 214 */
@@ -270,11 +357,12 @@ MODULE_DEVICE_TABLE(pci, sp5100_tco_pci_tbl);
270/* 357/*
271 * Init & exit routines 358 * Init & exit routines
272 */ 359 */
273
274static unsigned char sp5100_tco_setupdevice(void) 360static unsigned char sp5100_tco_setupdevice(void)
275{ 361{
276 struct pci_dev *dev = NULL; 362 struct pci_dev *dev = NULL;
363 const char *dev_name = NULL;
277 u32 val; 364 u32 val;
365 u32 index_reg, data_reg, base_addr;
278 366
279 /* Match the PCI device */ 367 /* Match the PCI device */
280 for_each_pci_dev(dev) { 368 for_each_pci_dev(dev) {
@@ -287,29 +375,160 @@ static unsigned char sp5100_tco_setupdevice(void)
287 if (!sp5100_tco_pci) 375 if (!sp5100_tco_pci)
288 return 0; 376 return 0;
289 377
378 pr_info("PCI Revision ID: 0x%x\n", sp5100_tco_pci->revision);
379
380 /*
381 * Determine type of southbridge chipset.
382 */
383 if (sp5100_tco_pci->revision >= 0x40) {
384 dev_name = SB800_DEVNAME;
385 index_reg = SB800_IO_PM_INDEX_REG;
386 data_reg = SB800_IO_PM_DATA_REG;
387 base_addr = SB800_PM_WATCHDOG_BASE;
388 } else {
389 dev_name = SP5100_DEVNAME;
390 index_reg = SP5100_IO_PM_INDEX_REG;
391 data_reg = SP5100_IO_PM_DATA_REG;
392 base_addr = SP5100_PM_WATCHDOG_BASE;
393 }
394
290 /* Request the IO ports used by this driver */ 395 /* Request the IO ports used by this driver */
291 pm_iobase = SP5100_IO_PM_INDEX_REG; 396 pm_iobase = SP5100_IO_PM_INDEX_REG;
292 if (!request_region(pm_iobase, SP5100_PM_IOPORTS_SIZE, "SP5100 TCO")) { 397 if (!request_region(pm_iobase, SP5100_PM_IOPORTS_SIZE, dev_name)) {
293 pr_err("I/O address 0x%04x already in use\n", pm_iobase); 398 pr_err("I/O address 0x%04x already in use\n", pm_iobase);
294 goto exit; 399 goto exit;
295 } 400 }
296 401
297 /* Find the watchdog base address. */ 402 /*
298 outb(SP5100_PM_WATCHDOG_BASE3, SP5100_IO_PM_INDEX_REG); 403 * First, Find the watchdog timer MMIO address from indirect I/O.
299 val = inb(SP5100_IO_PM_DATA_REG); 404 */
300 outb(SP5100_PM_WATCHDOG_BASE2, SP5100_IO_PM_INDEX_REG); 405 outb(base_addr+3, index_reg);
301 val = val << 8 | inb(SP5100_IO_PM_DATA_REG); 406 val = inb(data_reg);
302 outb(SP5100_PM_WATCHDOG_BASE1, SP5100_IO_PM_INDEX_REG); 407 outb(base_addr+2, index_reg);
303 val = val << 8 | inb(SP5100_IO_PM_DATA_REG); 408 val = val << 8 | inb(data_reg);
304 outb(SP5100_PM_WATCHDOG_BASE0, SP5100_IO_PM_INDEX_REG); 409 outb(base_addr+1, index_reg);
305 /* Low three bits of BASE0 are reserved. */ 410 val = val << 8 | inb(data_reg);
306 val = val << 8 | (inb(SP5100_IO_PM_DATA_REG) & 0xf8); 411 outb(base_addr+0, index_reg);
412 /* Low three bits of BASE are reserved */
413 val = val << 8 | (inb(data_reg) & 0xf8);
414
415 pr_debug("Got 0x%04x from indirect I/O\n", val);
416
417 /* Check MMIO address conflict */
418 if (request_mem_region_exclusive(val, SP5100_WDT_MEM_MAP_SIZE,
419 dev_name))
420 goto setup_wdt;
421 else
422 pr_debug("MMIO address 0x%04x already in use\n", val);
423
424 /*
425 * Secondly, Find the watchdog timer MMIO address
426 * from SBResource_MMIO register.
427 */
428 if (sp5100_tco_pci->revision >= 0x40) {
429 /* Read SBResource_MMIO from AcpiMmioEn(PM_Reg: 24h) */
430 outb(SB800_PM_ACPI_MMIO_EN+3, SB800_IO_PM_INDEX_REG);
431 val = inb(SB800_IO_PM_DATA_REG);
432 outb(SB800_PM_ACPI_MMIO_EN+2, SB800_IO_PM_INDEX_REG);
433 val = val << 8 | inb(SB800_IO_PM_DATA_REG);
434 outb(SB800_PM_ACPI_MMIO_EN+1, SB800_IO_PM_INDEX_REG);
435 val = val << 8 | inb(SB800_IO_PM_DATA_REG);
436 outb(SB800_PM_ACPI_MMIO_EN+0, SB800_IO_PM_INDEX_REG);
437 val = val << 8 | inb(SB800_IO_PM_DATA_REG);
438 } else {
439 /* Read SBResource_MMIO from PCI config(PCI_Reg: 9Ch) */
440 pci_read_config_dword(sp5100_tco_pci,
441 SP5100_SB_RESOURCE_MMIO_BASE, &val);
442 }
443
444 /* The SBResource_MMIO is enabled and mapped memory space? */
445 if ((val & (SB800_ACPI_MMIO_DECODE_EN | SB800_ACPI_MMIO_SEL)) ==
446 SB800_ACPI_MMIO_DECODE_EN) {
447 /* Clear unnecessary the low twelve bits */
448 val &= ~0xFFF;
449 /* Add the Watchdog Timer offset to base address. */
450 val += SB800_PM_WDT_MMIO_OFFSET;
451 /* Check MMIO address conflict */
452 if (request_mem_region_exclusive(val, SP5100_WDT_MEM_MAP_SIZE,
453 dev_name)) {
454 pr_debug("Got 0x%04x from SBResource_MMIO register\n",
455 val);
456 goto setup_wdt;
457 } else
458 pr_debug("MMIO address 0x%04x already in use\n", val);
459 } else
460 pr_debug("SBResource_MMIO is disabled(0x%04x)\n", val);
461
462 /*
463 * Lastly re-programming the watchdog timer MMIO address,
464 * This method is a last resort...
465 *
466 * Before re-programming, to ensure that the watchdog timer
467 * is disabled, disable the watchdog timer.
468 */
469 tco_timer_disable();
470
471 if (force_addr) {
472 /*
473 * Force the use of watchdog timer MMIO address, and aligned to
474 * 8byte boundary.
475 */
476 force_addr &= ~0x7;
477 val = force_addr;
478
479 pr_info("Force the use of 0x%04x as MMIO address\n", val);
480 } else {
481 /*
482 * Get empty slot into the resource tree for watchdog timer.
483 */
484 if (allocate_resource(&iomem_resource,
485 &wdt_res,
486 SP5100_WDT_MEM_MAP_SIZE,
487 0xf0000000,
488 0xfffffff8,
489 0x8,
490 NULL,
491 NULL)) {
492 pr_err("MMIO allocation failed\n");
493 goto unreg_region;
494 }
495
496 val = resbase_phys = wdt_res.start;
497 pr_debug("Got 0x%04x from resource tree\n", val);
498 }
499
500 /* Restore to the low three bits, if chipset is SB8x0(or later) */
501 if (sp5100_tco_pci->revision >= 0x40) {
502 u8 reserved_bit;
503 reserved_bit = inb(base_addr) & 0x7;
504 val |= (u32)reserved_bit;
505 }
506
507 /* Re-programming the watchdog timer base address */
508 outb(base_addr+0, index_reg);
509 /* Low three bits of BASE are reserved */
510 outb((val >> 0) & 0xf8, data_reg);
511 outb(base_addr+1, index_reg);
512 outb((val >> 8) & 0xff, data_reg);
513 outb(base_addr+2, index_reg);
514 outb((val >> 16) & 0xff, data_reg);
515 outb(base_addr+3, index_reg);
516 outb((val >> 24) & 0xff, data_reg);
517
518 /*
519 * Clear unnecessary the low three bits,
520 * if chipset is SB8x0(or later)
521 */
522 if (sp5100_tco_pci->revision >= 0x40)
523 val &= ~0x7;
307 524
308 if (!request_mem_region_exclusive(val, SP5100_WDT_MEM_MAP_SIZE, 525 if (!request_mem_region_exclusive(val, SP5100_WDT_MEM_MAP_SIZE,
309 "SP5100 TCO")) { 526 dev_name)) {
310 pr_err("mmio address 0x%04x already in use\n", val); 527 pr_err("MMIO address 0x%04x already in use\n", val);
311 goto unreg_region; 528 goto unreg_resource;
312 } 529 }
530
531setup_wdt:
313 tcobase_phys = val; 532 tcobase_phys = val;
314 533
315 tcobase = ioremap(val, SP5100_WDT_MEM_MAP_SIZE); 534 tcobase = ioremap(val, SP5100_WDT_MEM_MAP_SIZE);
@@ -318,26 +537,18 @@ static unsigned char sp5100_tco_setupdevice(void)
318 goto unreg_mem_region; 537 goto unreg_mem_region;
319 } 538 }
320 539
321 /* Enable watchdog decode bit */ 540 pr_info("Using 0x%04x for watchdog MMIO address\n", val);
322 pci_read_config_dword(sp5100_tco_pci,
323 SP5100_PCI_WATCHDOG_MISC_REG,
324 &val);
325
326 val |= SP5100_PCI_WATCHDOG_DECODE_EN;
327 541
328 pci_write_config_dword(sp5100_tco_pci, 542 /* Setup the watchdog timer */
329 SP5100_PCI_WATCHDOG_MISC_REG, 543 tco_timer_enable();
330 val);
331 544
332 /* Enable Watchdog timer and set the resolution to 1 sec. */ 545 /* Check that the watchdog action is set to reset the system */
333 outb(SP5100_PM_WATCHDOG_CONTROL, SP5100_IO_PM_INDEX_REG);
334 val = inb(SP5100_IO_PM_DATA_REG);
335 val |= SP5100_PM_WATCHDOG_SECOND_RES;
336 val &= ~SP5100_PM_WATCHDOG_DISABLE;
337 outb(val, SP5100_IO_PM_DATA_REG);
338
339 /* Check that the watchdog action is set to reset the system. */
340 val = readl(SP5100_WDT_CONTROL(tcobase)); 546 val = readl(SP5100_WDT_CONTROL(tcobase));
547 /*
548 * Save WatchDogFired status, because WatchDogFired flag is
549 * cleared here.
550 */
551 tco_wdt_fired = val & SP5100_PM_WATCHDOG_FIRED;
341 val &= ~SP5100_PM_WATCHDOG_ACTION_RESET; 552 val &= ~SP5100_PM_WATCHDOG_ACTION_RESET;
342 writel(val, SP5100_WDT_CONTROL(tcobase)); 553 writel(val, SP5100_WDT_CONTROL(tcobase));
343 554
@@ -355,6 +566,9 @@ static unsigned char sp5100_tco_setupdevice(void)
355 566
356unreg_mem_region: 567unreg_mem_region:
357 release_mem_region(tcobase_phys, SP5100_WDT_MEM_MAP_SIZE); 568 release_mem_region(tcobase_phys, SP5100_WDT_MEM_MAP_SIZE);
569unreg_resource:
570 if (resbase_phys)
571 release_resource(&wdt_res);
358unreg_region: 572unreg_region:
359 release_region(pm_iobase, SP5100_PM_IOPORTS_SIZE); 573 release_region(pm_iobase, SP5100_PM_IOPORTS_SIZE);
360exit: 574exit:
@@ -364,23 +578,18 @@ exit:
364static int sp5100_tco_init(struct platform_device *dev) 578static int sp5100_tco_init(struct platform_device *dev)
365{ 579{
366 int ret; 580 int ret;
367 u32 val; 581 char addr_str[16];
368 582
369 /* Check whether or not the hardware watchdog is there. If found, then 583 /*
584 * Check whether or not the hardware watchdog is there. If found, then
370 * set it up. 585 * set it up.
371 */ 586 */
372 if (!sp5100_tco_setupdevice()) 587 if (!sp5100_tco_setupdevice())
373 return -ENODEV; 588 return -ENODEV;
374 589
375 /* Check to see if last reboot was due to watchdog timeout */ 590 /* Check to see if last reboot was due to watchdog timeout */
376 pr_info("Watchdog reboot %sdetected\n", 591 pr_info("Last reboot was %striggered by watchdog.\n",
377 readl(SP5100_WDT_CONTROL(tcobase)) & SP5100_PM_WATCHDOG_FIRED ? 592 tco_wdt_fired ? "" : "not ");
378 "" : "not ");
379
380 /* Clear out the old status */
381 val = readl(SP5100_WDT_CONTROL(tcobase));
382 val &= ~SP5100_PM_WATCHDOG_FIRED;
383 writel(val, SP5100_WDT_CONTROL(tcobase));
384 593
385 /* 594 /*
386 * Check that the heartbeat value is within it's range. 595 * Check that the heartbeat value is within it's range.
@@ -400,14 +609,24 @@ static int sp5100_tco_init(struct platform_device *dev)
400 609
401 clear_bit(0, &timer_alive); 610 clear_bit(0, &timer_alive);
402 611
403 pr_info("initialized (0x%p). heartbeat=%d sec (nowayout=%d)\n", 612 /* Show module parameters */
404 tcobase, heartbeat, nowayout); 613 if (force_addr == tcobase_phys)
614 /* The force_addr is vaild */
615 sprintf(addr_str, "0x%04x", force_addr);
616 else
617 strcpy(addr_str, "none");
618
619 pr_info("initialized (0x%p). heartbeat=%d sec (nowayout=%d, "
620 "force_addr=%s)\n",
621 tcobase, heartbeat, nowayout, addr_str);
405 622
406 return 0; 623 return 0;
407 624
408exit: 625exit:
409 iounmap(tcobase); 626 iounmap(tcobase);
410 release_mem_region(tcobase_phys, SP5100_WDT_MEM_MAP_SIZE); 627 release_mem_region(tcobase_phys, SP5100_WDT_MEM_MAP_SIZE);
628 if (resbase_phys)
629 release_resource(&wdt_res);
411 release_region(pm_iobase, SP5100_PM_IOPORTS_SIZE); 630 release_region(pm_iobase, SP5100_PM_IOPORTS_SIZE);
412 return ret; 631 return ret;
413} 632}
@@ -422,6 +641,8 @@ static void sp5100_tco_cleanup(void)
422 misc_deregister(&sp5100_tco_miscdev); 641 misc_deregister(&sp5100_tco_miscdev);
423 iounmap(tcobase); 642 iounmap(tcobase);
424 release_mem_region(tcobase_phys, SP5100_WDT_MEM_MAP_SIZE); 643 release_mem_region(tcobase_phys, SP5100_WDT_MEM_MAP_SIZE);
644 if (resbase_phys)
645 release_resource(&wdt_res);
425 release_region(pm_iobase, SP5100_PM_IOPORTS_SIZE); 646 release_region(pm_iobase, SP5100_PM_IOPORTS_SIZE);
426} 647}
427 648
@@ -451,7 +672,7 @@ static int __init sp5100_tco_init_module(void)
451{ 672{
452 int err; 673 int err;
453 674
454 pr_info("SP5100 TCO WatchDog Timer Driver v%s\n", TCO_VERSION); 675 pr_info("SP5100/SB800 TCO WatchDog Timer Driver v%s\n", TCO_VERSION);
455 676
456 err = platform_driver_register(&sp5100_tco_driver); 677 err = platform_driver_register(&sp5100_tco_driver);
457 if (err) 678 if (err)
@@ -475,13 +696,13 @@ static void __exit sp5100_tco_cleanup_module(void)
475{ 696{
476 platform_device_unregister(sp5100_tco_platform_device); 697 platform_device_unregister(sp5100_tco_platform_device);
477 platform_driver_unregister(&sp5100_tco_driver); 698 platform_driver_unregister(&sp5100_tco_driver);
478 pr_info("SP5100 TCO Watchdog Module Unloaded\n"); 699 pr_info("SP5100/SB800 TCO Watchdog Module Unloaded\n");
479} 700}
480 701
481module_init(sp5100_tco_init_module); 702module_init(sp5100_tco_init_module);
482module_exit(sp5100_tco_cleanup_module); 703module_exit(sp5100_tco_cleanup_module);
483 704
484MODULE_AUTHOR("Priyanka Gupta"); 705MODULE_AUTHOR("Priyanka Gupta");
485MODULE_DESCRIPTION("TCO timer driver for SP5100 chipset"); 706MODULE_DESCRIPTION("TCO timer driver for SP5100/SB800 chipset");
486MODULE_LICENSE("GPL"); 707MODULE_LICENSE("GPL");
487MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR); 708MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
diff --git a/drivers/watchdog/sp5100_tco.h b/drivers/watchdog/sp5100_tco.h
index a5a16cc90a34..71594a0c14b7 100644
--- a/drivers/watchdog/sp5100_tco.h
+++ b/drivers/watchdog/sp5100_tco.h
@@ -9,33 +9,57 @@
9/* 9/*
10 * Some address definitions for the Watchdog 10 * Some address definitions for the Watchdog
11 */ 11 */
12
13#define SP5100_WDT_MEM_MAP_SIZE 0x08 12#define SP5100_WDT_MEM_MAP_SIZE 0x08
14#define SP5100_WDT_CONTROL(base) ((base) + 0x00) /* Watchdog Control */ 13#define SP5100_WDT_CONTROL(base) ((base) + 0x00) /* Watchdog Control */
15#define SP5100_WDT_COUNT(base) ((base) + 0x04) /* Watchdog Count */ 14#define SP5100_WDT_COUNT(base) ((base) + 0x04) /* Watchdog Count */
16 15
17#define SP5100_WDT_START_STOP_BIT 1 16#define SP5100_WDT_START_STOP_BIT (1 << 0)
18#define SP5100_WDT_TRIGGER_BIT (1 << 7) 17#define SP5100_WDT_TRIGGER_BIT (1 << 7)
19 18
20#define SP5100_PCI_WATCHDOG_MISC_REG 0x41
21#define SP5100_PCI_WATCHDOG_DECODE_EN (1 << 3)
22
23#define SP5100_PM_IOPORTS_SIZE 0x02 19#define SP5100_PM_IOPORTS_SIZE 0x02
24 20
25/* These two IO registers are hardcoded and there doesn't seem to be a way to 21/*
22 * These two IO registers are hardcoded and there doesn't seem to be a way to
26 * read them from a register. 23 * read them from a register.
27 */ 24 */
25
26/* For SP5100/SB7x0 chipset */
28#define SP5100_IO_PM_INDEX_REG 0xCD6 27#define SP5100_IO_PM_INDEX_REG 0xCD6
29#define SP5100_IO_PM_DATA_REG 0xCD7 28#define SP5100_IO_PM_DATA_REG 0xCD7
30 29
30#define SP5100_SB_RESOURCE_MMIO_BASE 0x9C
31
31#define SP5100_PM_WATCHDOG_CONTROL 0x69 32#define SP5100_PM_WATCHDOG_CONTROL 0x69
32#define SP5100_PM_WATCHDOG_BASE0 0x6C 33#define SP5100_PM_WATCHDOG_BASE 0x6C
33#define SP5100_PM_WATCHDOG_BASE1 0x6D
34#define SP5100_PM_WATCHDOG_BASE2 0x6E
35#define SP5100_PM_WATCHDOG_BASE3 0x6F
36 34
37#define SP5100_PM_WATCHDOG_FIRED (1 << 1) 35#define SP5100_PM_WATCHDOG_FIRED (1 << 1)
38#define SP5100_PM_WATCHDOG_ACTION_RESET (1 << 2) 36#define SP5100_PM_WATCHDOG_ACTION_RESET (1 << 2)
39 37
40#define SP5100_PM_WATCHDOG_DISABLE 1 38#define SP5100_PCI_WATCHDOG_MISC_REG 0x41
39#define SP5100_PCI_WATCHDOG_DECODE_EN (1 << 3)
40
41#define SP5100_PM_WATCHDOG_DISABLE (1 << 0)
41#define SP5100_PM_WATCHDOG_SECOND_RES (3 << 1) 42#define SP5100_PM_WATCHDOG_SECOND_RES (3 << 1)
43
44#define SP5100_DEVNAME "SP5100 TCO"
45
46
47/* For SB8x0(or later) chipset */
48#define SB800_IO_PM_INDEX_REG 0xCD6
49#define SB800_IO_PM_DATA_REG 0xCD7
50
51#define SB800_PM_ACPI_MMIO_EN 0x24
52#define SB800_PM_WATCHDOG_CONTROL 0x48
53#define SB800_PM_WATCHDOG_BASE 0x48
54#define SB800_PM_WATCHDOG_CONFIG 0x4C
55
56#define SB800_PCI_WATCHDOG_DECODE_EN (1 << 0)
57#define SB800_PM_WATCHDOG_DISABLE (1 << 2)
58#define SB800_PM_WATCHDOG_SECOND_RES (3 << 0)
59#define SB800_ACPI_MMIO_DECODE_EN (1 << 0)
60#define SB800_ACPI_MMIO_SEL (1 << 2)
61
62
63#define SB800_PM_WDT_MMIO_OFFSET 0xB00
64
65#define SB800_DEVNAME "SB800 TCO"