aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/watchdog
diff options
context:
space:
mode:
authorPeter Tyser <ptyser@xes-inc.com>2014-03-10 17:34:55 -0400
committerLee Jones <lee.jones@linaro.org>2014-03-19 05:00:03 -0400
commit24b3a1670b47e75be633ae0b5c07945c446f9d29 (patch)
treee57b32c9a75acf52085af9bd831e8fed36960ff5 /drivers/watchdog
parenteb71d4dec4a5e010e34b9d7afdb5af41884c388e (diff)
watchdog: iTCO_wdt: Add support for v3 silicon
Some new Atom's, eg Avoton and Bay Trail, have slightly different iTCO functionality: - The watchdog timer ticks at 1 second instead of .6 seconds - Some 8 and 16-bit registers were combined into 32-bit registers - Some registers were removed (DAT_IN, DAT_OUT, MESSAGE) - The BOOT_STS field in TCO_STS was removed - The NO_REBOOT bit is in the PMC area instead of GCS Update the driver to support the above changes and bump the version to 1.11. Signed-off-by: Peter Tyser <ptyser@xes-inc.com> Tested-by: Rajat Jain <rajatjain@juniper.net> Reviewed-by: Guenter Roeck <linux@roeck-us.net> Signed-off-by: Lee Jones <lee.jones@linaro.org>
Diffstat (limited to 'drivers/watchdog')
-rw-r--r--drivers/watchdog/iTCO_wdt.c137
1 files changed, 82 insertions, 55 deletions
diff --git a/drivers/watchdog/iTCO_wdt.c b/drivers/watchdog/iTCO_wdt.c
index 04f8af65acfd..6d5928ffe604 100644
--- a/drivers/watchdog/iTCO_wdt.c
+++ b/drivers/watchdog/iTCO_wdt.c
@@ -48,7 +48,7 @@
48 48
49/* Module and version information */ 49/* Module and version information */
50#define DRV_NAME "iTCO_wdt" 50#define DRV_NAME "iTCO_wdt"
51#define DRV_VERSION "1.10" 51#define DRV_VERSION "1.11"
52 52
53/* Includes */ 53/* Includes */
54#include <linux/module.h> /* For module specific items */ 54#include <linux/module.h> /* For module specific items */
@@ -92,9 +92,12 @@ static struct { /* this is private data for the iTCO_wdt device */
92 unsigned int iTCO_version; 92 unsigned int iTCO_version;
93 struct resource *tco_res; 93 struct resource *tco_res;
94 struct resource *smi_res; 94 struct resource *smi_res;
95 struct resource *gcs_res; 95 /*
96 /* NO_REBOOT flag is Memory-Mapped GCS register bit 5 (TCO version 2)*/ 96 * NO_REBOOT flag is Memory-Mapped GCS register bit 5 (TCO version 2),
97 unsigned long __iomem *gcs; 97 * or memory-mapped PMC register bit 4 (TCO version 3).
98 */
99 struct resource *gcs_pmc_res;
100 unsigned long __iomem *gcs_pmc;
98 /* the lock for io operations */ 101 /* the lock for io operations */
99 spinlock_t io_lock; 102 spinlock_t io_lock;
100 struct platform_device *dev; 103 struct platform_device *dev;
@@ -125,11 +128,19 @@ MODULE_PARM_DESC(turn_SMI_watchdog_clear_off,
125 * Some TCO specific functions 128 * Some TCO specific functions
126 */ 129 */
127 130
128static inline unsigned int seconds_to_ticks(int seconds) 131/*
132 * The iTCO v1 and v2's internal timer is stored as ticks which decrement
133 * every 0.6 seconds. v3's internal timer is stored as seconds (some
134 * datasheets incorrectly state 0.6 seconds).
135 */
136static inline unsigned int seconds_to_ticks(int secs)
129{ 137{
130 /* the internal timer is stored as ticks which decrement 138 return iTCO_wdt_private.iTCO_version == 3 ? secs : (secs * 10) / 6;
131 * every 0.6 seconds */ 139}
132 return (seconds * 10) / 6; 140
141static inline unsigned int ticks_to_seconds(int ticks)
142{
143 return iTCO_wdt_private.iTCO_version == 3 ? ticks : (ticks * 6) / 10;
133} 144}
134 145
135static void iTCO_wdt_set_NO_REBOOT_bit(void) 146static void iTCO_wdt_set_NO_REBOOT_bit(void)
@@ -137,10 +148,14 @@ static void iTCO_wdt_set_NO_REBOOT_bit(void)
137 u32 val32; 148 u32 val32;
138 149
139 /* Set the NO_REBOOT bit: this disables reboots */ 150 /* Set the NO_REBOOT bit: this disables reboots */
140 if (iTCO_wdt_private.iTCO_version == 2) { 151 if (iTCO_wdt_private.iTCO_version == 3) {
141 val32 = readl(iTCO_wdt_private.gcs); 152 val32 = readl(iTCO_wdt_private.gcs_pmc);
153 val32 |= 0x00000010;
154 writel(val32, iTCO_wdt_private.gcs_pmc);
155 } else if (iTCO_wdt_private.iTCO_version == 2) {
156 val32 = readl(iTCO_wdt_private.gcs_pmc);
142 val32 |= 0x00000020; 157 val32 |= 0x00000020;
143 writel(val32, iTCO_wdt_private.gcs); 158 writel(val32, iTCO_wdt_private.gcs_pmc);
144 } else if (iTCO_wdt_private.iTCO_version == 1) { 159 } else if (iTCO_wdt_private.iTCO_version == 1) {
145 pci_read_config_dword(iTCO_wdt_private.pdev, 0xd4, &val32); 160 pci_read_config_dword(iTCO_wdt_private.pdev, 0xd4, &val32);
146 val32 |= 0x00000002; 161 val32 |= 0x00000002;
@@ -154,12 +169,20 @@ static int iTCO_wdt_unset_NO_REBOOT_bit(void)
154 u32 val32; 169 u32 val32;
155 170
156 /* Unset the NO_REBOOT bit: this enables reboots */ 171 /* Unset the NO_REBOOT bit: this enables reboots */
157 if (iTCO_wdt_private.iTCO_version == 2) { 172 if (iTCO_wdt_private.iTCO_version == 3) {
158 val32 = readl(iTCO_wdt_private.gcs); 173 val32 = readl(iTCO_wdt_private.gcs_pmc);
174 val32 &= 0xffffffef;
175 writel(val32, iTCO_wdt_private.gcs_pmc);
176
177 val32 = readl(iTCO_wdt_private.gcs_pmc);
178 if (val32 & 0x00000010)
179 ret = -EIO;
180 } else if (iTCO_wdt_private.iTCO_version == 2) {
181 val32 = readl(iTCO_wdt_private.gcs_pmc);
159 val32 &= 0xffffffdf; 182 val32 &= 0xffffffdf;
160 writel(val32, iTCO_wdt_private.gcs); 183 writel(val32, iTCO_wdt_private.gcs_pmc);
161 184
162 val32 = readl(iTCO_wdt_private.gcs); 185 val32 = readl(iTCO_wdt_private.gcs_pmc);
163 if (val32 & 0x00000020) 186 if (val32 & 0x00000020)
164 ret = -EIO; 187 ret = -EIO;
165 } else if (iTCO_wdt_private.iTCO_version == 1) { 188 } else if (iTCO_wdt_private.iTCO_version == 1) {
@@ -192,7 +215,7 @@ static int iTCO_wdt_start(struct watchdog_device *wd_dev)
192 215
193 /* Force the timer to its reload value by writing to the TCO_RLD 216 /* Force the timer to its reload value by writing to the TCO_RLD
194 register */ 217 register */
195 if (iTCO_wdt_private.iTCO_version == 2) 218 if (iTCO_wdt_private.iTCO_version >= 2)
196 outw(0x01, TCO_RLD); 219 outw(0x01, TCO_RLD);
197 else if (iTCO_wdt_private.iTCO_version == 1) 220 else if (iTCO_wdt_private.iTCO_version == 1)
198 outb(0x01, TCO_RLD); 221 outb(0x01, TCO_RLD);
@@ -240,9 +263,9 @@ static int iTCO_wdt_ping(struct watchdog_device *wd_dev)
240 iTCO_vendor_pre_keepalive(iTCO_wdt_private.smi_res, wd_dev->timeout); 263 iTCO_vendor_pre_keepalive(iTCO_wdt_private.smi_res, wd_dev->timeout);
241 264
242 /* Reload the timer by writing to the TCO Timer Counter register */ 265 /* Reload the timer by writing to the TCO Timer Counter register */
243 if (iTCO_wdt_private.iTCO_version == 2) 266 if (iTCO_wdt_private.iTCO_version >= 2) {
244 outw(0x01, TCO_RLD); 267 outw(0x01, TCO_RLD);
245 else if (iTCO_wdt_private.iTCO_version == 1) { 268 } else if (iTCO_wdt_private.iTCO_version == 1) {
246 /* Reset the timeout status bit so that the timer 269 /* Reset the timeout status bit so that the timer
247 * needs to count down twice again before rebooting */ 270 * needs to count down twice again before rebooting */
248 outw(0x0008, TCO1_STS); /* write 1 to clear bit */ 271 outw(0x0008, TCO1_STS); /* write 1 to clear bit */
@@ -270,14 +293,14 @@ static int iTCO_wdt_set_timeout(struct watchdog_device *wd_dev, unsigned int t)
270 /* "Values of 0h-3h are ignored and should not be attempted" */ 293 /* "Values of 0h-3h are ignored and should not be attempted" */
271 if (tmrval < 0x04) 294 if (tmrval < 0x04)
272 return -EINVAL; 295 return -EINVAL;
273 if (((iTCO_wdt_private.iTCO_version == 2) && (tmrval > 0x3ff)) || 296 if (((iTCO_wdt_private.iTCO_version >= 2) && (tmrval > 0x3ff)) ||
274 ((iTCO_wdt_private.iTCO_version == 1) && (tmrval > 0x03f))) 297 ((iTCO_wdt_private.iTCO_version == 1) && (tmrval > 0x03f)))
275 return -EINVAL; 298 return -EINVAL;
276 299
277 iTCO_vendor_pre_set_heartbeat(tmrval); 300 iTCO_vendor_pre_set_heartbeat(tmrval);
278 301
279 /* Write new heartbeat to watchdog */ 302 /* Write new heartbeat to watchdog */
280 if (iTCO_wdt_private.iTCO_version == 2) { 303 if (iTCO_wdt_private.iTCO_version >= 2) {
281 spin_lock(&iTCO_wdt_private.io_lock); 304 spin_lock(&iTCO_wdt_private.io_lock);
282 val16 = inw(TCOv2_TMR); 305 val16 = inw(TCOv2_TMR);
283 val16 &= 0xfc00; 306 val16 &= 0xfc00;
@@ -312,13 +335,13 @@ static unsigned int iTCO_wdt_get_timeleft(struct watchdog_device *wd_dev)
312 unsigned int time_left = 0; 335 unsigned int time_left = 0;
313 336
314 /* read the TCO Timer */ 337 /* read the TCO Timer */
315 if (iTCO_wdt_private.iTCO_version == 2) { 338 if (iTCO_wdt_private.iTCO_version >= 2) {
316 spin_lock(&iTCO_wdt_private.io_lock); 339 spin_lock(&iTCO_wdt_private.io_lock);
317 val16 = inw(TCO_RLD); 340 val16 = inw(TCO_RLD);
318 val16 &= 0x3ff; 341 val16 &= 0x3ff;
319 spin_unlock(&iTCO_wdt_private.io_lock); 342 spin_unlock(&iTCO_wdt_private.io_lock);
320 343
321 time_left = (val16 * 6) / 10; 344 time_left = ticks_to_seconds(val16);
322 } else if (iTCO_wdt_private.iTCO_version == 1) { 345 } else if (iTCO_wdt_private.iTCO_version == 1) {
323 spin_lock(&iTCO_wdt_private.io_lock); 346 spin_lock(&iTCO_wdt_private.io_lock);
324 val8 = inb(TCO_RLD); 347 val8 = inb(TCO_RLD);
@@ -327,7 +350,7 @@ static unsigned int iTCO_wdt_get_timeleft(struct watchdog_device *wd_dev)
327 val8 += (inb(TCOv1_TMR) & 0x3f); 350 val8 += (inb(TCOv1_TMR) & 0x3f);
328 spin_unlock(&iTCO_wdt_private.io_lock); 351 spin_unlock(&iTCO_wdt_private.io_lock);
329 352
330 time_left = (val8 * 6) / 10; 353 time_left = ticks_to_seconds(val8);
331 } 354 }
332 return time_left; 355 return time_left;
333} 356}
@@ -376,16 +399,16 @@ static void iTCO_wdt_cleanup(void)
376 resource_size(iTCO_wdt_private.tco_res)); 399 resource_size(iTCO_wdt_private.tco_res));
377 release_region(iTCO_wdt_private.smi_res->start, 400 release_region(iTCO_wdt_private.smi_res->start,
378 resource_size(iTCO_wdt_private.smi_res)); 401 resource_size(iTCO_wdt_private.smi_res));
379 if (iTCO_wdt_private.iTCO_version == 2) { 402 if (iTCO_wdt_private.iTCO_version >= 2) {
380 iounmap(iTCO_wdt_private.gcs); 403 iounmap(iTCO_wdt_private.gcs_pmc);
381 release_mem_region(iTCO_wdt_private.gcs_res->start, 404 release_mem_region(iTCO_wdt_private.gcs_pmc_res->start,
382 resource_size(iTCO_wdt_private.gcs_res)); 405 resource_size(iTCO_wdt_private.gcs_pmc_res));
383 } 406 }
384 407
385 iTCO_wdt_private.tco_res = NULL; 408 iTCO_wdt_private.tco_res = NULL;
386 iTCO_wdt_private.smi_res = NULL; 409 iTCO_wdt_private.smi_res = NULL;
387 iTCO_wdt_private.gcs_res = NULL; 410 iTCO_wdt_private.gcs_pmc_res = NULL;
388 iTCO_wdt_private.gcs = NULL; 411 iTCO_wdt_private.gcs_pmc = NULL;
389} 412}
390 413
391static int iTCO_wdt_probe(struct platform_device *dev) 414static int iTCO_wdt_probe(struct platform_device *dev)
@@ -414,27 +437,27 @@ static int iTCO_wdt_probe(struct platform_device *dev)
414 iTCO_wdt_private.pdev = to_pci_dev(dev->dev.parent); 437 iTCO_wdt_private.pdev = to_pci_dev(dev->dev.parent);
415 438
416 /* 439 /*
417 * Get the Memory-Mapped GCS register, we need it for the 440 * Get the Memory-Mapped GCS or PMC register, we need it for the
418 * NO_REBOOT flag (TCO v2). 441 * NO_REBOOT flag (TCO v2 and v3).
419 */ 442 */
420 if (iTCO_wdt_private.iTCO_version == 2) { 443 if (iTCO_wdt_private.iTCO_version >= 2) {
421 iTCO_wdt_private.gcs_res = platform_get_resource(dev, 444 iTCO_wdt_private.gcs_pmc_res = platform_get_resource(dev,
422 IORESOURCE_MEM, 445 IORESOURCE_MEM,
423 ICH_RES_MEM_GCS); 446 ICH_RES_MEM_GCS_PMC);
424 447
425 if (!iTCO_wdt_private.gcs_res) 448 if (!iTCO_wdt_private.gcs_pmc_res)
426 goto out; 449 goto out;
427 450
428 if (!request_mem_region(iTCO_wdt_private.gcs_res->start, 451 if (!request_mem_region(iTCO_wdt_private.gcs_pmc_res->start,
429 resource_size(iTCO_wdt_private.gcs_res), dev->name)) { 452 resource_size(iTCO_wdt_private.gcs_pmc_res), dev->name)) {
430 ret = -EBUSY; 453 ret = -EBUSY;
431 goto out; 454 goto out;
432 } 455 }
433 iTCO_wdt_private.gcs = ioremap(iTCO_wdt_private.gcs_res->start, 456 iTCO_wdt_private.gcs_pmc = ioremap(iTCO_wdt_private.gcs_pmc_res->start,
434 resource_size(iTCO_wdt_private.gcs_res)); 457 resource_size(iTCO_wdt_private.gcs_pmc_res));
435 if (!iTCO_wdt_private.gcs) { 458 if (!iTCO_wdt_private.gcs_pmc) {
436 ret = -EIO; 459 ret = -EIO;
437 goto unreg_gcs; 460 goto unreg_gcs_pmc;
438 } 461 }
439 } 462 }
440 463
@@ -442,7 +465,7 @@ static int iTCO_wdt_probe(struct platform_device *dev)
442 if (iTCO_wdt_unset_NO_REBOOT_bit() && iTCO_vendor_check_noreboot_on()) { 465 if (iTCO_wdt_unset_NO_REBOOT_bit() && iTCO_vendor_check_noreboot_on()) {
443 pr_info("unable to reset NO_REBOOT flag, device disabled by hardware/BIOS\n"); 466 pr_info("unable to reset NO_REBOOT flag, device disabled by hardware/BIOS\n");
444 ret = -ENODEV; /* Cannot reset NO_REBOOT bit */ 467 ret = -ENODEV; /* Cannot reset NO_REBOOT bit */
445 goto unmap_gcs; 468 goto unmap_gcs_pmc;
446 } 469 }
447 470
448 /* Set the NO_REBOOT bit to prevent later reboots, just for sure */ 471 /* Set the NO_REBOOT bit to prevent later reboots, just for sure */
@@ -454,7 +477,7 @@ static int iTCO_wdt_probe(struct platform_device *dev)
454 pr_err("I/O address 0x%04llx already in use, device disabled\n", 477 pr_err("I/O address 0x%04llx already in use, device disabled\n",
455 (u64)SMI_EN); 478 (u64)SMI_EN);
456 ret = -EBUSY; 479 ret = -EBUSY;
457 goto unmap_gcs; 480 goto unmap_gcs_pmc;
458 } 481 }
459 if (turn_SMI_watchdog_clear_off >= iTCO_wdt_private.iTCO_version) { 482 if (turn_SMI_watchdog_clear_off >= iTCO_wdt_private.iTCO_version) {
460 /* 483 /*
@@ -478,9 +501,13 @@ static int iTCO_wdt_probe(struct platform_device *dev)
478 ich_info->name, ich_info->iTCO_version, (u64)TCOBASE); 501 ich_info->name, ich_info->iTCO_version, (u64)TCOBASE);
479 502
480 /* Clear out the (probably old) status */ 503 /* Clear out the (probably old) status */
481 outw(0x0008, TCO1_STS); /* Clear the Time Out Status bit */ 504 if (iTCO_wdt_private.iTCO_version == 3) {
482 outw(0x0002, TCO2_STS); /* Clear SECOND_TO_STS bit */ 505 outl(0x20008, TCO1_STS);
483 outw(0x0004, TCO2_STS); /* Clear BOOT_STS bit */ 506 } else {
507 outw(0x0008, TCO1_STS); /* Clear the Time Out Status bit */
508 outw(0x0002, TCO2_STS); /* Clear SECOND_TO_STS bit */
509 outw(0x0004, TCO2_STS); /* Clear BOOT_STS bit */
510 }
484 511
485 iTCO_wdt_watchdog_dev.bootstatus = 0; 512 iTCO_wdt_watchdog_dev.bootstatus = 0;
486 iTCO_wdt_watchdog_dev.timeout = WATCHDOG_TIMEOUT; 513 iTCO_wdt_watchdog_dev.timeout = WATCHDOG_TIMEOUT;
@@ -515,18 +542,18 @@ unreg_tco:
515unreg_smi: 542unreg_smi:
516 release_region(iTCO_wdt_private.smi_res->start, 543 release_region(iTCO_wdt_private.smi_res->start,
517 resource_size(iTCO_wdt_private.smi_res)); 544 resource_size(iTCO_wdt_private.smi_res));
518unmap_gcs: 545unmap_gcs_pmc:
519 if (iTCO_wdt_private.iTCO_version == 2) 546 if (iTCO_wdt_private.iTCO_version >= 2)
520 iounmap(iTCO_wdt_private.gcs); 547 iounmap(iTCO_wdt_private.gcs_pmc);
521unreg_gcs: 548unreg_gcs_pmc:
522 if (iTCO_wdt_private.iTCO_version == 2) 549 if (iTCO_wdt_private.iTCO_version >= 2)
523 release_mem_region(iTCO_wdt_private.gcs_res->start, 550 release_mem_region(iTCO_wdt_private.gcs_pmc_res->start,
524 resource_size(iTCO_wdt_private.gcs_res)); 551 resource_size(iTCO_wdt_private.gcs_pmc_res));
525out: 552out:
526 iTCO_wdt_private.tco_res = NULL; 553 iTCO_wdt_private.tco_res = NULL;
527 iTCO_wdt_private.smi_res = NULL; 554 iTCO_wdt_private.smi_res = NULL;
528 iTCO_wdt_private.gcs_res = NULL; 555 iTCO_wdt_private.gcs_pmc_res = NULL;
529 iTCO_wdt_private.gcs = NULL; 556 iTCO_wdt_private.gcs_pmc = NULL;
530 557
531 return ret; 558 return ret;
532} 559}