in sp5100_tco.c [226:384]
static int sp5100_tco_setupdevice(struct device *dev,
struct watchdog_device *wdd)
{
struct sp5100_tco *tco = watchdog_get_drvdata(wdd);
const char *dev_name;
u32 mmio_addr = 0, val;
int ret;
/* Request the IO ports used by this driver */
if (!request_muxed_region(SP5100_IO_PM_INDEX_REG,
SP5100_PM_IOPORTS_SIZE, "sp5100_tco")) {
dev_err(dev, "I/O address 0x%04x already in use\n",
SP5100_IO_PM_INDEX_REG);
return -EBUSY;
}
/*
* Determine type of southbridge chipset.
*/
switch (tco->tco_reg_layout) {
case sp5100:
dev_name = SP5100_DEVNAME;
mmio_addr = sp5100_tco_read_pm_reg32(SP5100_PM_WATCHDOG_BASE) &
0xfffffff8;
break;
case sb800:
dev_name = SB800_DEVNAME;
mmio_addr = sp5100_tco_read_pm_reg32(SB800_PM_WATCHDOG_BASE) &
0xfffffff8;
break;
case efch:
dev_name = SB800_DEVNAME;
/*
* On Family 17h devices, the EFCH_PM_DECODEEN_WDT_TMREN bit of
* EFCH_PM_DECODEEN not only enables the EFCH_PM_WDT_ADDR memory
* region, it also enables the watchdog itself.
*/
if (boot_cpu_data.x86 == 0x17) {
val = sp5100_tco_read_pm_reg8(EFCH_PM_DECODEEN);
if (!(val & EFCH_PM_DECODEEN_WDT_TMREN)) {
sp5100_tco_update_pm_reg8(EFCH_PM_DECODEEN, 0xff,
EFCH_PM_DECODEEN_WDT_TMREN);
}
}
val = sp5100_tco_read_pm_reg8(EFCH_PM_DECODEEN);
if (val & EFCH_PM_DECODEEN_WDT_TMREN)
mmio_addr = EFCH_PM_WDT_ADDR;
break;
default:
return -ENODEV;
}
/* Check MMIO address conflict */
if (!mmio_addr ||
!devm_request_mem_region(dev, mmio_addr, SP5100_WDT_MEM_MAP_SIZE,
dev_name)) {
if (mmio_addr)
dev_dbg(dev, "MMIO address 0x%08x already in use\n",
mmio_addr);
switch (tco->tco_reg_layout) {
case sp5100:
/*
* Secondly, Find the watchdog timer MMIO address
* from SBResource_MMIO register.
*/
/* Read SBResource_MMIO from PCI config(PCI_Reg: 9Ch) */
pci_read_config_dword(sp5100_tco_pci,
SP5100_SB_RESOURCE_MMIO_BASE,
&mmio_addr);
if ((mmio_addr & (SB800_ACPI_MMIO_DECODE_EN |
SB800_ACPI_MMIO_SEL)) !=
SB800_ACPI_MMIO_DECODE_EN) {
ret = -ENODEV;
goto unreg_region;
}
mmio_addr &= ~0xFFF;
mmio_addr += SB800_PM_WDT_MMIO_OFFSET;
break;
case sb800:
/* Read SBResource_MMIO from AcpiMmioEn(PM_Reg: 24h) */
mmio_addr =
sp5100_tco_read_pm_reg32(SB800_PM_ACPI_MMIO_EN);
if ((mmio_addr & (SB800_ACPI_MMIO_DECODE_EN |
SB800_ACPI_MMIO_SEL)) !=
SB800_ACPI_MMIO_DECODE_EN) {
ret = -ENODEV;
goto unreg_region;
}
mmio_addr &= ~0xFFF;
mmio_addr += SB800_PM_WDT_MMIO_OFFSET;
break;
case efch:
val = sp5100_tco_read_pm_reg8(EFCH_PM_ISACONTROL);
if (!(val & EFCH_PM_ISACONTROL_MMIOEN)) {
ret = -ENODEV;
goto unreg_region;
}
mmio_addr = EFCH_PM_ACPI_MMIO_ADDR +
EFCH_PM_ACPI_MMIO_WDT_OFFSET;
break;
}
dev_dbg(dev, "Got 0x%08x from SBResource_MMIO register\n",
mmio_addr);
if (!devm_request_mem_region(dev, mmio_addr,
SP5100_WDT_MEM_MAP_SIZE,
dev_name)) {
dev_dbg(dev, "MMIO address 0x%08x already in use\n",
mmio_addr);
ret = -EBUSY;
goto unreg_region;
}
}
tco->tcobase = devm_ioremap(dev, mmio_addr, SP5100_WDT_MEM_MAP_SIZE);
if (!tco->tcobase) {
dev_err(dev, "failed to get tcobase address\n");
ret = -ENOMEM;
goto unreg_region;
}
dev_info(dev, "Using 0x%08x for watchdog MMIO address\n", mmio_addr);
/* Setup the watchdog timer */
tco_timer_enable(tco);
val = readl(SP5100_WDT_CONTROL(tco->tcobase));
if (val & SP5100_WDT_DISABLED) {
dev_err(dev, "Watchdog hardware is disabled\n");
ret = -ENODEV;
goto unreg_region;
}
/*
* Save WatchDogFired status, because WatchDogFired flag is
* cleared here.
*/
if (val & SP5100_WDT_FIRED)
wdd->bootstatus = WDIOF_CARDRESET;
/* Set watchdog action to reset the system */
val &= ~SP5100_WDT_ACTION_RESET;
writel(val, SP5100_WDT_CONTROL(tco->tcobase));
/* Set a reasonable heartbeat before we stop the timer */
tco_timer_set_timeout(wdd, wdd->timeout);
/*
* Stop the TCO before we change anything so we don't race with
* a zeroed timer.
*/
tco_timer_stop(wdd);
release_region(SP5100_IO_PM_INDEX_REG, SP5100_PM_IOPORTS_SIZE);
return 0;
unreg_region:
release_region(SP5100_IO_PM_INDEX_REG, SP5100_PM_IOPORTS_SIZE);
return ret;
}