Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-or-later
0002 /*
0003  *  sp5100_tco :    TCO timer driver for sp5100 chipsets
0004  *
0005  *  (c) Copyright 2009 Google Inc., All Rights Reserved.
0006  *
0007  *  Based on i8xx_tco.c:
0008  *  (c) Copyright 2000 kernel concepts <nils@kernelconcepts.de>, All Rights
0009  *  Reserved.
0010  *              https://www.kernelconcepts.de
0011  *
0012  *  See AMD Publication 43009 "AMD SB700/710/750 Register Reference Guide",
0013  *      AMD Publication 44413 "AMD SP5100 Register Reference Guide"
0014  *      AMD Publication 45482 "AMD SB800-Series Southbridges Register
0015  *                                                        Reference Guide"
0016  *      AMD Publication 48751 "BIOS and Kernel Developer’s Guide (BKDG)
0017  *              for AMD Family 16h Models 00h-0Fh Processors"
0018  *      AMD Publication 51192 "AMD Bolton FCH Register Reference Guide"
0019  *      AMD Publication 52740 "BIOS and Kernel Developer’s Guide (BKDG)
0020  *              for AMD Family 16h Models 30h-3Fh Processors"
0021  *      AMD Publication 55570-B1-PUB "Processor Programming Reference (PPR)
0022  *              for AMD Family 17h Model 18h, Revision B1
0023  *              Processors (PUB)
0024  *      AMD Publication 55772-A1-PUB "Processor Programming Reference (PPR)
0025  *              for AMD Family 17h Model 20h, Revision A1
0026  *              Processors (PUB)
0027  */
0028 
0029 /*
0030  *  Includes, defines, variables, module parameters, ...
0031  */
0032 
0033 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
0034 
0035 #include <linux/init.h>
0036 #include <linux/io.h>
0037 #include <linux/ioport.h>
0038 #include <linux/module.h>
0039 #include <linux/moduleparam.h>
0040 #include <linux/pci.h>
0041 #include <linux/platform_device.h>
0042 #include <linux/types.h>
0043 #include <linux/watchdog.h>
0044 
0045 #include "sp5100_tco.h"
0046 
0047 #define TCO_DRIVER_NAME "sp5100-tco"
0048 
0049 /* internal variables */
0050 
0051 enum tco_reg_layout {
0052     sp5100, sb800, efch, efch_mmio
0053 };
0054 
0055 struct sp5100_tco {
0056     struct watchdog_device wdd;
0057     void __iomem *tcobase;
0058     enum tco_reg_layout tco_reg_layout;
0059 };
0060 
0061 /* the watchdog platform device */
0062 static struct platform_device *sp5100_tco_platform_device;
0063 /* the associated PCI device */
0064 static struct pci_dev *sp5100_tco_pci;
0065 
0066 /* module parameters */
0067 
0068 #define WATCHDOG_HEARTBEAT 60   /* 60 sec default heartbeat. */
0069 static int heartbeat = WATCHDOG_HEARTBEAT;  /* in seconds */
0070 module_param(heartbeat, int, 0);
0071 MODULE_PARM_DESC(heartbeat, "Watchdog heartbeat in seconds. (default="
0072          __MODULE_STRING(WATCHDOG_HEARTBEAT) ")");
0073 
0074 static bool nowayout = WATCHDOG_NOWAYOUT;
0075 module_param(nowayout, bool, 0);
0076 MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started."
0077         " (default=" __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
0078 
0079 /*
0080  * Some TCO specific functions
0081  */
0082 
0083 static enum tco_reg_layout tco_reg_layout(struct pci_dev *dev)
0084 {
0085     if (dev->vendor == PCI_VENDOR_ID_ATI &&
0086         dev->device == PCI_DEVICE_ID_ATI_SBX00_SMBUS &&
0087         dev->revision < 0x40) {
0088         return sp5100;
0089     } else if (dev->vendor == PCI_VENDOR_ID_AMD &&
0090         sp5100_tco_pci->device == PCI_DEVICE_ID_AMD_KERNCZ_SMBUS &&
0091         sp5100_tco_pci->revision >= AMD_ZEN_SMBUS_PCI_REV) {
0092         return efch_mmio;
0093     } else if (dev->vendor == PCI_VENDOR_ID_AMD &&
0094         ((dev->device == PCI_DEVICE_ID_AMD_HUDSON2_SMBUS &&
0095          dev->revision >= 0x41) ||
0096         (dev->device == PCI_DEVICE_ID_AMD_KERNCZ_SMBUS &&
0097          dev->revision >= 0x49))) {
0098         return efch;
0099     }
0100     return sb800;
0101 }
0102 
0103 static int tco_timer_start(struct watchdog_device *wdd)
0104 {
0105     struct sp5100_tco *tco = watchdog_get_drvdata(wdd);
0106     u32 val;
0107 
0108     val = readl(SP5100_WDT_CONTROL(tco->tcobase));
0109     val |= SP5100_WDT_START_STOP_BIT;
0110     writel(val, SP5100_WDT_CONTROL(tco->tcobase));
0111 
0112     return 0;
0113 }
0114 
0115 static int tco_timer_stop(struct watchdog_device *wdd)
0116 {
0117     struct sp5100_tco *tco = watchdog_get_drvdata(wdd);
0118     u32 val;
0119 
0120     val = readl(SP5100_WDT_CONTROL(tco->tcobase));
0121     val &= ~SP5100_WDT_START_STOP_BIT;
0122     writel(val, SP5100_WDT_CONTROL(tco->tcobase));
0123 
0124     return 0;
0125 }
0126 
0127 static int tco_timer_ping(struct watchdog_device *wdd)
0128 {
0129     struct sp5100_tco *tco = watchdog_get_drvdata(wdd);
0130     u32 val;
0131 
0132     val = readl(SP5100_WDT_CONTROL(tco->tcobase));
0133     val |= SP5100_WDT_TRIGGER_BIT;
0134     writel(val, SP5100_WDT_CONTROL(tco->tcobase));
0135 
0136     return 0;
0137 }
0138 
0139 static int tco_timer_set_timeout(struct watchdog_device *wdd,
0140                  unsigned int t)
0141 {
0142     struct sp5100_tco *tco = watchdog_get_drvdata(wdd);
0143 
0144     /* Write new heartbeat to watchdog */
0145     writel(t, SP5100_WDT_COUNT(tco->tcobase));
0146 
0147     wdd->timeout = t;
0148 
0149     return 0;
0150 }
0151 
0152 static unsigned int tco_timer_get_timeleft(struct watchdog_device *wdd)
0153 {
0154     struct sp5100_tco *tco = watchdog_get_drvdata(wdd);
0155 
0156     return readl(SP5100_WDT_COUNT(tco->tcobase));
0157 }
0158 
0159 static u8 sp5100_tco_read_pm_reg8(u8 index)
0160 {
0161     outb(index, SP5100_IO_PM_INDEX_REG);
0162     return inb(SP5100_IO_PM_DATA_REG);
0163 }
0164 
0165 static void sp5100_tco_update_pm_reg8(u8 index, u8 reset, u8 set)
0166 {
0167     u8 val;
0168 
0169     outb(index, SP5100_IO_PM_INDEX_REG);
0170     val = inb(SP5100_IO_PM_DATA_REG);
0171     val &= reset;
0172     val |= set;
0173     outb(val, SP5100_IO_PM_DATA_REG);
0174 }
0175 
0176 static void tco_timer_enable(struct sp5100_tco *tco)
0177 {
0178     u32 val;
0179 
0180     switch (tco->tco_reg_layout) {
0181     case sb800:
0182         /* For SB800 or later */
0183         /* Set the Watchdog timer resolution to 1 sec */
0184         sp5100_tco_update_pm_reg8(SB800_PM_WATCHDOG_CONFIG,
0185                       0xff, SB800_PM_WATCHDOG_SECOND_RES);
0186 
0187         /* Enable watchdog decode bit and watchdog timer */
0188         sp5100_tco_update_pm_reg8(SB800_PM_WATCHDOG_CONTROL,
0189                       ~SB800_PM_WATCHDOG_DISABLE,
0190                       SB800_PCI_WATCHDOG_DECODE_EN);
0191         break;
0192     case sp5100:
0193         /* For SP5100 or SB7x0 */
0194         /* Enable watchdog decode bit */
0195         pci_read_config_dword(sp5100_tco_pci,
0196                       SP5100_PCI_WATCHDOG_MISC_REG,
0197                       &val);
0198 
0199         val |= SP5100_PCI_WATCHDOG_DECODE_EN;
0200 
0201         pci_write_config_dword(sp5100_tco_pci,
0202                        SP5100_PCI_WATCHDOG_MISC_REG,
0203                        val);
0204 
0205         /* Enable Watchdog timer and set the resolution to 1 sec */
0206         sp5100_tco_update_pm_reg8(SP5100_PM_WATCHDOG_CONTROL,
0207                       ~SP5100_PM_WATCHDOG_DISABLE,
0208                       SP5100_PM_WATCHDOG_SECOND_RES);
0209         break;
0210     case efch:
0211         /* Set the Watchdog timer resolution to 1 sec and enable */
0212         sp5100_tco_update_pm_reg8(EFCH_PM_DECODEEN3,
0213                       ~EFCH_PM_WATCHDOG_DISABLE,
0214                       EFCH_PM_DECODEEN_SECOND_RES);
0215         break;
0216     default:
0217         break;
0218     }
0219 }
0220 
0221 static u32 sp5100_tco_read_pm_reg32(u8 index)
0222 {
0223     u32 val = 0;
0224     int i;
0225 
0226     for (i = 3; i >= 0; i--)
0227         val = (val << 8) + sp5100_tco_read_pm_reg8(index + i);
0228 
0229     return val;
0230 }
0231 
0232 static u32 sp5100_tco_request_region(struct device *dev,
0233                      u32 mmio_addr,
0234                      const char *dev_name)
0235 {
0236     if (!devm_request_mem_region(dev, mmio_addr, SP5100_WDT_MEM_MAP_SIZE,
0237                      dev_name)) {
0238         dev_dbg(dev, "MMIO address 0x%08x already in use\n", mmio_addr);
0239         return 0;
0240     }
0241 
0242     return mmio_addr;
0243 }
0244 
0245 static u32 sp5100_tco_prepare_base(struct sp5100_tco *tco,
0246                    u32 mmio_addr,
0247                    u32 alt_mmio_addr,
0248                    const char *dev_name)
0249 {
0250     struct device *dev = tco->wdd.parent;
0251 
0252     dev_dbg(dev, "Got 0x%08x from SBResource_MMIO register\n", mmio_addr);
0253 
0254     if (!mmio_addr && !alt_mmio_addr)
0255         return -ENODEV;
0256 
0257     /* Check for MMIO address and alternate MMIO address conflicts */
0258     if (mmio_addr)
0259         mmio_addr = sp5100_tco_request_region(dev, mmio_addr, dev_name);
0260 
0261     if (!mmio_addr && alt_mmio_addr)
0262         mmio_addr = sp5100_tco_request_region(dev, alt_mmio_addr, dev_name);
0263 
0264     if (!mmio_addr) {
0265         dev_err(dev, "Failed to reserve MMIO or alternate MMIO region\n");
0266         return -EBUSY;
0267     }
0268 
0269     tco->tcobase = devm_ioremap(dev, mmio_addr, SP5100_WDT_MEM_MAP_SIZE);
0270     if (!tco->tcobase) {
0271         dev_err(dev, "MMIO address 0x%08x failed mapping\n", mmio_addr);
0272         devm_release_mem_region(dev, mmio_addr, SP5100_WDT_MEM_MAP_SIZE);
0273         return -ENOMEM;
0274     }
0275 
0276     dev_info(dev, "Using 0x%08x for watchdog MMIO address\n", mmio_addr);
0277 
0278     return 0;
0279 }
0280 
0281 static int sp5100_tco_timer_init(struct sp5100_tco *tco)
0282 {
0283     struct watchdog_device *wdd = &tco->wdd;
0284     struct device *dev = wdd->parent;
0285     u32 val;
0286 
0287     val = readl(SP5100_WDT_CONTROL(tco->tcobase));
0288     if (val & SP5100_WDT_DISABLED) {
0289         dev_err(dev, "Watchdog hardware is disabled\n");
0290         return -ENODEV;
0291     }
0292 
0293     /*
0294      * Save WatchDogFired status, because WatchDogFired flag is
0295      * cleared here.
0296      */
0297     if (val & SP5100_WDT_FIRED)
0298         wdd->bootstatus = WDIOF_CARDRESET;
0299 
0300     /* Set watchdog action to reset the system */
0301     val &= ~SP5100_WDT_ACTION_RESET;
0302     writel(val, SP5100_WDT_CONTROL(tco->tcobase));
0303 
0304     /* Set a reasonable heartbeat before we stop the timer */
0305     tco_timer_set_timeout(wdd, wdd->timeout);
0306 
0307     /*
0308      * Stop the TCO before we change anything so we don't race with
0309      * a zeroed timer.
0310      */
0311     tco_timer_stop(wdd);
0312 
0313     return 0;
0314 }
0315 
0316 static u8 efch_read_pm_reg8(void __iomem *addr, u8 index)
0317 {
0318     return readb(addr + index);
0319 }
0320 
0321 static void efch_update_pm_reg8(void __iomem *addr, u8 index, u8 reset, u8 set)
0322 {
0323     u8 val;
0324 
0325     val = readb(addr + index);
0326     val &= reset;
0327     val |= set;
0328     writeb(val, addr + index);
0329 }
0330 
0331 static void tco_timer_enable_mmio(void __iomem *addr)
0332 {
0333     efch_update_pm_reg8(addr, EFCH_PM_DECODEEN3,
0334                 ~EFCH_PM_WATCHDOG_DISABLE,
0335                 EFCH_PM_DECODEEN_SECOND_RES);
0336 }
0337 
0338 static int sp5100_tco_setupdevice_mmio(struct device *dev,
0339                        struct watchdog_device *wdd)
0340 {
0341     struct sp5100_tco *tco = watchdog_get_drvdata(wdd);
0342     const char *dev_name = SB800_DEVNAME;
0343     u32 mmio_addr = 0, alt_mmio_addr = 0;
0344     struct resource *res;
0345     void __iomem *addr;
0346     int ret;
0347     u32 val;
0348 
0349     res = request_mem_region_muxed(EFCH_PM_ACPI_MMIO_PM_ADDR,
0350                        EFCH_PM_ACPI_MMIO_PM_SIZE,
0351                        "sp5100_tco");
0352 
0353     if (!res) {
0354         dev_err(dev,
0355             "Memory region 0x%08x already in use\n",
0356             EFCH_PM_ACPI_MMIO_PM_ADDR);
0357         return -EBUSY;
0358     }
0359 
0360     addr = ioremap(EFCH_PM_ACPI_MMIO_PM_ADDR, EFCH_PM_ACPI_MMIO_PM_SIZE);
0361     if (!addr) {
0362         dev_err(dev, "Address mapping failed\n");
0363         ret = -ENOMEM;
0364         goto out;
0365     }
0366 
0367     /*
0368      * EFCH_PM_DECODEEN_WDT_TMREN is dual purpose. This bitfield
0369      * enables sp5100_tco register MMIO space decoding. The bitfield
0370      * also starts the timer operation. Enable if not already enabled.
0371      */
0372     val = efch_read_pm_reg8(addr, EFCH_PM_DECODEEN);
0373     if (!(val & EFCH_PM_DECODEEN_WDT_TMREN)) {
0374         efch_update_pm_reg8(addr, EFCH_PM_DECODEEN, 0xff,
0375                     EFCH_PM_DECODEEN_WDT_TMREN);
0376     }
0377 
0378     /* Error if the timer could not be enabled */
0379     val = efch_read_pm_reg8(addr, EFCH_PM_DECODEEN);
0380     if (!(val & EFCH_PM_DECODEEN_WDT_TMREN)) {
0381         dev_err(dev, "Failed to enable the timer\n");
0382         ret = -EFAULT;
0383         goto out;
0384     }
0385 
0386     mmio_addr = EFCH_PM_WDT_ADDR;
0387 
0388     /* Determine alternate MMIO base address */
0389     val = efch_read_pm_reg8(addr, EFCH_PM_ISACONTROL);
0390     if (val & EFCH_PM_ISACONTROL_MMIOEN)
0391         alt_mmio_addr = EFCH_PM_ACPI_MMIO_ADDR +
0392             EFCH_PM_ACPI_MMIO_WDT_OFFSET;
0393 
0394     ret = sp5100_tco_prepare_base(tco, mmio_addr, alt_mmio_addr, dev_name);
0395     if (!ret) {
0396         tco_timer_enable_mmio(addr);
0397         ret = sp5100_tco_timer_init(tco);
0398     }
0399 
0400 out:
0401     if (addr)
0402         iounmap(addr);
0403 
0404     release_resource(res);
0405     kfree(res);
0406 
0407     return ret;
0408 }
0409 
0410 static int sp5100_tco_setupdevice(struct device *dev,
0411                   struct watchdog_device *wdd)
0412 {
0413     struct sp5100_tco *tco = watchdog_get_drvdata(wdd);
0414     const char *dev_name;
0415     u32 mmio_addr = 0, val;
0416     u32 alt_mmio_addr = 0;
0417     int ret;
0418 
0419     if (tco->tco_reg_layout == efch_mmio)
0420         return sp5100_tco_setupdevice_mmio(dev, wdd);
0421 
0422     /* Request the IO ports used by this driver */
0423     if (!request_muxed_region(SP5100_IO_PM_INDEX_REG,
0424                   SP5100_PM_IOPORTS_SIZE, "sp5100_tco")) {
0425         dev_err(dev, "I/O address 0x%04x already in use\n",
0426             SP5100_IO_PM_INDEX_REG);
0427         return -EBUSY;
0428     }
0429 
0430     /*
0431      * Determine type of southbridge chipset.
0432      */
0433     switch (tco->tco_reg_layout) {
0434     case sp5100:
0435         dev_name = SP5100_DEVNAME;
0436         mmio_addr = sp5100_tco_read_pm_reg32(SP5100_PM_WATCHDOG_BASE) &
0437                                 0xfffffff8;
0438 
0439         /*
0440          * Secondly, find the watchdog timer MMIO address
0441          * from SBResource_MMIO register.
0442          */
0443 
0444         /* Read SBResource_MMIO from PCI config(PCI_Reg: 9Ch) */
0445         pci_read_config_dword(sp5100_tco_pci,
0446                       SP5100_SB_RESOURCE_MMIO_BASE,
0447                       &val);
0448 
0449         /* Verify MMIO is enabled and using bar0 */
0450         if ((val & SB800_ACPI_MMIO_MASK) == SB800_ACPI_MMIO_DECODE_EN)
0451             alt_mmio_addr = (val & ~0xfff) + SB800_PM_WDT_MMIO_OFFSET;
0452         break;
0453     case sb800:
0454         dev_name = SB800_DEVNAME;
0455         mmio_addr = sp5100_tco_read_pm_reg32(SB800_PM_WATCHDOG_BASE) &
0456                                 0xfffffff8;
0457 
0458         /* Read SBResource_MMIO from AcpiMmioEn(PM_Reg: 24h) */
0459         val = sp5100_tco_read_pm_reg32(SB800_PM_ACPI_MMIO_EN);
0460 
0461         /* Verify MMIO is enabled and using bar0 */
0462         if ((val & SB800_ACPI_MMIO_MASK) == SB800_ACPI_MMIO_DECODE_EN)
0463             alt_mmio_addr = (val & ~0xfff) + SB800_PM_WDT_MMIO_OFFSET;
0464         break;
0465     case efch:
0466         dev_name = SB800_DEVNAME;
0467         val = sp5100_tco_read_pm_reg8(EFCH_PM_DECODEEN);
0468         if (val & EFCH_PM_DECODEEN_WDT_TMREN)
0469             mmio_addr = EFCH_PM_WDT_ADDR;
0470 
0471         val = sp5100_tco_read_pm_reg8(EFCH_PM_ISACONTROL);
0472         if (val & EFCH_PM_ISACONTROL_MMIOEN)
0473             alt_mmio_addr = EFCH_PM_ACPI_MMIO_ADDR +
0474                 EFCH_PM_ACPI_MMIO_WDT_OFFSET;
0475         break;
0476     default:
0477         return -ENODEV;
0478     }
0479 
0480     ret = sp5100_tco_prepare_base(tco, mmio_addr, alt_mmio_addr, dev_name);
0481     if (!ret) {
0482         /* Setup the watchdog timer */
0483         tco_timer_enable(tco);
0484         ret = sp5100_tco_timer_init(tco);
0485     }
0486 
0487     release_region(SP5100_IO_PM_INDEX_REG, SP5100_PM_IOPORTS_SIZE);
0488     return ret;
0489 }
0490 
0491 static struct watchdog_info sp5100_tco_wdt_info = {
0492     .identity = "SP5100 TCO timer",
0493     .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE,
0494 };
0495 
0496 static const struct watchdog_ops sp5100_tco_wdt_ops = {
0497     .owner = THIS_MODULE,
0498     .start = tco_timer_start,
0499     .stop = tco_timer_stop,
0500     .ping = tco_timer_ping,
0501     .set_timeout = tco_timer_set_timeout,
0502     .get_timeleft = tco_timer_get_timeleft,
0503 };
0504 
0505 static int sp5100_tco_probe(struct platform_device *pdev)
0506 {
0507     struct device *dev = &pdev->dev;
0508     struct watchdog_device *wdd;
0509     struct sp5100_tco *tco;
0510     int ret;
0511 
0512     tco = devm_kzalloc(dev, sizeof(*tco), GFP_KERNEL);
0513     if (!tco)
0514         return -ENOMEM;
0515 
0516     tco->tco_reg_layout = tco_reg_layout(sp5100_tco_pci);
0517 
0518     wdd = &tco->wdd;
0519     wdd->parent = dev;
0520     wdd->info = &sp5100_tco_wdt_info;
0521     wdd->ops = &sp5100_tco_wdt_ops;
0522     wdd->timeout = WATCHDOG_HEARTBEAT;
0523     wdd->min_timeout = 1;
0524     wdd->max_timeout = 0xffff;
0525 
0526     watchdog_init_timeout(wdd, heartbeat, NULL);
0527     watchdog_set_nowayout(wdd, nowayout);
0528     watchdog_stop_on_reboot(wdd);
0529     watchdog_stop_on_unregister(wdd);
0530     watchdog_set_drvdata(wdd, tco);
0531 
0532     ret = sp5100_tco_setupdevice(dev, wdd);
0533     if (ret)
0534         return ret;
0535 
0536     ret = devm_watchdog_register_device(dev, wdd);
0537     if (ret)
0538         return ret;
0539 
0540     /* Show module parameters */
0541     dev_info(dev, "initialized. heartbeat=%d sec (nowayout=%d)\n",
0542          wdd->timeout, nowayout);
0543 
0544     return 0;
0545 }
0546 
0547 static struct platform_driver sp5100_tco_driver = {
0548     .probe      = sp5100_tco_probe,
0549     .driver     = {
0550         .name   = TCO_DRIVER_NAME,
0551     },
0552 };
0553 
0554 /*
0555  * Data for PCI driver interface
0556  *
0557  * This data only exists for exporting the supported
0558  * PCI ids via MODULE_DEVICE_TABLE.  We do not actually
0559  * register a pci_driver, because someone else might
0560  * want to register another driver on the same PCI id.
0561  */
0562 static const struct pci_device_id sp5100_tco_pci_tbl[] = {
0563     { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_SBX00_SMBUS, PCI_ANY_ID,
0564       PCI_ANY_ID, },
0565     { PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_HUDSON2_SMBUS, PCI_ANY_ID,
0566       PCI_ANY_ID, },
0567     { PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_KERNCZ_SMBUS, PCI_ANY_ID,
0568       PCI_ANY_ID, },
0569     { 0, },         /* End of list */
0570 };
0571 MODULE_DEVICE_TABLE(pci, sp5100_tco_pci_tbl);
0572 
0573 static int __init sp5100_tco_init(void)
0574 {
0575     struct pci_dev *dev = NULL;
0576     int err;
0577 
0578     /* Match the PCI device */
0579     for_each_pci_dev(dev) {
0580         if (pci_match_id(sp5100_tco_pci_tbl, dev) != NULL) {
0581             sp5100_tco_pci = dev;
0582             break;
0583         }
0584     }
0585 
0586     if (!sp5100_tco_pci)
0587         return -ENODEV;
0588 
0589     pr_info("SP5100/SB800 TCO WatchDog Timer Driver\n");
0590 
0591     err = platform_driver_register(&sp5100_tco_driver);
0592     if (err)
0593         return err;
0594 
0595     sp5100_tco_platform_device =
0596         platform_device_register_simple(TCO_DRIVER_NAME, -1, NULL, 0);
0597     if (IS_ERR(sp5100_tco_platform_device)) {
0598         err = PTR_ERR(sp5100_tco_platform_device);
0599         goto unreg_platform_driver;
0600     }
0601 
0602     return 0;
0603 
0604 unreg_platform_driver:
0605     platform_driver_unregister(&sp5100_tco_driver);
0606     return err;
0607 }
0608 
0609 static void __exit sp5100_tco_exit(void)
0610 {
0611     platform_device_unregister(sp5100_tco_platform_device);
0612     platform_driver_unregister(&sp5100_tco_driver);
0613 }
0614 
0615 module_init(sp5100_tco_init);
0616 module_exit(sp5100_tco_exit);
0617 
0618 MODULE_AUTHOR("Priyanka Gupta");
0619 MODULE_DESCRIPTION("TCO timer driver for SP5100/SB800 chipset");
0620 MODULE_LICENSE("GPL");