root/drivers/watchdog/sp5100_tco.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. tco_timer_start
  2. tco_timer_stop
  3. tco_timer_ping
  4. tco_timer_set_timeout
  5. sp5100_tco_read_pm_reg8
  6. sp5100_tco_update_pm_reg8
  7. tco_timer_enable
  8. sp5100_tco_read_pm_reg32
  9. sp5100_tco_setupdevice
  10. sp5100_tco_probe
  11. sp5100_tco_init
  12. sp5100_tco_exit

   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  *                              http://www.kernelconcepts.de
  11  *
  12  *      See AMD Publication 43009 "AMD SB700/710/750 Register Reference Guide",
  13  *          AMD Publication 45482 "AMD SB800-Series Southbridges Register
  14  *                                                            Reference Guide"
  15  *          AMD Publication 48751 "BIOS and Kernel Developer’s Guide (BKDG)
  16  *                              for AMD Family 16h Models 00h-0Fh Processors"
  17  *          AMD Publication 51192 "AMD Bolton FCH Register Reference Guide"
  18  *          AMD Publication 52740 "BIOS and Kernel Developer’s Guide (BKDG)
  19  *                              for AMD Family 16h Models 30h-3Fh Processors"
  20  */
  21 
  22 /*
  23  *      Includes, defines, variables, module parameters, ...
  24  */
  25 
  26 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
  27 
  28 #include <linux/init.h>
  29 #include <linux/io.h>
  30 #include <linux/ioport.h>
  31 #include <linux/module.h>
  32 #include <linux/moduleparam.h>
  33 #include <linux/pci.h>
  34 #include <linux/platform_device.h>
  35 #include <linux/types.h>
  36 #include <linux/watchdog.h>
  37 
  38 #include "sp5100_tco.h"
  39 
  40 #define TCO_DRIVER_NAME "sp5100-tco"
  41 
  42 /* internal variables */
  43 
  44 enum tco_reg_layout {
  45         sp5100, sb800, efch
  46 };
  47 
  48 struct sp5100_tco {
  49         struct watchdog_device wdd;
  50         void __iomem *tcobase;
  51         enum tco_reg_layout tco_reg_layout;
  52 };
  53 
  54 /* the watchdog platform device */
  55 static struct platform_device *sp5100_tco_platform_device;
  56 /* the associated PCI device */
  57 static struct pci_dev *sp5100_tco_pci;
  58 
  59 /* module parameters */
  60 
  61 #define WATCHDOG_HEARTBEAT 60   /* 60 sec default heartbeat. */
  62 static int heartbeat = WATCHDOG_HEARTBEAT;  /* in seconds */
  63 module_param(heartbeat, int, 0);
  64 MODULE_PARM_DESC(heartbeat, "Watchdog heartbeat in seconds. (default="
  65                  __MODULE_STRING(WATCHDOG_HEARTBEAT) ")");
  66 
  67 static bool nowayout = WATCHDOG_NOWAYOUT;
  68 module_param(nowayout, bool, 0);
  69 MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started."
  70                 " (default=" __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
  71 
  72 /*
  73  * Some TCO specific functions
  74  */
  75 
  76 static enum tco_reg_layout tco_reg_layout(struct pci_dev *dev)
  77 {
  78         if (dev->vendor == PCI_VENDOR_ID_ATI &&
  79             dev->device == PCI_DEVICE_ID_ATI_SBX00_SMBUS &&
  80             dev->revision < 0x40) {
  81                 return sp5100;
  82         } else if (dev->vendor == PCI_VENDOR_ID_AMD &&
  83             ((dev->device == PCI_DEVICE_ID_AMD_HUDSON2_SMBUS &&
  84              dev->revision >= 0x41) ||
  85             (dev->device == PCI_DEVICE_ID_AMD_KERNCZ_SMBUS &&
  86              dev->revision >= 0x49))) {
  87                 return efch;
  88         }
  89         return sb800;
  90 }
  91 
  92 static int tco_timer_start(struct watchdog_device *wdd)
  93 {
  94         struct sp5100_tco *tco = watchdog_get_drvdata(wdd);
  95         u32 val;
  96 
  97         val = readl(SP5100_WDT_CONTROL(tco->tcobase));
  98         val |= SP5100_WDT_START_STOP_BIT;
  99         writel(val, SP5100_WDT_CONTROL(tco->tcobase));
 100 
 101         return 0;
 102 }
 103 
 104 static int tco_timer_stop(struct watchdog_device *wdd)
 105 {
 106         struct sp5100_tco *tco = watchdog_get_drvdata(wdd);
 107         u32 val;
 108 
 109         val = readl(SP5100_WDT_CONTROL(tco->tcobase));
 110         val &= ~SP5100_WDT_START_STOP_BIT;
 111         writel(val, SP5100_WDT_CONTROL(tco->tcobase));
 112 
 113         return 0;
 114 }
 115 
 116 static int tco_timer_ping(struct watchdog_device *wdd)
 117 {
 118         struct sp5100_tco *tco = watchdog_get_drvdata(wdd);
 119         u32 val;
 120 
 121         val = readl(SP5100_WDT_CONTROL(tco->tcobase));
 122         val |= SP5100_WDT_TRIGGER_BIT;
 123         writel(val, SP5100_WDT_CONTROL(tco->tcobase));
 124 
 125         return 0;
 126 }
 127 
 128 static int tco_timer_set_timeout(struct watchdog_device *wdd,
 129                                  unsigned int t)
 130 {
 131         struct sp5100_tco *tco = watchdog_get_drvdata(wdd);
 132 
 133         /* Write new heartbeat to watchdog */
 134         writel(t, SP5100_WDT_COUNT(tco->tcobase));
 135 
 136         wdd->timeout = t;
 137 
 138         return 0;
 139 }
 140 
 141 static u8 sp5100_tco_read_pm_reg8(u8 index)
 142 {
 143         outb(index, SP5100_IO_PM_INDEX_REG);
 144         return inb(SP5100_IO_PM_DATA_REG);
 145 }
 146 
 147 static void sp5100_tco_update_pm_reg8(u8 index, u8 reset, u8 set)
 148 {
 149         u8 val;
 150 
 151         outb(index, SP5100_IO_PM_INDEX_REG);
 152         val = inb(SP5100_IO_PM_DATA_REG);
 153         val &= reset;
 154         val |= set;
 155         outb(val, SP5100_IO_PM_DATA_REG);
 156 }
 157 
 158 static void tco_timer_enable(struct sp5100_tco *tco)
 159 {
 160         u32 val;
 161 
 162         switch (tco->tco_reg_layout) {
 163         case sb800:
 164                 /* For SB800 or later */
 165                 /* Set the Watchdog timer resolution to 1 sec */
 166                 sp5100_tco_update_pm_reg8(SB800_PM_WATCHDOG_CONFIG,
 167                                           0xff, SB800_PM_WATCHDOG_SECOND_RES);
 168 
 169                 /* Enable watchdog decode bit and watchdog timer */
 170                 sp5100_tco_update_pm_reg8(SB800_PM_WATCHDOG_CONTROL,
 171                                           ~SB800_PM_WATCHDOG_DISABLE,
 172                                           SB800_PCI_WATCHDOG_DECODE_EN);
 173                 break;
 174         case sp5100:
 175                 /* For SP5100 or SB7x0 */
 176                 /* Enable watchdog decode bit */
 177                 pci_read_config_dword(sp5100_tco_pci,
 178                                       SP5100_PCI_WATCHDOG_MISC_REG,
 179                                       &val);
 180 
 181                 val |= SP5100_PCI_WATCHDOG_DECODE_EN;
 182 
 183                 pci_write_config_dword(sp5100_tco_pci,
 184                                        SP5100_PCI_WATCHDOG_MISC_REG,
 185                                        val);
 186 
 187                 /* Enable Watchdog timer and set the resolution to 1 sec */
 188                 sp5100_tco_update_pm_reg8(SP5100_PM_WATCHDOG_CONTROL,
 189                                           ~SP5100_PM_WATCHDOG_DISABLE,
 190                                           SP5100_PM_WATCHDOG_SECOND_RES);
 191                 break;
 192         case efch:
 193                 /* Set the Watchdog timer resolution to 1 sec and enable */
 194                 sp5100_tco_update_pm_reg8(EFCH_PM_DECODEEN3,
 195                                           ~EFCH_PM_WATCHDOG_DISABLE,
 196                                           EFCH_PM_DECODEEN_SECOND_RES);
 197                 break;
 198         }
 199 }
 200 
 201 static u32 sp5100_tco_read_pm_reg32(u8 index)
 202 {
 203         u32 val = 0;
 204         int i;
 205 
 206         for (i = 3; i >= 0; i--)
 207                 val = (val << 8) + sp5100_tco_read_pm_reg8(index + i);
 208 
 209         return val;
 210 }
 211 
 212 static int sp5100_tco_setupdevice(struct device *dev,
 213                                   struct watchdog_device *wdd)
 214 {
 215         struct sp5100_tco *tco = watchdog_get_drvdata(wdd);
 216         const char *dev_name;
 217         u32 mmio_addr = 0, val;
 218         int ret;
 219 
 220         /* Request the IO ports used by this driver */
 221         if (!request_muxed_region(SP5100_IO_PM_INDEX_REG,
 222                                   SP5100_PM_IOPORTS_SIZE, "sp5100_tco")) {
 223                 dev_err(dev, "I/O address 0x%04x already in use\n",
 224                         SP5100_IO_PM_INDEX_REG);
 225                 return -EBUSY;
 226         }
 227 
 228         /*
 229          * Determine type of southbridge chipset.
 230          */
 231         switch (tco->tco_reg_layout) {
 232         case sp5100:
 233                 dev_name = SP5100_DEVNAME;
 234                 mmio_addr = sp5100_tco_read_pm_reg32(SP5100_PM_WATCHDOG_BASE) &
 235                                                                 0xfffffff8;
 236                 break;
 237         case sb800:
 238                 dev_name = SB800_DEVNAME;
 239                 mmio_addr = sp5100_tco_read_pm_reg32(SB800_PM_WATCHDOG_BASE) &
 240                                                                 0xfffffff8;
 241                 break;
 242         case efch:
 243                 dev_name = SB800_DEVNAME;
 244                 val = sp5100_tco_read_pm_reg8(EFCH_PM_DECODEEN);
 245                 if (val & EFCH_PM_DECODEEN_WDT_TMREN)
 246                         mmio_addr = EFCH_PM_WDT_ADDR;
 247                 break;
 248         default:
 249                 return -ENODEV;
 250         }
 251 
 252         /* Check MMIO address conflict */
 253         if (!mmio_addr ||
 254             !devm_request_mem_region(dev, mmio_addr, SP5100_WDT_MEM_MAP_SIZE,
 255                                      dev_name)) {
 256                 if (mmio_addr)
 257                         dev_dbg(dev, "MMIO address 0x%08x already in use\n",
 258                                 mmio_addr);
 259                 switch (tco->tco_reg_layout) {
 260                 case sp5100:
 261                         /*
 262                          * Secondly, Find the watchdog timer MMIO address
 263                          * from SBResource_MMIO register.
 264                          */
 265                         /* Read SBResource_MMIO from PCI config(PCI_Reg: 9Ch) */
 266                         pci_read_config_dword(sp5100_tco_pci,
 267                                               SP5100_SB_RESOURCE_MMIO_BASE,
 268                                               &mmio_addr);
 269                         if ((mmio_addr & (SB800_ACPI_MMIO_DECODE_EN |
 270                                           SB800_ACPI_MMIO_SEL)) !=
 271                                                   SB800_ACPI_MMIO_DECODE_EN) {
 272                                 ret = -ENODEV;
 273                                 goto unreg_region;
 274                         }
 275                         mmio_addr &= ~0xFFF;
 276                         mmio_addr += SB800_PM_WDT_MMIO_OFFSET;
 277                         break;
 278                 case sb800:
 279                         /* Read SBResource_MMIO from AcpiMmioEn(PM_Reg: 24h) */
 280                         mmio_addr =
 281                                 sp5100_tco_read_pm_reg32(SB800_PM_ACPI_MMIO_EN);
 282                         if ((mmio_addr & (SB800_ACPI_MMIO_DECODE_EN |
 283                                           SB800_ACPI_MMIO_SEL)) !=
 284                                                   SB800_ACPI_MMIO_DECODE_EN) {
 285                                 ret = -ENODEV;
 286                                 goto unreg_region;
 287                         }
 288                         mmio_addr &= ~0xFFF;
 289                         mmio_addr += SB800_PM_WDT_MMIO_OFFSET;
 290                         break;
 291                 case efch:
 292                         val = sp5100_tco_read_pm_reg8(EFCH_PM_ISACONTROL);
 293                         if (!(val & EFCH_PM_ISACONTROL_MMIOEN)) {
 294                                 ret = -ENODEV;
 295                                 goto unreg_region;
 296                         }
 297                         mmio_addr = EFCH_PM_ACPI_MMIO_ADDR +
 298                                     EFCH_PM_ACPI_MMIO_WDT_OFFSET;
 299                         break;
 300                 }
 301                 dev_dbg(dev, "Got 0x%08x from SBResource_MMIO register\n",
 302                         mmio_addr);
 303                 if (!devm_request_mem_region(dev, mmio_addr,
 304                                              SP5100_WDT_MEM_MAP_SIZE,
 305                                              dev_name)) {
 306                         dev_dbg(dev, "MMIO address 0x%08x already in use\n",
 307                                 mmio_addr);
 308                         ret = -EBUSY;
 309                         goto unreg_region;
 310                 }
 311         }
 312 
 313         tco->tcobase = devm_ioremap(dev, mmio_addr, SP5100_WDT_MEM_MAP_SIZE);
 314         if (!tco->tcobase) {
 315                 dev_err(dev, "failed to get tcobase address\n");
 316                 ret = -ENOMEM;
 317                 goto unreg_region;
 318         }
 319 
 320         dev_info(dev, "Using 0x%08x for watchdog MMIO address\n", mmio_addr);
 321 
 322         /* Setup the watchdog timer */
 323         tco_timer_enable(tco);
 324 
 325         val = readl(SP5100_WDT_CONTROL(tco->tcobase));
 326         if (val & SP5100_WDT_DISABLED) {
 327                 dev_err(dev, "Watchdog hardware is disabled\n");
 328                 ret = -ENODEV;
 329                 goto unreg_region;
 330         }
 331 
 332         /*
 333          * Save WatchDogFired status, because WatchDogFired flag is
 334          * cleared here.
 335          */
 336         if (val & SP5100_WDT_FIRED)
 337                 wdd->bootstatus = WDIOF_CARDRESET;
 338         /* Set watchdog action to reset the system */
 339         val &= ~SP5100_WDT_ACTION_RESET;
 340         writel(val, SP5100_WDT_CONTROL(tco->tcobase));
 341 
 342         /* Set a reasonable heartbeat before we stop the timer */
 343         tco_timer_set_timeout(wdd, wdd->timeout);
 344 
 345         /*
 346          * Stop the TCO before we change anything so we don't race with
 347          * a zeroed timer.
 348          */
 349         tco_timer_stop(wdd);
 350 
 351         release_region(SP5100_IO_PM_INDEX_REG, SP5100_PM_IOPORTS_SIZE);
 352 
 353         return 0;
 354 
 355 unreg_region:
 356         release_region(SP5100_IO_PM_INDEX_REG, SP5100_PM_IOPORTS_SIZE);
 357         return ret;
 358 }
 359 
 360 static struct watchdog_info sp5100_tco_wdt_info = {
 361         .identity = "SP5100 TCO timer",
 362         .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE,
 363 };
 364 
 365 static const struct watchdog_ops sp5100_tco_wdt_ops = {
 366         .owner = THIS_MODULE,
 367         .start = tco_timer_start,
 368         .stop = tco_timer_stop,
 369         .ping = tco_timer_ping,
 370         .set_timeout = tco_timer_set_timeout,
 371 };
 372 
 373 static int sp5100_tco_probe(struct platform_device *pdev)
 374 {
 375         struct device *dev = &pdev->dev;
 376         struct watchdog_device *wdd;
 377         struct sp5100_tco *tco;
 378         int ret;
 379 
 380         tco = devm_kzalloc(dev, sizeof(*tco), GFP_KERNEL);
 381         if (!tco)
 382                 return -ENOMEM;
 383 
 384         tco->tco_reg_layout = tco_reg_layout(sp5100_tco_pci);
 385 
 386         wdd = &tco->wdd;
 387         wdd->parent = dev;
 388         wdd->info = &sp5100_tco_wdt_info;
 389         wdd->ops = &sp5100_tco_wdt_ops;
 390         wdd->timeout = WATCHDOG_HEARTBEAT;
 391         wdd->min_timeout = 1;
 392         wdd->max_timeout = 0xffff;
 393 
 394         watchdog_init_timeout(wdd, heartbeat, NULL);
 395         watchdog_set_nowayout(wdd, nowayout);
 396         watchdog_stop_on_reboot(wdd);
 397         watchdog_stop_on_unregister(wdd);
 398         watchdog_set_drvdata(wdd, tco);
 399 
 400         ret = sp5100_tco_setupdevice(dev, wdd);
 401         if (ret)
 402                 return ret;
 403 
 404         ret = devm_watchdog_register_device(dev, wdd);
 405         if (ret)
 406                 return ret;
 407 
 408         /* Show module parameters */
 409         dev_info(dev, "initialized. heartbeat=%d sec (nowayout=%d)\n",
 410                  wdd->timeout, nowayout);
 411 
 412         return 0;
 413 }
 414 
 415 static struct platform_driver sp5100_tco_driver = {
 416         .probe          = sp5100_tco_probe,
 417         .driver         = {
 418                 .name   = TCO_DRIVER_NAME,
 419         },
 420 };
 421 
 422 /*
 423  * Data for PCI driver interface
 424  *
 425  * This data only exists for exporting the supported
 426  * PCI ids via MODULE_DEVICE_TABLE.  We do not actually
 427  * register a pci_driver, because someone else might
 428  * want to register another driver on the same PCI id.
 429  */
 430 static const struct pci_device_id sp5100_tco_pci_tbl[] = {
 431         { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_SBX00_SMBUS, PCI_ANY_ID,
 432           PCI_ANY_ID, },
 433         { PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_HUDSON2_SMBUS, PCI_ANY_ID,
 434           PCI_ANY_ID, },
 435         { PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_KERNCZ_SMBUS, PCI_ANY_ID,
 436           PCI_ANY_ID, },
 437         { 0, },                 /* End of list */
 438 };
 439 MODULE_DEVICE_TABLE(pci, sp5100_tco_pci_tbl);
 440 
 441 static int __init sp5100_tco_init(void)
 442 {
 443         struct pci_dev *dev = NULL;
 444         int err;
 445 
 446         /* Match the PCI device */
 447         for_each_pci_dev(dev) {
 448                 if (pci_match_id(sp5100_tco_pci_tbl, dev) != NULL) {
 449                         sp5100_tco_pci = dev;
 450                         break;
 451                 }
 452         }
 453 
 454         if (!sp5100_tco_pci)
 455                 return -ENODEV;
 456 
 457         pr_info("SP5100/SB800 TCO WatchDog Timer Driver\n");
 458 
 459         err = platform_driver_register(&sp5100_tco_driver);
 460         if (err)
 461                 return err;
 462 
 463         sp5100_tco_platform_device =
 464                 platform_device_register_simple(TCO_DRIVER_NAME, -1, NULL, 0);
 465         if (IS_ERR(sp5100_tco_platform_device)) {
 466                 err = PTR_ERR(sp5100_tco_platform_device);
 467                 goto unreg_platform_driver;
 468         }
 469 
 470         return 0;
 471 
 472 unreg_platform_driver:
 473         platform_driver_unregister(&sp5100_tco_driver);
 474         return err;
 475 }
 476 
 477 static void __exit sp5100_tco_exit(void)
 478 {
 479         platform_device_unregister(sp5100_tco_platform_device);
 480         platform_driver_unregister(&sp5100_tco_driver);
 481 }
 482 
 483 module_init(sp5100_tco_init);
 484 module_exit(sp5100_tco_exit);
 485 
 486 MODULE_AUTHOR("Priyanka Gupta");
 487 MODULE_DESCRIPTION("TCO timer driver for SP5100/SB800 chipset");
 488 MODULE_LICENSE("GPL");

/* [<][>][^][v][top][bottom][index][help] */