mirror of https://gitee.com/openkylin/linux.git
Merge git://www.linux-watchdog.org/linux-watchdog
Pull watchdog updates from Wim Van Sebroeck: "This contains: - new driver for ST's LPC Watchdog - new driver for Conexant Digicolor CX92755 SoC - new driver for DA9062 watchdog - Addition of the watchdog registration deferral mechanism - several improvements on omap_wdt - several improvements and reboot-support for imgpdc_wdt - max63xx_wdt improvements - imx2_wdt improvements - dw_wdt improvements - and other small improvements and fixes" * git://www.linux-watchdog.org/linux-watchdog: (37 commits) watchdog: omap_wdt: early_enable module parameter watchdog: gpio_wdt: Add option for early registration watchdog: watchdog_core: Add watchdog registration deferral mechanism watchdog: max63xx: dynamically allocate device watchdog: imx2_wdt: Disable previously acquired clock on error path watchdog: imx2_wdt: Check for clk_prepare_enable() error watchdog: hpwdt: Add support for WDIOC_SETOPTIONS watchdog: docs: omap_wdt also understands nowayout watchdog: omap_wdt: implement get_timeleft watchdog: da9062: DA9062 watchdog driver watchdog: imx2_wdt: set watchdog parent device watchdog: mena21_wdt: Fix possible NULL pointer dereference watchdog: dw_wdt: keepalive the watchdog at write time watchdog: dw_wdt: No need for a spinlock watchdog: imx2_wdt: also set wdog->timeout to new_timeout watchdog: Allow compile test of GPIO consumers if !GPIOLIB watchdog: cadence: Add dependency on HAS_IOMEM watchdog: max63xx_wdt: Constify platform_device_id watchdog: MAX63XX_WATCHDOG does not depend on ARM watchdog: imgpdc: Add some documentation about the timeout ...
This commit is contained in:
commit
93899e39e8
|
@ -0,0 +1,25 @@
|
||||||
|
Conexant Digicolor SoCs Watchdog timer
|
||||||
|
|
||||||
|
The watchdog functionality in Conexant Digicolor SoCs relies on the so called
|
||||||
|
"Agent Communication" block. This block includes the eight programmable system
|
||||||
|
timer counters. The first timer (called "Timer A") is the only one that can be
|
||||||
|
used as watchdog.
|
||||||
|
|
||||||
|
Required properties:
|
||||||
|
|
||||||
|
- compatible : Should be "cnxt,cx92755-wdt"
|
||||||
|
- reg : Specifies base physical address and size of the registers
|
||||||
|
- clocks : phandle; specifies the clock that drives the timer
|
||||||
|
|
||||||
|
Optional properties:
|
||||||
|
|
||||||
|
- timeout-sec : Contains the watchdog timeout in seconds
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
watchdog@f0000fc0 {
|
||||||
|
compatible = "cnxt,cx92755-wdt";
|
||||||
|
reg = <0xf0000fc0 0x8>;
|
||||||
|
clocks = <&main_clk>;
|
||||||
|
timeout-sec = <15>;
|
||||||
|
};
|
|
@ -1,10 +1,11 @@
|
||||||
TI Watchdog Timer (WDT) Controller for OMAP
|
TI Watchdog Timer (WDT) Controller for OMAP
|
||||||
|
|
||||||
Required properties:
|
Required properties:
|
||||||
compatible:
|
- compatible : "ti,omap3-wdt" for OMAP3 or "ti,omap4-wdt" for OMAP4
|
||||||
- "ti,omap3-wdt" for OMAP3
|
- ti,hwmods : Name of the hwmod associated to the WDT
|
||||||
- "ti,omap4-wdt" for OMAP4
|
|
||||||
- ti,hwmods: Name of the hwmod associated to the WDT
|
Optional properties:
|
||||||
|
- timeout-sec : default watchdog timeout in seconds
|
||||||
|
|
||||||
Examples:
|
Examples:
|
||||||
|
|
||||||
|
|
|
@ -36,6 +36,10 @@ The watchdog_unregister_device routine deregisters a registered watchdog timer
|
||||||
device. The parameter of this routine is the pointer to the registered
|
device. The parameter of this routine is the pointer to the registered
|
||||||
watchdog_device structure.
|
watchdog_device structure.
|
||||||
|
|
||||||
|
The watchdog subsystem includes an registration deferral mechanism,
|
||||||
|
which allows you to register an watchdog as early as you wish during
|
||||||
|
the boot process.
|
||||||
|
|
||||||
The watchdog device structure looks like this:
|
The watchdog device structure looks like this:
|
||||||
|
|
||||||
struct watchdog_device {
|
struct watchdog_device {
|
||||||
|
@ -52,6 +56,7 @@ struct watchdog_device {
|
||||||
void *driver_data;
|
void *driver_data;
|
||||||
struct mutex lock;
|
struct mutex lock;
|
||||||
unsigned long status;
|
unsigned long status;
|
||||||
|
struct list_head deferred;
|
||||||
};
|
};
|
||||||
|
|
||||||
It contains following fields:
|
It contains following fields:
|
||||||
|
@ -80,6 +85,8 @@ It contains following fields:
|
||||||
information about the status of the device (Like: is the watchdog timer
|
information about the status of the device (Like: is the watchdog timer
|
||||||
running/active, is the nowayout bit set, is the device opened via
|
running/active, is the nowayout bit set, is the device opened via
|
||||||
the /dev/watchdog interface or not, ...).
|
the /dev/watchdog interface or not, ...).
|
||||||
|
* deferred: entry in wtd_deferred_reg_list which is used to
|
||||||
|
register early initialized watchdogs.
|
||||||
|
|
||||||
The list of watchdog operations is defined as:
|
The list of watchdog operations is defined as:
|
||||||
|
|
||||||
|
|
|
@ -208,6 +208,9 @@ nowayout: Watchdog cannot be stopped once started
|
||||||
-------------------------------------------------
|
-------------------------------------------------
|
||||||
omap_wdt:
|
omap_wdt:
|
||||||
timer_margin: initial watchdog timeout (in seconds)
|
timer_margin: initial watchdog timeout (in seconds)
|
||||||
|
early_enable: Watchdog is started on module insertion (default=0
|
||||||
|
nowayout: Watchdog cannot be stopped once started
|
||||||
|
(default=kernel config parameter)
|
||||||
-------------------------------------------------
|
-------------------------------------------------
|
||||||
orion_wdt:
|
orion_wdt:
|
||||||
heartbeat: Initial watchdog heartbeat in seconds
|
heartbeat: Initial watchdog heartbeat in seconds
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
|
||||||
#
|
#
|
||||||
# Watchdog device configuration
|
# Watchdog device configuration
|
||||||
#
|
#
|
||||||
|
@ -96,6 +97,15 @@ config DA9063_WATCHDOG
|
||||||
|
|
||||||
This driver can be built as a module. The module name is da9063_wdt.
|
This driver can be built as a module. The module name is da9063_wdt.
|
||||||
|
|
||||||
|
config DA9062_WATCHDOG
|
||||||
|
tristate "Dialog DA9062 Watchdog"
|
||||||
|
depends on MFD_DA9062
|
||||||
|
select WATCHDOG_CORE
|
||||||
|
help
|
||||||
|
Support for the watchdog in the DA9062 PMIC.
|
||||||
|
|
||||||
|
This driver can be built as a module. The module name is da9062_wdt.
|
||||||
|
|
||||||
config GPIO_WATCHDOG
|
config GPIO_WATCHDOG
|
||||||
tristate "Watchdog device controlled through GPIO-line"
|
tristate "Watchdog device controlled through GPIO-line"
|
||||||
depends on OF_GPIO
|
depends on OF_GPIO
|
||||||
|
@ -104,6 +114,17 @@ config GPIO_WATCHDOG
|
||||||
If you say yes here you get support for watchdog device
|
If you say yes here you get support for watchdog device
|
||||||
controlled through GPIO-line.
|
controlled through GPIO-line.
|
||||||
|
|
||||||
|
config GPIO_WATCHDOG_ARCH_INITCALL
|
||||||
|
bool "Register the watchdog as early as possible"
|
||||||
|
depends on GPIO_WATCHDOG=y
|
||||||
|
help
|
||||||
|
In some situations, the default initcall level (module_init)
|
||||||
|
in not early enough in the boot process to avoid the watchdog
|
||||||
|
to be triggered.
|
||||||
|
If you say yes here, the initcall level would be raised to
|
||||||
|
arch_initcall.
|
||||||
|
If in doubt, say N.
|
||||||
|
|
||||||
config MENF21BMC_WATCHDOG
|
config MENF21BMC_WATCHDOG
|
||||||
tristate "MEN 14F021P00 BMC Watchdog"
|
tristate "MEN 14F021P00 BMC Watchdog"
|
||||||
depends on MFD_MENF21BMC
|
depends on MFD_MENF21BMC
|
||||||
|
@ -169,6 +190,7 @@ config AT91SAM9X_WATCHDOG
|
||||||
|
|
||||||
config CADENCE_WATCHDOG
|
config CADENCE_WATCHDOG
|
||||||
tristate "Cadence Watchdog Timer"
|
tristate "Cadence Watchdog Timer"
|
||||||
|
depends on HAS_IOMEM
|
||||||
select WATCHDOG_CORE
|
select WATCHDOG_CORE
|
||||||
help
|
help
|
||||||
Say Y here if you want to include support for the watchdog
|
Say Y here if you want to include support for the watchdog
|
||||||
|
@ -408,7 +430,7 @@ config TS72XX_WATCHDOG
|
||||||
|
|
||||||
config MAX63XX_WATCHDOG
|
config MAX63XX_WATCHDOG
|
||||||
tristate "Max63xx watchdog"
|
tristate "Max63xx watchdog"
|
||||||
depends on ARM && HAS_IOMEM
|
depends on HAS_IOMEM
|
||||||
select WATCHDOG_CORE
|
select WATCHDOG_CORE
|
||||||
help
|
help
|
||||||
Support for memory mapped max63{69,70,71,72,73,74} watchdog timer.
|
Support for memory mapped max63{69,70,71,72,73,74} watchdog timer.
|
||||||
|
@ -526,6 +548,16 @@ config MEDIATEK_WATCHDOG
|
||||||
To compile this driver as a module, choose M here: the
|
To compile this driver as a module, choose M here: the
|
||||||
module will be called mtk_wdt.
|
module will be called mtk_wdt.
|
||||||
|
|
||||||
|
config DIGICOLOR_WATCHDOG
|
||||||
|
tristate "Conexant Digicolor SoCs watchdog support"
|
||||||
|
depends on ARCH_DIGICOLOR
|
||||||
|
select WATCHDOG_CORE
|
||||||
|
help
|
||||||
|
Say Y here to include support for the watchdog timer
|
||||||
|
in Conexant Digicolor SoCs.
|
||||||
|
To compile this driver as a module, choose M here: the
|
||||||
|
module will be called digicolor_wdt.
|
||||||
|
|
||||||
# AVR32 Architecture
|
# AVR32 Architecture
|
||||||
|
|
||||||
config AT32AP700X_WDT
|
config AT32AP700X_WDT
|
||||||
|
@ -1355,7 +1387,7 @@ config BOOKE_WDT_DEFAULT_TIMEOUT
|
||||||
config MEN_A21_WDT
|
config MEN_A21_WDT
|
||||||
tristate "MEN A21 VME CPU Carrier Board Watchdog Timer"
|
tristate "MEN A21 VME CPU Carrier Board Watchdog Timer"
|
||||||
select WATCHDOG_CORE
|
select WATCHDOG_CORE
|
||||||
depends on GPIOLIB
|
depends on GPIOLIB || COMPILE_TEST
|
||||||
help
|
help
|
||||||
Watchdog driver for MEN A21 VMEbus CPU Carrier Boards.
|
Watchdog driver for MEN A21 VMEbus CPU Carrier Boards.
|
||||||
|
|
||||||
|
|
|
@ -65,6 +65,7 @@ obj-$(CONFIG_BCM_KONA_WDT) += bcm_kona_wdt.o
|
||||||
obj-$(CONFIG_TEGRA_WATCHDOG) += tegra_wdt.o
|
obj-$(CONFIG_TEGRA_WATCHDOG) += tegra_wdt.o
|
||||||
obj-$(CONFIG_MESON_WATCHDOG) += meson_wdt.o
|
obj-$(CONFIG_MESON_WATCHDOG) += meson_wdt.o
|
||||||
obj-$(CONFIG_MEDIATEK_WATCHDOG) += mtk_wdt.o
|
obj-$(CONFIG_MEDIATEK_WATCHDOG) += mtk_wdt.o
|
||||||
|
obj-$(CONFIG_DIGICOLOR_WATCHDOG) += digicolor_wdt.o
|
||||||
|
|
||||||
# AVR32 Architecture
|
# AVR32 Architecture
|
||||||
obj-$(CONFIG_AT32AP700X_WDT) += at32ap700x_wdt.o
|
obj-$(CONFIG_AT32AP700X_WDT) += at32ap700x_wdt.o
|
||||||
|
@ -180,6 +181,7 @@ obj-$(CONFIG_XEN_WDT) += xen_wdt.o
|
||||||
# Architecture Independent
|
# Architecture Independent
|
||||||
obj-$(CONFIG_DA9052_WATCHDOG) += da9052_wdt.o
|
obj-$(CONFIG_DA9052_WATCHDOG) += da9052_wdt.o
|
||||||
obj-$(CONFIG_DA9055_WATCHDOG) += da9055_wdt.o
|
obj-$(CONFIG_DA9055_WATCHDOG) += da9055_wdt.o
|
||||||
|
obj-$(CONFIG_DA9062_WATCHDOG) += da9062_wdt.o
|
||||||
obj-$(CONFIG_DA9063_WATCHDOG) += da9063_wdt.o
|
obj-$(CONFIG_DA9063_WATCHDOG) += da9063_wdt.o
|
||||||
obj-$(CONFIG_GPIO_WATCHDOG) += gpio_wdt.o
|
obj-$(CONFIG_GPIO_WATCHDOG) += gpio_wdt.o
|
||||||
obj-$(CONFIG_WM831X_WATCHDOG) += wm831x_wdt.o
|
obj-$(CONFIG_WM831X_WATCHDOG) += wm831x_wdt.o
|
||||||
|
|
|
@ -40,9 +40,9 @@
|
||||||
#define DRV_NAME "AT91SAM9 Watchdog"
|
#define DRV_NAME "AT91SAM9 Watchdog"
|
||||||
|
|
||||||
#define wdt_read(wdt, field) \
|
#define wdt_read(wdt, field) \
|
||||||
__raw_readl((wdt)->base + (field))
|
readl_relaxed((wdt)->base + (field))
|
||||||
#define wdt_write(wtd, field, val) \
|
#define wdt_write(wtd, field, val) \
|
||||||
__raw_writel((val), (wdt)->base + (field))
|
writel_relaxed((val), (wdt)->base + (field))
|
||||||
|
|
||||||
/* AT91SAM9 watchdog runs a 12bit counter @ 256Hz,
|
/* AT91SAM9 watchdog runs a 12bit counter @ 256Hz,
|
||||||
* use this to convert a watchdog
|
* use this to convert a watchdog
|
||||||
|
|
|
@ -0,0 +1,253 @@
|
||||||
|
/*
|
||||||
|
* da9062_wdt.c - WDT device driver for DA9062
|
||||||
|
* Copyright (C) 2015 Dialog Semiconductor Ltd.
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU General Public License
|
||||||
|
* as published by the Free Software Foundation; either version 2
|
||||||
|
* of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <linux/kernel.h>
|
||||||
|
#include <linux/module.h>
|
||||||
|
#include <linux/watchdog.h>
|
||||||
|
#include <linux/platform_device.h>
|
||||||
|
#include <linux/uaccess.h>
|
||||||
|
#include <linux/slab.h>
|
||||||
|
#include <linux/delay.h>
|
||||||
|
#include <linux/jiffies.h>
|
||||||
|
#include <linux/mfd/da9062/registers.h>
|
||||||
|
#include <linux/mfd/da9062/core.h>
|
||||||
|
#include <linux/regmap.h>
|
||||||
|
#include <linux/of.h>
|
||||||
|
|
||||||
|
static const unsigned int wdt_timeout[] = { 0, 2, 4, 8, 16, 32, 65, 131 };
|
||||||
|
#define DA9062_TWDSCALE_DISABLE 0
|
||||||
|
#define DA9062_TWDSCALE_MIN 1
|
||||||
|
#define DA9062_TWDSCALE_MAX (ARRAY_SIZE(wdt_timeout) - 1)
|
||||||
|
#define DA9062_WDT_MIN_TIMEOUT wdt_timeout[DA9062_TWDSCALE_MIN]
|
||||||
|
#define DA9062_WDT_MAX_TIMEOUT wdt_timeout[DA9062_TWDSCALE_MAX]
|
||||||
|
#define DA9062_WDG_DEFAULT_TIMEOUT wdt_timeout[DA9062_TWDSCALE_MAX-1]
|
||||||
|
#define DA9062_RESET_PROTECTION_MS 300
|
||||||
|
|
||||||
|
struct da9062_watchdog {
|
||||||
|
struct da9062 *hw;
|
||||||
|
struct watchdog_device wdtdev;
|
||||||
|
unsigned long j_time_stamp;
|
||||||
|
};
|
||||||
|
|
||||||
|
static void da9062_set_window_start(struct da9062_watchdog *wdt)
|
||||||
|
{
|
||||||
|
wdt->j_time_stamp = jiffies;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void da9062_apply_window_protection(struct da9062_watchdog *wdt)
|
||||||
|
{
|
||||||
|
unsigned long delay = msecs_to_jiffies(DA9062_RESET_PROTECTION_MS);
|
||||||
|
unsigned long timeout = wdt->j_time_stamp + delay;
|
||||||
|
unsigned long now = jiffies;
|
||||||
|
unsigned int diff_ms;
|
||||||
|
|
||||||
|
/* if time-limit has not elapsed then wait for remainder */
|
||||||
|
if (time_before(now, timeout)) {
|
||||||
|
diff_ms = jiffies_to_msecs(timeout-now);
|
||||||
|
dev_dbg(wdt->hw->dev,
|
||||||
|
"Kicked too quickly. Delaying %u msecs\n", diff_ms);
|
||||||
|
msleep(diff_ms);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static unsigned int da9062_wdt_timeout_to_sel(unsigned int secs)
|
||||||
|
{
|
||||||
|
unsigned int i;
|
||||||
|
|
||||||
|
for (i = DA9062_TWDSCALE_MIN; i <= DA9062_TWDSCALE_MAX; i++) {
|
||||||
|
if (wdt_timeout[i] >= secs)
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
|
||||||
|
return DA9062_TWDSCALE_MAX;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int da9062_reset_watchdog_timer(struct da9062_watchdog *wdt)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
da9062_apply_window_protection(wdt);
|
||||||
|
|
||||||
|
ret = regmap_update_bits(wdt->hw->regmap,
|
||||||
|
DA9062AA_CONTROL_F,
|
||||||
|
DA9062AA_WATCHDOG_MASK,
|
||||||
|
DA9062AA_WATCHDOG_MASK);
|
||||||
|
|
||||||
|
da9062_set_window_start(wdt);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int da9062_wdt_update_timeout_register(struct da9062_watchdog *wdt,
|
||||||
|
unsigned int regval)
|
||||||
|
{
|
||||||
|
struct da9062 *chip = wdt->hw;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
ret = da9062_reset_watchdog_timer(wdt);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
return regmap_update_bits(chip->regmap,
|
||||||
|
DA9062AA_CONTROL_D,
|
||||||
|
DA9062AA_TWDSCALE_MASK,
|
||||||
|
regval);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int da9062_wdt_start(struct watchdog_device *wdd)
|
||||||
|
{
|
||||||
|
struct da9062_watchdog *wdt = watchdog_get_drvdata(wdd);
|
||||||
|
unsigned int selector;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
selector = da9062_wdt_timeout_to_sel(wdt->wdtdev.timeout);
|
||||||
|
ret = da9062_wdt_update_timeout_register(wdt, selector);
|
||||||
|
if (ret)
|
||||||
|
dev_err(wdt->hw->dev, "Watchdog failed to start (err = %d)\n",
|
||||||
|
ret);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int da9062_wdt_stop(struct watchdog_device *wdd)
|
||||||
|
{
|
||||||
|
struct da9062_watchdog *wdt = watchdog_get_drvdata(wdd);
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
ret = da9062_reset_watchdog_timer(wdt);
|
||||||
|
if (ret) {
|
||||||
|
dev_err(wdt->hw->dev, "Failed to ping the watchdog (err = %d)\n",
|
||||||
|
ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = regmap_update_bits(wdt->hw->regmap,
|
||||||
|
DA9062AA_CONTROL_D,
|
||||||
|
DA9062AA_TWDSCALE_MASK,
|
||||||
|
DA9062_TWDSCALE_DISABLE);
|
||||||
|
if (ret)
|
||||||
|
dev_err(wdt->hw->dev, "Watchdog failed to stop (err = %d)\n",
|
||||||
|
ret);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int da9062_wdt_ping(struct watchdog_device *wdd)
|
||||||
|
{
|
||||||
|
struct da9062_watchdog *wdt = watchdog_get_drvdata(wdd);
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
ret = da9062_reset_watchdog_timer(wdt);
|
||||||
|
if (ret)
|
||||||
|
dev_err(wdt->hw->dev, "Failed to ping the watchdog (err = %d)\n",
|
||||||
|
ret);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int da9062_wdt_set_timeout(struct watchdog_device *wdd,
|
||||||
|
unsigned int timeout)
|
||||||
|
{
|
||||||
|
struct da9062_watchdog *wdt = watchdog_get_drvdata(wdd);
|
||||||
|
unsigned int selector;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
selector = da9062_wdt_timeout_to_sel(timeout);
|
||||||
|
ret = da9062_wdt_update_timeout_register(wdt, selector);
|
||||||
|
if (ret)
|
||||||
|
dev_err(wdt->hw->dev, "Failed to set watchdog timeout (err = %d)\n",
|
||||||
|
ret);
|
||||||
|
else
|
||||||
|
wdd->timeout = wdt_timeout[selector];
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct watchdog_info da9062_watchdog_info = {
|
||||||
|
.options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING,
|
||||||
|
.identity = "DA9062 WDT",
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct watchdog_ops da9062_watchdog_ops = {
|
||||||
|
.owner = THIS_MODULE,
|
||||||
|
.start = da9062_wdt_start,
|
||||||
|
.stop = da9062_wdt_stop,
|
||||||
|
.ping = da9062_wdt_ping,
|
||||||
|
.set_timeout = da9062_wdt_set_timeout,
|
||||||
|
};
|
||||||
|
|
||||||
|
static int da9062_wdt_probe(struct platform_device *pdev)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
struct da9062 *chip;
|
||||||
|
struct da9062_watchdog *wdt;
|
||||||
|
|
||||||
|
chip = dev_get_drvdata(pdev->dev.parent);
|
||||||
|
if (!chip)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
wdt = devm_kzalloc(&pdev->dev, sizeof(*wdt), GFP_KERNEL);
|
||||||
|
if (!wdt)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
wdt->hw = chip;
|
||||||
|
|
||||||
|
wdt->wdtdev.info = &da9062_watchdog_info;
|
||||||
|
wdt->wdtdev.ops = &da9062_watchdog_ops;
|
||||||
|
wdt->wdtdev.min_timeout = DA9062_WDT_MIN_TIMEOUT;
|
||||||
|
wdt->wdtdev.max_timeout = DA9062_WDT_MAX_TIMEOUT;
|
||||||
|
wdt->wdtdev.timeout = DA9062_WDG_DEFAULT_TIMEOUT;
|
||||||
|
wdt->wdtdev.status = WATCHDOG_NOWAYOUT_INIT_STATUS;
|
||||||
|
|
||||||
|
watchdog_set_drvdata(&wdt->wdtdev, wdt);
|
||||||
|
dev_set_drvdata(&pdev->dev, wdt);
|
||||||
|
|
||||||
|
ret = watchdog_register_device(&wdt->wdtdev);
|
||||||
|
if (ret < 0) {
|
||||||
|
dev_err(wdt->hw->dev,
|
||||||
|
"watchdog registration failed (%d)\n", ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
da9062_set_window_start(wdt);
|
||||||
|
|
||||||
|
ret = da9062_wdt_ping(&wdt->wdtdev);
|
||||||
|
if (ret < 0)
|
||||||
|
watchdog_unregister_device(&wdt->wdtdev);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int da9062_wdt_remove(struct platform_device *pdev)
|
||||||
|
{
|
||||||
|
struct da9062_watchdog *wdt = dev_get_drvdata(&pdev->dev);
|
||||||
|
|
||||||
|
watchdog_unregister_device(&wdt->wdtdev);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct platform_driver da9062_wdt_driver = {
|
||||||
|
.probe = da9062_wdt_probe,
|
||||||
|
.remove = da9062_wdt_remove,
|
||||||
|
.driver = {
|
||||||
|
.name = "da9062-watchdog",
|
||||||
|
},
|
||||||
|
};
|
||||||
|
module_platform_driver(da9062_wdt_driver);
|
||||||
|
|
||||||
|
MODULE_AUTHOR("S Twiss <stwiss.opensource@diasemi.com>");
|
||||||
|
MODULE_DESCRIPTION("WDT device driver for Dialog DA9062");
|
||||||
|
MODULE_LICENSE("GPL");
|
||||||
|
MODULE_ALIAS("platform:da9062-watchdog");
|
|
@ -0,0 +1,205 @@
|
||||||
|
/*
|
||||||
|
* Watchdog driver for Conexant Digicolor
|
||||||
|
*
|
||||||
|
* Copyright (C) 2015 Paradox Innovation Ltd.
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU General Public License as published by the
|
||||||
|
* Free Software Foundation; either version 2 of the License, or (at your
|
||||||
|
* option) any later version.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <linux/types.h>
|
||||||
|
#include <linux/module.h>
|
||||||
|
#include <linux/io.h>
|
||||||
|
#include <linux/delay.h>
|
||||||
|
#include <linux/clk.h>
|
||||||
|
#include <linux/watchdog.h>
|
||||||
|
#include <linux/reboot.h>
|
||||||
|
#include <linux/platform_device.h>
|
||||||
|
#include <linux/of_address.h>
|
||||||
|
|
||||||
|
#define TIMER_A_CONTROL 0
|
||||||
|
#define TIMER_A_COUNT 4
|
||||||
|
|
||||||
|
#define TIMER_A_ENABLE_COUNT BIT(0)
|
||||||
|
#define TIMER_A_ENABLE_WATCHDOG BIT(1)
|
||||||
|
|
||||||
|
struct dc_wdt {
|
||||||
|
void __iomem *base;
|
||||||
|
struct clk *clk;
|
||||||
|
struct notifier_block restart_handler;
|
||||||
|
spinlock_t lock;
|
||||||
|
};
|
||||||
|
|
||||||
|
static unsigned timeout;
|
||||||
|
module_param(timeout, uint, 0);
|
||||||
|
MODULE_PARM_DESC(timeout, "Watchdog timeout in seconds");
|
||||||
|
|
||||||
|
static void dc_wdt_set(struct dc_wdt *wdt, u32 ticks)
|
||||||
|
{
|
||||||
|
unsigned long flags;
|
||||||
|
|
||||||
|
spin_lock_irqsave(&wdt->lock, flags);
|
||||||
|
|
||||||
|
writel_relaxed(0, wdt->base + TIMER_A_CONTROL);
|
||||||
|
writel_relaxed(ticks, wdt->base + TIMER_A_COUNT);
|
||||||
|
writel_relaxed(TIMER_A_ENABLE_COUNT | TIMER_A_ENABLE_WATCHDOG,
|
||||||
|
wdt->base + TIMER_A_CONTROL);
|
||||||
|
|
||||||
|
spin_unlock_irqrestore(&wdt->lock, flags);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int dc_restart_handler(struct notifier_block *this, unsigned long mode,
|
||||||
|
void *cmd)
|
||||||
|
{
|
||||||
|
struct dc_wdt *wdt = container_of(this, struct dc_wdt, restart_handler);
|
||||||
|
|
||||||
|
dc_wdt_set(wdt, 1);
|
||||||
|
/* wait for reset to assert... */
|
||||||
|
mdelay(500);
|
||||||
|
|
||||||
|
return NOTIFY_DONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int dc_wdt_start(struct watchdog_device *wdog)
|
||||||
|
{
|
||||||
|
struct dc_wdt *wdt = watchdog_get_drvdata(wdog);
|
||||||
|
|
||||||
|
dc_wdt_set(wdt, wdog->timeout * clk_get_rate(wdt->clk));
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int dc_wdt_stop(struct watchdog_device *wdog)
|
||||||
|
{
|
||||||
|
struct dc_wdt *wdt = watchdog_get_drvdata(wdog);
|
||||||
|
|
||||||
|
writel_relaxed(0, wdt->base + TIMER_A_CONTROL);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int dc_wdt_set_timeout(struct watchdog_device *wdog, unsigned int t)
|
||||||
|
{
|
||||||
|
struct dc_wdt *wdt = watchdog_get_drvdata(wdog);
|
||||||
|
|
||||||
|
dc_wdt_set(wdt, t * clk_get_rate(wdt->clk));
|
||||||
|
wdog->timeout = t;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static unsigned int dc_wdt_get_timeleft(struct watchdog_device *wdog)
|
||||||
|
{
|
||||||
|
struct dc_wdt *wdt = watchdog_get_drvdata(wdog);
|
||||||
|
uint32_t count = readl_relaxed(wdt->base + TIMER_A_COUNT);
|
||||||
|
|
||||||
|
return count / clk_get_rate(wdt->clk);
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct watchdog_ops dc_wdt_ops = {
|
||||||
|
.owner = THIS_MODULE,
|
||||||
|
.start = dc_wdt_start,
|
||||||
|
.stop = dc_wdt_stop,
|
||||||
|
.set_timeout = dc_wdt_set_timeout,
|
||||||
|
.get_timeleft = dc_wdt_get_timeleft,
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct watchdog_info dc_wdt_info = {
|
||||||
|
.options = WDIOF_SETTIMEOUT | WDIOF_MAGICCLOSE
|
||||||
|
| WDIOF_KEEPALIVEPING,
|
||||||
|
.identity = "Conexant Digicolor Watchdog",
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct watchdog_device dc_wdt_wdd = {
|
||||||
|
.info = &dc_wdt_info,
|
||||||
|
.ops = &dc_wdt_ops,
|
||||||
|
.min_timeout = 1,
|
||||||
|
};
|
||||||
|
|
||||||
|
static int dc_wdt_probe(struct platform_device *pdev)
|
||||||
|
{
|
||||||
|
struct device *dev = &pdev->dev;
|
||||||
|
struct device_node *np = dev->of_node;
|
||||||
|
struct dc_wdt *wdt;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
wdt = devm_kzalloc(dev, sizeof(struct dc_wdt), GFP_KERNEL);
|
||||||
|
if (!wdt)
|
||||||
|
return -ENOMEM;
|
||||||
|
platform_set_drvdata(pdev, wdt);
|
||||||
|
|
||||||
|
wdt->base = of_iomap(np, 0);
|
||||||
|
if (!wdt->base) {
|
||||||
|
dev_err(dev, "Failed to remap watchdog regs");
|
||||||
|
return -ENODEV;
|
||||||
|
}
|
||||||
|
|
||||||
|
wdt->clk = devm_clk_get(&pdev->dev, NULL);
|
||||||
|
if (IS_ERR(wdt->clk)) {
|
||||||
|
ret = PTR_ERR(wdt->clk);
|
||||||
|
goto err_iounmap;
|
||||||
|
}
|
||||||
|
dc_wdt_wdd.max_timeout = U32_MAX / clk_get_rate(wdt->clk);
|
||||||
|
dc_wdt_wdd.timeout = dc_wdt_wdd.max_timeout;
|
||||||
|
|
||||||
|
spin_lock_init(&wdt->lock);
|
||||||
|
|
||||||
|
watchdog_set_drvdata(&dc_wdt_wdd, wdt);
|
||||||
|
watchdog_init_timeout(&dc_wdt_wdd, timeout, dev);
|
||||||
|
ret = watchdog_register_device(&dc_wdt_wdd);
|
||||||
|
if (ret) {
|
||||||
|
dev_err(dev, "Failed to register watchdog device");
|
||||||
|
goto err_iounmap;
|
||||||
|
}
|
||||||
|
|
||||||
|
wdt->restart_handler.notifier_call = dc_restart_handler;
|
||||||
|
wdt->restart_handler.priority = 128;
|
||||||
|
ret = register_restart_handler(&wdt->restart_handler);
|
||||||
|
if (ret)
|
||||||
|
dev_warn(&pdev->dev, "cannot register restart handler\n");
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
err_iounmap:
|
||||||
|
iounmap(wdt->base);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int dc_wdt_remove(struct platform_device *pdev)
|
||||||
|
{
|
||||||
|
struct dc_wdt *wdt = platform_get_drvdata(pdev);
|
||||||
|
|
||||||
|
unregister_restart_handler(&wdt->restart_handler);
|
||||||
|
watchdog_unregister_device(&dc_wdt_wdd);
|
||||||
|
iounmap(wdt->base);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void dc_wdt_shutdown(struct platform_device *pdev)
|
||||||
|
{
|
||||||
|
dc_wdt_stop(&dc_wdt_wdd);
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct of_device_id dc_wdt_of_match[] = {
|
||||||
|
{ .compatible = "cnxt,cx92755-wdt", },
|
||||||
|
{},
|
||||||
|
};
|
||||||
|
MODULE_DEVICE_TABLE(of, dc_wdt_of_match);
|
||||||
|
|
||||||
|
static struct platform_driver dc_wdt_driver = {
|
||||||
|
.probe = dc_wdt_probe,
|
||||||
|
.remove = dc_wdt_remove,
|
||||||
|
.shutdown = dc_wdt_shutdown,
|
||||||
|
.driver = {
|
||||||
|
.name = "digicolor-wdt",
|
||||||
|
.of_match_table = dc_wdt_of_match,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
module_platform_driver(dc_wdt_driver);
|
||||||
|
|
||||||
|
MODULE_AUTHOR("Baruch Siach <baruch@tkos.co.il>");
|
||||||
|
MODULE_DESCRIPTION("Driver for Conexant Digicolor watchdog timer");
|
||||||
|
MODULE_LICENSE("GPL");
|
|
@ -35,7 +35,6 @@
|
||||||
#include <linux/pm.h>
|
#include <linux/pm.h>
|
||||||
#include <linux/platform_device.h>
|
#include <linux/platform_device.h>
|
||||||
#include <linux/reboot.h>
|
#include <linux/reboot.h>
|
||||||
#include <linux/spinlock.h>
|
|
||||||
#include <linux/timer.h>
|
#include <linux/timer.h>
|
||||||
#include <linux/uaccess.h>
|
#include <linux/uaccess.h>
|
||||||
#include <linux/watchdog.h>
|
#include <linux/watchdog.h>
|
||||||
|
@ -61,7 +60,6 @@ MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started "
|
||||||
#define WDT_TIMEOUT (HZ / 2)
|
#define WDT_TIMEOUT (HZ / 2)
|
||||||
|
|
||||||
static struct {
|
static struct {
|
||||||
spinlock_t lock;
|
|
||||||
void __iomem *regs;
|
void __iomem *regs;
|
||||||
struct clk *clk;
|
struct clk *clk;
|
||||||
unsigned long in_use;
|
unsigned long in_use;
|
||||||
|
@ -177,7 +175,6 @@ static int dw_wdt_open(struct inode *inode, struct file *filp)
|
||||||
/* Make sure we don't get unloaded. */
|
/* Make sure we don't get unloaded. */
|
||||||
__module_get(THIS_MODULE);
|
__module_get(THIS_MODULE);
|
||||||
|
|
||||||
spin_lock(&dw_wdt.lock);
|
|
||||||
if (!dw_wdt_is_enabled()) {
|
if (!dw_wdt_is_enabled()) {
|
||||||
/*
|
/*
|
||||||
* The watchdog is not currently enabled. Set the timeout to
|
* The watchdog is not currently enabled. Set the timeout to
|
||||||
|
@ -190,8 +187,6 @@ static int dw_wdt_open(struct inode *inode, struct file *filp)
|
||||||
|
|
||||||
dw_wdt_set_next_heartbeat();
|
dw_wdt_set_next_heartbeat();
|
||||||
|
|
||||||
spin_unlock(&dw_wdt.lock);
|
|
||||||
|
|
||||||
return nonseekable_open(inode, filp);
|
return nonseekable_open(inode, filp);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -220,6 +215,7 @@ static ssize_t dw_wdt_write(struct file *filp, const char __user *buf,
|
||||||
}
|
}
|
||||||
|
|
||||||
dw_wdt_set_next_heartbeat();
|
dw_wdt_set_next_heartbeat();
|
||||||
|
dw_wdt_keepalive();
|
||||||
mod_timer(&dw_wdt.timer, jiffies + WDT_TIMEOUT);
|
mod_timer(&dw_wdt.timer, jiffies + WDT_TIMEOUT);
|
||||||
|
|
||||||
return len;
|
return len;
|
||||||
|
@ -348,8 +344,6 @@ static int dw_wdt_drv_probe(struct platform_device *pdev)
|
||||||
if (ret)
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
spin_lock_init(&dw_wdt.lock);
|
|
||||||
|
|
||||||
ret = misc_register(&dw_wdt_miscdev);
|
ret = misc_register(&dw_wdt_miscdev);
|
||||||
if (ret)
|
if (ret)
|
||||||
goto out_disable_clk;
|
goto out_disable_clk;
|
||||||
|
|
|
@ -267,7 +267,16 @@ static struct platform_driver gpio_wdt_driver = {
|
||||||
.probe = gpio_wdt_probe,
|
.probe = gpio_wdt_probe,
|
||||||
.remove = gpio_wdt_remove,
|
.remove = gpio_wdt_remove,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#ifdef CONFIG_GPIO_WATCHDOG_ARCH_INITCALL
|
||||||
|
static int __init gpio_wdt_init(void)
|
||||||
|
{
|
||||||
|
return platform_driver_register(&gpio_wdt_driver);
|
||||||
|
}
|
||||||
|
arch_initcall(gpio_wdt_init);
|
||||||
|
#else
|
||||||
module_platform_driver(gpio_wdt_driver);
|
module_platform_driver(gpio_wdt_driver);
|
||||||
|
#endif
|
||||||
|
|
||||||
MODULE_AUTHOR("Alexander Shiyan <shc_work@mail.ru>");
|
MODULE_AUTHOR("Alexander Shiyan <shc_work@mail.ru>");
|
||||||
MODULE_DESCRIPTION("GPIO Watchdog");
|
MODULE_DESCRIPTION("GPIO Watchdog");
|
||||||
|
|
|
@ -588,7 +588,7 @@ static long hpwdt_ioctl(struct file *file, unsigned int cmd,
|
||||||
{
|
{
|
||||||
void __user *argp = (void __user *)arg;
|
void __user *argp = (void __user *)arg;
|
||||||
int __user *p = argp;
|
int __user *p = argp;
|
||||||
int new_margin;
|
int new_margin, options;
|
||||||
int ret = -ENOTTY;
|
int ret = -ENOTTY;
|
||||||
|
|
||||||
switch (cmd) {
|
switch (cmd) {
|
||||||
|
@ -608,6 +608,20 @@ static long hpwdt_ioctl(struct file *file, unsigned int cmd,
|
||||||
ret = 0;
|
ret = 0;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case WDIOC_SETOPTIONS:
|
||||||
|
ret = get_user(options, p);
|
||||||
|
if (ret)
|
||||||
|
break;
|
||||||
|
|
||||||
|
if (options & WDIOS_DISABLECARD)
|
||||||
|
hpwdt_stop();
|
||||||
|
|
||||||
|
if (options & WDIOS_ENABLECARD) {
|
||||||
|
hpwdt_start();
|
||||||
|
hpwdt_ping();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
case WDIOC_SETTIMEOUT:
|
case WDIOC_SETTIMEOUT:
|
||||||
ret = get_user(new_margin, p);
|
ret = get_user(new_margin, p);
|
||||||
if (ret)
|
if (ret)
|
||||||
|
|
|
@ -9,6 +9,35 @@
|
||||||
*
|
*
|
||||||
* Based on drivers/watchdog/sunxi_wdt.c Copyright (c) 2013 Carlo Caione
|
* Based on drivers/watchdog/sunxi_wdt.c Copyright (c) 2013 Carlo Caione
|
||||||
* 2012 Henrik Nordstrom
|
* 2012 Henrik Nordstrom
|
||||||
|
*
|
||||||
|
* Notes
|
||||||
|
* -----
|
||||||
|
* The timeout value is rounded to the next power of two clock cycles.
|
||||||
|
* This is configured using the PDC_WDT_CONFIG register, according to this
|
||||||
|
* formula:
|
||||||
|
*
|
||||||
|
* timeout = 2^(delay + 1) clock cycles
|
||||||
|
*
|
||||||
|
* Where 'delay' is the value written in PDC_WDT_CONFIG register.
|
||||||
|
*
|
||||||
|
* Therefore, the hardware only allows to program watchdog timeouts, expressed
|
||||||
|
* as a power of two number of watchdog clock cycles. The current implementation
|
||||||
|
* guarantees that the actual watchdog timeout will be _at least_ the value
|
||||||
|
* programmed in the imgpdg_wdt driver.
|
||||||
|
*
|
||||||
|
* The following table shows how the user-configured timeout relates
|
||||||
|
* to the actual hardware timeout (watchdog clock @ 40000 Hz):
|
||||||
|
*
|
||||||
|
* input timeout | WD_DELAY | actual timeout
|
||||||
|
* -----------------------------------
|
||||||
|
* 10 | 18 | 13 seconds
|
||||||
|
* 20 | 19 | 26 seconds
|
||||||
|
* 30 | 20 | 52 seconds
|
||||||
|
* 60 | 21 | 104 seconds
|
||||||
|
*
|
||||||
|
* Albeit coarse, this granularity would suffice most watchdog uses.
|
||||||
|
* If the platform allows it, the user should be able to change the watchdog
|
||||||
|
* clock rate and achieve a finer timeout granularity.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <linux/clk.h>
|
#include <linux/clk.h>
|
||||||
|
@ -16,6 +45,7 @@
|
||||||
#include <linux/log2.h>
|
#include <linux/log2.h>
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
#include <linux/platform_device.h>
|
#include <linux/platform_device.h>
|
||||||
|
#include <linux/reboot.h>
|
||||||
#include <linux/slab.h>
|
#include <linux/slab.h>
|
||||||
#include <linux/watchdog.h>
|
#include <linux/watchdog.h>
|
||||||
|
|
||||||
|
@ -42,7 +72,7 @@
|
||||||
#define PDC_WDT_MIN_TIMEOUT 1
|
#define PDC_WDT_MIN_TIMEOUT 1
|
||||||
#define PDC_WDT_DEF_TIMEOUT 64
|
#define PDC_WDT_DEF_TIMEOUT 64
|
||||||
|
|
||||||
static int heartbeat = PDC_WDT_DEF_TIMEOUT;
|
static int heartbeat;
|
||||||
module_param(heartbeat, int, 0);
|
module_param(heartbeat, int, 0);
|
||||||
MODULE_PARM_DESC(heartbeat, "Watchdog heartbeats in seconds "
|
MODULE_PARM_DESC(heartbeat, "Watchdog heartbeats in seconds "
|
||||||
"(default=" __MODULE_STRING(PDC_WDT_DEF_TIMEOUT) ")");
|
"(default=" __MODULE_STRING(PDC_WDT_DEF_TIMEOUT) ")");
|
||||||
|
@ -57,6 +87,7 @@ struct pdc_wdt_dev {
|
||||||
struct clk *wdt_clk;
|
struct clk *wdt_clk;
|
||||||
struct clk *sys_clk;
|
struct clk *sys_clk;
|
||||||
void __iomem *base;
|
void __iomem *base;
|
||||||
|
struct notifier_block restart_handler;
|
||||||
};
|
};
|
||||||
|
|
||||||
static int pdc_wdt_keepalive(struct watchdog_device *wdt_dev)
|
static int pdc_wdt_keepalive(struct watchdog_device *wdt_dev)
|
||||||
|
@ -84,18 +115,24 @@ static int pdc_wdt_stop(struct watchdog_device *wdt_dev)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void __pdc_wdt_set_timeout(struct pdc_wdt_dev *wdt)
|
||||||
|
{
|
||||||
|
unsigned long clk_rate = clk_get_rate(wdt->wdt_clk);
|
||||||
|
unsigned int val;
|
||||||
|
|
||||||
|
val = readl(wdt->base + PDC_WDT_CONFIG) & ~PDC_WDT_CONFIG_DELAY_MASK;
|
||||||
|
val |= order_base_2(wdt->wdt_dev.timeout * clk_rate) - 1;
|
||||||
|
writel(val, wdt->base + PDC_WDT_CONFIG);
|
||||||
|
}
|
||||||
|
|
||||||
static int pdc_wdt_set_timeout(struct watchdog_device *wdt_dev,
|
static int pdc_wdt_set_timeout(struct watchdog_device *wdt_dev,
|
||||||
unsigned int new_timeout)
|
unsigned int new_timeout)
|
||||||
{
|
{
|
||||||
unsigned int val;
|
|
||||||
struct pdc_wdt_dev *wdt = watchdog_get_drvdata(wdt_dev);
|
struct pdc_wdt_dev *wdt = watchdog_get_drvdata(wdt_dev);
|
||||||
unsigned long clk_rate = clk_get_rate(wdt->wdt_clk);
|
|
||||||
|
|
||||||
wdt->wdt_dev.timeout = new_timeout;
|
wdt->wdt_dev.timeout = new_timeout;
|
||||||
|
|
||||||
val = readl(wdt->base + PDC_WDT_CONFIG) & ~PDC_WDT_CONFIG_DELAY_MASK;
|
__pdc_wdt_set_timeout(wdt);
|
||||||
val |= order_base_2(new_timeout * clk_rate) - 1;
|
|
||||||
writel(val, wdt->base + PDC_WDT_CONFIG);
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -106,6 +143,8 @@ static int pdc_wdt_start(struct watchdog_device *wdt_dev)
|
||||||
unsigned int val;
|
unsigned int val;
|
||||||
struct pdc_wdt_dev *wdt = watchdog_get_drvdata(wdt_dev);
|
struct pdc_wdt_dev *wdt = watchdog_get_drvdata(wdt_dev);
|
||||||
|
|
||||||
|
__pdc_wdt_set_timeout(wdt);
|
||||||
|
|
||||||
val = readl(wdt->base + PDC_WDT_CONFIG);
|
val = readl(wdt->base + PDC_WDT_CONFIG);
|
||||||
val |= PDC_WDT_CONFIG_ENABLE;
|
val |= PDC_WDT_CONFIG_ENABLE;
|
||||||
writel(val, wdt->base + PDC_WDT_CONFIG);
|
writel(val, wdt->base + PDC_WDT_CONFIG);
|
||||||
|
@ -128,8 +167,21 @@ static const struct watchdog_ops pdc_wdt_ops = {
|
||||||
.set_timeout = pdc_wdt_set_timeout,
|
.set_timeout = pdc_wdt_set_timeout,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static int pdc_wdt_restart(struct notifier_block *this, unsigned long mode,
|
||||||
|
void *cmd)
|
||||||
|
{
|
||||||
|
struct pdc_wdt_dev *wdt = container_of(this, struct pdc_wdt_dev,
|
||||||
|
restart_handler);
|
||||||
|
|
||||||
|
/* Assert SOFT_RESET */
|
||||||
|
writel(0x1, wdt->base + PDC_WDT_SOFT_RESET);
|
||||||
|
|
||||||
|
return NOTIFY_OK;
|
||||||
|
}
|
||||||
|
|
||||||
static int pdc_wdt_probe(struct platform_device *pdev)
|
static int pdc_wdt_probe(struct platform_device *pdev)
|
||||||
{
|
{
|
||||||
|
u64 div;
|
||||||
int ret, val;
|
int ret, val;
|
||||||
unsigned long clk_rate;
|
unsigned long clk_rate;
|
||||||
struct resource *res;
|
struct resource *res;
|
||||||
|
@ -189,16 +241,15 @@ static int pdc_wdt_probe(struct platform_device *pdev)
|
||||||
|
|
||||||
pdc_wdt->wdt_dev.info = &pdc_wdt_info;
|
pdc_wdt->wdt_dev.info = &pdc_wdt_info;
|
||||||
pdc_wdt->wdt_dev.ops = &pdc_wdt_ops;
|
pdc_wdt->wdt_dev.ops = &pdc_wdt_ops;
|
||||||
pdc_wdt->wdt_dev.max_timeout = 1 << PDC_WDT_CONFIG_DELAY_MASK;
|
|
||||||
|
div = 1ULL << (PDC_WDT_CONFIG_DELAY_MASK + 1);
|
||||||
|
do_div(div, clk_rate);
|
||||||
|
pdc_wdt->wdt_dev.max_timeout = div;
|
||||||
|
pdc_wdt->wdt_dev.timeout = PDC_WDT_DEF_TIMEOUT;
|
||||||
pdc_wdt->wdt_dev.parent = &pdev->dev;
|
pdc_wdt->wdt_dev.parent = &pdev->dev;
|
||||||
watchdog_set_drvdata(&pdc_wdt->wdt_dev, pdc_wdt);
|
watchdog_set_drvdata(&pdc_wdt->wdt_dev, pdc_wdt);
|
||||||
|
|
||||||
ret = watchdog_init_timeout(&pdc_wdt->wdt_dev, heartbeat, &pdev->dev);
|
watchdog_init_timeout(&pdc_wdt->wdt_dev, heartbeat, &pdev->dev);
|
||||||
if (ret < 0) {
|
|
||||||
pdc_wdt->wdt_dev.timeout = pdc_wdt->wdt_dev.max_timeout;
|
|
||||||
dev_warn(&pdev->dev,
|
|
||||||
"Initial timeout out of range! setting max timeout\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
pdc_wdt_stop(&pdc_wdt->wdt_dev);
|
pdc_wdt_stop(&pdc_wdt->wdt_dev);
|
||||||
|
|
||||||
|
@ -238,6 +289,13 @@ static int pdc_wdt_probe(struct platform_device *pdev)
|
||||||
if (ret)
|
if (ret)
|
||||||
goto disable_wdt_clk;
|
goto disable_wdt_clk;
|
||||||
|
|
||||||
|
pdc_wdt->restart_handler.notifier_call = pdc_wdt_restart;
|
||||||
|
pdc_wdt->restart_handler.priority = 128;
|
||||||
|
ret = register_restart_handler(&pdc_wdt->restart_handler);
|
||||||
|
if (ret)
|
||||||
|
dev_warn(&pdev->dev, "failed to register restart handler: %d\n",
|
||||||
|
ret);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
disable_wdt_clk:
|
disable_wdt_clk:
|
||||||
|
|
|
@ -166,6 +166,8 @@ static int imx2_wdt_set_timeout(struct watchdog_device *wdog,
|
||||||
{
|
{
|
||||||
struct imx2_wdt_device *wdev = watchdog_get_drvdata(wdog);
|
struct imx2_wdt_device *wdev = watchdog_get_drvdata(wdog);
|
||||||
|
|
||||||
|
wdog->timeout = new_timeout;
|
||||||
|
|
||||||
regmap_update_bits(wdev->regmap, IMX2_WDT_WCR, IMX2_WDT_WCR_WT,
|
regmap_update_bits(wdev->regmap, IMX2_WDT_WCR, IMX2_WDT_WCR_WT,
|
||||||
WDOG_SEC_TO_COUNT(new_timeout));
|
WDOG_SEC_TO_COUNT(new_timeout));
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -256,8 +258,11 @@ static int __init imx2_wdt_probe(struct platform_device *pdev)
|
||||||
wdog->ops = &imx2_wdt_ops;
|
wdog->ops = &imx2_wdt_ops;
|
||||||
wdog->min_timeout = 1;
|
wdog->min_timeout = 1;
|
||||||
wdog->max_timeout = IMX2_WDT_MAX_TIME;
|
wdog->max_timeout = IMX2_WDT_MAX_TIME;
|
||||||
|
wdog->parent = &pdev->dev;
|
||||||
|
|
||||||
clk_prepare_enable(wdev->clk);
|
ret = clk_prepare_enable(wdev->clk);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
regmap_read(wdev->regmap, IMX2_WDT_WRSR, &val);
|
regmap_read(wdev->regmap, IMX2_WDT_WRSR, &val);
|
||||||
wdog->bootstatus = val & IMX2_WDT_WRSR_TOUT ? WDIOF_CARDRESET : 0;
|
wdog->bootstatus = val & IMX2_WDT_WRSR_TOUT ? WDIOF_CARDRESET : 0;
|
||||||
|
@ -286,7 +291,7 @@ static int __init imx2_wdt_probe(struct platform_device *pdev)
|
||||||
ret = watchdog_register_device(wdog);
|
ret = watchdog_register_device(wdog);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
dev_err(&pdev->dev, "cannot register watchdog device\n");
|
dev_err(&pdev->dev, "cannot register watchdog device\n");
|
||||||
return ret;
|
goto disable_clk;
|
||||||
}
|
}
|
||||||
|
|
||||||
wdev->restart_handler.notifier_call = imx2_restart_handler;
|
wdev->restart_handler.notifier_call = imx2_restart_handler;
|
||||||
|
@ -299,6 +304,10 @@ static int __init imx2_wdt_probe(struct platform_device *pdev)
|
||||||
wdog->timeout, nowayout);
|
wdog->timeout, nowayout);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
|
disable_clk:
|
||||||
|
clk_disable_unprepare(wdev->clk);
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int __exit imx2_wdt_remove(struct platform_device *pdev)
|
static int __exit imx2_wdt_remove(struct platform_device *pdev)
|
||||||
|
@ -362,8 +371,11 @@ static int imx2_wdt_resume(struct device *dev)
|
||||||
{
|
{
|
||||||
struct watchdog_device *wdog = dev_get_drvdata(dev);
|
struct watchdog_device *wdog = dev_get_drvdata(dev);
|
||||||
struct imx2_wdt_device *wdev = watchdog_get_drvdata(wdog);
|
struct imx2_wdt_device *wdev = watchdog_get_drvdata(wdog);
|
||||||
|
int ret;
|
||||||
|
|
||||||
clk_prepare_enable(wdev->clk);
|
ret = clk_prepare_enable(wdev->clk);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
if (watchdog_active(wdog) && !imx2_wdt_is_running(wdev)) {
|
if (watchdog_active(wdog) && !imx2_wdt_is_running(wdev)) {
|
||||||
/*
|
/*
|
||||||
|
|
|
@ -39,10 +39,22 @@ static bool nowayout = WATCHDOG_NOWAYOUT;
|
||||||
#define MAX6369_WDSET (7 << 0)
|
#define MAX6369_WDSET (7 << 0)
|
||||||
#define MAX6369_WDI (1 << 3)
|
#define MAX6369_WDI (1 << 3)
|
||||||
|
|
||||||
static DEFINE_SPINLOCK(io_lock);
|
#define MAX6369_WDSET_DISABLED 3
|
||||||
|
|
||||||
static int nodelay;
|
static int nodelay;
|
||||||
static void __iomem *wdt_base;
|
|
||||||
|
struct max63xx_wdt {
|
||||||
|
struct watchdog_device wdd;
|
||||||
|
const struct max63xx_timeout *timeout;
|
||||||
|
|
||||||
|
/* memory mapping */
|
||||||
|
void __iomem *base;
|
||||||
|
spinlock_t lock;
|
||||||
|
|
||||||
|
/* WDI and WSET bits write access routines */
|
||||||
|
void (*ping)(struct max63xx_wdt *wdt);
|
||||||
|
void (*set)(struct max63xx_wdt *wdt, u8 set);
|
||||||
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* The timeout values used are actually the absolute minimum the chip
|
* The timeout values used are actually the absolute minimum the chip
|
||||||
|
@ -59,25 +71,25 @@ static void __iomem *wdt_base;
|
||||||
|
|
||||||
/* Timeouts in second */
|
/* Timeouts in second */
|
||||||
struct max63xx_timeout {
|
struct max63xx_timeout {
|
||||||
u8 wdset;
|
const u8 wdset;
|
||||||
u8 tdelay;
|
const u8 tdelay;
|
||||||
u8 twd;
|
const u8 twd;
|
||||||
};
|
};
|
||||||
|
|
||||||
static struct max63xx_timeout max6369_table[] = {
|
static const struct max63xx_timeout max6369_table[] = {
|
||||||
{ 5, 1, 1 },
|
{ 5, 1, 1 },
|
||||||
{ 6, 10, 10 },
|
{ 6, 10, 10 },
|
||||||
{ 7, 60, 60 },
|
{ 7, 60, 60 },
|
||||||
{ },
|
{ },
|
||||||
};
|
};
|
||||||
|
|
||||||
static struct max63xx_timeout max6371_table[] = {
|
static const struct max63xx_timeout max6371_table[] = {
|
||||||
{ 6, 60, 3 },
|
{ 6, 60, 3 },
|
||||||
{ 7, 60, 60 },
|
{ 7, 60, 60 },
|
||||||
{ },
|
{ },
|
||||||
};
|
};
|
||||||
|
|
||||||
static struct max63xx_timeout max6373_table[] = {
|
static const struct max63xx_timeout max6373_table[] = {
|
||||||
{ 2, 60, 1 },
|
{ 2, 60, 1 },
|
||||||
{ 5, 0, 1 },
|
{ 5, 0, 1 },
|
||||||
{ 1, 3, 3 },
|
{ 1, 3, 3 },
|
||||||
|
@ -86,8 +98,6 @@ static struct max63xx_timeout max6373_table[] = {
|
||||||
{ },
|
{ },
|
||||||
};
|
};
|
||||||
|
|
||||||
static struct max63xx_timeout *current_timeout;
|
|
||||||
|
|
||||||
static struct max63xx_timeout *
|
static struct max63xx_timeout *
|
||||||
max63xx_select_timeout(struct max63xx_timeout *table, int value)
|
max63xx_select_timeout(struct max63xx_timeout *table, int value)
|
||||||
{
|
{
|
||||||
|
@ -108,59 +118,32 @@ max63xx_select_timeout(struct max63xx_timeout *table, int value)
|
||||||
|
|
||||||
static int max63xx_wdt_ping(struct watchdog_device *wdd)
|
static int max63xx_wdt_ping(struct watchdog_device *wdd)
|
||||||
{
|
{
|
||||||
u8 val;
|
struct max63xx_wdt *wdt = watchdog_get_drvdata(wdd);
|
||||||
|
|
||||||
spin_lock(&io_lock);
|
wdt->ping(wdt);
|
||||||
|
|
||||||
val = __raw_readb(wdt_base);
|
|
||||||
|
|
||||||
__raw_writeb(val | MAX6369_WDI, wdt_base);
|
|
||||||
__raw_writeb(val & ~MAX6369_WDI, wdt_base);
|
|
||||||
|
|
||||||
spin_unlock(&io_lock);
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int max63xx_wdt_start(struct watchdog_device *wdd)
|
static int max63xx_wdt_start(struct watchdog_device *wdd)
|
||||||
{
|
{
|
||||||
struct max63xx_timeout *entry = watchdog_get_drvdata(wdd);
|
struct max63xx_wdt *wdt = watchdog_get_drvdata(wdd);
|
||||||
u8 val;
|
|
||||||
|
|
||||||
spin_lock(&io_lock);
|
wdt->set(wdt, wdt->timeout->wdset);
|
||||||
|
|
||||||
val = __raw_readb(wdt_base);
|
|
||||||
val &= ~MAX6369_WDSET;
|
|
||||||
val |= entry->wdset;
|
|
||||||
__raw_writeb(val, wdt_base);
|
|
||||||
|
|
||||||
spin_unlock(&io_lock);
|
|
||||||
|
|
||||||
/* check for a edge triggered startup */
|
/* check for a edge triggered startup */
|
||||||
if (entry->tdelay == 0)
|
if (wdt->timeout->tdelay == 0)
|
||||||
max63xx_wdt_ping(wdd);
|
wdt->ping(wdt);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int max63xx_wdt_stop(struct watchdog_device *wdd)
|
static int max63xx_wdt_stop(struct watchdog_device *wdd)
|
||||||
{
|
{
|
||||||
u8 val;
|
struct max63xx_wdt *wdt = watchdog_get_drvdata(wdd);
|
||||||
|
|
||||||
spin_lock(&io_lock);
|
wdt->set(wdt, MAX6369_WDSET_DISABLED);
|
||||||
|
|
||||||
val = __raw_readb(wdt_base);
|
|
||||||
val &= ~MAX6369_WDSET;
|
|
||||||
val |= 3;
|
|
||||||
__raw_writeb(val, wdt_base);
|
|
||||||
|
|
||||||
spin_unlock(&io_lock);
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static const struct watchdog_info max63xx_wdt_info = {
|
|
||||||
.options = WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE,
|
|
||||||
.identity = "max63xx Watchdog",
|
|
||||||
};
|
|
||||||
|
|
||||||
static const struct watchdog_ops max63xx_wdt_ops = {
|
static const struct watchdog_ops max63xx_wdt_ops = {
|
||||||
.owner = THIS_MODULE,
|
.owner = THIS_MODULE,
|
||||||
.start = max63xx_wdt_start,
|
.start = max63xx_wdt_start,
|
||||||
|
@ -168,53 +151,108 @@ static const struct watchdog_ops max63xx_wdt_ops = {
|
||||||
.ping = max63xx_wdt_ping,
|
.ping = max63xx_wdt_ping,
|
||||||
};
|
};
|
||||||
|
|
||||||
static struct watchdog_device max63xx_wdt_dev = {
|
static const struct watchdog_info max63xx_wdt_info = {
|
||||||
.info = &max63xx_wdt_info,
|
.options = WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE,
|
||||||
.ops = &max63xx_wdt_ops,
|
.identity = "max63xx Watchdog",
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static void max63xx_mmap_ping(struct max63xx_wdt *wdt)
|
||||||
|
{
|
||||||
|
u8 val;
|
||||||
|
|
||||||
|
spin_lock(&wdt->lock);
|
||||||
|
|
||||||
|
val = __raw_readb(wdt->base);
|
||||||
|
|
||||||
|
__raw_writeb(val | MAX6369_WDI, wdt->base);
|
||||||
|
__raw_writeb(val & ~MAX6369_WDI, wdt->base);
|
||||||
|
|
||||||
|
spin_unlock(&wdt->lock);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void max63xx_mmap_set(struct max63xx_wdt *wdt, u8 set)
|
||||||
|
{
|
||||||
|
u8 val;
|
||||||
|
|
||||||
|
spin_lock(&wdt->lock);
|
||||||
|
|
||||||
|
val = __raw_readb(wdt->base);
|
||||||
|
val &= ~MAX6369_WDSET;
|
||||||
|
val |= set & MAX6369_WDSET;
|
||||||
|
__raw_writeb(val, wdt->base);
|
||||||
|
|
||||||
|
spin_unlock(&wdt->lock);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int max63xx_mmap_init(struct platform_device *p, struct max63xx_wdt *wdt)
|
||||||
|
{
|
||||||
|
struct resource *mem = platform_get_resource(p, IORESOURCE_MEM, 0);
|
||||||
|
|
||||||
|
wdt->base = devm_ioremap_resource(&p->dev, mem);
|
||||||
|
if (IS_ERR(wdt->base))
|
||||||
|
return PTR_ERR(wdt->base);
|
||||||
|
|
||||||
|
spin_lock_init(&wdt->lock);
|
||||||
|
|
||||||
|
wdt->ping = max63xx_mmap_ping;
|
||||||
|
wdt->set = max63xx_mmap_set;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static int max63xx_wdt_probe(struct platform_device *pdev)
|
static int max63xx_wdt_probe(struct platform_device *pdev)
|
||||||
{
|
{
|
||||||
struct resource *wdt_mem;
|
struct max63xx_wdt *wdt;
|
||||||
struct max63xx_timeout *table;
|
struct max63xx_timeout *table;
|
||||||
|
int err;
|
||||||
|
|
||||||
|
wdt = devm_kzalloc(&pdev->dev, sizeof(*wdt), GFP_KERNEL);
|
||||||
|
if (!wdt)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
table = (struct max63xx_timeout *)pdev->id_entry->driver_data;
|
table = (struct max63xx_timeout *)pdev->id_entry->driver_data;
|
||||||
|
|
||||||
if (heartbeat < 1 || heartbeat > MAX_HEARTBEAT)
|
if (heartbeat < 1 || heartbeat > MAX_HEARTBEAT)
|
||||||
heartbeat = DEFAULT_HEARTBEAT;
|
heartbeat = DEFAULT_HEARTBEAT;
|
||||||
|
|
||||||
dev_info(&pdev->dev, "requesting %ds heartbeat\n", heartbeat);
|
wdt->timeout = max63xx_select_timeout(table, heartbeat);
|
||||||
current_timeout = max63xx_select_timeout(table, heartbeat);
|
if (!wdt->timeout) {
|
||||||
|
dev_err(&pdev->dev, "unable to satisfy %ds heartbeat request\n",
|
||||||
if (!current_timeout) {
|
heartbeat);
|
||||||
dev_err(&pdev->dev, "unable to satisfy heartbeat request\n");
|
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
err = max63xx_mmap_init(pdev, wdt);
|
||||||
|
if (err)
|
||||||
|
return err;
|
||||||
|
|
||||||
|
platform_set_drvdata(pdev, &wdt->wdd);
|
||||||
|
watchdog_set_drvdata(&wdt->wdd, wdt);
|
||||||
|
|
||||||
|
wdt->wdd.parent = &pdev->dev;
|
||||||
|
wdt->wdd.timeout = wdt->timeout->twd;
|
||||||
|
wdt->wdd.info = &max63xx_wdt_info;
|
||||||
|
wdt->wdd.ops = &max63xx_wdt_ops;
|
||||||
|
|
||||||
|
watchdog_set_nowayout(&wdt->wdd, nowayout);
|
||||||
|
|
||||||
|
err = watchdog_register_device(&wdt->wdd);
|
||||||
|
if (err)
|
||||||
|
return err;
|
||||||
|
|
||||||
dev_info(&pdev->dev, "using %ds heartbeat with %ds initial delay\n",
|
dev_info(&pdev->dev, "using %ds heartbeat with %ds initial delay\n",
|
||||||
current_timeout->twd, current_timeout->tdelay);
|
wdt->timeout->twd, wdt->timeout->tdelay);
|
||||||
|
return 0;
|
||||||
heartbeat = current_timeout->twd;
|
|
||||||
|
|
||||||
wdt_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
|
||||||
wdt_base = devm_ioremap_resource(&pdev->dev, wdt_mem);
|
|
||||||
if (IS_ERR(wdt_base))
|
|
||||||
return PTR_ERR(wdt_base);
|
|
||||||
|
|
||||||
max63xx_wdt_dev.timeout = heartbeat;
|
|
||||||
watchdog_set_nowayout(&max63xx_wdt_dev, nowayout);
|
|
||||||
watchdog_set_drvdata(&max63xx_wdt_dev, current_timeout);
|
|
||||||
|
|
||||||
return watchdog_register_device(&max63xx_wdt_dev);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int max63xx_wdt_remove(struct platform_device *pdev)
|
static int max63xx_wdt_remove(struct platform_device *pdev)
|
||||||
{
|
{
|
||||||
watchdog_unregister_device(&max63xx_wdt_dev);
|
struct watchdog_device *wdd = platform_get_drvdata(pdev);
|
||||||
|
|
||||||
|
watchdog_unregister_device(wdd);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct platform_device_id max63xx_id_table[] = {
|
static const struct platform_device_id max63xx_id_table[] = {
|
||||||
{ "max6369_wdt", (kernel_ulong_t)max6369_table, },
|
{ "max6369_wdt", (kernel_ulong_t)max6369_table, },
|
||||||
{ "max6370_wdt", (kernel_ulong_t)max6369_table, },
|
{ "max6370_wdt", (kernel_ulong_t)max6369_table, },
|
||||||
{ "max6371_wdt", (kernel_ulong_t)max6371_table, },
|
{ "max6371_wdt", (kernel_ulong_t)max6371_table, },
|
||||||
|
|
|
@ -208,14 +208,15 @@ static int a21_wdt_probe(struct platform_device *pdev)
|
||||||
else if (reset == 7)
|
else if (reset == 7)
|
||||||
a21_wdt.bootstatus |= WDIOF_EXTERN2;
|
a21_wdt.bootstatus |= WDIOF_EXTERN2;
|
||||||
|
|
||||||
|
drv->wdt = a21_wdt;
|
||||||
|
dev_set_drvdata(&pdev->dev, drv);
|
||||||
|
|
||||||
ret = watchdog_register_device(&a21_wdt);
|
ret = watchdog_register_device(&a21_wdt);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
dev_err(&pdev->dev, "Cannot register watchdog device\n");
|
dev_err(&pdev->dev, "Cannot register watchdog device\n");
|
||||||
goto err_register_wd;
|
goto err_register_wd;
|
||||||
}
|
}
|
||||||
|
|
||||||
dev_set_drvdata(&pdev->dev, drv);
|
|
||||||
|
|
||||||
dev_info(&pdev->dev, "MEN A21 watchdog timer driver enabled\n");
|
dev_info(&pdev->dev, "MEN A21 watchdog timer driver enabled\n");
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
|
@ -53,7 +53,15 @@ static unsigned timer_margin;
|
||||||
module_param(timer_margin, uint, 0);
|
module_param(timer_margin, uint, 0);
|
||||||
MODULE_PARM_DESC(timer_margin, "initial watchdog timeout (in seconds)");
|
MODULE_PARM_DESC(timer_margin, "initial watchdog timeout (in seconds)");
|
||||||
|
|
||||||
|
#define to_omap_wdt_dev(_wdog) container_of(_wdog, struct omap_wdt_dev, wdog)
|
||||||
|
|
||||||
|
static bool early_enable;
|
||||||
|
module_param(early_enable, bool, 0);
|
||||||
|
MODULE_PARM_DESC(early_enable,
|
||||||
|
"Watchdog is started on module insertion (default=0)");
|
||||||
|
|
||||||
struct omap_wdt_dev {
|
struct omap_wdt_dev {
|
||||||
|
struct watchdog_device wdog;
|
||||||
void __iomem *base; /* physical */
|
void __iomem *base; /* physical */
|
||||||
struct device *dev;
|
struct device *dev;
|
||||||
bool omap_wdt_users;
|
bool omap_wdt_users;
|
||||||
|
@ -123,7 +131,7 @@ static void omap_wdt_set_timer(struct omap_wdt_dev *wdev,
|
||||||
|
|
||||||
static int omap_wdt_start(struct watchdog_device *wdog)
|
static int omap_wdt_start(struct watchdog_device *wdog)
|
||||||
{
|
{
|
||||||
struct omap_wdt_dev *wdev = watchdog_get_drvdata(wdog);
|
struct omap_wdt_dev *wdev = to_omap_wdt_dev(wdog);
|
||||||
void __iomem *base = wdev->base;
|
void __iomem *base = wdev->base;
|
||||||
|
|
||||||
mutex_lock(&wdev->lock);
|
mutex_lock(&wdev->lock);
|
||||||
|
@ -132,6 +140,13 @@ static int omap_wdt_start(struct watchdog_device *wdog)
|
||||||
|
|
||||||
pm_runtime_get_sync(wdev->dev);
|
pm_runtime_get_sync(wdev->dev);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Make sure the watchdog is disabled. This is unfortunately required
|
||||||
|
* because writing to various registers with the watchdog running has no
|
||||||
|
* effect.
|
||||||
|
*/
|
||||||
|
omap_wdt_disable(wdev);
|
||||||
|
|
||||||
/* initialize prescaler */
|
/* initialize prescaler */
|
||||||
while (readl_relaxed(base + OMAP_WATCHDOG_WPS) & 0x01)
|
while (readl_relaxed(base + OMAP_WATCHDOG_WPS) & 0x01)
|
||||||
cpu_relax();
|
cpu_relax();
|
||||||
|
@ -151,7 +166,7 @@ static int omap_wdt_start(struct watchdog_device *wdog)
|
||||||
|
|
||||||
static int omap_wdt_stop(struct watchdog_device *wdog)
|
static int omap_wdt_stop(struct watchdog_device *wdog)
|
||||||
{
|
{
|
||||||
struct omap_wdt_dev *wdev = watchdog_get_drvdata(wdog);
|
struct omap_wdt_dev *wdev = to_omap_wdt_dev(wdog);
|
||||||
|
|
||||||
mutex_lock(&wdev->lock);
|
mutex_lock(&wdev->lock);
|
||||||
omap_wdt_disable(wdev);
|
omap_wdt_disable(wdev);
|
||||||
|
@ -163,7 +178,7 @@ static int omap_wdt_stop(struct watchdog_device *wdog)
|
||||||
|
|
||||||
static int omap_wdt_ping(struct watchdog_device *wdog)
|
static int omap_wdt_ping(struct watchdog_device *wdog)
|
||||||
{
|
{
|
||||||
struct omap_wdt_dev *wdev = watchdog_get_drvdata(wdog);
|
struct omap_wdt_dev *wdev = to_omap_wdt_dev(wdog);
|
||||||
|
|
||||||
mutex_lock(&wdev->lock);
|
mutex_lock(&wdev->lock);
|
||||||
omap_wdt_reload(wdev);
|
omap_wdt_reload(wdev);
|
||||||
|
@ -175,7 +190,7 @@ static int omap_wdt_ping(struct watchdog_device *wdog)
|
||||||
static int omap_wdt_set_timeout(struct watchdog_device *wdog,
|
static int omap_wdt_set_timeout(struct watchdog_device *wdog,
|
||||||
unsigned int timeout)
|
unsigned int timeout)
|
||||||
{
|
{
|
||||||
struct omap_wdt_dev *wdev = watchdog_get_drvdata(wdog);
|
struct omap_wdt_dev *wdev = to_omap_wdt_dev(wdog);
|
||||||
|
|
||||||
mutex_lock(&wdev->lock);
|
mutex_lock(&wdev->lock);
|
||||||
omap_wdt_disable(wdev);
|
omap_wdt_disable(wdev);
|
||||||
|
@ -188,6 +203,16 @@ static int omap_wdt_set_timeout(struct watchdog_device *wdog,
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static unsigned int omap_wdt_get_timeleft(struct watchdog_device *wdog)
|
||||||
|
{
|
||||||
|
struct omap_wdt_dev *wdev = watchdog_get_drvdata(wdog);
|
||||||
|
void __iomem *base = wdev->base;
|
||||||
|
u32 value;
|
||||||
|
|
||||||
|
value = readl_relaxed(base + OMAP_WATCHDOG_CRR);
|
||||||
|
return GET_WCCR_SECS(value);
|
||||||
|
}
|
||||||
|
|
||||||
static const struct watchdog_info omap_wdt_info = {
|
static const struct watchdog_info omap_wdt_info = {
|
||||||
.options = WDIOF_SETTIMEOUT | WDIOF_MAGICCLOSE | WDIOF_KEEPALIVEPING,
|
.options = WDIOF_SETTIMEOUT | WDIOF_MAGICCLOSE | WDIOF_KEEPALIVEPING,
|
||||||
.identity = "OMAP Watchdog",
|
.identity = "OMAP Watchdog",
|
||||||
|
@ -199,21 +224,16 @@ static const struct watchdog_ops omap_wdt_ops = {
|
||||||
.stop = omap_wdt_stop,
|
.stop = omap_wdt_stop,
|
||||||
.ping = omap_wdt_ping,
|
.ping = omap_wdt_ping,
|
||||||
.set_timeout = omap_wdt_set_timeout,
|
.set_timeout = omap_wdt_set_timeout,
|
||||||
|
.get_timeleft = omap_wdt_get_timeleft,
|
||||||
};
|
};
|
||||||
|
|
||||||
static int omap_wdt_probe(struct platform_device *pdev)
|
static int omap_wdt_probe(struct platform_device *pdev)
|
||||||
{
|
{
|
||||||
struct omap_wd_timer_platform_data *pdata = dev_get_platdata(&pdev->dev);
|
struct omap_wd_timer_platform_data *pdata = dev_get_platdata(&pdev->dev);
|
||||||
struct watchdog_device *omap_wdt;
|
|
||||||
struct resource *res;
|
struct resource *res;
|
||||||
struct omap_wdt_dev *wdev;
|
struct omap_wdt_dev *wdev;
|
||||||
u32 rs;
|
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
omap_wdt = devm_kzalloc(&pdev->dev, sizeof(*omap_wdt), GFP_KERNEL);
|
|
||||||
if (!omap_wdt)
|
|
||||||
return -ENOMEM;
|
|
||||||
|
|
||||||
wdev = devm_kzalloc(&pdev->dev, sizeof(*wdev), GFP_KERNEL);
|
wdev = devm_kzalloc(&pdev->dev, sizeof(*wdev), GFP_KERNEL);
|
||||||
if (!wdev)
|
if (!wdev)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
@ -229,35 +249,30 @@ static int omap_wdt_probe(struct platform_device *pdev)
|
||||||
if (IS_ERR(wdev->base))
|
if (IS_ERR(wdev->base))
|
||||||
return PTR_ERR(wdev->base);
|
return PTR_ERR(wdev->base);
|
||||||
|
|
||||||
omap_wdt->info = &omap_wdt_info;
|
wdev->wdog.info = &omap_wdt_info;
|
||||||
omap_wdt->ops = &omap_wdt_ops;
|
wdev->wdog.ops = &omap_wdt_ops;
|
||||||
omap_wdt->min_timeout = TIMER_MARGIN_MIN;
|
wdev->wdog.min_timeout = TIMER_MARGIN_MIN;
|
||||||
omap_wdt->max_timeout = TIMER_MARGIN_MAX;
|
wdev->wdog.max_timeout = TIMER_MARGIN_MAX;
|
||||||
|
|
||||||
if (timer_margin >= TIMER_MARGIN_MIN &&
|
if (watchdog_init_timeout(&wdev->wdog, timer_margin, &pdev->dev) < 0)
|
||||||
timer_margin <= TIMER_MARGIN_MAX)
|
wdev->wdog.timeout = TIMER_MARGIN_DEFAULT;
|
||||||
omap_wdt->timeout = timer_margin;
|
|
||||||
else
|
|
||||||
omap_wdt->timeout = TIMER_MARGIN_DEFAULT;
|
|
||||||
|
|
||||||
watchdog_set_drvdata(omap_wdt, wdev);
|
watchdog_set_nowayout(&wdev->wdog, nowayout);
|
||||||
watchdog_set_nowayout(omap_wdt, nowayout);
|
|
||||||
|
|
||||||
platform_set_drvdata(pdev, omap_wdt);
|
platform_set_drvdata(pdev, wdev);
|
||||||
|
|
||||||
pm_runtime_enable(wdev->dev);
|
pm_runtime_enable(wdev->dev);
|
||||||
pm_runtime_get_sync(wdev->dev);
|
pm_runtime_get_sync(wdev->dev);
|
||||||
|
|
||||||
if (pdata && pdata->read_reset_sources)
|
if (pdata && pdata->read_reset_sources) {
|
||||||
rs = pdata->read_reset_sources();
|
u32 rs = pdata->read_reset_sources();
|
||||||
else
|
if (rs & (1 << OMAP_MPU_WD_RST_SRC_ID_SHIFT))
|
||||||
rs = 0;
|
wdev->wdog.bootstatus = WDIOF_CARDRESET;
|
||||||
omap_wdt->bootstatus = (rs & (1 << OMAP_MPU_WD_RST_SRC_ID_SHIFT)) ?
|
}
|
||||||
WDIOF_CARDRESET : 0;
|
|
||||||
|
|
||||||
omap_wdt_disable(wdev);
|
omap_wdt_disable(wdev);
|
||||||
|
|
||||||
ret = watchdog_register_device(omap_wdt);
|
ret = watchdog_register_device(&wdev->wdog);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
pm_runtime_disable(wdev->dev);
|
pm_runtime_disable(wdev->dev);
|
||||||
return ret;
|
return ret;
|
||||||
|
@ -265,17 +280,19 @@ static int omap_wdt_probe(struct platform_device *pdev)
|
||||||
|
|
||||||
pr_info("OMAP Watchdog Timer Rev 0x%02x: initial timeout %d sec\n",
|
pr_info("OMAP Watchdog Timer Rev 0x%02x: initial timeout %d sec\n",
|
||||||
readl_relaxed(wdev->base + OMAP_WATCHDOG_REV) & 0xFF,
|
readl_relaxed(wdev->base + OMAP_WATCHDOG_REV) & 0xFF,
|
||||||
omap_wdt->timeout);
|
wdev->wdog.timeout);
|
||||||
|
|
||||||
pm_runtime_put_sync(wdev->dev);
|
pm_runtime_put_sync(wdev->dev);
|
||||||
|
|
||||||
|
if (early_enable)
|
||||||
|
omap_wdt_start(&wdev->wdog);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void omap_wdt_shutdown(struct platform_device *pdev)
|
static void omap_wdt_shutdown(struct platform_device *pdev)
|
||||||
{
|
{
|
||||||
struct watchdog_device *wdog = platform_get_drvdata(pdev);
|
struct omap_wdt_dev *wdev = platform_get_drvdata(pdev);
|
||||||
struct omap_wdt_dev *wdev = watchdog_get_drvdata(wdog);
|
|
||||||
|
|
||||||
mutex_lock(&wdev->lock);
|
mutex_lock(&wdev->lock);
|
||||||
if (wdev->omap_wdt_users) {
|
if (wdev->omap_wdt_users) {
|
||||||
|
@ -287,11 +304,10 @@ static void omap_wdt_shutdown(struct platform_device *pdev)
|
||||||
|
|
||||||
static int omap_wdt_remove(struct platform_device *pdev)
|
static int omap_wdt_remove(struct platform_device *pdev)
|
||||||
{
|
{
|
||||||
struct watchdog_device *wdog = platform_get_drvdata(pdev);
|
struct omap_wdt_dev *wdev = platform_get_drvdata(pdev);
|
||||||
struct omap_wdt_dev *wdev = watchdog_get_drvdata(wdog);
|
|
||||||
|
|
||||||
pm_runtime_disable(wdev->dev);
|
pm_runtime_disable(wdev->dev);
|
||||||
watchdog_unregister_device(wdog);
|
watchdog_unregister_device(&wdev->wdog);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -306,8 +322,7 @@ static int omap_wdt_remove(struct platform_device *pdev)
|
||||||
|
|
||||||
static int omap_wdt_suspend(struct platform_device *pdev, pm_message_t state)
|
static int omap_wdt_suspend(struct platform_device *pdev, pm_message_t state)
|
||||||
{
|
{
|
||||||
struct watchdog_device *wdog = platform_get_drvdata(pdev);
|
struct omap_wdt_dev *wdev = platform_get_drvdata(pdev);
|
||||||
struct omap_wdt_dev *wdev = watchdog_get_drvdata(wdog);
|
|
||||||
|
|
||||||
mutex_lock(&wdev->lock);
|
mutex_lock(&wdev->lock);
|
||||||
if (wdev->omap_wdt_users) {
|
if (wdev->omap_wdt_users) {
|
||||||
|
@ -321,8 +336,7 @@ static int omap_wdt_suspend(struct platform_device *pdev, pm_message_t state)
|
||||||
|
|
||||||
static int omap_wdt_resume(struct platform_device *pdev)
|
static int omap_wdt_resume(struct platform_device *pdev)
|
||||||
{
|
{
|
||||||
struct watchdog_device *wdog = platform_get_drvdata(pdev);
|
struct omap_wdt_dev *wdev = platform_get_drvdata(pdev);
|
||||||
struct omap_wdt_dev *wdev = watchdog_get_drvdata(wdog);
|
|
||||||
|
|
||||||
mutex_lock(&wdev->lock);
|
mutex_lock(&wdev->lock);
|
||||||
if (wdev->omap_wdt_users) {
|
if (wdev->omap_wdt_users) {
|
||||||
|
|
|
@ -50,5 +50,6 @@
|
||||||
|
|
||||||
#define PTV 0 /* prescale */
|
#define PTV 0 /* prescale */
|
||||||
#define GET_WLDR_VAL(secs) (0xffffffff - ((secs) * (32768/(1<<PTV))) + 1)
|
#define GET_WLDR_VAL(secs) (0xffffffff - ((secs) * (32768/(1<<PTV))) + 1)
|
||||||
|
#define GET_WCCR_SECS(val) ((0xffffffff - (val) + 1) / (32768/(1<<PTV)))
|
||||||
|
|
||||||
#endif /* _OMAP_WATCHDOG_H */
|
#endif /* _OMAP_WATCHDOG_H */
|
||||||
|
|
|
@ -197,7 +197,7 @@ static int st_wdog_probe(struct platform_device *pdev)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* LPC can either run in RTC or WDT mode */
|
/* LPC can either run as a Clocksource or in RTC or WDT mode */
|
||||||
if (mode != ST_LPC_MODE_WDT)
|
if (mode != ST_LPC_MODE_WDT)
|
||||||
return -ENODEV;
|
return -ENODEV;
|
||||||
|
|
||||||
|
|
|
@ -43,6 +43,45 @@
|
||||||
static DEFINE_IDA(watchdog_ida);
|
static DEFINE_IDA(watchdog_ida);
|
||||||
static struct class *watchdog_class;
|
static struct class *watchdog_class;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Deferred Registration infrastructure.
|
||||||
|
*
|
||||||
|
* Sometimes watchdog drivers needs to be loaded as soon as possible,
|
||||||
|
* for example when it's impossible to disable it. To do so,
|
||||||
|
* raising the initcall level of the watchdog driver is a solution.
|
||||||
|
* But in such case, the miscdev is maybe not ready (subsys_initcall), and
|
||||||
|
* watchdog_core need miscdev to register the watchdog as a char device.
|
||||||
|
*
|
||||||
|
* The deferred registration infrastructure offer a way for the watchdog
|
||||||
|
* subsystem to register a watchdog properly, even before miscdev is ready.
|
||||||
|
*/
|
||||||
|
|
||||||
|
static DEFINE_MUTEX(wtd_deferred_reg_mutex);
|
||||||
|
static LIST_HEAD(wtd_deferred_reg_list);
|
||||||
|
static bool wtd_deferred_reg_done;
|
||||||
|
|
||||||
|
static int watchdog_deferred_registration_add(struct watchdog_device *wdd)
|
||||||
|
{
|
||||||
|
list_add_tail(&wdd->deferred,
|
||||||
|
&wtd_deferred_reg_list);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void watchdog_deferred_registration_del(struct watchdog_device *wdd)
|
||||||
|
{
|
||||||
|
struct list_head *p, *n;
|
||||||
|
struct watchdog_device *wdd_tmp;
|
||||||
|
|
||||||
|
list_for_each_safe(p, n, &wtd_deferred_reg_list) {
|
||||||
|
wdd_tmp = list_entry(p, struct watchdog_device,
|
||||||
|
deferred);
|
||||||
|
if (wdd_tmp == wdd) {
|
||||||
|
list_del(&wdd_tmp->deferred);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static void watchdog_check_min_max_timeout(struct watchdog_device *wdd)
|
static void watchdog_check_min_max_timeout(struct watchdog_device *wdd)
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
|
@ -98,17 +137,7 @@ int watchdog_init_timeout(struct watchdog_device *wdd,
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(watchdog_init_timeout);
|
EXPORT_SYMBOL_GPL(watchdog_init_timeout);
|
||||||
|
|
||||||
/**
|
static int __watchdog_register_device(struct watchdog_device *wdd)
|
||||||
* watchdog_register_device() - register a watchdog device
|
|
||||||
* @wdd: watchdog device
|
|
||||||
*
|
|
||||||
* Register a watchdog device with the kernel so that the
|
|
||||||
* watchdog timer can be accessed from userspace.
|
|
||||||
*
|
|
||||||
* A zero is returned on success and a negative errno code for
|
|
||||||
* failure.
|
|
||||||
*/
|
|
||||||
int watchdog_register_device(struct watchdog_device *wdd)
|
|
||||||
{
|
{
|
||||||
int ret, id, devno;
|
int ret, id, devno;
|
||||||
|
|
||||||
|
@ -164,16 +193,33 @@ int watchdog_register_device(struct watchdog_device *wdd)
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(watchdog_register_device);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* watchdog_unregister_device() - unregister a watchdog device
|
* watchdog_register_device() - register a watchdog device
|
||||||
* @wdd: watchdog device to unregister
|
* @wdd: watchdog device
|
||||||
*
|
*
|
||||||
* Unregister a watchdog device that was previously successfully
|
* Register a watchdog device with the kernel so that the
|
||||||
* registered with watchdog_register_device().
|
* watchdog timer can be accessed from userspace.
|
||||||
|
*
|
||||||
|
* A zero is returned on success and a negative errno code for
|
||||||
|
* failure.
|
||||||
*/
|
*/
|
||||||
void watchdog_unregister_device(struct watchdog_device *wdd)
|
|
||||||
|
int watchdog_register_device(struct watchdog_device *wdd)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
mutex_lock(&wtd_deferred_reg_mutex);
|
||||||
|
if (wtd_deferred_reg_done)
|
||||||
|
ret = __watchdog_register_device(wdd);
|
||||||
|
else
|
||||||
|
ret = watchdog_deferred_registration_add(wdd);
|
||||||
|
mutex_unlock(&wtd_deferred_reg_mutex);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(watchdog_register_device);
|
||||||
|
|
||||||
|
static void __watchdog_unregister_device(struct watchdog_device *wdd)
|
||||||
{
|
{
|
||||||
int ret;
|
int ret;
|
||||||
int devno;
|
int devno;
|
||||||
|
@ -189,8 +235,43 @@ void watchdog_unregister_device(struct watchdog_device *wdd)
|
||||||
ida_simple_remove(&watchdog_ida, wdd->id);
|
ida_simple_remove(&watchdog_ida, wdd->id);
|
||||||
wdd->dev = NULL;
|
wdd->dev = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* watchdog_unregister_device() - unregister a watchdog device
|
||||||
|
* @wdd: watchdog device to unregister
|
||||||
|
*
|
||||||
|
* Unregister a watchdog device that was previously successfully
|
||||||
|
* registered with watchdog_register_device().
|
||||||
|
*/
|
||||||
|
|
||||||
|
void watchdog_unregister_device(struct watchdog_device *wdd)
|
||||||
|
{
|
||||||
|
mutex_lock(&wtd_deferred_reg_mutex);
|
||||||
|
if (wtd_deferred_reg_done)
|
||||||
|
__watchdog_unregister_device(wdd);
|
||||||
|
else
|
||||||
|
watchdog_deferred_registration_del(wdd);
|
||||||
|
mutex_unlock(&wtd_deferred_reg_mutex);
|
||||||
|
}
|
||||||
|
|
||||||
EXPORT_SYMBOL_GPL(watchdog_unregister_device);
|
EXPORT_SYMBOL_GPL(watchdog_unregister_device);
|
||||||
|
|
||||||
|
static int __init watchdog_deferred_registration(void)
|
||||||
|
{
|
||||||
|
mutex_lock(&wtd_deferred_reg_mutex);
|
||||||
|
wtd_deferred_reg_done = true;
|
||||||
|
while (!list_empty(&wtd_deferred_reg_list)) {
|
||||||
|
struct watchdog_device *wdd;
|
||||||
|
|
||||||
|
wdd = list_first_entry(&wtd_deferred_reg_list,
|
||||||
|
struct watchdog_device, deferred);
|
||||||
|
list_del(&wdd->deferred);
|
||||||
|
__watchdog_register_device(wdd);
|
||||||
|
}
|
||||||
|
mutex_unlock(&wtd_deferred_reg_mutex);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static int __init watchdog_init(void)
|
static int __init watchdog_init(void)
|
||||||
{
|
{
|
||||||
int err;
|
int err;
|
||||||
|
@ -207,6 +288,7 @@ static int __init watchdog_init(void)
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
watchdog_deferred_registration();
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -217,7 +299,7 @@ static void __exit watchdog_exit(void)
|
||||||
ida_destroy(&watchdog_ida);
|
ida_destroy(&watchdog_ida);
|
||||||
}
|
}
|
||||||
|
|
||||||
subsys_initcall(watchdog_init);
|
subsys_initcall_sync(watchdog_init);
|
||||||
module_exit(watchdog_exit);
|
module_exit(watchdog_exit);
|
||||||
|
|
||||||
MODULE_AUTHOR("Alan Cox <alan@lxorguk.ukuu.org.uk>");
|
MODULE_AUTHOR("Alan Cox <alan@lxorguk.ukuu.org.uk>");
|
||||||
|
|
|
@ -65,6 +65,8 @@ struct watchdog_ops {
|
||||||
* @driver-data:Pointer to the drivers private data.
|
* @driver-data:Pointer to the drivers private data.
|
||||||
* @lock: Lock for watchdog core internal use only.
|
* @lock: Lock for watchdog core internal use only.
|
||||||
* @status: Field that contains the devices internal status bits.
|
* @status: Field that contains the devices internal status bits.
|
||||||
|
* @deferred: entry in wtd_deferred_reg_list which is used to
|
||||||
|
* register early initialized watchdogs.
|
||||||
*
|
*
|
||||||
* The watchdog_device structure contains all information about a
|
* The watchdog_device structure contains all information about a
|
||||||
* watchdog timer device.
|
* watchdog timer device.
|
||||||
|
@ -95,6 +97,7 @@ struct watchdog_device {
|
||||||
#define WDOG_ALLOW_RELEASE 2 /* Did we receive the magic char ? */
|
#define WDOG_ALLOW_RELEASE 2 /* Did we receive the magic char ? */
|
||||||
#define WDOG_NO_WAY_OUT 3 /* Is 'nowayout' feature set ? */
|
#define WDOG_NO_WAY_OUT 3 /* Is 'nowayout' feature set ? */
|
||||||
#define WDOG_UNREGISTERED 4 /* Has the device been unregistered */
|
#define WDOG_UNREGISTERED 4 /* Has the device been unregistered */
|
||||||
|
struct list_head deferred;
|
||||||
};
|
};
|
||||||
|
|
||||||
#define WATCHDOG_NOWAYOUT IS_BUILTIN(CONFIG_WATCHDOG_NOWAYOUT)
|
#define WATCHDOG_NOWAYOUT IS_BUILTIN(CONFIG_WATCHDOG_NOWAYOUT)
|
||||||
|
|
Loading…
Reference in New Issue