mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2025-01-04 04:04:19 +00:00
linux-watchdog 5.18-rc1 tag
-----BEGIN PGP SIGNATURE----- Version: GnuPG v2.0.14 (GNU/Linux) iEYEABECAAYFAmJF7qQACgkQ+iyteGJfRsowRQCeOfozOBwouAz4tVIERCaYgril D6sAn34jNnuYybj4YtEIBRCHWfbG6GsQ =oHoS -----END PGP SIGNATURE----- Merge tag 'linux-watchdog-5.18-rc1' of git://www.linux-watchdog.org/linux-watchdog Pull watchdog updates from Wim Van Sebroeck: - add support for BCM4908 - renesas_wdt: add R-Car Gen4 support - improve watchdog_dev function documentation - sp5100_tco: replace the cd6h/cd7h port I/O with MMIO accesses during initialization - several other small improvements and fixes * tag 'linux-watchdog-5.18-rc1' of git://www.linux-watchdog.org/linux-watchdog: Watchdog: sp5100_tco: Enable Family 17h+ CPUs Watchdog: sp5100_tco: Add initialization using EFCH MMIO Watchdog: sp5100_tco: Refactor MMIO base address initialization Watchdog: sp5100_tco: Move timer initialization into function watchdog: ixp4xx: Implement restart watchdog: orion_wdt: support pretimeout on Armada-XP watchdog: allow building BCM7038_WDT for BCM4908 watchdog: renesas_wdt: Add R-Car Gen4 support dt-bindings: watchdog: renesas-wdt: Document r8a779f0 support watchdog: Improve watchdog_dev function documentation watchdog: aspeed: add nowayout support watchdog: rti-wdt: Add missing pm_runtime_disable() in probe function watchdog: imx2_wdg: Alow ping on suspend
This commit is contained in:
commit
354b8bf222
@ -55,6 +55,11 @@ properties:
|
||||
- renesas,r8a779a0-wdt # R-Car V3U
|
||||
- const: renesas,rcar-gen3-wdt # R-Car Gen3 and RZ/G2
|
||||
|
||||
- items:
|
||||
- enum:
|
||||
- renesas,r8a779f0-wdt # R-Car S4-8
|
||||
- const: renesas,rcar-gen4-wdt # R-Car Gen4
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
|
@ -1779,7 +1779,7 @@ config BCM7038_WDT
|
||||
tristate "BCM63xx/BCM7038 Watchdog"
|
||||
select WATCHDOG_CORE
|
||||
depends on HAS_IOMEM
|
||||
depends on ARCH_BRCMSTB || BMIPS_GENERIC || BCM63XX || COMPILE_TEST
|
||||
depends on ARCH_BCM4908 || ARCH_BRCMSTB || BMIPS_GENERIC || BCM63XX || COMPILE_TEST
|
||||
help
|
||||
Watchdog driver for the built-in hardware in Broadcom 7038 and
|
||||
later SoCs used in set-top boxes. BCM7038 was made public
|
||||
|
@ -13,6 +13,11 @@
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/watchdog.h>
|
||||
|
||||
static bool nowayout = WATCHDOG_NOWAYOUT;
|
||||
module_param(nowayout, bool, 0);
|
||||
MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default="
|
||||
__MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
|
||||
|
||||
struct aspeed_wdt {
|
||||
struct watchdog_device wdd;
|
||||
void __iomem *base;
|
||||
@ -266,6 +271,8 @@ static int aspeed_wdt_probe(struct platform_device *pdev)
|
||||
wdt->wdd.timeout = WDT_DEFAULT_TIMEOUT;
|
||||
watchdog_init_timeout(&wdt->wdd, 0, dev);
|
||||
|
||||
watchdog_set_nowayout(&wdt->wdd, nowayout);
|
||||
|
||||
np = dev->of_node;
|
||||
|
||||
ofdid = of_match_node(aspeed_wdt_of_table, np);
|
||||
|
@ -66,6 +66,7 @@ struct imx2_wdt_device {
|
||||
struct watchdog_device wdog;
|
||||
bool ext_reset;
|
||||
bool clk_is_on;
|
||||
bool no_ping;
|
||||
};
|
||||
|
||||
static bool nowayout = WATCHDOG_NOWAYOUT;
|
||||
@ -312,12 +313,18 @@ static int __init imx2_wdt_probe(struct platform_device *pdev)
|
||||
|
||||
wdev->ext_reset = of_property_read_bool(dev->of_node,
|
||||
"fsl,ext-reset-output");
|
||||
/*
|
||||
* The i.MX7D doesn't support low power mode, so we need to ping the watchdog
|
||||
* during suspend.
|
||||
*/
|
||||
wdev->no_ping = !of_device_is_compatible(dev->of_node, "fsl,imx7d-wdt");
|
||||
platform_set_drvdata(pdev, wdog);
|
||||
watchdog_set_drvdata(wdog, wdev);
|
||||
watchdog_set_nowayout(wdog, nowayout);
|
||||
watchdog_set_restart_priority(wdog, 128);
|
||||
watchdog_init_timeout(wdog, timeout, dev);
|
||||
watchdog_stop_ping_on_suspend(wdog);
|
||||
if (wdev->no_ping)
|
||||
watchdog_stop_ping_on_suspend(wdog);
|
||||
|
||||
if (imx2_wdt_is_running(wdev)) {
|
||||
imx2_wdt_set_timeout(wdog, wdog->timeout);
|
||||
@ -366,9 +373,11 @@ static int __maybe_unused imx2_wdt_suspend(struct device *dev)
|
||||
imx2_wdt_ping(wdog);
|
||||
}
|
||||
|
||||
clk_disable_unprepare(wdev->clk);
|
||||
if (wdev->no_ping) {
|
||||
clk_disable_unprepare(wdev->clk);
|
||||
|
||||
wdev->clk_is_on = false;
|
||||
wdev->clk_is_on = false;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -380,11 +389,14 @@ static int __maybe_unused imx2_wdt_resume(struct device *dev)
|
||||
struct imx2_wdt_device *wdev = watchdog_get_drvdata(wdog);
|
||||
int ret;
|
||||
|
||||
ret = clk_prepare_enable(wdev->clk);
|
||||
if (ret)
|
||||
return ret;
|
||||
if (wdev->no_ping) {
|
||||
ret = clk_prepare_enable(wdev->clk);
|
||||
|
||||
wdev->clk_is_on = true;
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
wdev->clk_is_on = true;
|
||||
}
|
||||
|
||||
if (watchdog_active(wdog) && !imx2_wdt_is_running(wdev)) {
|
||||
/*
|
||||
@ -407,6 +419,7 @@ static SIMPLE_DEV_PM_OPS(imx2_wdt_pm_ops, imx2_wdt_suspend,
|
||||
|
||||
static const struct of_device_id imx2_wdt_dt_ids[] = {
|
||||
{ .compatible = "fsl,imx21-wdt", },
|
||||
{ .compatible = "fsl,imx7d-wdt", },
|
||||
{ /* sentinel */ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, imx2_wdt_dt_ids);
|
||||
|
@ -84,10 +84,24 @@ static int ixp4xx_wdt_set_timeout(struct watchdog_device *wdd,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ixp4xx_wdt_restart(struct watchdog_device *wdd,
|
||||
unsigned long action, void *data)
|
||||
{
|
||||
struct ixp4xx_wdt *iwdt = to_ixp4xx_wdt(wdd);
|
||||
|
||||
__raw_writel(IXP4XX_WDT_KEY, iwdt->base + IXP4XX_OSWK_OFFSET);
|
||||
__raw_writel(0, iwdt->base + IXP4XX_OSWT_OFFSET);
|
||||
__raw_writel(IXP4XX_WDT_COUNT_ENABLE | IXP4XX_WDT_RESET_ENABLE,
|
||||
iwdt->base + IXP4XX_OSWE_OFFSET);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct watchdog_ops ixp4xx_wdt_ops = {
|
||||
.start = ixp4xx_wdt_start,
|
||||
.stop = ixp4xx_wdt_stop,
|
||||
.set_timeout = ixp4xx_wdt_set_timeout,
|
||||
.restart = ixp4xx_wdt_restart,
|
||||
.owner = THIS_MODULE,
|
||||
};
|
||||
|
||||
|
@ -238,8 +238,10 @@ static int armada370_start(struct watchdog_device *wdt_dev)
|
||||
atomic_io_modify(dev->reg + TIMER_A370_STATUS, WDT_A370_EXPIRED, 0);
|
||||
|
||||
/* Enable watchdog timer */
|
||||
atomic_io_modify(dev->reg + TIMER_CTRL, dev->data->wdt_enable_bit,
|
||||
dev->data->wdt_enable_bit);
|
||||
reg = dev->data->wdt_enable_bit;
|
||||
if (dev->wdt.info->options & WDIOF_PRETIMEOUT)
|
||||
reg |= TIMER1_ENABLE_BIT;
|
||||
atomic_io_modify(dev->reg + TIMER_CTRL, reg, reg);
|
||||
|
||||
/* Enable reset on watchdog */
|
||||
reg = readl(dev->rstout);
|
||||
@ -312,7 +314,7 @@ static int armada375_stop(struct watchdog_device *wdt_dev)
|
||||
static int armada370_stop(struct watchdog_device *wdt_dev)
|
||||
{
|
||||
struct orion_watchdog *dev = watchdog_get_drvdata(wdt_dev);
|
||||
u32 reg;
|
||||
u32 reg, mask;
|
||||
|
||||
/* Disable reset on watchdog */
|
||||
reg = readl(dev->rstout);
|
||||
@ -320,7 +322,10 @@ static int armada370_stop(struct watchdog_device *wdt_dev)
|
||||
writel(reg, dev->rstout);
|
||||
|
||||
/* Disable watchdog timer */
|
||||
atomic_io_modify(dev->reg + TIMER_CTRL, dev->data->wdt_enable_bit, 0);
|
||||
mask = dev->data->wdt_enable_bit;
|
||||
if (wdt_dev->info->options & WDIOF_PRETIMEOUT)
|
||||
mask |= TIMER1_ENABLE_BIT;
|
||||
atomic_io_modify(dev->reg + TIMER_CTRL, mask, 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -327,6 +327,7 @@ static SIMPLE_DEV_PM_OPS(rwdt_pm_ops, rwdt_suspend, rwdt_resume);
|
||||
static const struct of_device_id rwdt_ids[] = {
|
||||
{ .compatible = "renesas,rcar-gen2-wdt", },
|
||||
{ .compatible = "renesas,rcar-gen3-wdt", },
|
||||
{ .compatible = "renesas,rcar-gen4-wdt", },
|
||||
{ /* sentinel */ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, rwdt_ids);
|
||||
|
@ -228,6 +228,7 @@ static int rti_wdt_probe(struct platform_device *pdev)
|
||||
ret = pm_runtime_get_sync(dev);
|
||||
if (ret) {
|
||||
pm_runtime_put_noidle(dev);
|
||||
pm_runtime_disable(&pdev->dev);
|
||||
return dev_err_probe(dev, ret, "runtime pm failed\n");
|
||||
}
|
||||
|
||||
|
@ -49,7 +49,7 @@
|
||||
/* internal variables */
|
||||
|
||||
enum tco_reg_layout {
|
||||
sp5100, sb800, efch
|
||||
sp5100, sb800, efch, efch_mmio
|
||||
};
|
||||
|
||||
struct sp5100_tco {
|
||||
@ -86,6 +86,10 @@ static enum tco_reg_layout tco_reg_layout(struct pci_dev *dev)
|
||||
dev->device == PCI_DEVICE_ID_ATI_SBX00_SMBUS &&
|
||||
dev->revision < 0x40) {
|
||||
return sp5100;
|
||||
} else if (dev->vendor == PCI_VENDOR_ID_AMD &&
|
||||
sp5100_tco_pci->device == PCI_DEVICE_ID_AMD_KERNCZ_SMBUS &&
|
||||
sp5100_tco_pci->revision >= AMD_ZEN_SMBUS_PCI_REV) {
|
||||
return efch_mmio;
|
||||
} else if (dev->vendor == PCI_VENDOR_ID_AMD &&
|
||||
((dev->device == PCI_DEVICE_ID_AMD_HUDSON2_SMBUS &&
|
||||
dev->revision >= 0x41) ||
|
||||
@ -209,6 +213,8 @@ static void tco_timer_enable(struct sp5100_tco *tco)
|
||||
~EFCH_PM_WATCHDOG_DISABLE,
|
||||
EFCH_PM_DECODEEN_SECOND_RES);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@ -223,14 +229,195 @@ static u32 sp5100_tco_read_pm_reg32(u8 index)
|
||||
return val;
|
||||
}
|
||||
|
||||
static u32 sp5100_tco_request_region(struct device *dev,
|
||||
u32 mmio_addr,
|
||||
const char *dev_name)
|
||||
{
|
||||
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);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return mmio_addr;
|
||||
}
|
||||
|
||||
static u32 sp5100_tco_prepare_base(struct sp5100_tco *tco,
|
||||
u32 mmio_addr,
|
||||
u32 alt_mmio_addr,
|
||||
const char *dev_name)
|
||||
{
|
||||
struct device *dev = tco->wdd.parent;
|
||||
|
||||
dev_dbg(dev, "Got 0x%08x from SBResource_MMIO register\n", mmio_addr);
|
||||
|
||||
if (!mmio_addr && !alt_mmio_addr)
|
||||
return -ENODEV;
|
||||
|
||||
/* Check for MMIO address and alternate MMIO address conflicts */
|
||||
if (mmio_addr)
|
||||
mmio_addr = sp5100_tco_request_region(dev, mmio_addr, dev_name);
|
||||
|
||||
if (!mmio_addr && alt_mmio_addr)
|
||||
mmio_addr = sp5100_tco_request_region(dev, alt_mmio_addr, dev_name);
|
||||
|
||||
if (!mmio_addr) {
|
||||
dev_err(dev, "Failed to reserve MMIO or alternate MMIO region\n");
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
tco->tcobase = devm_ioremap(dev, mmio_addr, SP5100_WDT_MEM_MAP_SIZE);
|
||||
if (!tco->tcobase) {
|
||||
dev_err(dev, "MMIO address 0x%08x failed mapping\n", mmio_addr);
|
||||
devm_release_mem_region(dev, mmio_addr, SP5100_WDT_MEM_MAP_SIZE);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
dev_info(dev, "Using 0x%08x for watchdog MMIO address\n", mmio_addr);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sp5100_tco_timer_init(struct sp5100_tco *tco)
|
||||
{
|
||||
struct watchdog_device *wdd = &tco->wdd;
|
||||
struct device *dev = wdd->parent;
|
||||
u32 val;
|
||||
|
||||
val = readl(SP5100_WDT_CONTROL(tco->tcobase));
|
||||
if (val & SP5100_WDT_DISABLED) {
|
||||
dev_err(dev, "Watchdog hardware is disabled\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
/*
|
||||
* 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);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static u8 efch_read_pm_reg8(void __iomem *addr, u8 index)
|
||||
{
|
||||
return readb(addr + index);
|
||||
}
|
||||
|
||||
static void efch_update_pm_reg8(void __iomem *addr, u8 index, u8 reset, u8 set)
|
||||
{
|
||||
u8 val;
|
||||
|
||||
val = readb(addr + index);
|
||||
val &= reset;
|
||||
val |= set;
|
||||
writeb(val, addr + index);
|
||||
}
|
||||
|
||||
static void tco_timer_enable_mmio(void __iomem *addr)
|
||||
{
|
||||
efch_update_pm_reg8(addr, EFCH_PM_DECODEEN3,
|
||||
~EFCH_PM_WATCHDOG_DISABLE,
|
||||
EFCH_PM_DECODEEN_SECOND_RES);
|
||||
}
|
||||
|
||||
static int sp5100_tco_setupdevice_mmio(struct device *dev,
|
||||
struct watchdog_device *wdd)
|
||||
{
|
||||
struct sp5100_tco *tco = watchdog_get_drvdata(wdd);
|
||||
const char *dev_name = SB800_DEVNAME;
|
||||
u32 mmio_addr = 0, alt_mmio_addr = 0;
|
||||
struct resource *res;
|
||||
void __iomem *addr;
|
||||
int ret;
|
||||
u32 val;
|
||||
|
||||
res = request_mem_region_muxed(EFCH_PM_ACPI_MMIO_PM_ADDR,
|
||||
EFCH_PM_ACPI_MMIO_PM_SIZE,
|
||||
"sp5100_tco");
|
||||
|
||||
if (!res) {
|
||||
dev_err(dev,
|
||||
"Memory region 0x%08x already in use\n",
|
||||
EFCH_PM_ACPI_MMIO_PM_ADDR);
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
addr = ioremap(EFCH_PM_ACPI_MMIO_PM_ADDR, EFCH_PM_ACPI_MMIO_PM_SIZE);
|
||||
if (!addr) {
|
||||
dev_err(dev, "Address mapping failed\n");
|
||||
ret = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/*
|
||||
* EFCH_PM_DECODEEN_WDT_TMREN is dual purpose. This bitfield
|
||||
* enables sp5100_tco register MMIO space decoding. The bitfield
|
||||
* also starts the timer operation. Enable if not already enabled.
|
||||
*/
|
||||
val = efch_read_pm_reg8(addr, EFCH_PM_DECODEEN);
|
||||
if (!(val & EFCH_PM_DECODEEN_WDT_TMREN)) {
|
||||
efch_update_pm_reg8(addr, EFCH_PM_DECODEEN, 0xff,
|
||||
EFCH_PM_DECODEEN_WDT_TMREN);
|
||||
}
|
||||
|
||||
/* Error if the timer could not be enabled */
|
||||
val = efch_read_pm_reg8(addr, EFCH_PM_DECODEEN);
|
||||
if (!(val & EFCH_PM_DECODEEN_WDT_TMREN)) {
|
||||
dev_err(dev, "Failed to enable the timer\n");
|
||||
ret = -EFAULT;
|
||||
goto out;
|
||||
}
|
||||
|
||||
mmio_addr = EFCH_PM_WDT_ADDR;
|
||||
|
||||
/* Determine alternate MMIO base address */
|
||||
val = efch_read_pm_reg8(addr, EFCH_PM_ISACONTROL);
|
||||
if (val & EFCH_PM_ISACONTROL_MMIOEN)
|
||||
alt_mmio_addr = EFCH_PM_ACPI_MMIO_ADDR +
|
||||
EFCH_PM_ACPI_MMIO_WDT_OFFSET;
|
||||
|
||||
ret = sp5100_tco_prepare_base(tco, mmio_addr, alt_mmio_addr, dev_name);
|
||||
if (!ret) {
|
||||
tco_timer_enable_mmio(addr);
|
||||
ret = sp5100_tco_timer_init(tco);
|
||||
}
|
||||
|
||||
out:
|
||||
if (addr)
|
||||
iounmap(addr);
|
||||
|
||||
release_resource(res);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
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;
|
||||
u32 alt_mmio_addr = 0;
|
||||
int ret;
|
||||
|
||||
if (tco->tco_reg_layout == efch_mmio)
|
||||
return sp5100_tco_setupdevice_mmio(dev, wdd);
|
||||
|
||||
/* Request the IO ports used by this driver */
|
||||
if (!request_muxed_region(SP5100_IO_PM_INDEX_REG,
|
||||
SP5100_PM_IOPORTS_SIZE, "sp5100_tco")) {
|
||||
@ -247,138 +434,55 @@ static int sp5100_tco_setupdevice(struct device *dev,
|
||||
dev_name = SP5100_DEVNAME;
|
||||
mmio_addr = sp5100_tco_read_pm_reg32(SP5100_PM_WATCHDOG_BASE) &
|
||||
0xfffffff8;
|
||||
|
||||
/*
|
||||
* 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,
|
||||
&val);
|
||||
|
||||
/* Verify MMIO is enabled and using bar0 */
|
||||
if ((val & SB800_ACPI_MMIO_MASK) == SB800_ACPI_MMIO_DECODE_EN)
|
||||
alt_mmio_addr = (val & ~0xfff) + SB800_PM_WDT_MMIO_OFFSET;
|
||||
break;
|
||||
case sb800:
|
||||
dev_name = SB800_DEVNAME;
|
||||
mmio_addr = sp5100_tco_read_pm_reg32(SB800_PM_WATCHDOG_BASE) &
|
||||
0xfffffff8;
|
||||
|
||||
/* Read SBResource_MMIO from AcpiMmioEn(PM_Reg: 24h) */
|
||||
val = sp5100_tco_read_pm_reg32(SB800_PM_ACPI_MMIO_EN);
|
||||
|
||||
/* Verify MMIO is enabled and using bar0 */
|
||||
if ((val & SB800_ACPI_MMIO_MASK) == SB800_ACPI_MMIO_DECODE_EN)
|
||||
alt_mmio_addr = (val & ~0xfff) + SB800_PM_WDT_MMIO_OFFSET;
|
||||
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;
|
||||
|
||||
val = sp5100_tco_read_pm_reg8(EFCH_PM_ISACONTROL);
|
||||
if (val & EFCH_PM_ISACONTROL_MMIOEN)
|
||||
alt_mmio_addr = EFCH_PM_ACPI_MMIO_ADDR +
|
||||
EFCH_PM_ACPI_MMIO_WDT_OFFSET;
|
||||
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;
|
||||
}
|
||||
ret = sp5100_tco_prepare_base(tco, mmio_addr, alt_mmio_addr, dev_name);
|
||||
if (!ret) {
|
||||
/* Setup the watchdog timer */
|
||||
tco_timer_enable(tco);
|
||||
ret = sp5100_tco_timer_init(tco);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
@ -58,6 +58,7 @@
|
||||
#define SB800_PM_WATCHDOG_SECOND_RES GENMASK(1, 0)
|
||||
#define SB800_ACPI_MMIO_DECODE_EN BIT(0)
|
||||
#define SB800_ACPI_MMIO_SEL BIT(1)
|
||||
#define SB800_ACPI_MMIO_MASK GENMASK(1, 0)
|
||||
|
||||
#define SB800_PM_WDT_MMIO_OFFSET 0xB00
|
||||
|
||||
@ -82,4 +83,10 @@
|
||||
#define EFCH_PM_ISACONTROL_MMIOEN BIT(1)
|
||||
|
||||
#define EFCH_PM_ACPI_MMIO_ADDR 0xfed80000
|
||||
#define EFCH_PM_ACPI_MMIO_PM_OFFSET 0x00000300
|
||||
#define EFCH_PM_ACPI_MMIO_WDT_OFFSET 0x00000b00
|
||||
|
||||
#define EFCH_PM_ACPI_MMIO_PM_ADDR (EFCH_PM_ACPI_MMIO_ADDR + \
|
||||
EFCH_PM_ACPI_MMIO_PM_OFFSET)
|
||||
#define EFCH_PM_ACPI_MMIO_PM_SIZE 8
|
||||
#define AMD_ZEN_SMBUS_PCI_REV 0x51
|
||||
|
@ -171,17 +171,17 @@ static int __watchdog_ping(struct watchdog_device *wdd)
|
||||
}
|
||||
|
||||
/*
|
||||
* watchdog_ping: ping the watchdog.
|
||||
* @wdd: the watchdog device to ping
|
||||
* watchdog_ping - ping the watchdog
|
||||
* @wdd: The watchdog device to ping
|
||||
*
|
||||
* The caller must hold wd_data->lock.
|
||||
* If the watchdog has no own ping operation then it needs to be
|
||||
* restarted via the start operation. This wrapper function does
|
||||
* exactly that.
|
||||
* We only ping when the watchdog device is running.
|
||||
* The caller must hold wd_data->lock.
|
||||
*
|
||||
* If the watchdog has no own ping operation then it needs to be
|
||||
* restarted via the start operation. This wrapper function does
|
||||
* exactly that.
|
||||
* We only ping when the watchdog device is running.
|
||||
* Return: 0 on success, error otherwise.
|
||||
*/
|
||||
|
||||
static int watchdog_ping(struct watchdog_device *wdd)
|
||||
{
|
||||
struct watchdog_core_data *wd_data = wdd->wd_data;
|
||||
@ -231,16 +231,14 @@ static enum hrtimer_restart watchdog_timer_expired(struct hrtimer *timer)
|
||||
}
|
||||
|
||||
/*
|
||||
* watchdog_start: wrapper to start the watchdog.
|
||||
* @wdd: the watchdog device to start
|
||||
* watchdog_start - wrapper to start the watchdog
|
||||
* @wdd: The watchdog device to start
|
||||
*
|
||||
* The caller must hold wd_data->lock.
|
||||
* Start the watchdog if it is not active and mark it active.
|
||||
* The caller must hold wd_data->lock.
|
||||
*
|
||||
* Start the watchdog if it is not active and mark it active.
|
||||
* This function returns zero on success or a negative errno code for
|
||||
* failure.
|
||||
* Return: 0 on success or a negative errno code for failure.
|
||||
*/
|
||||
|
||||
static int watchdog_start(struct watchdog_device *wdd)
|
||||
{
|
||||
struct watchdog_core_data *wd_data = wdd->wd_data;
|
||||
@ -274,17 +272,15 @@ static int watchdog_start(struct watchdog_device *wdd)
|
||||
}
|
||||
|
||||
/*
|
||||
* watchdog_stop: wrapper to stop the watchdog.
|
||||
* @wdd: the watchdog device to stop
|
||||
* watchdog_stop - wrapper to stop the watchdog
|
||||
* @wdd: The watchdog device to stop
|
||||
*
|
||||
* The caller must hold wd_data->lock.
|
||||
* Stop the watchdog if it is still active and unmark it active.
|
||||
* If the 'nowayout' feature was set, the watchdog cannot be stopped.
|
||||
* The caller must hold wd_data->lock.
|
||||
*
|
||||
* Stop the watchdog if it is still active and unmark it active.
|
||||
* This function returns zero on success or a negative errno code for
|
||||
* failure.
|
||||
* If the 'nowayout' feature was set, the watchdog cannot be stopped.
|
||||
* Return: 0 on success or a negative errno code for failure.
|
||||
*/
|
||||
|
||||
static int watchdog_stop(struct watchdog_device *wdd)
|
||||
{
|
||||
int err = 0;
|
||||
@ -315,14 +311,14 @@ static int watchdog_stop(struct watchdog_device *wdd)
|
||||
}
|
||||
|
||||
/*
|
||||
* watchdog_get_status: wrapper to get the watchdog status
|
||||
* @wdd: the watchdog device to get the status from
|
||||
* watchdog_get_status - wrapper to get the watchdog status
|
||||
* @wdd: The watchdog device to get the status from
|
||||
*
|
||||
* The caller must hold wd_data->lock.
|
||||
* Get the watchdog's status flags.
|
||||
* The caller must hold wd_data->lock.
|
||||
*
|
||||
* Get the watchdog's status flags.
|
||||
* Return: watchdog's status flags.
|
||||
*/
|
||||
|
||||
static unsigned int watchdog_get_status(struct watchdog_device *wdd)
|
||||
{
|
||||
struct watchdog_core_data *wd_data = wdd->wd_data;
|
||||
@ -352,13 +348,14 @@ static unsigned int watchdog_get_status(struct watchdog_device *wdd)
|
||||
}
|
||||
|
||||
/*
|
||||
* watchdog_set_timeout: set the watchdog timer timeout
|
||||
* @wdd: the watchdog device to set the timeout for
|
||||
* @timeout: timeout to set in seconds
|
||||
* watchdog_set_timeout - set the watchdog timer timeout
|
||||
* @wdd: The watchdog device to set the timeout for
|
||||
* @timeout: Timeout to set in seconds
|
||||
*
|
||||
* The caller must hold wd_data->lock.
|
||||
* The caller must hold wd_data->lock.
|
||||
*
|
||||
* Return: 0 if successful, error otherwise.
|
||||
*/
|
||||
|
||||
static int watchdog_set_timeout(struct watchdog_device *wdd,
|
||||
unsigned int timeout)
|
||||
{
|
||||
@ -385,11 +382,12 @@ static int watchdog_set_timeout(struct watchdog_device *wdd,
|
||||
}
|
||||
|
||||
/*
|
||||
* watchdog_set_pretimeout: set the watchdog timer pretimeout
|
||||
* @wdd: the watchdog device to set the timeout for
|
||||
* @timeout: pretimeout to set in seconds
|
||||
* watchdog_set_pretimeout - set the watchdog timer pretimeout
|
||||
* @wdd: The watchdog device to set the timeout for
|
||||
* @timeout: pretimeout to set in seconds
|
||||
*
|
||||
* Return: 0 if successful, error otherwise.
|
||||
*/
|
||||
|
||||
static int watchdog_set_pretimeout(struct watchdog_device *wdd,
|
||||
unsigned int timeout)
|
||||
{
|
||||
@ -410,15 +408,15 @@ static int watchdog_set_pretimeout(struct watchdog_device *wdd,
|
||||
}
|
||||
|
||||
/*
|
||||
* watchdog_get_timeleft: wrapper to get the time left before a reboot
|
||||
* @wdd: the watchdog device to get the remaining time from
|
||||
* @timeleft: the time that's left
|
||||
* watchdog_get_timeleft - wrapper to get the time left before a reboot
|
||||
* @wdd: The watchdog device to get the remaining time from
|
||||
* @timeleft: The time that's left
|
||||
*
|
||||
* The caller must hold wd_data->lock.
|
||||
* Get the time before a watchdog will reboot (if not pinged).
|
||||
* The caller must hold wd_data->lock.
|
||||
*
|
||||
* Get the time before a watchdog will reboot (if not pinged).
|
||||
* Return: 0 if successful, error otherwise.
|
||||
*/
|
||||
|
||||
static int watchdog_get_timeleft(struct watchdog_device *wdd,
|
||||
unsigned int *timeleft)
|
||||
{
|
||||
@ -635,14 +633,15 @@ __ATTRIBUTE_GROUPS(wdt);
|
||||
#endif
|
||||
|
||||
/*
|
||||
* watchdog_ioctl_op: call the watchdog drivers ioctl op if defined
|
||||
* @wdd: the watchdog device to do the ioctl on
|
||||
* @cmd: watchdog command
|
||||
* @arg: argument pointer
|
||||
* watchdog_ioctl_op - call the watchdog drivers ioctl op if defined
|
||||
* @wdd: The watchdog device to do the ioctl on
|
||||
* @cmd: Watchdog command
|
||||
* @arg: Argument pointer
|
||||
*
|
||||
* The caller must hold wd_data->lock.
|
||||
* The caller must hold wd_data->lock.
|
||||
*
|
||||
* Return: 0 if successful, error otherwise.
|
||||
*/
|
||||
|
||||
static int watchdog_ioctl_op(struct watchdog_device *wdd, unsigned int cmd,
|
||||
unsigned long arg)
|
||||
{
|
||||
@ -653,17 +652,18 @@ static int watchdog_ioctl_op(struct watchdog_device *wdd, unsigned int cmd,
|
||||
}
|
||||
|
||||
/*
|
||||
* watchdog_write: writes to the watchdog.
|
||||
* @file: file from VFS
|
||||
* @data: user address of data
|
||||
* @len: length of data
|
||||
* @ppos: pointer to the file offset
|
||||
* watchdog_write - writes to the watchdog
|
||||
* @file: File from VFS
|
||||
* @data: User address of data
|
||||
* @len: Length of data
|
||||
* @ppos: Pointer to the file offset
|
||||
*
|
||||
* A write to a watchdog device is defined as a keepalive ping.
|
||||
* Writing the magic 'V' sequence allows the next close to turn
|
||||
* off the watchdog (if 'nowayout' is not set).
|
||||
* A write to a watchdog device is defined as a keepalive ping.
|
||||
* Writing the magic 'V' sequence allows the next close to turn
|
||||
* off the watchdog (if 'nowayout' is not set).
|
||||
*
|
||||
* Return: @len if successful, error otherwise.
|
||||
*/
|
||||
|
||||
static ssize_t watchdog_write(struct file *file, const char __user *data,
|
||||
size_t len, loff_t *ppos)
|
||||
{
|
||||
@ -706,13 +706,15 @@ static ssize_t watchdog_write(struct file *file, const char __user *data,
|
||||
}
|
||||
|
||||
/*
|
||||
* watchdog_ioctl: handle the different ioctl's for the watchdog device.
|
||||
* @file: file handle to the device
|
||||
* @cmd: watchdog command
|
||||
* @arg: argument pointer
|
||||
* watchdog_ioctl - handle the different ioctl's for the watchdog device
|
||||
* @file: File handle to the device
|
||||
* @cmd: Watchdog command
|
||||
* @arg: Argument pointer
|
||||
*
|
||||
* The watchdog API defines a common set of functions for all watchdogs
|
||||
* according to their available features.
|
||||
* The watchdog API defines a common set of functions for all watchdogs
|
||||
* according to their available features.
|
||||
*
|
||||
* Return: 0 if successful, error otherwise.
|
||||
*/
|
||||
|
||||
static long watchdog_ioctl(struct file *file, unsigned int cmd,
|
||||
@ -819,15 +821,16 @@ static long watchdog_ioctl(struct file *file, unsigned int cmd,
|
||||
}
|
||||
|
||||
/*
|
||||
* watchdog_open: open the /dev/watchdog* devices.
|
||||
* @inode: inode of device
|
||||
* @file: file handle to device
|
||||
* watchdog_open - open the /dev/watchdog* devices
|
||||
* @inode: Inode of device
|
||||
* @file: File handle to device
|
||||
*
|
||||
* When the /dev/watchdog* device gets opened, we start the watchdog.
|
||||
* Watch out: the /dev/watchdog device is single open, so we make sure
|
||||
* it can only be opened once.
|
||||
* When the /dev/watchdog* device gets opened, we start the watchdog.
|
||||
* Watch out: the /dev/watchdog device is single open, so we make sure
|
||||
* it can only be opened once.
|
||||
*
|
||||
* Return: 0 if successful, error otherwise.
|
||||
*/
|
||||
|
||||
static int watchdog_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
struct watchdog_core_data *wd_data;
|
||||
@ -896,15 +899,16 @@ static void watchdog_core_data_release(struct device *dev)
|
||||
}
|
||||
|
||||
/*
|
||||
* watchdog_release: release the watchdog device.
|
||||
* @inode: inode of device
|
||||
* @file: file handle to device
|
||||
* watchdog_release - release the watchdog device
|
||||
* @inode: Inode of device
|
||||
* @file: File handle to device
|
||||
*
|
||||
* This is the code for when /dev/watchdog gets closed. We will only
|
||||
* stop the watchdog when we have received the magic char (and nowayout
|
||||
* was not set), else the watchdog will keep running.
|
||||
* This is the code for when /dev/watchdog gets closed. We will only
|
||||
* stop the watchdog when we have received the magic char (and nowayout
|
||||
* was not set), else the watchdog will keep running.
|
||||
*
|
||||
* Always returns 0.
|
||||
*/
|
||||
|
||||
static int watchdog_release(struct inode *inode, struct file *file)
|
||||
{
|
||||
struct watchdog_core_data *wd_data = file->private_data;
|
||||
@ -977,14 +981,15 @@ static struct class watchdog_class = {
|
||||
};
|
||||
|
||||
/*
|
||||
* watchdog_cdev_register: register watchdog character device
|
||||
* @wdd: watchdog device
|
||||
* watchdog_cdev_register - register watchdog character device
|
||||
* @wdd: Watchdog device
|
||||
*
|
||||
* Register a watchdog character device including handling the legacy
|
||||
* /dev/watchdog node. /dev/watchdog is actually a miscdevice and
|
||||
* thus we set it up like that.
|
||||
* Register a watchdog character device including handling the legacy
|
||||
* /dev/watchdog node. /dev/watchdog is actually a miscdevice and
|
||||
* thus we set it up like that.
|
||||
*
|
||||
* Return: 0 if successful, error otherwise.
|
||||
*/
|
||||
|
||||
static int watchdog_cdev_register(struct watchdog_device *wdd)
|
||||
{
|
||||
struct watchdog_core_data *wd_data;
|
||||
@ -1074,13 +1079,12 @@ static int watchdog_cdev_register(struct watchdog_device *wdd)
|
||||
}
|
||||
|
||||
/*
|
||||
* watchdog_cdev_unregister: unregister watchdog character device
|
||||
* @watchdog: watchdog device
|
||||
* watchdog_cdev_unregister - unregister watchdog character device
|
||||
* @wdd: Watchdog device
|
||||
*
|
||||
* Unregister watchdog character device and if needed the legacy
|
||||
* /dev/watchdog device.
|
||||
* Unregister watchdog character device and if needed the legacy
|
||||
* /dev/watchdog device.
|
||||
*/
|
||||
|
||||
static void watchdog_cdev_unregister(struct watchdog_device *wdd)
|
||||
{
|
||||
struct watchdog_core_data *wd_data = wdd->wd_data;
|
||||
@ -1109,15 +1113,16 @@ static void watchdog_cdev_unregister(struct watchdog_device *wdd)
|
||||
put_device(&wd_data->dev);
|
||||
}
|
||||
|
||||
/*
|
||||
* watchdog_dev_register: register a watchdog device
|
||||
* @wdd: watchdog device
|
||||
/**
|
||||
* watchdog_dev_register - register a watchdog device
|
||||
* @wdd: Watchdog device
|
||||
*
|
||||
* Register a watchdog device including handling the legacy
|
||||
* /dev/watchdog node. /dev/watchdog is actually a miscdevice and
|
||||
* thus we set it up like that.
|
||||
* Register a watchdog device including handling the legacy
|
||||
* /dev/watchdog node. /dev/watchdog is actually a miscdevice and
|
||||
* thus we set it up like that.
|
||||
*
|
||||
* Return: 0 if successful, error otherwise.
|
||||
*/
|
||||
|
||||
int watchdog_dev_register(struct watchdog_device *wdd)
|
||||
{
|
||||
int ret;
|
||||
@ -1133,30 +1138,31 @@ int watchdog_dev_register(struct watchdog_device *wdd)
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* watchdog_dev_unregister: unregister a watchdog device
|
||||
* @watchdog: watchdog device
|
||||
/**
|
||||
* watchdog_dev_unregister - unregister a watchdog device
|
||||
* @wdd: watchdog device
|
||||
*
|
||||
* Unregister watchdog device and if needed the legacy
|
||||
* /dev/watchdog device.
|
||||
* Unregister watchdog device and if needed the legacy
|
||||
* /dev/watchdog device.
|
||||
*/
|
||||
|
||||
void watchdog_dev_unregister(struct watchdog_device *wdd)
|
||||
{
|
||||
watchdog_unregister_pretimeout(wdd);
|
||||
watchdog_cdev_unregister(wdd);
|
||||
}
|
||||
|
||||
/*
|
||||
* watchdog_set_last_hw_keepalive: set last HW keepalive time for watchdog
|
||||
* @wdd: watchdog device
|
||||
* @last_ping_ms: time since last HW heartbeat
|
||||
/**
|
||||
* watchdog_set_last_hw_keepalive - set last HW keepalive time for watchdog
|
||||
* @wdd: Watchdog device
|
||||
* @last_ping_ms: Time since last HW heartbeat
|
||||
*
|
||||
* Adjusts the last known HW keepalive time for a watchdog timer.
|
||||
* This is needed if the watchdog is already running when the probe
|
||||
* function is called, and it can't be pinged immediately. This
|
||||
* function must be called immediately after watchdog registration,
|
||||
* and min_hw_heartbeat_ms must be set for this to be useful.
|
||||
* Adjusts the last known HW keepalive time for a watchdog timer.
|
||||
* This is needed if the watchdog is already running when the probe
|
||||
* function is called, and it can't be pinged immediately. This
|
||||
* function must be called immediately after watchdog registration,
|
||||
* and min_hw_heartbeat_ms must be set for this to be useful.
|
||||
*
|
||||
* Return: 0 if successful, error otherwise.
|
||||
*/
|
||||
int watchdog_set_last_hw_keepalive(struct watchdog_device *wdd,
|
||||
unsigned int last_ping_ms)
|
||||
@ -1180,12 +1186,13 @@ int watchdog_set_last_hw_keepalive(struct watchdog_device *wdd,
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(watchdog_set_last_hw_keepalive);
|
||||
|
||||
/*
|
||||
* watchdog_dev_init: init dev part of watchdog core
|
||||
/**
|
||||
* watchdog_dev_init - init dev part of watchdog core
|
||||
*
|
||||
* Allocate a range of chardev nodes to use for watchdog devices
|
||||
* Allocate a range of chardev nodes to use for watchdog devices.
|
||||
*
|
||||
* Return: 0 if successful, error otherwise.
|
||||
*/
|
||||
|
||||
int __init watchdog_dev_init(void)
|
||||
{
|
||||
int err;
|
||||
@ -1218,12 +1225,11 @@ int __init watchdog_dev_init(void)
|
||||
return err;
|
||||
}
|
||||
|
||||
/*
|
||||
* watchdog_dev_exit: exit dev part of watchdog core
|
||||
/**
|
||||
* watchdog_dev_exit - exit dev part of watchdog core
|
||||
*
|
||||
* Release the range of chardev nodes used for watchdog devices
|
||||
* Release the range of chardev nodes used for watchdog devices.
|
||||
*/
|
||||
|
||||
void __exit watchdog_dev_exit(void)
|
||||
{
|
||||
unregister_chrdev_region(watchdog_devt, MAX_DOGS);
|
||||
|
Loading…
Reference in New Issue
Block a user