Commit 93899e39 authored by Linus Torvalds's avatar Linus Torvalds

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
  ...
parents 5f1201d5 b2102eb3
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>;
};
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 ...@@ -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 { ...@@ -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: ...@@ -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 ...@@ -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
......
# #
# Watchdog device configuration # Watchdog device configuration
# #
...@@ -96,6 +97,15 @@ config DA9063_WATCHDOG ...@@ -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 ...@@ -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 ...@@ -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 ...@@ -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 ...@@ -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 ...@@ -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 ...@@ -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 ...@@ -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 @@ ...@@ -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
......
/*
* 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");
/*
* 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 @@ ...@@ -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 " ...@@ -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) ...@@ -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) ...@@ -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, ...@@ -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) ...@@ -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 = { ...@@ -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, ...@@ -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, ...@@ -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 @@ ...@@ -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 @@ ...@@ -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 @@ ...@@ -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 { ...@@ -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) ...@@ -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) ...@@ -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 = { ...@@ -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) ...@@ -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) ...@@ -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, ...@@ -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) ...@@ -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) ...@@ -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) ...@@ -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) ...@@ -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; ...@@ -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; ...@@ -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[] = { ...@@ -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) ...@@ -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);
val = __raw_readb(wdt_base); wdt->ping(wdt);
__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 = { ...@@ -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;
} }
dev_info(&pdev->dev, "using %ds heartbeat with %ds initial delay\n", err = max63xx_mmap_init(pdev, wdt);
current_timeout->twd, current_timeout->tdelay); if (err)
return err;
platform_set_drvdata(pdev, &wdt->wdd);
watchdog_set_drvdata(&wdt->wdd, wdt);
heartbeat = current_timeout->twd; wdt->wdd.parent = &pdev->dev;
wdt->wdd.timeout = wdt->timeout->twd;
wdt->wdd.info = &max63xx_wdt_info;
wdt->wdd.ops = &max63xx_wdt_ops;
wdt_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); watchdog_set_nowayout(&wdt->wdd, nowayout);
wdt_base = devm_ioremap_resource(&pdev->dev, wdt_mem);
if (IS_ERR(wdt_base))
return PTR_ERR(wdt_base);
max63xx_wdt_dev.timeout = heartbeat; err = watchdog_register_device(&wdt->wdd);
watchdog_set_nowayout(&max63xx_wdt_dev, nowayout); if (err)
watchdog_set_drvdata(&max63xx_wdt_dev, current_timeout); return err;
return watchdog_register_device(&max63xx_wdt_dev); dev_info(&pdev->dev, "using %ds heartbeat with %ds initial delay\n",
wdt->timeout->twd, wdt->timeout->tdelay);
return 0;
} }
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) ...@@ -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; ...@@ -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, ...@@ -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) ...@@ -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) ...@@ -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) ...@@ -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) ...@@ -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, ...@@ -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 = { ...@@ -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) ...@@ -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) ...@@ -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) ...@@ -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) ...@@ -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) ...@@ -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 @@ ...@@ -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) ...@@ -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 @@ ...@@ -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, ...@@ -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) ...@@ -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) ...@@ -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) ...@@ -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) ...@@ -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 { ...@@ -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 { ...@@ -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)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment