sp5100_tco.c (16017B)
1// SPDX-License-Identifier: GPL-2.0-or-later 2/* 3 * sp5100_tco : TCO timer driver for sp5100 chipsets 4 * 5 * (c) Copyright 2009 Google Inc., All Rights Reserved. 6 * 7 * Based on i8xx_tco.c: 8 * (c) Copyright 2000 kernel concepts <nils@kernelconcepts.de>, All Rights 9 * Reserved. 10 * https://www.kernelconcepts.de 11 * 12 * See AMD Publication 43009 "AMD SB700/710/750 Register Reference Guide", 13 * AMD Publication 44413 "AMD SP5100 Register Reference Guide" 14 * AMD Publication 45482 "AMD SB800-Series Southbridges Register 15 * Reference Guide" 16 * AMD Publication 48751 "BIOS and Kernel Developer’s Guide (BKDG) 17 * for AMD Family 16h Models 00h-0Fh Processors" 18 * AMD Publication 51192 "AMD Bolton FCH Register Reference Guide" 19 * AMD Publication 52740 "BIOS and Kernel Developer’s Guide (BKDG) 20 * for AMD Family 16h Models 30h-3Fh Processors" 21 * AMD Publication 55570-B1-PUB "Processor Programming Reference (PPR) 22 * for AMD Family 17h Model 18h, Revision B1 23 * Processors (PUB) 24 * AMD Publication 55772-A1-PUB "Processor Programming Reference (PPR) 25 * for AMD Family 17h Model 20h, Revision A1 26 * Processors (PUB) 27 */ 28 29/* 30 * Includes, defines, variables, module parameters, ... 31 */ 32 33#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 34 35#include <linux/init.h> 36#include <linux/io.h> 37#include <linux/ioport.h> 38#include <linux/module.h> 39#include <linux/moduleparam.h> 40#include <linux/pci.h> 41#include <linux/platform_device.h> 42#include <linux/types.h> 43#include <linux/watchdog.h> 44 45#include "sp5100_tco.h" 46 47#define TCO_DRIVER_NAME "sp5100-tco" 48 49/* internal variables */ 50 51enum tco_reg_layout { 52 sp5100, sb800, efch, efch_mmio 53}; 54 55struct sp5100_tco { 56 struct watchdog_device wdd; 57 void __iomem *tcobase; 58 enum tco_reg_layout tco_reg_layout; 59}; 60 61/* the watchdog platform device */ 62static struct platform_device *sp5100_tco_platform_device; 63/* the associated PCI device */ 64static struct pci_dev *sp5100_tco_pci; 65 66/* module parameters */ 67 68#define WATCHDOG_HEARTBEAT 60 /* 60 sec default heartbeat. */ 69static int heartbeat = WATCHDOG_HEARTBEAT; /* in seconds */ 70module_param(heartbeat, int, 0); 71MODULE_PARM_DESC(heartbeat, "Watchdog heartbeat in seconds. (default=" 72 __MODULE_STRING(WATCHDOG_HEARTBEAT) ")"); 73 74static bool nowayout = WATCHDOG_NOWAYOUT; 75module_param(nowayout, bool, 0); 76MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started." 77 " (default=" __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); 78 79/* 80 * Some TCO specific functions 81 */ 82 83static enum tco_reg_layout tco_reg_layout(struct pci_dev *dev) 84{ 85 if (dev->vendor == PCI_VENDOR_ID_ATI && 86 dev->device == PCI_DEVICE_ID_ATI_SBX00_SMBUS && 87 dev->revision < 0x40) { 88 return sp5100; 89 } else if (dev->vendor == PCI_VENDOR_ID_AMD && 90 sp5100_tco_pci->device == PCI_DEVICE_ID_AMD_KERNCZ_SMBUS && 91 sp5100_tco_pci->revision >= AMD_ZEN_SMBUS_PCI_REV) { 92 return efch_mmio; 93 } else if (dev->vendor == PCI_VENDOR_ID_AMD && 94 ((dev->device == PCI_DEVICE_ID_AMD_HUDSON2_SMBUS && 95 dev->revision >= 0x41) || 96 (dev->device == PCI_DEVICE_ID_AMD_KERNCZ_SMBUS && 97 dev->revision >= 0x49))) { 98 return efch; 99 } 100 return sb800; 101} 102 103static int tco_timer_start(struct watchdog_device *wdd) 104{ 105 struct sp5100_tco *tco = watchdog_get_drvdata(wdd); 106 u32 val; 107 108 val = readl(SP5100_WDT_CONTROL(tco->tcobase)); 109 val |= SP5100_WDT_START_STOP_BIT; 110 writel(val, SP5100_WDT_CONTROL(tco->tcobase)); 111 112 return 0; 113} 114 115static int tco_timer_stop(struct watchdog_device *wdd) 116{ 117 struct sp5100_tco *tco = watchdog_get_drvdata(wdd); 118 u32 val; 119 120 val = readl(SP5100_WDT_CONTROL(tco->tcobase)); 121 val &= ~SP5100_WDT_START_STOP_BIT; 122 writel(val, SP5100_WDT_CONTROL(tco->tcobase)); 123 124 return 0; 125} 126 127static int tco_timer_ping(struct watchdog_device *wdd) 128{ 129 struct sp5100_tco *tco = watchdog_get_drvdata(wdd); 130 u32 val; 131 132 val = readl(SP5100_WDT_CONTROL(tco->tcobase)); 133 val |= SP5100_WDT_TRIGGER_BIT; 134 writel(val, SP5100_WDT_CONTROL(tco->tcobase)); 135 136 return 0; 137} 138 139static int tco_timer_set_timeout(struct watchdog_device *wdd, 140 unsigned int t) 141{ 142 struct sp5100_tco *tco = watchdog_get_drvdata(wdd); 143 144 /* Write new heartbeat to watchdog */ 145 writel(t, SP5100_WDT_COUNT(tco->tcobase)); 146 147 wdd->timeout = t; 148 149 return 0; 150} 151 152static unsigned int tco_timer_get_timeleft(struct watchdog_device *wdd) 153{ 154 struct sp5100_tco *tco = watchdog_get_drvdata(wdd); 155 156 return readl(SP5100_WDT_COUNT(tco->tcobase)); 157} 158 159static u8 sp5100_tco_read_pm_reg8(u8 index) 160{ 161 outb(index, SP5100_IO_PM_INDEX_REG); 162 return inb(SP5100_IO_PM_DATA_REG); 163} 164 165static void sp5100_tco_update_pm_reg8(u8 index, u8 reset, u8 set) 166{ 167 u8 val; 168 169 outb(index, SP5100_IO_PM_INDEX_REG); 170 val = inb(SP5100_IO_PM_DATA_REG); 171 val &= reset; 172 val |= set; 173 outb(val, SP5100_IO_PM_DATA_REG); 174} 175 176static void tco_timer_enable(struct sp5100_tco *tco) 177{ 178 u32 val; 179 180 switch (tco->tco_reg_layout) { 181 case sb800: 182 /* For SB800 or later */ 183 /* Set the Watchdog timer resolution to 1 sec */ 184 sp5100_tco_update_pm_reg8(SB800_PM_WATCHDOG_CONFIG, 185 0xff, SB800_PM_WATCHDOG_SECOND_RES); 186 187 /* Enable watchdog decode bit and watchdog timer */ 188 sp5100_tco_update_pm_reg8(SB800_PM_WATCHDOG_CONTROL, 189 ~SB800_PM_WATCHDOG_DISABLE, 190 SB800_PCI_WATCHDOG_DECODE_EN); 191 break; 192 case sp5100: 193 /* For SP5100 or SB7x0 */ 194 /* Enable watchdog decode bit */ 195 pci_read_config_dword(sp5100_tco_pci, 196 SP5100_PCI_WATCHDOG_MISC_REG, 197 &val); 198 199 val |= SP5100_PCI_WATCHDOG_DECODE_EN; 200 201 pci_write_config_dword(sp5100_tco_pci, 202 SP5100_PCI_WATCHDOG_MISC_REG, 203 val); 204 205 /* Enable Watchdog timer and set the resolution to 1 sec */ 206 sp5100_tco_update_pm_reg8(SP5100_PM_WATCHDOG_CONTROL, 207 ~SP5100_PM_WATCHDOG_DISABLE, 208 SP5100_PM_WATCHDOG_SECOND_RES); 209 break; 210 case efch: 211 /* Set the Watchdog timer resolution to 1 sec and enable */ 212 sp5100_tco_update_pm_reg8(EFCH_PM_DECODEEN3, 213 ~EFCH_PM_WATCHDOG_DISABLE, 214 EFCH_PM_DECODEEN_SECOND_RES); 215 break; 216 default: 217 break; 218 } 219} 220 221static u32 sp5100_tco_read_pm_reg32(u8 index) 222{ 223 u32 val = 0; 224 int i; 225 226 for (i = 3; i >= 0; i--) 227 val = (val << 8) + sp5100_tco_read_pm_reg8(index + i); 228 229 return val; 230} 231 232static u32 sp5100_tco_request_region(struct device *dev, 233 u32 mmio_addr, 234 const char *dev_name) 235{ 236 if (!devm_request_mem_region(dev, mmio_addr, SP5100_WDT_MEM_MAP_SIZE, 237 dev_name)) { 238 dev_dbg(dev, "MMIO address 0x%08x already in use\n", mmio_addr); 239 return 0; 240 } 241 242 return mmio_addr; 243} 244 245static u32 sp5100_tco_prepare_base(struct sp5100_tco *tco, 246 u32 mmio_addr, 247 u32 alt_mmio_addr, 248 const char *dev_name) 249{ 250 struct device *dev = tco->wdd.parent; 251 252 dev_dbg(dev, "Got 0x%08x from SBResource_MMIO register\n", mmio_addr); 253 254 if (!mmio_addr && !alt_mmio_addr) 255 return -ENODEV; 256 257 /* Check for MMIO address and alternate MMIO address conflicts */ 258 if (mmio_addr) 259 mmio_addr = sp5100_tco_request_region(dev, mmio_addr, dev_name); 260 261 if (!mmio_addr && alt_mmio_addr) 262 mmio_addr = sp5100_tco_request_region(dev, alt_mmio_addr, dev_name); 263 264 if (!mmio_addr) { 265 dev_err(dev, "Failed to reserve MMIO or alternate MMIO region\n"); 266 return -EBUSY; 267 } 268 269 tco->tcobase = devm_ioremap(dev, mmio_addr, SP5100_WDT_MEM_MAP_SIZE); 270 if (!tco->tcobase) { 271 dev_err(dev, "MMIO address 0x%08x failed mapping\n", mmio_addr); 272 devm_release_mem_region(dev, mmio_addr, SP5100_WDT_MEM_MAP_SIZE); 273 return -ENOMEM; 274 } 275 276 dev_info(dev, "Using 0x%08x for watchdog MMIO address\n", mmio_addr); 277 278 return 0; 279} 280 281static int sp5100_tco_timer_init(struct sp5100_tco *tco) 282{ 283 struct watchdog_device *wdd = &tco->wdd; 284 struct device *dev = wdd->parent; 285 u32 val; 286 287 val = readl(SP5100_WDT_CONTROL(tco->tcobase)); 288 if (val & SP5100_WDT_DISABLED) { 289 dev_err(dev, "Watchdog hardware is disabled\n"); 290 return -ENODEV; 291 } 292 293 /* 294 * Save WatchDogFired status, because WatchDogFired flag is 295 * cleared here. 296 */ 297 if (val & SP5100_WDT_FIRED) 298 wdd->bootstatus = WDIOF_CARDRESET; 299 300 /* Set watchdog action to reset the system */ 301 val &= ~SP5100_WDT_ACTION_RESET; 302 writel(val, SP5100_WDT_CONTROL(tco->tcobase)); 303 304 /* Set a reasonable heartbeat before we stop the timer */ 305 tco_timer_set_timeout(wdd, wdd->timeout); 306 307 /* 308 * Stop the TCO before we change anything so we don't race with 309 * a zeroed timer. 310 */ 311 tco_timer_stop(wdd); 312 313 return 0; 314} 315 316static u8 efch_read_pm_reg8(void __iomem *addr, u8 index) 317{ 318 return readb(addr + index); 319} 320 321static void efch_update_pm_reg8(void __iomem *addr, u8 index, u8 reset, u8 set) 322{ 323 u8 val; 324 325 val = readb(addr + index); 326 val &= reset; 327 val |= set; 328 writeb(val, addr + index); 329} 330 331static void tco_timer_enable_mmio(void __iomem *addr) 332{ 333 efch_update_pm_reg8(addr, EFCH_PM_DECODEEN3, 334 ~EFCH_PM_WATCHDOG_DISABLE, 335 EFCH_PM_DECODEEN_SECOND_RES); 336} 337 338static int sp5100_tco_setupdevice_mmio(struct device *dev, 339 struct watchdog_device *wdd) 340{ 341 struct sp5100_tco *tco = watchdog_get_drvdata(wdd); 342 const char *dev_name = SB800_DEVNAME; 343 u32 mmio_addr = 0, alt_mmio_addr = 0; 344 struct resource *res; 345 void __iomem *addr; 346 int ret; 347 u32 val; 348 349 res = request_mem_region_muxed(EFCH_PM_ACPI_MMIO_PM_ADDR, 350 EFCH_PM_ACPI_MMIO_PM_SIZE, 351 "sp5100_tco"); 352 353 if (!res) { 354 dev_err(dev, 355 "Memory region 0x%08x already in use\n", 356 EFCH_PM_ACPI_MMIO_PM_ADDR); 357 return -EBUSY; 358 } 359 360 addr = ioremap(EFCH_PM_ACPI_MMIO_PM_ADDR, EFCH_PM_ACPI_MMIO_PM_SIZE); 361 if (!addr) { 362 dev_err(dev, "Address mapping failed\n"); 363 ret = -ENOMEM; 364 goto out; 365 } 366 367 /* 368 * EFCH_PM_DECODEEN_WDT_TMREN is dual purpose. This bitfield 369 * enables sp5100_tco register MMIO space decoding. The bitfield 370 * also starts the timer operation. Enable if not already enabled. 371 */ 372 val = efch_read_pm_reg8(addr, EFCH_PM_DECODEEN); 373 if (!(val & EFCH_PM_DECODEEN_WDT_TMREN)) { 374 efch_update_pm_reg8(addr, EFCH_PM_DECODEEN, 0xff, 375 EFCH_PM_DECODEEN_WDT_TMREN); 376 } 377 378 /* Error if the timer could not be enabled */ 379 val = efch_read_pm_reg8(addr, EFCH_PM_DECODEEN); 380 if (!(val & EFCH_PM_DECODEEN_WDT_TMREN)) { 381 dev_err(dev, "Failed to enable the timer\n"); 382 ret = -EFAULT; 383 goto out; 384 } 385 386 mmio_addr = EFCH_PM_WDT_ADDR; 387 388 /* Determine alternate MMIO base address */ 389 val = efch_read_pm_reg8(addr, EFCH_PM_ISACONTROL); 390 if (val & EFCH_PM_ISACONTROL_MMIOEN) 391 alt_mmio_addr = EFCH_PM_ACPI_MMIO_ADDR + 392 EFCH_PM_ACPI_MMIO_WDT_OFFSET; 393 394 ret = sp5100_tco_prepare_base(tco, mmio_addr, alt_mmio_addr, dev_name); 395 if (!ret) { 396 tco_timer_enable_mmio(addr); 397 ret = sp5100_tco_timer_init(tco); 398 } 399 400out: 401 if (addr) 402 iounmap(addr); 403 404 release_resource(res); 405 406 return ret; 407} 408 409static int sp5100_tco_setupdevice(struct device *dev, 410 struct watchdog_device *wdd) 411{ 412 struct sp5100_tco *tco = watchdog_get_drvdata(wdd); 413 const char *dev_name; 414 u32 mmio_addr = 0, val; 415 u32 alt_mmio_addr = 0; 416 int ret; 417 418 if (tco->tco_reg_layout == efch_mmio) 419 return sp5100_tco_setupdevice_mmio(dev, wdd); 420 421 /* Request the IO ports used by this driver */ 422 if (!request_muxed_region(SP5100_IO_PM_INDEX_REG, 423 SP5100_PM_IOPORTS_SIZE, "sp5100_tco")) { 424 dev_err(dev, "I/O address 0x%04x already in use\n", 425 SP5100_IO_PM_INDEX_REG); 426 return -EBUSY; 427 } 428 429 /* 430 * Determine type of southbridge chipset. 431 */ 432 switch (tco->tco_reg_layout) { 433 case sp5100: 434 dev_name = SP5100_DEVNAME; 435 mmio_addr = sp5100_tco_read_pm_reg32(SP5100_PM_WATCHDOG_BASE) & 436 0xfffffff8; 437 438 /* 439 * Secondly, find the watchdog timer MMIO address 440 * from SBResource_MMIO register. 441 */ 442 443 /* Read SBResource_MMIO from PCI config(PCI_Reg: 9Ch) */ 444 pci_read_config_dword(sp5100_tco_pci, 445 SP5100_SB_RESOURCE_MMIO_BASE, 446 &val); 447 448 /* Verify MMIO is enabled and using bar0 */ 449 if ((val & SB800_ACPI_MMIO_MASK) == SB800_ACPI_MMIO_DECODE_EN) 450 alt_mmio_addr = (val & ~0xfff) + SB800_PM_WDT_MMIO_OFFSET; 451 break; 452 case sb800: 453 dev_name = SB800_DEVNAME; 454 mmio_addr = sp5100_tco_read_pm_reg32(SB800_PM_WATCHDOG_BASE) & 455 0xfffffff8; 456 457 /* Read SBResource_MMIO from AcpiMmioEn(PM_Reg: 24h) */ 458 val = sp5100_tco_read_pm_reg32(SB800_PM_ACPI_MMIO_EN); 459 460 /* Verify MMIO is enabled and using bar0 */ 461 if ((val & SB800_ACPI_MMIO_MASK) == SB800_ACPI_MMIO_DECODE_EN) 462 alt_mmio_addr = (val & ~0xfff) + SB800_PM_WDT_MMIO_OFFSET; 463 break; 464 case efch: 465 dev_name = SB800_DEVNAME; 466 val = sp5100_tco_read_pm_reg8(EFCH_PM_DECODEEN); 467 if (val & EFCH_PM_DECODEEN_WDT_TMREN) 468 mmio_addr = EFCH_PM_WDT_ADDR; 469 470 val = sp5100_tco_read_pm_reg8(EFCH_PM_ISACONTROL); 471 if (val & EFCH_PM_ISACONTROL_MMIOEN) 472 alt_mmio_addr = EFCH_PM_ACPI_MMIO_ADDR + 473 EFCH_PM_ACPI_MMIO_WDT_OFFSET; 474 break; 475 default: 476 return -ENODEV; 477 } 478 479 ret = sp5100_tco_prepare_base(tco, mmio_addr, alt_mmio_addr, dev_name); 480 if (!ret) { 481 /* Setup the watchdog timer */ 482 tco_timer_enable(tco); 483 ret = sp5100_tco_timer_init(tco); 484 } 485 486 release_region(SP5100_IO_PM_INDEX_REG, SP5100_PM_IOPORTS_SIZE); 487 return ret; 488} 489 490static struct watchdog_info sp5100_tco_wdt_info = { 491 .identity = "SP5100 TCO timer", 492 .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE, 493}; 494 495static const struct watchdog_ops sp5100_tco_wdt_ops = { 496 .owner = THIS_MODULE, 497 .start = tco_timer_start, 498 .stop = tco_timer_stop, 499 .ping = tco_timer_ping, 500 .set_timeout = tco_timer_set_timeout, 501 .get_timeleft = tco_timer_get_timeleft, 502}; 503 504static int sp5100_tco_probe(struct platform_device *pdev) 505{ 506 struct device *dev = &pdev->dev; 507 struct watchdog_device *wdd; 508 struct sp5100_tco *tco; 509 int ret; 510 511 tco = devm_kzalloc(dev, sizeof(*tco), GFP_KERNEL); 512 if (!tco) 513 return -ENOMEM; 514 515 tco->tco_reg_layout = tco_reg_layout(sp5100_tco_pci); 516 517 wdd = &tco->wdd; 518 wdd->parent = dev; 519 wdd->info = &sp5100_tco_wdt_info; 520 wdd->ops = &sp5100_tco_wdt_ops; 521 wdd->timeout = WATCHDOG_HEARTBEAT; 522 wdd->min_timeout = 1; 523 wdd->max_timeout = 0xffff; 524 525 watchdog_init_timeout(wdd, heartbeat, NULL); 526 watchdog_set_nowayout(wdd, nowayout); 527 watchdog_stop_on_reboot(wdd); 528 watchdog_stop_on_unregister(wdd); 529 watchdog_set_drvdata(wdd, tco); 530 531 ret = sp5100_tco_setupdevice(dev, wdd); 532 if (ret) 533 return ret; 534 535 ret = devm_watchdog_register_device(dev, wdd); 536 if (ret) 537 return ret; 538 539 /* Show module parameters */ 540 dev_info(dev, "initialized. heartbeat=%d sec (nowayout=%d)\n", 541 wdd->timeout, nowayout); 542 543 return 0; 544} 545 546static struct platform_driver sp5100_tco_driver = { 547 .probe = sp5100_tco_probe, 548 .driver = { 549 .name = TCO_DRIVER_NAME, 550 }, 551}; 552 553/* 554 * Data for PCI driver interface 555 * 556 * This data only exists for exporting the supported 557 * PCI ids via MODULE_DEVICE_TABLE. We do not actually 558 * register a pci_driver, because someone else might 559 * want to register another driver on the same PCI id. 560 */ 561static const struct pci_device_id sp5100_tco_pci_tbl[] = { 562 { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_SBX00_SMBUS, PCI_ANY_ID, 563 PCI_ANY_ID, }, 564 { PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_HUDSON2_SMBUS, PCI_ANY_ID, 565 PCI_ANY_ID, }, 566 { PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_KERNCZ_SMBUS, PCI_ANY_ID, 567 PCI_ANY_ID, }, 568 { 0, }, /* End of list */ 569}; 570MODULE_DEVICE_TABLE(pci, sp5100_tco_pci_tbl); 571 572static int __init sp5100_tco_init(void) 573{ 574 struct pci_dev *dev = NULL; 575 int err; 576 577 /* Match the PCI device */ 578 for_each_pci_dev(dev) { 579 if (pci_match_id(sp5100_tco_pci_tbl, dev) != NULL) { 580 sp5100_tco_pci = dev; 581 break; 582 } 583 } 584 585 if (!sp5100_tco_pci) 586 return -ENODEV; 587 588 pr_info("SP5100/SB800 TCO WatchDog Timer Driver\n"); 589 590 err = platform_driver_register(&sp5100_tco_driver); 591 if (err) 592 return err; 593 594 sp5100_tco_platform_device = 595 platform_device_register_simple(TCO_DRIVER_NAME, -1, NULL, 0); 596 if (IS_ERR(sp5100_tco_platform_device)) { 597 err = PTR_ERR(sp5100_tco_platform_device); 598 goto unreg_platform_driver; 599 } 600 601 return 0; 602 603unreg_platform_driver: 604 platform_driver_unregister(&sp5100_tco_driver); 605 return err; 606} 607 608static void __exit sp5100_tco_exit(void) 609{ 610 platform_device_unregister(sp5100_tco_platform_device); 611 platform_driver_unregister(&sp5100_tco_driver); 612} 613 614module_init(sp5100_tco_init); 615module_exit(sp5100_tco_exit); 616 617MODULE_AUTHOR("Priyanka Gupta"); 618MODULE_DESCRIPTION("TCO timer driver for SP5100/SB800 chipset"); 619MODULE_LICENSE("GPL");