mirror of
https://github.com/torvalds/linux.git
synced 2024-11-11 06:31:49 +00:00
Merge git://www.linux-watchdog.org/linux-watchdog
Pull watchdog updates from Wim Van Sebroeck: - new driver for NXP LPC18xx Watchdog Timer - new driver for SAMA5D4 watchdog timer - add support for MCP79 to nv_tco driver - clean-up and improvement of the mpc8xxx watchdog driver - improvements to gpio-wdt - at91sam9_wdt clock improvements ... and other small fixes and improvements * git://www.linux-watchdog.org/linux-watchdog: (25 commits) Watchdog: Fix parent of watchdog_devices watchdog: at91rm9200: Correct check for syscon_node_to_regmap() errors watchdog: at91sam9: get and use slow clock Documentation: dt: binding: atmel-sama5d4-wdt: for SAMA5D4 watchdog driver watchdog: add a driver to support SAMA5D4 watchdog timer watchdog: mpc8xxx: allow to compile for MPC512x watchdog: mpc8xxx: use better error code when watchdog cannot be enabled watchdog: mpc8xxx: use dynamic memory for device specific data watchdog: mpc8xxx: use devm_ioremap_resource to map memory watchdog: mpc8xxx: make use of of_device_get_match_data watchdog: mpc8xxx: simplify registration watchdog: mpc8xxx: remove dead code watchdog: lpc18xx_wdt_get_timeleft() can be static DT: watchdog: Add NXP LPC18xx Watchdog Timer binding documentation watchdog: NXP LPC18xx Watchdog Timer Driver watchdog: gpio-wdt: ping already at startup for always running devices watchdog: gpio-wdt: be more strict about hw_algo matching Documentation: watchdog: at91sam9_wdt: add clocks property watchdog: booke_wdt: Use infrastructure to check timeout limits watchdog: (nv_tco) add support for MCP79 ...
This commit is contained in:
commit
51a73ba5f4
@ -0,0 +1,35 @@
|
||||
* Atmel SAMA5D4 Watchdog Timer (WDT) Controller
|
||||
|
||||
Required properties:
|
||||
- compatible: "atmel,sama5d4-wdt"
|
||||
- reg: base physical address and length of memory mapped region.
|
||||
|
||||
Optional properties:
|
||||
- timeout-sec: watchdog timeout value (in seconds).
|
||||
- interrupts: interrupt number to the CPU.
|
||||
- atmel,watchdog-type: should be "hardware" or "software".
|
||||
"hardware": enable watchdog fault reset. A watchdog fault triggers
|
||||
watchdog reset.
|
||||
"software": enable watchdog fault interrupt. A watchdog fault asserts
|
||||
watchdog interrupt.
|
||||
- atmel,idle-halt: present if you want to stop the watchdog when the CPU is
|
||||
in idle state.
|
||||
CAUTION: This property should be used with care, it actually makes the
|
||||
watchdog not counting when the CPU is in idle state, therefore the
|
||||
watchdog reset time depends on mean CPU usage and will not reset at all
|
||||
if the CPU stop working while it is in idle state, which is probably
|
||||
not what you want.
|
||||
- atmel,dbg-halt: present if you want to stop the watchdog when the CPU is
|
||||
in debug state.
|
||||
|
||||
Example:
|
||||
watchdog@fc068640 {
|
||||
compatible = "atmel,sama5d4-wdt";
|
||||
reg = <0xfc068640 0x10>;
|
||||
interrupts = <4 IRQ_TYPE_LEVEL_HIGH 5>;
|
||||
timeout-sec = <10>;
|
||||
atmel,watchdog-type = "hardware";
|
||||
atmel,dbg-halt;
|
||||
atmel,idle-halt;
|
||||
status = "okay";
|
||||
};
|
19
Documentation/devicetree/bindings/watchdog/lpc18xx-wdt.txt
Normal file
19
Documentation/devicetree/bindings/watchdog/lpc18xx-wdt.txt
Normal file
@ -0,0 +1,19 @@
|
||||
* NXP LPC18xx Watchdog Timer (WDT)
|
||||
|
||||
Required properties:
|
||||
- compatible: Should be "nxp,lpc1850-wwdt"
|
||||
- reg: Should contain WDT registers location and length
|
||||
- clocks: Must contain an entry for each entry in clock-names.
|
||||
- clock-names: Should contain "wdtclk" and "reg"; the watchdog counter
|
||||
clock and register interface clock respectively.
|
||||
- interrupts: Should contain WDT interrupt
|
||||
|
||||
Examples:
|
||||
|
||||
watchdog@40080000 {
|
||||
compatible = "nxp,lpc1850-wwdt";
|
||||
reg = <0x40080000 0x24>;
|
||||
clocks = <&cgu BASE_SAFE_CLK>, <&ccu1 CLK_CPU_WWDT>;
|
||||
clock-names = "wdtclk", "reg";
|
||||
interrupts = <49>;
|
||||
};
|
@ -41,6 +41,7 @@ static void term(int sig)
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
int flags;
|
||||
unsigned int ping_rate = 1;
|
||||
|
||||
fd = open("/dev/watchdog", O_WRONLY);
|
||||
|
||||
@ -63,22 +64,33 @@ int main(int argc, char *argv[])
|
||||
fprintf(stderr, "Watchdog card enabled.\n");
|
||||
fflush(stderr);
|
||||
goto end;
|
||||
} else if (!strncasecmp(argv[1], "-t", 2) && argv[2]) {
|
||||
flags = atoi(argv[2]);
|
||||
ioctl(fd, WDIOC_SETTIMEOUT, &flags);
|
||||
fprintf(stderr, "Watchdog timeout set to %u seconds.\n", flags);
|
||||
fflush(stderr);
|
||||
goto end;
|
||||
} else if (!strncasecmp(argv[1], "-p", 2) && argv[2]) {
|
||||
ping_rate = strtoul(argv[2], NULL, 0);
|
||||
fprintf(stderr, "Watchdog ping rate set to %u seconds.\n", ping_rate);
|
||||
fflush(stderr);
|
||||
} else {
|
||||
fprintf(stderr, "-d to disable, -e to enable.\n");
|
||||
fprintf(stderr, "-d to disable, -e to enable, -t <n> to set " \
|
||||
"the timeout,\n-p <n> to set the ping rate, and \n");
|
||||
fprintf(stderr, "run by itself to tick the card.\n");
|
||||
fflush(stderr);
|
||||
goto end;
|
||||
}
|
||||
} else {
|
||||
fprintf(stderr, "Watchdog Ticking Away!\n");
|
||||
fflush(stderr);
|
||||
}
|
||||
|
||||
fprintf(stderr, "Watchdog Ticking Away!\n");
|
||||
fflush(stderr);
|
||||
|
||||
signal(SIGINT, term);
|
||||
|
||||
while(1) {
|
||||
keep_alive();
|
||||
sleep(1);
|
||||
sleep(ping_rate);
|
||||
}
|
||||
end:
|
||||
close(fd);
|
||||
|
@ -364,6 +364,7 @@ int mei_watchdog_register(struct mei_device *dev)
|
||||
|
||||
int ret;
|
||||
|
||||
amt_wd_dev.parent = dev->dev;
|
||||
/* unlock to perserve correct locking order */
|
||||
mutex_unlock(&dev->device_lock);
|
||||
ret = watchdog_register_device(&amt_wd_dev);
|
||||
|
@ -188,6 +188,15 @@ config AT91SAM9X_WATCHDOG
|
||||
Watchdog timer embedded into AT91SAM9X and AT91CAP9 chips. This will
|
||||
reboot your system when the timeout is reached.
|
||||
|
||||
config SAMA5D4_WATCHDOG
|
||||
tristate "Atmel SAMA5D4 Watchdog Timer"
|
||||
depends on ARCH_AT91
|
||||
select WATCHDOG_CORE
|
||||
help
|
||||
Atmel SAMA5D4 watchdog timer is embedded into SAMA5D4 chips.
|
||||
Its Watchdog Timer Mode Register can be written more than once.
|
||||
This will reboot your system when the timeout is reached.
|
||||
|
||||
config CADENCE_WATCHDOG
|
||||
tristate "Cadence Watchdog Timer"
|
||||
depends on HAS_IOMEM
|
||||
@ -558,6 +567,17 @@ config DIGICOLOR_WATCHDOG
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called digicolor_wdt.
|
||||
|
||||
config LPC18XX_WATCHDOG
|
||||
tristate "LPC18xx/43xx Watchdog"
|
||||
depends on ARCH_LPC18XX || COMPILE_TEST
|
||||
select WATCHDOG_CORE
|
||||
help
|
||||
Say Y here if to include support for the watchdog timer
|
||||
in NXP LPC SoCs family, which includes LPC18xx/LPC43xx
|
||||
processors.
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called lpc18xx_wdt.
|
||||
|
||||
# AVR32 Architecture
|
||||
|
||||
config AT32AP700X_WDT
|
||||
@ -1334,7 +1354,7 @@ config MPC5200_WDT
|
||||
|
||||
config 8xxx_WDT
|
||||
tristate "MPC8xxx Platform Watchdog Timer"
|
||||
depends on PPC_8xx || PPC_83xx || PPC_86xx
|
||||
depends on PPC_8xx || PPC_83xx || PPC_86xx || PPC_MPC512x
|
||||
select WATCHDOG_CORE
|
||||
help
|
||||
This driver is for a SoC level watchdog that exists on some
|
||||
|
@ -41,6 +41,7 @@ obj-$(CONFIG_IXP4XX_WATCHDOG) += ixp4xx_wdt.o
|
||||
obj-$(CONFIG_KS8695_WATCHDOG) += ks8695_wdt.o
|
||||
obj-$(CONFIG_S3C2410_WATCHDOG) += s3c2410_wdt.o
|
||||
obj-$(CONFIG_SA1100_WATCHDOG) += sa1100_wdt.o
|
||||
obj-$(CONFIG_SAMA5D4_WATCHDOG) += sama5d4_wdt.o
|
||||
obj-$(CONFIG_DW_WATCHDOG) += dw_wdt.o
|
||||
obj-$(CONFIG_EP93XX_WATCHDOG) += ep93xx_wdt.o
|
||||
obj-$(CONFIG_PNX4008_WATCHDOG) += pnx4008_wdt.o
|
||||
@ -66,6 +67,7 @@ obj-$(CONFIG_TEGRA_WATCHDOG) += tegra_wdt.o
|
||||
obj-$(CONFIG_MESON_WATCHDOG) += meson_wdt.o
|
||||
obj-$(CONFIG_MEDIATEK_WATCHDOG) += mtk_wdt.o
|
||||
obj-$(CONFIG_DIGICOLOR_WATCHDOG) += digicolor_wdt.o
|
||||
obj-$(CONFIG_LPC18XX_WATCHDOG) += lpc18xx_wdt.o
|
||||
|
||||
# AVR32 Architecture
|
||||
obj-$(CONFIG_AT32AP700X_WDT) += at32ap700x_wdt.o
|
||||
|
@ -244,7 +244,7 @@ static int at91wdt_probe(struct platform_device *pdev)
|
||||
}
|
||||
|
||||
regmap_st = syscon_node_to_regmap(parent->of_node);
|
||||
if (!regmap_st)
|
||||
if (IS_ERR(regmap_st))
|
||||
return -ENODEV;
|
||||
|
||||
res = misc_register(&at91wdt_miscdev);
|
||||
|
@ -17,6 +17,7 @@
|
||||
|
||||
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
||||
|
||||
#include <linux/clk.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/interrupt.h>
|
||||
@ -90,6 +91,7 @@ struct at91wdt {
|
||||
unsigned long heartbeat; /* WDT heartbeat in jiffies */
|
||||
bool nowayout;
|
||||
unsigned int irq;
|
||||
struct clk *sclk;
|
||||
};
|
||||
|
||||
/* ......................................................................... */
|
||||
@ -352,15 +354,25 @@ static int __init at91wdt_probe(struct platform_device *pdev)
|
||||
if (IS_ERR(wdt->base))
|
||||
return PTR_ERR(wdt->base);
|
||||
|
||||
wdt->sclk = devm_clk_get(&pdev->dev, NULL);
|
||||
if (IS_ERR(wdt->sclk))
|
||||
return PTR_ERR(wdt->sclk);
|
||||
|
||||
err = clk_prepare_enable(wdt->sclk);
|
||||
if (err) {
|
||||
dev_err(&pdev->dev, "Could not enable slow clock\n");
|
||||
return err;
|
||||
}
|
||||
|
||||
if (pdev->dev.of_node) {
|
||||
err = of_at91wdt_init(pdev->dev.of_node, wdt);
|
||||
if (err)
|
||||
return err;
|
||||
goto err_clk;
|
||||
}
|
||||
|
||||
err = at91_wdt_init(pdev, wdt);
|
||||
if (err)
|
||||
return err;
|
||||
goto err_clk;
|
||||
|
||||
platform_set_drvdata(pdev, wdt);
|
||||
|
||||
@ -368,6 +380,11 @@ static int __init at91wdt_probe(struct platform_device *pdev)
|
||||
wdt->wdd.timeout, wdt->nowayout);
|
||||
|
||||
return 0;
|
||||
|
||||
err_clk:
|
||||
clk_disable_unprepare(wdt->sclk);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int __exit at91wdt_remove(struct platform_device *pdev)
|
||||
@ -377,6 +394,7 @@ static int __exit at91wdt_remove(struct platform_device *pdev)
|
||||
|
||||
pr_warn("I quit now, hardware will probably reboot!\n");
|
||||
del_timer(&wdt->timer);
|
||||
clk_disable_unprepare(wdt->sclk);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -22,11 +22,13 @@
|
||||
|
||||
#define AT91_WDT_MR 0x04 /* Watchdog Mode Register */
|
||||
#define AT91_WDT_WDV (0xfff << 0) /* Counter Value */
|
||||
#define AT91_WDT_SET_WDV(x) ((x) & AT91_WDT_WDV)
|
||||
#define AT91_WDT_WDFIEN (1 << 12) /* Fault Interrupt Enable */
|
||||
#define AT91_WDT_WDRSTEN (1 << 13) /* Reset Processor */
|
||||
#define AT91_WDT_WDRPROC (1 << 14) /* Timer Restart */
|
||||
#define AT91_WDT_WDDIS (1 << 15) /* Watchdog Disable */
|
||||
#define AT91_WDT_WDD (0xfff << 16) /* Delta Value */
|
||||
#define AT91_WDT_SET_WDD(x) (((x) << 16) & AT91_WDT_WDD)
|
||||
#define AT91_WDT_WDDBGHLT (1 << 28) /* Debug Halt */
|
||||
#define AT91_WDT_WDIDLEHLT (1 << 29) /* Idle Halt */
|
||||
|
||||
|
@ -182,6 +182,7 @@ static int bcm2835_wdt_probe(struct platform_device *pdev)
|
||||
watchdog_set_drvdata(&bcm2835_wdt_wdd, wdt);
|
||||
watchdog_init_timeout(&bcm2835_wdt_wdd, heartbeat, dev);
|
||||
watchdog_set_nowayout(&bcm2835_wdt_wdd, nowayout);
|
||||
bcm2835_wdt_wdd.parent = &pdev->dev;
|
||||
err = watchdog_register_device(&bcm2835_wdt_wdd);
|
||||
if (err) {
|
||||
dev_err(dev, "Failed to register watchdog device");
|
||||
|
@ -209,6 +209,7 @@ static int bcm47xx_wdt_probe(struct platform_device *pdev)
|
||||
|
||||
wdt->wdd.info = &bcm47xx_wdt_info;
|
||||
wdt->wdd.timeout = WDT_DEFAULT_TIME;
|
||||
wdt->wdd.parent = &pdev->dev;
|
||||
ret = wdt->wdd.ops->set_timeout(&wdt->wdd, timeout);
|
||||
if (ret)
|
||||
goto err_timer;
|
||||
|
@ -319,6 +319,7 @@ static int bcm_kona_wdt_probe(struct platform_device *pdev)
|
||||
spin_lock_init(&wdt->lock);
|
||||
platform_set_drvdata(pdev, wdt);
|
||||
watchdog_set_drvdata(&bcm_kona_wdt_wdd, wdt);
|
||||
bcm_kona_wdt_wdd.parent = &pdev->dev;
|
||||
|
||||
ret = bcm_kona_wdt_set_timeout_reg(&bcm_kona_wdt_wdd, 0);
|
||||
if (ret) {
|
||||
|
@ -186,8 +186,6 @@ static int booke_wdt_stop(struct watchdog_device *wdog)
|
||||
static int booke_wdt_set_timeout(struct watchdog_device *wdt_dev,
|
||||
unsigned int timeout)
|
||||
{
|
||||
if (timeout > MAX_WDT_TIMEOUT)
|
||||
return -EINVAL;
|
||||
wdt_dev->timeout = timeout;
|
||||
booke_wdt_set(wdt_dev);
|
||||
|
||||
@ -211,7 +209,6 @@ static struct watchdog_device booke_wdt_dev = {
|
||||
.info = &booke_wdt_info,
|
||||
.ops = &booke_wdt_ops,
|
||||
.min_timeout = 1,
|
||||
.max_timeout = 0xFFFF
|
||||
};
|
||||
|
||||
static void __exit booke_wdt_exit(void)
|
||||
@ -229,6 +226,7 @@ static int __init booke_wdt_init(void)
|
||||
booke_wdt_set_timeout(&booke_wdt_dev,
|
||||
period_to_sec(booke_wdt_period));
|
||||
watchdog_set_nowayout(&booke_wdt_dev, nowayout);
|
||||
booke_wdt_dev.max_timeout = MAX_WDT_TIMEOUT;
|
||||
if (booke_wdt_enabled)
|
||||
booke_wdt_start(&booke_wdt_dev);
|
||||
|
||||
|
@ -358,6 +358,7 @@ static int __init coh901327_probe(struct platform_device *pdev)
|
||||
if (ret < 0)
|
||||
coh901327_wdt.timeout = 60;
|
||||
|
||||
coh901327_wdt.parent = &pdev->dev;
|
||||
ret = watchdog_register_device(&coh901327_wdt);
|
||||
if (ret == 0)
|
||||
dev_info(&pdev->dev,
|
||||
|
@ -195,6 +195,7 @@ static int da9052_wdt_probe(struct platform_device *pdev)
|
||||
da9052_wdt->timeout = DA9052_DEF_TIMEOUT;
|
||||
da9052_wdt->info = &da9052_wdt_info;
|
||||
da9052_wdt->ops = &da9052_wdt_ops;
|
||||
da9052_wdt->parent = &pdev->dev;
|
||||
watchdog_set_drvdata(da9052_wdt, driver_data);
|
||||
|
||||
kref_init(&driver_data->kref);
|
||||
|
@ -161,6 +161,7 @@ static int da9055_wdt_probe(struct platform_device *pdev)
|
||||
da9055_wdt->timeout = DA9055_DEF_TIMEOUT;
|
||||
da9055_wdt->info = &da9055_wdt_info;
|
||||
da9055_wdt->ops = &da9055_wdt_ops;
|
||||
da9055_wdt->parent = &pdev->dev;
|
||||
watchdog_set_nowayout(da9055_wdt, nowayout);
|
||||
watchdog_set_drvdata(da9055_wdt, driver_data);
|
||||
|
||||
|
@ -210,6 +210,7 @@ static int da9062_wdt_probe(struct platform_device *pdev)
|
||||
wdt->wdtdev.max_timeout = DA9062_WDT_MAX_TIMEOUT;
|
||||
wdt->wdtdev.timeout = DA9062_WDG_DEFAULT_TIMEOUT;
|
||||
wdt->wdtdev.status = WATCHDOG_NOWAYOUT_INIT_STATUS;
|
||||
wdt->wdtdev.parent = &pdev->dev;
|
||||
|
||||
watchdog_set_drvdata(&wdt->wdtdev, wdt);
|
||||
dev_set_drvdata(&pdev->dev, wdt);
|
||||
|
@ -175,6 +175,7 @@ static int da9063_wdt_probe(struct platform_device *pdev)
|
||||
wdt->wdtdev.min_timeout = DA9063_WDT_MIN_TIMEOUT;
|
||||
wdt->wdtdev.max_timeout = DA9063_WDT_MAX_TIMEOUT;
|
||||
wdt->wdtdev.timeout = DA9063_WDG_TIMEOUT;
|
||||
wdt->wdtdev.parent = &pdev->dev;
|
||||
|
||||
wdt->wdtdev.status = WATCHDOG_NOWAYOUT_INIT_STATUS;
|
||||
|
||||
|
@ -179,6 +179,7 @@ static int davinci_wdt_probe(struct platform_device *pdev)
|
||||
wdd->min_timeout = 1;
|
||||
wdd->max_timeout = MAX_HEARTBEAT;
|
||||
wdd->timeout = DEFAULT_HEARTBEAT;
|
||||
wdd->parent = &pdev->dev;
|
||||
|
||||
watchdog_init_timeout(wdd, heartbeat, dev);
|
||||
|
||||
|
@ -143,6 +143,7 @@ static int dc_wdt_probe(struct platform_device *pdev)
|
||||
}
|
||||
dc_wdt_wdd.max_timeout = U32_MAX / clk_get_rate(wdt->clk);
|
||||
dc_wdt_wdd.timeout = dc_wdt_wdd.max_timeout;
|
||||
dc_wdt_wdd.parent = &pdev->dev;
|
||||
|
||||
spin_lock_init(&wdt->lock);
|
||||
|
||||
|
@ -132,6 +132,7 @@ static int ep93xx_wdt_probe(struct platform_device *pdev)
|
||||
val = readl(mmio_base + EP93XX_WATCHDOG);
|
||||
ep93xx_wdt_wdd.bootstatus = (val & 0x01) ? WDIOF_CARDRESET : 0;
|
||||
ep93xx_wdt_wdd.timeout = timeout;
|
||||
ep93xx_wdt_wdd.parent = &pdev->dev;
|
||||
|
||||
watchdog_set_nowayout(&ep93xx_wdt_wdd, nowayout);
|
||||
|
||||
|
@ -50,12 +50,41 @@ static void gpio_wdt_disable(struct gpio_wdt_priv *priv)
|
||||
gpio_direction_input(priv->gpio);
|
||||
}
|
||||
|
||||
static void gpio_wdt_hwping(unsigned long data)
|
||||
{
|
||||
struct watchdog_device *wdd = (struct watchdog_device *)data;
|
||||
struct gpio_wdt_priv *priv = watchdog_get_drvdata(wdd);
|
||||
|
||||
if (priv->armed && time_after(jiffies, priv->last_jiffies +
|
||||
msecs_to_jiffies(wdd->timeout * 1000))) {
|
||||
dev_crit(wdd->dev, "Timer expired. System will reboot soon!\n");
|
||||
return;
|
||||
}
|
||||
|
||||
/* Restart timer */
|
||||
mod_timer(&priv->timer, jiffies + priv->hw_margin);
|
||||
|
||||
switch (priv->hw_algo) {
|
||||
case HW_ALGO_TOGGLE:
|
||||
/* Toggle output pin */
|
||||
priv->state = !priv->state;
|
||||
gpio_set_value_cansleep(priv->gpio, priv->state);
|
||||
break;
|
||||
case HW_ALGO_LEVEL:
|
||||
/* Pulse */
|
||||
gpio_set_value_cansleep(priv->gpio, !priv->active_low);
|
||||
udelay(1);
|
||||
gpio_set_value_cansleep(priv->gpio, priv->active_low);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void gpio_wdt_start_impl(struct gpio_wdt_priv *priv)
|
||||
{
|
||||
priv->state = priv->active_low;
|
||||
gpio_direction_output(priv->gpio, priv->state);
|
||||
priv->last_jiffies = jiffies;
|
||||
mod_timer(&priv->timer, priv->last_jiffies + priv->hw_margin);
|
||||
gpio_wdt_hwping((unsigned long)&priv->wdd);
|
||||
}
|
||||
|
||||
static int gpio_wdt_start(struct watchdog_device *wdd)
|
||||
@ -97,35 +126,6 @@ static int gpio_wdt_set_timeout(struct watchdog_device *wdd, unsigned int t)
|
||||
return gpio_wdt_ping(wdd);
|
||||
}
|
||||
|
||||
static void gpio_wdt_hwping(unsigned long data)
|
||||
{
|
||||
struct watchdog_device *wdd = (struct watchdog_device *)data;
|
||||
struct gpio_wdt_priv *priv = watchdog_get_drvdata(wdd);
|
||||
|
||||
if (priv->armed && time_after(jiffies, priv->last_jiffies +
|
||||
msecs_to_jiffies(wdd->timeout * 1000))) {
|
||||
dev_crit(wdd->dev, "Timer expired. System will reboot soon!\n");
|
||||
return;
|
||||
}
|
||||
|
||||
/* Restart timer */
|
||||
mod_timer(&priv->timer, jiffies + priv->hw_margin);
|
||||
|
||||
switch (priv->hw_algo) {
|
||||
case HW_ALGO_TOGGLE:
|
||||
/* Toggle output pin */
|
||||
priv->state = !priv->state;
|
||||
gpio_set_value_cansleep(priv->gpio, priv->state);
|
||||
break;
|
||||
case HW_ALGO_LEVEL:
|
||||
/* Pulse */
|
||||
gpio_set_value_cansleep(priv->gpio, !priv->active_low);
|
||||
udelay(1);
|
||||
gpio_set_value_cansleep(priv->gpio, priv->active_low);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static int gpio_wdt_notify_sys(struct notifier_block *nb, unsigned long code,
|
||||
void *unused)
|
||||
{
|
||||
@ -182,10 +182,10 @@ static int gpio_wdt_probe(struct platform_device *pdev)
|
||||
ret = of_property_read_string(pdev->dev.of_node, "hw_algo", &algo);
|
||||
if (ret)
|
||||
return ret;
|
||||
if (!strncmp(algo, "toggle", 6)) {
|
||||
if (!strcmp(algo, "toggle")) {
|
||||
priv->hw_algo = HW_ALGO_TOGGLE;
|
||||
f = GPIOF_IN;
|
||||
} else if (!strncmp(algo, "level", 5)) {
|
||||
} else if (!strcmp(algo, "level")) {
|
||||
priv->hw_algo = HW_ALGO_LEVEL;
|
||||
f = priv->active_low ? GPIOF_OUT_INIT_HIGH : GPIOF_OUT_INIT_LOW;
|
||||
} else {
|
||||
@ -217,6 +217,7 @@ static int gpio_wdt_probe(struct platform_device *pdev)
|
||||
priv->wdd.ops = &gpio_wdt_ops;
|
||||
priv->wdd.min_timeout = SOFT_TIMEOUT_MIN;
|
||||
priv->wdd.max_timeout = SOFT_TIMEOUT_MAX;
|
||||
priv->wdd.parent = &pdev->dev;
|
||||
|
||||
if (watchdog_init_timeout(&priv->wdd, 0, &pdev->dev) < 0)
|
||||
priv->wdd.timeout = SOFT_TIMEOUT_DEF;
|
||||
|
@ -267,6 +267,7 @@ static int ie6xx_wdt_probe(struct platform_device *pdev)
|
||||
|
||||
ie6xx_wdt_dev.timeout = timeout;
|
||||
watchdog_set_nowayout(&ie6xx_wdt_dev, nowayout);
|
||||
ie6xx_wdt_dev.parent = &pdev->dev;
|
||||
|
||||
spin_lock_init(&ie6xx_wdt_data.unlock_sequence);
|
||||
|
||||
|
@ -316,6 +316,7 @@ static int pdc_wdt_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct pdc_wdt_dev *pdc_wdt = platform_get_drvdata(pdev);
|
||||
|
||||
unregister_restart_handler(&pdc_wdt->restart_handler);
|
||||
pdc_wdt_stop(&pdc_wdt->wdt_dev);
|
||||
watchdog_unregister_device(&pdc_wdt->wdt_dev);
|
||||
clk_disable_unprepare(pdc_wdt->wdt_clk);
|
||||
|
@ -137,6 +137,7 @@ static int mid_wdt_probe(struct platform_device *pdev)
|
||||
wdt_dev->min_timeout = MID_WDT_TIMEOUT_MIN;
|
||||
wdt_dev->max_timeout = MID_WDT_TIMEOUT_MAX;
|
||||
wdt_dev->timeout = MID_WDT_DEFAULT_TIMEOUT;
|
||||
wdt_dev->parent = &pdev->dev;
|
||||
|
||||
watchdog_set_drvdata(wdt_dev, &pdev->dev);
|
||||
platform_set_drvdata(pdev, wdt_dev);
|
||||
|
@ -174,6 +174,7 @@ static int jz4740_wdt_probe(struct platform_device *pdev)
|
||||
jz4740_wdt->timeout = heartbeat;
|
||||
jz4740_wdt->min_timeout = 1;
|
||||
jz4740_wdt->max_timeout = MAX_HEARTBEAT;
|
||||
jz4740_wdt->parent = &pdev->dev;
|
||||
watchdog_set_nowayout(jz4740_wdt, nowayout);
|
||||
watchdog_set_drvdata(jz4740_wdt, drvdata);
|
||||
|
||||
|
340
drivers/watchdog/lpc18xx_wdt.c
Normal file
340
drivers/watchdog/lpc18xx_wdt.c
Normal file
@ -0,0 +1,340 @@
|
||||
/*
|
||||
* NXP LPC18xx Watchdog Timer (WDT)
|
||||
*
|
||||
* Copyright (c) 2015 Ariel D'Alessandro <ariel@vanguardiasur.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 as published by
|
||||
* the Free Software Foundation.
|
||||
*
|
||||
* Notes
|
||||
* -----
|
||||
* The Watchdog consists of a fixed divide-by-4 clock pre-scaler and a 24-bit
|
||||
* counter which decrements on every clock cycle.
|
||||
*/
|
||||
|
||||
#include <linux/clk.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/reboot.h>
|
||||
#include <linux/watchdog.h>
|
||||
|
||||
/* Registers */
|
||||
#define LPC18XX_WDT_MOD 0x00
|
||||
#define LPC18XX_WDT_MOD_WDEN BIT(0)
|
||||
#define LPC18XX_WDT_MOD_WDRESET BIT(1)
|
||||
|
||||
#define LPC18XX_WDT_TC 0x04
|
||||
#define LPC18XX_WDT_TC_MIN 0xff
|
||||
#define LPC18XX_WDT_TC_MAX 0xffffff
|
||||
|
||||
#define LPC18XX_WDT_FEED 0x08
|
||||
#define LPC18XX_WDT_FEED_MAGIC1 0xaa
|
||||
#define LPC18XX_WDT_FEED_MAGIC2 0x55
|
||||
|
||||
#define LPC18XX_WDT_TV 0x0c
|
||||
|
||||
/* Clock pre-scaler */
|
||||
#define LPC18XX_WDT_CLK_DIV 4
|
||||
|
||||
/* Timeout values in seconds */
|
||||
#define LPC18XX_WDT_DEF_TIMEOUT 30U
|
||||
|
||||
static int heartbeat;
|
||||
module_param(heartbeat, int, 0);
|
||||
MODULE_PARM_DESC(heartbeat, "Watchdog heartbeats in seconds (default="
|
||||
__MODULE_STRING(LPC18XX_WDT_DEF_TIMEOUT) ")");
|
||||
|
||||
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 lpc18xx_wdt_dev {
|
||||
struct watchdog_device wdt_dev;
|
||||
struct clk *reg_clk;
|
||||
struct clk *wdt_clk;
|
||||
unsigned long clk_rate;
|
||||
void __iomem *base;
|
||||
struct timer_list timer;
|
||||
struct notifier_block restart_handler;
|
||||
spinlock_t lock;
|
||||
};
|
||||
|
||||
static int lpc18xx_wdt_feed(struct watchdog_device *wdt_dev)
|
||||
{
|
||||
struct lpc18xx_wdt_dev *lpc18xx_wdt = watchdog_get_drvdata(wdt_dev);
|
||||
unsigned long flags;
|
||||
|
||||
/*
|
||||
* An abort condition will occur if an interrupt happens during the feed
|
||||
* sequence.
|
||||
*/
|
||||
spin_lock_irqsave(&lpc18xx_wdt->lock, flags);
|
||||
writel(LPC18XX_WDT_FEED_MAGIC1, lpc18xx_wdt->base + LPC18XX_WDT_FEED);
|
||||
writel(LPC18XX_WDT_FEED_MAGIC2, lpc18xx_wdt->base + LPC18XX_WDT_FEED);
|
||||
spin_unlock_irqrestore(&lpc18xx_wdt->lock, flags);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void lpc18xx_wdt_timer_feed(unsigned long data)
|
||||
{
|
||||
struct watchdog_device *wdt_dev = (struct watchdog_device *)data;
|
||||
struct lpc18xx_wdt_dev *lpc18xx_wdt = watchdog_get_drvdata(wdt_dev);
|
||||
|
||||
lpc18xx_wdt_feed(wdt_dev);
|
||||
|
||||
/* Use safe value (1/2 of real timeout) */
|
||||
mod_timer(&lpc18xx_wdt->timer, jiffies +
|
||||
msecs_to_jiffies((wdt_dev->timeout * MSEC_PER_SEC) / 2));
|
||||
}
|
||||
|
||||
/*
|
||||
* Since LPC18xx Watchdog cannot be disabled in hardware, we must keep feeding
|
||||
* it with a timer until userspace watchdog software takes over.
|
||||
*/
|
||||
static int lpc18xx_wdt_stop(struct watchdog_device *wdt_dev)
|
||||
{
|
||||
lpc18xx_wdt_timer_feed((unsigned long)wdt_dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void __lpc18xx_wdt_set_timeout(struct lpc18xx_wdt_dev *lpc18xx_wdt)
|
||||
{
|
||||
unsigned int val;
|
||||
|
||||
val = DIV_ROUND_UP(lpc18xx_wdt->wdt_dev.timeout * lpc18xx_wdt->clk_rate,
|
||||
LPC18XX_WDT_CLK_DIV);
|
||||
writel(val, lpc18xx_wdt->base + LPC18XX_WDT_TC);
|
||||
}
|
||||
|
||||
static int lpc18xx_wdt_set_timeout(struct watchdog_device *wdt_dev,
|
||||
unsigned int new_timeout)
|
||||
{
|
||||
struct lpc18xx_wdt_dev *lpc18xx_wdt = watchdog_get_drvdata(wdt_dev);
|
||||
|
||||
lpc18xx_wdt->wdt_dev.timeout = new_timeout;
|
||||
__lpc18xx_wdt_set_timeout(lpc18xx_wdt);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static unsigned int lpc18xx_wdt_get_timeleft(struct watchdog_device *wdt_dev)
|
||||
{
|
||||
struct lpc18xx_wdt_dev *lpc18xx_wdt = watchdog_get_drvdata(wdt_dev);
|
||||
unsigned int val;
|
||||
|
||||
val = readl(lpc18xx_wdt->base + LPC18XX_WDT_TV);
|
||||
return (val * LPC18XX_WDT_CLK_DIV) / lpc18xx_wdt->clk_rate;
|
||||
}
|
||||
|
||||
static int lpc18xx_wdt_start(struct watchdog_device *wdt_dev)
|
||||
{
|
||||
struct lpc18xx_wdt_dev *lpc18xx_wdt = watchdog_get_drvdata(wdt_dev);
|
||||
unsigned int val;
|
||||
|
||||
if (timer_pending(&lpc18xx_wdt->timer))
|
||||
del_timer(&lpc18xx_wdt->timer);
|
||||
|
||||
val = readl(lpc18xx_wdt->base + LPC18XX_WDT_MOD);
|
||||
val |= LPC18XX_WDT_MOD_WDEN;
|
||||
val |= LPC18XX_WDT_MOD_WDRESET;
|
||||
writel(val, lpc18xx_wdt->base + LPC18XX_WDT_MOD);
|
||||
|
||||
/*
|
||||
* Setting the WDEN bit in the WDMOD register is not sufficient to
|
||||
* enable the Watchdog. A valid feed sequence must be completed after
|
||||
* setting WDEN before the Watchdog is capable of generating a reset.
|
||||
*/
|
||||
lpc18xx_wdt_feed(wdt_dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct watchdog_info lpc18xx_wdt_info = {
|
||||
.identity = "NXP LPC18xx Watchdog",
|
||||
.options = WDIOF_SETTIMEOUT |
|
||||
WDIOF_KEEPALIVEPING |
|
||||
WDIOF_MAGICCLOSE,
|
||||
};
|
||||
|
||||
static const struct watchdog_ops lpc18xx_wdt_ops = {
|
||||
.owner = THIS_MODULE,
|
||||
.start = lpc18xx_wdt_start,
|
||||
.stop = lpc18xx_wdt_stop,
|
||||
.ping = lpc18xx_wdt_feed,
|
||||
.set_timeout = lpc18xx_wdt_set_timeout,
|
||||
.get_timeleft = lpc18xx_wdt_get_timeleft,
|
||||
};
|
||||
|
||||
static int lpc18xx_wdt_restart(struct notifier_block *this, unsigned long mode,
|
||||
void *cmd)
|
||||
{
|
||||
struct lpc18xx_wdt_dev *lpc18xx_wdt = container_of(this,
|
||||
struct lpc18xx_wdt_dev, restart_handler);
|
||||
unsigned long flags;
|
||||
int val;
|
||||
|
||||
/*
|
||||
* Incorrect feed sequence causes immediate watchdog reset if enabled.
|
||||
*/
|
||||
spin_lock_irqsave(&lpc18xx_wdt->lock, flags);
|
||||
|
||||
val = readl(lpc18xx_wdt->base + LPC18XX_WDT_MOD);
|
||||
val |= LPC18XX_WDT_MOD_WDEN;
|
||||
val |= LPC18XX_WDT_MOD_WDRESET;
|
||||
writel(val, lpc18xx_wdt->base + LPC18XX_WDT_MOD);
|
||||
|
||||
writel(LPC18XX_WDT_FEED_MAGIC1, lpc18xx_wdt->base + LPC18XX_WDT_FEED);
|
||||
writel(LPC18XX_WDT_FEED_MAGIC2, lpc18xx_wdt->base + LPC18XX_WDT_FEED);
|
||||
|
||||
writel(LPC18XX_WDT_FEED_MAGIC1, lpc18xx_wdt->base + LPC18XX_WDT_FEED);
|
||||
writel(LPC18XX_WDT_FEED_MAGIC1, lpc18xx_wdt->base + LPC18XX_WDT_FEED);
|
||||
|
||||
spin_unlock_irqrestore(&lpc18xx_wdt->lock, flags);
|
||||
|
||||
return NOTIFY_OK;
|
||||
}
|
||||
|
||||
static int lpc18xx_wdt_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct lpc18xx_wdt_dev *lpc18xx_wdt;
|
||||
struct device *dev = &pdev->dev;
|
||||
struct resource *res;
|
||||
int ret;
|
||||
|
||||
lpc18xx_wdt = devm_kzalloc(dev, sizeof(*lpc18xx_wdt), GFP_KERNEL);
|
||||
if (!lpc18xx_wdt)
|
||||
return -ENOMEM;
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
lpc18xx_wdt->base = devm_ioremap_resource(dev, res);
|
||||
if (IS_ERR(lpc18xx_wdt->base))
|
||||
return PTR_ERR(lpc18xx_wdt->base);
|
||||
|
||||
lpc18xx_wdt->reg_clk = devm_clk_get(dev, "reg");
|
||||
if (IS_ERR(lpc18xx_wdt->reg_clk)) {
|
||||
dev_err(dev, "failed to get the reg clock\n");
|
||||
return PTR_ERR(lpc18xx_wdt->reg_clk);
|
||||
}
|
||||
|
||||
lpc18xx_wdt->wdt_clk = devm_clk_get(dev, "wdtclk");
|
||||
if (IS_ERR(lpc18xx_wdt->wdt_clk)) {
|
||||
dev_err(dev, "failed to get the wdt clock\n");
|
||||
return PTR_ERR(lpc18xx_wdt->wdt_clk);
|
||||
}
|
||||
|
||||
ret = clk_prepare_enable(lpc18xx_wdt->reg_clk);
|
||||
if (ret) {
|
||||
dev_err(dev, "could not prepare or enable sys clock\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = clk_prepare_enable(lpc18xx_wdt->wdt_clk);
|
||||
if (ret) {
|
||||
dev_err(dev, "could not prepare or enable wdt clock\n");
|
||||
goto disable_reg_clk;
|
||||
}
|
||||
|
||||
/* We use the clock rate to calculate timeouts */
|
||||
lpc18xx_wdt->clk_rate = clk_get_rate(lpc18xx_wdt->wdt_clk);
|
||||
if (lpc18xx_wdt->clk_rate == 0) {
|
||||
dev_err(dev, "failed to get clock rate\n");
|
||||
ret = -EINVAL;
|
||||
goto disable_wdt_clk;
|
||||
}
|
||||
|
||||
lpc18xx_wdt->wdt_dev.info = &lpc18xx_wdt_info;
|
||||
lpc18xx_wdt->wdt_dev.ops = &lpc18xx_wdt_ops;
|
||||
|
||||
lpc18xx_wdt->wdt_dev.min_timeout = DIV_ROUND_UP(LPC18XX_WDT_TC_MIN *
|
||||
LPC18XX_WDT_CLK_DIV, lpc18xx_wdt->clk_rate);
|
||||
|
||||
lpc18xx_wdt->wdt_dev.max_timeout = (LPC18XX_WDT_TC_MAX *
|
||||
LPC18XX_WDT_CLK_DIV) / lpc18xx_wdt->clk_rate;
|
||||
|
||||
lpc18xx_wdt->wdt_dev.timeout = min(lpc18xx_wdt->wdt_dev.max_timeout,
|
||||
LPC18XX_WDT_DEF_TIMEOUT);
|
||||
|
||||
spin_lock_init(&lpc18xx_wdt->lock);
|
||||
|
||||
lpc18xx_wdt->wdt_dev.parent = dev;
|
||||
watchdog_set_drvdata(&lpc18xx_wdt->wdt_dev, lpc18xx_wdt);
|
||||
|
||||
ret = watchdog_init_timeout(&lpc18xx_wdt->wdt_dev, heartbeat, dev);
|
||||
|
||||
__lpc18xx_wdt_set_timeout(lpc18xx_wdt);
|
||||
|
||||
setup_timer(&lpc18xx_wdt->timer, lpc18xx_wdt_timer_feed,
|
||||
(unsigned long)&lpc18xx_wdt->wdt_dev);
|
||||
|
||||
watchdog_set_nowayout(&lpc18xx_wdt->wdt_dev, nowayout);
|
||||
|
||||
platform_set_drvdata(pdev, lpc18xx_wdt);
|
||||
|
||||
ret = watchdog_register_device(&lpc18xx_wdt->wdt_dev);
|
||||
if (ret)
|
||||
goto disable_wdt_clk;
|
||||
|
||||
lpc18xx_wdt->restart_handler.notifier_call = lpc18xx_wdt_restart;
|
||||
lpc18xx_wdt->restart_handler.priority = 128;
|
||||
ret = register_restart_handler(&lpc18xx_wdt->restart_handler);
|
||||
if (ret)
|
||||
dev_warn(dev, "failed to register restart handler: %d\n", ret);
|
||||
|
||||
return 0;
|
||||
|
||||
disable_wdt_clk:
|
||||
clk_disable_unprepare(lpc18xx_wdt->wdt_clk);
|
||||
disable_reg_clk:
|
||||
clk_disable_unprepare(lpc18xx_wdt->reg_clk);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void lpc18xx_wdt_shutdown(struct platform_device *pdev)
|
||||
{
|
||||
struct lpc18xx_wdt_dev *lpc18xx_wdt = platform_get_drvdata(pdev);
|
||||
|
||||
lpc18xx_wdt_stop(&lpc18xx_wdt->wdt_dev);
|
||||
}
|
||||
|
||||
static int lpc18xx_wdt_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct lpc18xx_wdt_dev *lpc18xx_wdt = platform_get_drvdata(pdev);
|
||||
|
||||
unregister_restart_handler(&lpc18xx_wdt->restart_handler);
|
||||
|
||||
dev_warn(&pdev->dev, "I quit now, hardware will probably reboot!\n");
|
||||
del_timer(&lpc18xx_wdt->timer);
|
||||
|
||||
watchdog_unregister_device(&lpc18xx_wdt->wdt_dev);
|
||||
clk_disable_unprepare(lpc18xx_wdt->wdt_clk);
|
||||
clk_disable_unprepare(lpc18xx_wdt->reg_clk);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id lpc18xx_wdt_match[] = {
|
||||
{ .compatible = "nxp,lpc1850-wwdt" },
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, lpc18xx_wdt_match);
|
||||
|
||||
static struct platform_driver lpc18xx_wdt_driver = {
|
||||
.driver = {
|
||||
.name = "lpc18xx-wdt",
|
||||
.of_match_table = lpc18xx_wdt_match,
|
||||
},
|
||||
.probe = lpc18xx_wdt_probe,
|
||||
.remove = lpc18xx_wdt_remove,
|
||||
.shutdown = lpc18xx_wdt_shutdown,
|
||||
};
|
||||
module_platform_driver(lpc18xx_wdt_driver);
|
||||
|
||||
MODULE_AUTHOR("Ariel D'Alessandro <ariel@vanguardiasur.com.ar>");
|
||||
MODULE_DESCRIPTION("NXP LPC18xx Watchdog Timer Driver");
|
||||
MODULE_LICENSE("GPL v2");
|
@ -197,6 +197,7 @@ static int a21_wdt_probe(struct platform_device *pdev)
|
||||
watchdog_init_timeout(&a21_wdt, 30, &pdev->dev);
|
||||
watchdog_set_nowayout(&a21_wdt, nowayout);
|
||||
watchdog_set_drvdata(&a21_wdt, drv);
|
||||
a21_wdt.parent = &pdev->dev;
|
||||
|
||||
reset = a21_wdt_get_bootstatus(drv);
|
||||
if (reset == 2)
|
||||
|
@ -130,6 +130,7 @@ static int menf21bmc_wdt_probe(struct platform_device *pdev)
|
||||
drv_data->wdt.info = &menf21bmc_wdt_info;
|
||||
drv_data->wdt.min_timeout = BMC_WD_TIMEOUT_MIN;
|
||||
drv_data->wdt.max_timeout = BMC_WD_TIMEOUT_MAX;
|
||||
drv_data->wdt.parent = &pdev->dev;
|
||||
drv_data->i2c_client = i2c_client;
|
||||
|
||||
/*
|
||||
|
@ -50,8 +50,12 @@ struct mpc8xxx_wdt_type {
|
||||
bool hw_enabled;
|
||||
};
|
||||
|
||||
static struct mpc8xxx_wdt __iomem *wd_base;
|
||||
static int mpc8xxx_wdt_init_late(void);
|
||||
struct mpc8xxx_wdt_ddata {
|
||||
struct mpc8xxx_wdt __iomem *base;
|
||||
struct watchdog_device wdd;
|
||||
struct timer_list timer;
|
||||
spinlock_t lock;
|
||||
};
|
||||
|
||||
static u16 timeout = 0xffff;
|
||||
module_param(timeout, ushort, 0);
|
||||
@ -68,65 +72,59 @@ module_param(nowayout, bool, 0);
|
||||
MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started "
|
||||
"(default=" __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
|
||||
|
||||
/*
|
||||
* We always prescale, but if someone really doesn't want to they can set this
|
||||
* to 0
|
||||
*/
|
||||
static int prescale = 1;
|
||||
|
||||
static DEFINE_SPINLOCK(wdt_spinlock);
|
||||
|
||||
static void mpc8xxx_wdt_keepalive(void)
|
||||
static void mpc8xxx_wdt_keepalive(struct mpc8xxx_wdt_ddata *ddata)
|
||||
{
|
||||
/* Ping the WDT */
|
||||
spin_lock(&wdt_spinlock);
|
||||
out_be16(&wd_base->swsrr, 0x556c);
|
||||
out_be16(&wd_base->swsrr, 0xaa39);
|
||||
spin_unlock(&wdt_spinlock);
|
||||
spin_lock(&ddata->lock);
|
||||
out_be16(&ddata->base->swsrr, 0x556c);
|
||||
out_be16(&ddata->base->swsrr, 0xaa39);
|
||||
spin_unlock(&ddata->lock);
|
||||
}
|
||||
|
||||
static struct watchdog_device mpc8xxx_wdt_dev;
|
||||
static void mpc8xxx_wdt_timer_ping(unsigned long arg);
|
||||
static DEFINE_TIMER(wdt_timer, mpc8xxx_wdt_timer_ping, 0,
|
||||
(unsigned long)&mpc8xxx_wdt_dev);
|
||||
|
||||
static void mpc8xxx_wdt_timer_ping(unsigned long arg)
|
||||
{
|
||||
struct watchdog_device *w = (struct watchdog_device *)arg;
|
||||
struct mpc8xxx_wdt_ddata *ddata = (void *)arg;
|
||||
|
||||
mpc8xxx_wdt_keepalive();
|
||||
mpc8xxx_wdt_keepalive(ddata);
|
||||
/* We're pinging it twice faster than needed, just to be sure. */
|
||||
mod_timer(&wdt_timer, jiffies + HZ * w->timeout / 2);
|
||||
mod_timer(&ddata->timer, jiffies + HZ * ddata->wdd.timeout / 2);
|
||||
}
|
||||
|
||||
static int mpc8xxx_wdt_start(struct watchdog_device *w)
|
||||
{
|
||||
u32 tmp = SWCRR_SWEN;
|
||||
struct mpc8xxx_wdt_ddata *ddata =
|
||||
container_of(w, struct mpc8xxx_wdt_ddata, wdd);
|
||||
|
||||
u32 tmp = SWCRR_SWEN | SWCRR_SWPR;
|
||||
|
||||
/* Good, fire up the show */
|
||||
if (prescale)
|
||||
tmp |= SWCRR_SWPR;
|
||||
if (reset)
|
||||
tmp |= SWCRR_SWRI;
|
||||
|
||||
tmp |= timeout << 16;
|
||||
|
||||
out_be32(&wd_base->swcrr, tmp);
|
||||
out_be32(&ddata->base->swcrr, tmp);
|
||||
|
||||
del_timer_sync(&wdt_timer);
|
||||
del_timer_sync(&ddata->timer);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mpc8xxx_wdt_ping(struct watchdog_device *w)
|
||||
{
|
||||
mpc8xxx_wdt_keepalive();
|
||||
struct mpc8xxx_wdt_ddata *ddata =
|
||||
container_of(w, struct mpc8xxx_wdt_ddata, wdd);
|
||||
|
||||
mpc8xxx_wdt_keepalive(ddata);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mpc8xxx_wdt_stop(struct watchdog_device *w)
|
||||
{
|
||||
mod_timer(&wdt_timer, jiffies);
|
||||
struct mpc8xxx_wdt_ddata *ddata =
|
||||
container_of(w, struct mpc8xxx_wdt_ddata, wdd);
|
||||
|
||||
mod_timer(&ddata->timer, jiffies);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -143,53 +141,57 @@ static struct watchdog_ops mpc8xxx_wdt_ops = {
|
||||
.stop = mpc8xxx_wdt_stop,
|
||||
};
|
||||
|
||||
static struct watchdog_device mpc8xxx_wdt_dev = {
|
||||
.info = &mpc8xxx_wdt_info,
|
||||
.ops = &mpc8xxx_wdt_ops,
|
||||
};
|
||||
|
||||
static const struct of_device_id mpc8xxx_wdt_match[];
|
||||
static int mpc8xxx_wdt_probe(struct platform_device *ofdev)
|
||||
{
|
||||
int ret;
|
||||
const struct of_device_id *match;
|
||||
struct device_node *np = ofdev->dev.of_node;
|
||||
struct resource *res;
|
||||
const struct mpc8xxx_wdt_type *wdt_type;
|
||||
struct mpc8xxx_wdt_ddata *ddata;
|
||||
u32 freq = fsl_get_sys_freq();
|
||||
bool enabled;
|
||||
unsigned int timeout_sec;
|
||||
|
||||
match = of_match_device(mpc8xxx_wdt_match, &ofdev->dev);
|
||||
if (!match)
|
||||
wdt_type = of_device_get_match_data(&ofdev->dev);
|
||||
if (!wdt_type)
|
||||
return -EINVAL;
|
||||
wdt_type = match->data;
|
||||
|
||||
if (!freq || freq == -1)
|
||||
return -EINVAL;
|
||||
|
||||
wd_base = of_iomap(np, 0);
|
||||
if (!wd_base)
|
||||
ddata = devm_kzalloc(&ofdev->dev, sizeof(*ddata), GFP_KERNEL);
|
||||
if (!ddata)
|
||||
return -ENOMEM;
|
||||
|
||||
enabled = in_be32(&wd_base->swcrr) & SWCRR_SWEN;
|
||||
res = platform_get_resource(ofdev, IORESOURCE_MEM, 0);
|
||||
ddata->base = devm_ioremap_resource(&ofdev->dev, res);
|
||||
if (IS_ERR(ddata->base))
|
||||
return PTR_ERR(ddata->base);
|
||||
|
||||
enabled = in_be32(&ddata->base->swcrr) & SWCRR_SWEN;
|
||||
if (!enabled && wdt_type->hw_enabled) {
|
||||
pr_info("could not be enabled in software\n");
|
||||
ret = -ENOSYS;
|
||||
goto err_unmap;
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
/* Calculate the timeout in seconds */
|
||||
if (prescale)
|
||||
timeout_sec = (timeout * wdt_type->prescaler) / freq;
|
||||
else
|
||||
timeout_sec = timeout / freq;
|
||||
spin_lock_init(&ddata->lock);
|
||||
setup_timer(&ddata->timer, mpc8xxx_wdt_timer_ping,
|
||||
(unsigned long)ddata);
|
||||
|
||||
mpc8xxx_wdt_dev.timeout = timeout_sec;
|
||||
#ifdef MODULE
|
||||
ret = mpc8xxx_wdt_init_late();
|
||||
if (ret)
|
||||
goto err_unmap;
|
||||
#endif
|
||||
ddata->wdd.info = &mpc8xxx_wdt_info,
|
||||
ddata->wdd.ops = &mpc8xxx_wdt_ops,
|
||||
|
||||
/* Calculate the timeout in seconds */
|
||||
timeout_sec = (timeout * wdt_type->prescaler) / freq;
|
||||
|
||||
ddata->wdd.timeout = timeout_sec;
|
||||
|
||||
watchdog_set_nowayout(&ddata->wdd, nowayout);
|
||||
|
||||
ret = watchdog_register_device(&ddata->wdd);
|
||||
if (ret) {
|
||||
pr_err("cannot register watchdog device (err=%d)\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
pr_info("WDT driver for MPC8xxx initialized. mode:%s timeout=%d (%d seconds)\n",
|
||||
reset ? "reset" : "interrupt", timeout, timeout_sec);
|
||||
@ -200,21 +202,20 @@ static int mpc8xxx_wdt_probe(struct platform_device *ofdev)
|
||||
* userspace handles it.
|
||||
*/
|
||||
if (enabled)
|
||||
mod_timer(&wdt_timer, jiffies);
|
||||
mod_timer(&ddata->timer, jiffies);
|
||||
|
||||
platform_set_drvdata(ofdev, ddata);
|
||||
return 0;
|
||||
err_unmap:
|
||||
iounmap(wd_base);
|
||||
wd_base = NULL;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int mpc8xxx_wdt_remove(struct platform_device *ofdev)
|
||||
{
|
||||
struct mpc8xxx_wdt_ddata *ddata = platform_get_drvdata(ofdev);
|
||||
|
||||
pr_crit("Watchdog removed, expect the %s soon!\n",
|
||||
reset ? "reset" : "machine check exception");
|
||||
del_timer_sync(&wdt_timer);
|
||||
watchdog_unregister_device(&mpc8xxx_wdt_dev);
|
||||
iounmap(wd_base);
|
||||
del_timer_sync(&ddata->timer);
|
||||
watchdog_unregister_device(&ddata->wdd);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -253,31 +254,6 @@ static struct platform_driver mpc8xxx_wdt_driver = {
|
||||
},
|
||||
};
|
||||
|
||||
/*
|
||||
* We do wdt initialization in two steps: arch_initcall probes the wdt
|
||||
* very early to start pinging the watchdog (misc devices are not yet
|
||||
* available), and later module_init() just registers the misc device.
|
||||
*/
|
||||
static int mpc8xxx_wdt_init_late(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (!wd_base)
|
||||
return -ENODEV;
|
||||
|
||||
watchdog_set_nowayout(&mpc8xxx_wdt_dev, nowayout);
|
||||
|
||||
ret = watchdog_register_device(&mpc8xxx_wdt_dev);
|
||||
if (ret) {
|
||||
pr_err("cannot register watchdog device (err=%d)\n", ret);
|
||||
return ret;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
#ifndef MODULE
|
||||
module_init(mpc8xxx_wdt_init_late);
|
||||
#endif
|
||||
|
||||
static int __init mpc8xxx_wdt_init(void)
|
||||
{
|
||||
return platform_driver_register(&mpc8xxx_wdt_driver);
|
||||
|
@ -210,6 +210,14 @@ static int mtk_wdt_probe(struct platform_device *pdev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void mtk_wdt_shutdown(struct platform_device *pdev)
|
||||
{
|
||||
struct mtk_wdt_dev *mtk_wdt = platform_get_drvdata(pdev);
|
||||
|
||||
if (watchdog_active(&mtk_wdt->wdt_dev))
|
||||
mtk_wdt_stop(&mtk_wdt->wdt_dev);
|
||||
}
|
||||
|
||||
static int mtk_wdt_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct mtk_wdt_dev *mtk_wdt = platform_get_drvdata(pdev);
|
||||
@ -221,17 +229,48 @@ static int mtk_wdt_remove(struct platform_device *pdev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
static int mtk_wdt_suspend(struct device *dev)
|
||||
{
|
||||
struct mtk_wdt_dev *mtk_wdt = dev_get_drvdata(dev);
|
||||
|
||||
if (watchdog_active(&mtk_wdt->wdt_dev))
|
||||
mtk_wdt_stop(&mtk_wdt->wdt_dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mtk_wdt_resume(struct device *dev)
|
||||
{
|
||||
struct mtk_wdt_dev *mtk_wdt = dev_get_drvdata(dev);
|
||||
|
||||
if (watchdog_active(&mtk_wdt->wdt_dev)) {
|
||||
mtk_wdt_start(&mtk_wdt->wdt_dev);
|
||||
mtk_wdt_ping(&mtk_wdt->wdt_dev);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
static const struct of_device_id mtk_wdt_dt_ids[] = {
|
||||
{ .compatible = "mediatek,mt6589-wdt" },
|
||||
{ /* sentinel */ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, mtk_wdt_dt_ids);
|
||||
|
||||
static const struct dev_pm_ops mtk_wdt_pm_ops = {
|
||||
SET_SYSTEM_SLEEP_PM_OPS(mtk_wdt_suspend,
|
||||
mtk_wdt_resume)
|
||||
};
|
||||
|
||||
static struct platform_driver mtk_wdt_driver = {
|
||||
.probe = mtk_wdt_probe,
|
||||
.remove = mtk_wdt_remove,
|
||||
.shutdown = mtk_wdt_shutdown,
|
||||
.driver = {
|
||||
.name = DRV_NAME,
|
||||
.pm = &mtk_wdt_pm_ops,
|
||||
.of_match_table = mtk_wdt_dt_ids,
|
||||
},
|
||||
};
|
||||
|
@ -294,6 +294,8 @@ static const struct pci_device_id tco_pci_tbl[] = {
|
||||
PCI_ANY_ID, PCI_ANY_ID, },
|
||||
{ PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_MCP55_SMBUS,
|
||||
PCI_ANY_ID, PCI_ANY_ID, },
|
||||
{ PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_MCP79_SMBUS,
|
||||
PCI_ANY_ID, PCI_ANY_ID, },
|
||||
{ 0, }, /* End of list */
|
||||
};
|
||||
MODULE_DEVICE_TABLE(pci, tco_pci_tbl);
|
||||
|
@ -253,6 +253,7 @@ static int omap_wdt_probe(struct platform_device *pdev)
|
||||
wdev->wdog.ops = &omap_wdt_ops;
|
||||
wdev->wdog.min_timeout = TIMER_MARGIN_MIN;
|
||||
wdev->wdog.max_timeout = TIMER_MARGIN_MAX;
|
||||
wdev->wdog.parent = &pdev->dev;
|
||||
|
||||
if (watchdog_init_timeout(&wdev->wdog, timer_margin, &pdev->dev) < 0)
|
||||
wdev->wdog.timeout = TIMER_MARGIN_DEFAULT;
|
||||
|
@ -567,6 +567,7 @@ static int orion_wdt_probe(struct platform_device *pdev)
|
||||
|
||||
dev->wdt.timeout = wdt_max_duration;
|
||||
dev->wdt.max_timeout = wdt_max_duration;
|
||||
dev->wdt.parent = &pdev->dev;
|
||||
watchdog_init_timeout(&dev->wdt, heartbeat, &pdev->dev);
|
||||
|
||||
platform_set_drvdata(pdev, &dev->wdt);
|
||||
|
@ -167,6 +167,7 @@ static int pnx4008_wdt_probe(struct platform_device *pdev)
|
||||
|
||||
pnx4008_wdd.bootstatus = (readl(WDTIM_RES(wdt_base)) & WDOG_RESET) ?
|
||||
WDIOF_CARDRESET : 0;
|
||||
pnx4008_wdd.parent = &pdev->dev;
|
||||
watchdog_set_nowayout(&pnx4008_wdd, nowayout);
|
||||
|
||||
pnx4008_wdt_stop(&pnx4008_wdd); /* disable for now */
|
||||
|
@ -171,6 +171,7 @@ static int qcom_wdt_probe(struct platform_device *pdev)
|
||||
wdt->wdd.ops = &qcom_wdt_ops;
|
||||
wdt->wdd.min_timeout = 1;
|
||||
wdt->wdd.max_timeout = 0x10000000U / wdt->rate;
|
||||
wdt->wdd.parent = &pdev->dev;
|
||||
|
||||
/*
|
||||
* If 'timeout-sec' unspecified in devicetree, assume a 30 second
|
||||
|
@ -127,6 +127,7 @@ static int retu_wdt_probe(struct platform_device *pdev)
|
||||
retu_wdt->timeout = RETU_WDT_MAX_TIMER;
|
||||
retu_wdt->min_timeout = 0;
|
||||
retu_wdt->max_timeout = RETU_WDT_MAX_TIMER;
|
||||
retu_wdt->parent = &pdev->dev;
|
||||
|
||||
watchdog_set_drvdata(retu_wdt, wdev);
|
||||
watchdog_set_nowayout(retu_wdt, nowayout);
|
||||
|
@ -161,6 +161,7 @@ static int rt288x_wdt_probe(struct platform_device *pdev)
|
||||
rt288x_wdt_dev.dev = &pdev->dev;
|
||||
rt288x_wdt_dev.bootstatus = rt288x_wdt_bootcause();
|
||||
rt288x_wdt_dev.max_timeout = (0xfffful / rt288x_wdt_freq);
|
||||
rt288x_wdt_dev.parent = &pdev->dev;
|
||||
|
||||
watchdog_init_timeout(&rt288x_wdt_dev, rt288x_wdt_dev.max_timeout,
|
||||
&pdev->dev);
|
||||
|
@ -607,6 +607,7 @@ static int s3c2410wdt_probe(struct platform_device *pdev)
|
||||
watchdog_set_nowayout(&wdt->wdt_device, nowayout);
|
||||
|
||||
wdt->wdt_device.bootstatus = s3c2410wdt_get_bootstatus(wdt);
|
||||
wdt->wdt_device.parent = &pdev->dev;
|
||||
|
||||
ret = watchdog_register_device(&wdt->wdt_device);
|
||||
if (ret) {
|
||||
|
280
drivers/watchdog/sama5d4_wdt.c
Normal file
280
drivers/watchdog/sama5d4_wdt.c
Normal file
@ -0,0 +1,280 @@
|
||||
/*
|
||||
* Driver for Atmel SAMA5D4 Watchdog Timer
|
||||
*
|
||||
* Copyright (C) 2015 Atmel Corporation
|
||||
*
|
||||
* Licensed under GPLv2.
|
||||
*/
|
||||
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_irq.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/reboot.h>
|
||||
#include <linux/watchdog.h>
|
||||
|
||||
#include "at91sam9_wdt.h"
|
||||
|
||||
/* minimum and maximum watchdog timeout, in seconds */
|
||||
#define MIN_WDT_TIMEOUT 1
|
||||
#define MAX_WDT_TIMEOUT 16
|
||||
#define WDT_DEFAULT_TIMEOUT MAX_WDT_TIMEOUT
|
||||
|
||||
#define WDT_SEC2TICKS(s) ((s) ? (((s) << 8) - 1) : 0)
|
||||
|
||||
struct sama5d4_wdt {
|
||||
struct watchdog_device wdd;
|
||||
void __iomem *reg_base;
|
||||
u32 config;
|
||||
};
|
||||
|
||||
static int wdt_timeout = WDT_DEFAULT_TIMEOUT;
|
||||
static bool nowayout = WATCHDOG_NOWAYOUT;
|
||||
|
||||
module_param(wdt_timeout, int, 0);
|
||||
MODULE_PARM_DESC(wdt_timeout,
|
||||
"Watchdog timeout in seconds. (default = "
|
||||
__MODULE_STRING(WDT_DEFAULT_TIMEOUT) ")");
|
||||
|
||||
module_param(nowayout, bool, 0);
|
||||
MODULE_PARM_DESC(nowayout,
|
||||
"Watchdog cannot be stopped once started (default="
|
||||
__MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
|
||||
|
||||
#define wdt_read(wdt, field) \
|
||||
readl_relaxed((wdt)->reg_base + (field))
|
||||
|
||||
#define wdt_write(wtd, field, val) \
|
||||
writel_relaxed((val), (wdt)->reg_base + (field))
|
||||
|
||||
static int sama5d4_wdt_start(struct watchdog_device *wdd)
|
||||
{
|
||||
struct sama5d4_wdt *wdt = watchdog_get_drvdata(wdd);
|
||||
u32 reg;
|
||||
|
||||
reg = wdt_read(wdt, AT91_WDT_MR);
|
||||
reg &= ~AT91_WDT_WDDIS;
|
||||
wdt_write(wdt, AT91_WDT_MR, reg);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sama5d4_wdt_stop(struct watchdog_device *wdd)
|
||||
{
|
||||
struct sama5d4_wdt *wdt = watchdog_get_drvdata(wdd);
|
||||
u32 reg;
|
||||
|
||||
reg = wdt_read(wdt, AT91_WDT_MR);
|
||||
reg |= AT91_WDT_WDDIS;
|
||||
wdt_write(wdt, AT91_WDT_MR, reg);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sama5d4_wdt_ping(struct watchdog_device *wdd)
|
||||
{
|
||||
struct sama5d4_wdt *wdt = watchdog_get_drvdata(wdd);
|
||||
|
||||
wdt_write(wdt, AT91_WDT_CR, AT91_WDT_KEY | AT91_WDT_WDRSTT);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sama5d4_wdt_set_timeout(struct watchdog_device *wdd,
|
||||
unsigned int timeout)
|
||||
{
|
||||
struct sama5d4_wdt *wdt = watchdog_get_drvdata(wdd);
|
||||
u32 value = WDT_SEC2TICKS(timeout);
|
||||
u32 reg;
|
||||
|
||||
reg = wdt_read(wdt, AT91_WDT_MR);
|
||||
reg &= ~AT91_WDT_WDV;
|
||||
reg &= ~AT91_WDT_WDD;
|
||||
reg |= AT91_WDT_SET_WDV(value);
|
||||
reg |= AT91_WDT_SET_WDD(value);
|
||||
wdt_write(wdt, AT91_WDT_MR, reg);
|
||||
|
||||
wdd->timeout = timeout;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct watchdog_info sama5d4_wdt_info = {
|
||||
.options = WDIOF_SETTIMEOUT | WDIOF_MAGICCLOSE | WDIOF_KEEPALIVEPING,
|
||||
.identity = "Atmel SAMA5D4 Watchdog",
|
||||
};
|
||||
|
||||
static struct watchdog_ops sama5d4_wdt_ops = {
|
||||
.owner = THIS_MODULE,
|
||||
.start = sama5d4_wdt_start,
|
||||
.stop = sama5d4_wdt_stop,
|
||||
.ping = sama5d4_wdt_ping,
|
||||
.set_timeout = sama5d4_wdt_set_timeout,
|
||||
};
|
||||
|
||||
static irqreturn_t sama5d4_wdt_irq_handler(int irq, void *dev_id)
|
||||
{
|
||||
struct sama5d4_wdt *wdt = platform_get_drvdata(dev_id);
|
||||
|
||||
if (wdt_read(wdt, AT91_WDT_SR)) {
|
||||
pr_crit("Atmel Watchdog Software Reset\n");
|
||||
emergency_restart();
|
||||
pr_crit("Reboot didn't succeed\n");
|
||||
}
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int of_sama5d4_wdt_init(struct device_node *np, struct sama5d4_wdt *wdt)
|
||||
{
|
||||
const char *tmp;
|
||||
|
||||
wdt->config = AT91_WDT_WDDIS;
|
||||
|
||||
if (!of_property_read_string(np, "atmel,watchdog-type", &tmp) &&
|
||||
!strcmp(tmp, "software"))
|
||||
wdt->config |= AT91_WDT_WDFIEN;
|
||||
else
|
||||
wdt->config |= AT91_WDT_WDRSTEN;
|
||||
|
||||
if (of_property_read_bool(np, "atmel,idle-halt"))
|
||||
wdt->config |= AT91_WDT_WDIDLEHLT;
|
||||
|
||||
if (of_property_read_bool(np, "atmel,dbg-halt"))
|
||||
wdt->config |= AT91_WDT_WDDBGHLT;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sama5d4_wdt_init(struct sama5d4_wdt *wdt)
|
||||
{
|
||||
struct watchdog_device *wdd = &wdt->wdd;
|
||||
u32 value = WDT_SEC2TICKS(wdd->timeout);
|
||||
u32 reg;
|
||||
|
||||
/*
|
||||
* Because the fields WDV and WDD must not be modified when the WDDIS
|
||||
* bit is set, so clear the WDDIS bit before writing the WDT_MR.
|
||||
*/
|
||||
reg = wdt_read(wdt, AT91_WDT_MR);
|
||||
reg &= ~AT91_WDT_WDDIS;
|
||||
wdt_write(wdt, AT91_WDT_MR, reg);
|
||||
|
||||
reg = wdt->config;
|
||||
reg |= AT91_WDT_SET_WDD(value);
|
||||
reg |= AT91_WDT_SET_WDV(value);
|
||||
|
||||
wdt_write(wdt, AT91_WDT_MR, reg);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sama5d4_wdt_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct watchdog_device *wdd;
|
||||
struct sama5d4_wdt *wdt;
|
||||
struct resource *res;
|
||||
void __iomem *regs;
|
||||
u32 irq = 0;
|
||||
int ret;
|
||||
|
||||
wdt = devm_kzalloc(&pdev->dev, sizeof(*wdt), GFP_KERNEL);
|
||||
if (!wdt)
|
||||
return -ENOMEM;
|
||||
|
||||
wdd = &wdt->wdd;
|
||||
wdd->timeout = wdt_timeout;
|
||||
wdd->info = &sama5d4_wdt_info;
|
||||
wdd->ops = &sama5d4_wdt_ops;
|
||||
wdd->min_timeout = MIN_WDT_TIMEOUT;
|
||||
wdd->max_timeout = MAX_WDT_TIMEOUT;
|
||||
|
||||
watchdog_set_drvdata(wdd, wdt);
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
regs = devm_ioremap_resource(&pdev->dev, res);
|
||||
if (IS_ERR(regs))
|
||||
return PTR_ERR(regs);
|
||||
|
||||
wdt->reg_base = regs;
|
||||
|
||||
if (pdev->dev.of_node) {
|
||||
irq = irq_of_parse_and_map(pdev->dev.of_node, 0);
|
||||
if (!irq)
|
||||
dev_warn(&pdev->dev, "failed to get IRQ from DT\n");
|
||||
|
||||
ret = of_sama5d4_wdt_init(pdev->dev.of_node, wdt);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
if ((wdt->config & AT91_WDT_WDFIEN) && irq) {
|
||||
ret = devm_request_irq(&pdev->dev, irq, sama5d4_wdt_irq_handler,
|
||||
IRQF_SHARED | IRQF_IRQPOLL |
|
||||
IRQF_NO_SUSPEND, pdev->name, pdev);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev,
|
||||
"cannot register interrupt handler\n");
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
ret = watchdog_init_timeout(wdd, wdt_timeout, &pdev->dev);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "unable to set timeout value\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = sama5d4_wdt_init(wdt);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
watchdog_set_nowayout(wdd, nowayout);
|
||||
|
||||
ret = watchdog_register_device(wdd);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "failed to register watchdog device\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
platform_set_drvdata(pdev, wdt);
|
||||
|
||||
dev_info(&pdev->dev, "initialized (timeout = %d sec, nowayout = %d)\n",
|
||||
wdt_timeout, nowayout);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sama5d4_wdt_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct sama5d4_wdt *wdt = platform_get_drvdata(pdev);
|
||||
|
||||
sama5d4_wdt_stop(&wdt->wdd);
|
||||
|
||||
watchdog_unregister_device(&wdt->wdd);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id sama5d4_wdt_of_match[] = {
|
||||
{ .compatible = "atmel,sama5d4-wdt", },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, sama5d4_wdt_of_match);
|
||||
|
||||
static struct platform_driver sama5d4_wdt_driver = {
|
||||
.probe = sama5d4_wdt_probe,
|
||||
.remove = sama5d4_wdt_remove,
|
||||
.driver = {
|
||||
.name = "sama5d4_wdt",
|
||||
.of_match_table = sama5d4_wdt_of_match,
|
||||
}
|
||||
};
|
||||
module_platform_driver(sama5d4_wdt_driver);
|
||||
|
||||
MODULE_AUTHOR("Atmel Corporation");
|
||||
MODULE_DESCRIPTION("Atmel SAMA5D4 Watchdog Timer driver");
|
||||
MODULE_LICENSE("GPL v2");
|
@ -252,6 +252,7 @@ static int sh_wdt_probe(struct platform_device *pdev)
|
||||
|
||||
watchdog_set_nowayout(&sh_wdt_dev, nowayout);
|
||||
watchdog_set_drvdata(&sh_wdt_dev, wdt);
|
||||
sh_wdt_dev.parent = &pdev->dev;
|
||||
|
||||
spin_lock_init(&wdt->lock);
|
||||
|
||||
|
@ -154,6 +154,7 @@ static int sirfsoc_wdt_probe(struct platform_device *pdev)
|
||||
|
||||
watchdog_init_timeout(&sirfsoc_wdd, timeout, &pdev->dev);
|
||||
watchdog_set_nowayout(&sirfsoc_wdd, nowayout);
|
||||
sirfsoc_wdd.parent = &pdev->dev;
|
||||
|
||||
ret = watchdog_register_device(&sirfsoc_wdd);
|
||||
if (ret)
|
||||
|
@ -226,6 +226,7 @@ sp805_wdt_probe(struct amba_device *adev, const struct amba_id *id)
|
||||
wdt->adev = adev;
|
||||
wdt->wdd.info = &wdt_info;
|
||||
wdt->wdd.ops = &wdt_ops;
|
||||
wdt->wdd.parent = &adev->dev;
|
||||
|
||||
spin_lock_init(&wdt->lock);
|
||||
watchdog_set_nowayout(&wdt->wdd, nowayout);
|
||||
|
@ -241,6 +241,7 @@ static int st_wdog_probe(struct platform_device *pdev)
|
||||
return -EINVAL;
|
||||
}
|
||||
st_wdog_dev.max_timeout = 0xFFFFFFFF / st_wdog->clkrate;
|
||||
st_wdog_dev.parent = &pdev->dev;
|
||||
|
||||
ret = clk_prepare_enable(clk);
|
||||
if (ret) {
|
||||
|
@ -76,6 +76,7 @@ static int stmp3xxx_wdt_probe(struct platform_device *pdev)
|
||||
watchdog_set_drvdata(&stmp3xxx_wdd, &pdev->dev);
|
||||
|
||||
stmp3xxx_wdd.timeout = clamp_t(unsigned, heartbeat, 1, STMP3XXX_MAX_TIMEOUT);
|
||||
stmp3xxx_wdd.parent = &pdev->dev;
|
||||
|
||||
ret = watchdog_register_device(&stmp3xxx_wdd);
|
||||
if (ret < 0) {
|
||||
|
@ -184,7 +184,7 @@ static int sunxi_wdt_start(struct watchdog_device *wdt_dev)
|
||||
/* Set system reset function */
|
||||
reg = readl(wdt_base + regs->wdt_cfg);
|
||||
reg &= ~(regs->wdt_reset_mask);
|
||||
reg |= ~(regs->wdt_reset_val);
|
||||
reg |= regs->wdt_reset_val;
|
||||
writel(reg, wdt_base + regs->wdt_cfg);
|
||||
|
||||
/* Enable watchdog */
|
||||
|
@ -218,6 +218,7 @@ static int tegra_wdt_probe(struct platform_device *pdev)
|
||||
wdd->ops = &tegra_wdt_ops;
|
||||
wdd->min_timeout = MIN_WDT_TIMEOUT;
|
||||
wdd->max_timeout = MAX_WDT_TIMEOUT;
|
||||
wdd->parent = &pdev->dev;
|
||||
|
||||
watchdog_set_drvdata(wdd, wdt);
|
||||
|
||||
|
@ -83,6 +83,7 @@ static int twl4030_wdt_probe(struct platform_device *pdev)
|
||||
wdt->timeout = 30;
|
||||
wdt->min_timeout = 1;
|
||||
wdt->max_timeout = 30;
|
||||
wdt->parent = &pdev->dev;
|
||||
|
||||
watchdog_set_nowayout(wdt, nowayout);
|
||||
platform_set_drvdata(pdev, wdt);
|
||||
|
@ -131,6 +131,7 @@ static int __init txx9wdt_probe(struct platform_device *dev)
|
||||
txx9wdt.timeout = timeout;
|
||||
txx9wdt.min_timeout = 1;
|
||||
txx9wdt.max_timeout = WD_MAX_TIMEOUT;
|
||||
txx9wdt.parent = &dev->dev;
|
||||
watchdog_set_nowayout(&txx9wdt, nowayout);
|
||||
|
||||
ret = watchdog_register_device(&txx9wdt);
|
||||
|
@ -96,6 +96,7 @@ static int ux500_wdt_probe(struct platform_device *pdev)
|
||||
ux500_wdt.max_timeout = WATCHDOG_MAX28;
|
||||
}
|
||||
|
||||
ux500_wdt.parent = &pdev->dev;
|
||||
watchdog_set_nowayout(&ux500_wdt, nowayout);
|
||||
|
||||
/* disable auto off on sleep */
|
||||
|
@ -206,6 +206,7 @@ static int wdt_probe(struct pci_dev *pdev,
|
||||
timeout = WDT_TIMEOUT;
|
||||
|
||||
wdt_dev.timeout = timeout;
|
||||
wdt_dev.parent = &pdev->dev;
|
||||
watchdog_set_nowayout(&wdt_dev, nowayout);
|
||||
if (readl(wdt_mem) & VIA_WDT_FIRED)
|
||||
wdt_dev.bootstatus |= WDIOF_CARDRESET;
|
||||
|
@ -215,6 +215,7 @@ static int wm831x_wdt_probe(struct platform_device *pdev)
|
||||
|
||||
wm831x_wdt->info = &wm831x_wdt_info;
|
||||
wm831x_wdt->ops = &wm831x_wdt_ops;
|
||||
wm831x_wdt->parent = &pdev->dev;
|
||||
watchdog_set_nowayout(wm831x_wdt, nowayout);
|
||||
watchdog_set_drvdata(wm831x_wdt, driver_data);
|
||||
|
||||
|
@ -151,6 +151,7 @@ static int wm8350_wdt_probe(struct platform_device *pdev)
|
||||
|
||||
watchdog_set_nowayout(&wm8350_wdt, nowayout);
|
||||
watchdog_set_drvdata(&wm8350_wdt, wm8350);
|
||||
wm8350_wdt.parent = &pdev->dev;
|
||||
|
||||
/* Default to 4s timeout */
|
||||
wm8350_wdt_set_timeout(&wm8350_wdt, 4);
|
||||
|
Loading…
Reference in New Issue
Block a user