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