0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011
0012
0013
0014
0015
0016
0017
0018
0019
0020
0021
0022
0023
0024
0025
0026
0027
0028
0029
0030
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
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
0062 static struct platform_device *sp5100_tco_platform_device;
0063
0064 static struct pci_dev *sp5100_tco_pci;
0065
0066
0067
0068 #define WATCHDOG_HEARTBEAT 60
0069 static int heartbeat = WATCHDOG_HEARTBEAT;
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
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
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
0183
0184 sp5100_tco_update_pm_reg8(SB800_PM_WATCHDOG_CONFIG,
0185 0xff, SB800_PM_WATCHDOG_SECOND_RES);
0186
0187
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
0194
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
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
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
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
0295
0296
0297 if (val & SP5100_WDT_FIRED)
0298 wdd->bootstatus = WDIOF_CARDRESET;
0299
0300
0301 val &= ~SP5100_WDT_ACTION_RESET;
0302 writel(val, SP5100_WDT_CONTROL(tco->tcobase));
0303
0304
0305 tco_timer_set_timeout(wdd, wdd->timeout);
0306
0307
0308
0309
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
0369
0370
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
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
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
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
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
0441
0442
0443
0444
0445 pci_read_config_dword(sp5100_tco_pci,
0446 SP5100_SB_RESOURCE_MMIO_BASE,
0447 &val);
0448
0449
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
0459 val = sp5100_tco_read_pm_reg32(SB800_PM_ACPI_MMIO_EN);
0460
0461
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
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
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
0556
0557
0558
0559
0560
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, },
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
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");