Commit e3799a21 authored by Linus Torvalds's avatar Linus Torvalds

Merge git://www.linux-watchdog.org/linux-watchdog

Pull watchdog updates from Wim Van Sebroeck:

 - a new watchdog pretimeout governor framework

 - support to upload the firmware on the ziirave_wdt

 - several fixes and cleanups

* git://www.linux-watchdog.org/linux-watchdog: (26 commits)
  watchdog: imx2_wdt: add pretimeout function support
  watchdog: softdog: implement pretimeout support
  watchdog: pretimeout: add pretimeout_available_governors attribute
  watchdog: pretimeout: add option to select a pretimeout governor in runtime
  watchdog: pretimeout: add panic pretimeout governor
  watchdog: pretimeout: add noop pretimeout governor
  watchdog: add watchdog pretimeout governor framework
  watchdog: hpwdt: add support for iLO5
  fs: compat_ioctl: add pretimeout functions for watchdogs
  watchdog: add pretimeout support to the core
  watchdog: imx2_wdt: use preferred BIT macro instead of open coded values
  watchdog: st_wdt: Remove support for obsolete platforms
  watchdog: bindings: Remove obsolete platforms from dt doc.
  watchdog: mt7621_wdt: Remove assignment of dev pointer
  watchdog: rt2880_wdt: Remove assignment of dev pointer
  watchdog: constify watchdog_ops structures
  watchdog: tegra: constify watchdog_ops structures
  watchdog: iTCO_wdt: constify iTCO_wdt_pm structure
  watchdog: cadence_wdt: Fix the suspend resume
  watchdog: txx9wdt: Add missing clock (un)prepare calls for CCF
  ...
parents b67be92f 39487f66
...@@ -7,6 +7,8 @@ Required properties: ...@@ -7,6 +7,8 @@ Required properties:
- reg : Physical base address and size - reg : Physical base address and size
Optional properties: Optional properties:
- clocks : Input clock specifier. Refer to common clock
bindings.
- clock-frequency : Frequency of clock in Hz - clock-frequency : Frequency of clock in Hz
- xlnx,wdt-enable-once : 0 - Watchdog can be restarted - xlnx,wdt-enable-once : 0 - Watchdog can be restarted
1 - Watchdog can be enabled just once 1 - Watchdog can be enabled just once
...@@ -17,6 +19,7 @@ Example: ...@@ -17,6 +19,7 @@ Example:
axi-timebase-wdt@40100000 { axi-timebase-wdt@40100000 {
clock-frequency = <50000000>; clock-frequency = <50000000>;
compatible = "xlnx,xps-timebase-wdt-1.00.a"; compatible = "xlnx,xps-timebase-wdt-1.00.a";
clocks = <&clkc 15>;
reg = <0x40100000 0x10000>; reg = <0x40100000 0x10000>;
xlnx,wdt-enable-once = <0x0>; xlnx,wdt-enable-once = <0x0>;
xlnx,wdt-interval = <0x1b>; xlnx,wdt-interval = <0x1b>;
......
...@@ -9,8 +9,7 @@ functionality. ...@@ -9,8 +9,7 @@ functionality.
Required properties Required properties
- compatible : Must be one of: "st,stih407-lpc" "st,stih416-lpc" - compatible : Should be: "st,stih407-lpc"
"st,stih415-lpc" "st,stid127-lpc"
- reg : LPC registers base address + size - reg : LPC registers base address + size
- interrupts : LPC interrupt line number and associated flags - interrupts : LPC interrupt line number and associated flags
- clocks : Clock used by LPC device (See: ../clock/clock-bindings.txt) - clocks : Clock used by LPC device (See: ../clock/clock-bindings.txt)
......
...@@ -48,8 +48,10 @@ struct watchdog_device { ...@@ -48,8 +48,10 @@ struct watchdog_device {
const struct attribute_group **groups; const struct attribute_group **groups;
const struct watchdog_info *info; const struct watchdog_info *info;
const struct watchdog_ops *ops; const struct watchdog_ops *ops;
const struct watchdog_governor *gov;
unsigned int bootstatus; unsigned int bootstatus;
unsigned int timeout; unsigned int timeout;
unsigned int pretimeout;
unsigned int min_timeout; unsigned int min_timeout;
unsigned int max_timeout; unsigned int max_timeout;
unsigned int min_hw_heartbeat_ms; unsigned int min_hw_heartbeat_ms;
...@@ -74,9 +76,11 @@ It contains following fields: ...@@ -74,9 +76,11 @@ It contains following fields:
* info: a pointer to a watchdog_info structure. This structure gives some * info: a pointer to a watchdog_info structure. This structure gives some
additional information about the watchdog timer itself. (Like it's unique name) additional information about the watchdog timer itself. (Like it's unique name)
* ops: a pointer to the list of watchdog operations that the watchdog supports. * ops: a pointer to the list of watchdog operations that the watchdog supports.
* gov: a pointer to the assigned watchdog device pretimeout governor or NULL.
* timeout: the watchdog timer's timeout value (in seconds). * timeout: the watchdog timer's timeout value (in seconds).
This is the time after which the system will reboot if user space does This is the time after which the system will reboot if user space does
not send a heartbeat request if WDOG_ACTIVE is set. not send a heartbeat request if WDOG_ACTIVE is set.
* pretimeout: the watchdog timer's pretimeout value (in seconds).
* min_timeout: the watchdog timer's minimum timeout value (in seconds). * min_timeout: the watchdog timer's minimum timeout value (in seconds).
If set, the minimum configurable value for 'timeout'. If set, the minimum configurable value for 'timeout'.
* max_timeout: the watchdog timer's maximum timeout value (in seconds), * max_timeout: the watchdog timer's maximum timeout value (in seconds),
...@@ -121,6 +125,7 @@ struct watchdog_ops { ...@@ -121,6 +125,7 @@ struct watchdog_ops {
int (*ping)(struct watchdog_device *); int (*ping)(struct watchdog_device *);
unsigned int (*status)(struct watchdog_device *); unsigned int (*status)(struct watchdog_device *);
int (*set_timeout)(struct watchdog_device *, unsigned int); int (*set_timeout)(struct watchdog_device *, unsigned int);
int (*set_pretimeout)(struct watchdog_device *, unsigned int);
unsigned int (*get_timeleft)(struct watchdog_device *); unsigned int (*get_timeleft)(struct watchdog_device *);
int (*restart)(struct watchdog_device *); int (*restart)(struct watchdog_device *);
void (*ref)(struct watchdog_device *) __deprecated; void (*ref)(struct watchdog_device *) __deprecated;
...@@ -188,6 +193,23 @@ they are supported. These optional routines/operations are: ...@@ -188,6 +193,23 @@ they are supported. These optional routines/operations are:
If set_timeout is not provided but, WDIOF_SETTIMEOUT is set, the watchdog If set_timeout is not provided but, WDIOF_SETTIMEOUT is set, the watchdog
infrastructure updates the timeout value of the watchdog_device internally infrastructure updates the timeout value of the watchdog_device internally
to the requested value. to the requested value.
If the pretimeout feature is used (WDIOF_PRETIMEOUT), then set_timeout must
also take care of checking if pretimeout is still valid and set up the timer
accordingly. This can't be done in the core without races, so it is the
duty of the driver.
* set_pretimeout: this routine checks and changes the pretimeout value of
the watchdog. It is optional because not all watchdogs support pretimeout
notification. The timeout value is not an absolute time, but the number of
seconds before the actual timeout would happen. It returns 0 on success,
-EINVAL for "parameter out of range" and -EIO for "could not write value to
the watchdog". A value of 0 disables pretimeout notification.
(Note: the WDIOF_PRETIMEOUT needs to be set in the options field of the
watchdog's info structure).
If the watchdog driver does not have to perform any action but setting the
watchdog_device.pretimeout, this callback can be omitted. That means if
set_pretimeout is not provided but WDIOF_PRETIMEOUT is set, the watchdog
infrastructure updates the pretimeout value of the watchdog_device internally
to the requested value.
* get_timeleft: this routines returns the time that's left before a reset. * get_timeleft: this routines returns the time that's left before a reset.
* restart: this routine restarts the machine. It returns 0 on success or a * restart: this routine restarts the machine. It returns 0 on success or a
negative errno code for failure. negative errno code for failure.
...@@ -268,3 +290,14 @@ User should follow the following guidelines for setting the priority: ...@@ -268,3 +290,14 @@ User should follow the following guidelines for setting the priority:
* 128: default restart handler, use if no other handler is expected to be * 128: default restart handler, use if no other handler is expected to be
available, and/or if restart is sufficient to restart the entire system available, and/or if restart is sufficient to restart the entire system
* 255: highest priority, will preempt all other restart handlers * 255: highest priority, will preempt all other restart handlers
To raise a pretimeout notification, the following function should be used:
void watchdog_notify_pretimeout(struct watchdog_device *wdd)
The function can be called in the interrupt context. If watchdog pretimeout
governor framework (kbuild CONFIG_WATCHDOG_PRETIMEOUT_GOV symbol) is enabled,
an action is taken by a preconfigured pretimeout governor preassigned to
the watchdog device. If watchdog pretimeout governor framework is not
enabled, watchdog_notify_pretimeout() prints a notification message to
the kernel log buffer.
...@@ -1844,4 +1844,53 @@ config USBPCWATCHDOG ...@@ -1844,4 +1844,53 @@ config USBPCWATCHDOG
Most people will say N. Most people will say N.
comment "Watchdog Pretimeout Governors"
config WATCHDOG_PRETIMEOUT_GOV
bool "Enable watchdog pretimeout governors"
help
The option allows to select watchdog pretimeout governors.
if WATCHDOG_PRETIMEOUT_GOV
choice
prompt "Default Watchdog Pretimeout Governor"
default WATCHDOG_PRETIMEOUT_DEFAULT_GOV_PANIC
help
This option selects a default watchdog pretimeout governor.
The governor takes its action, if a watchdog is capable
to report a pretimeout event.
config WATCHDOG_PRETIMEOUT_DEFAULT_GOV_NOOP
bool "noop"
select WATCHDOG_PRETIMEOUT_GOV_NOOP
help
Use noop watchdog pretimeout governor by default. If noop
governor is selected by a user, write a short message to
the kernel log buffer and don't do any system changes.
config WATCHDOG_PRETIMEOUT_DEFAULT_GOV_PANIC
bool "panic"
select WATCHDOG_PRETIMEOUT_GOV_PANIC
help
Use panic watchdog pretimeout governor by default, if
a watchdog pretimeout event happens, consider that
a watchdog feeder is dead and reboot is unavoidable.
endchoice
config WATCHDOG_PRETIMEOUT_GOV_NOOP
tristate "Noop watchdog pretimeout governor"
help
Noop watchdog pretimeout governor, only an informational
message is added to kernel log buffer.
config WATCHDOG_PRETIMEOUT_GOV_PANIC
tristate "Panic watchdog pretimeout governor"
help
Panic watchdog pretimeout governor, on watchdog pretimeout
event put the kernel into panic.
endif # WATCHDOG_PRETIMEOUT_GOV
endif # WATCHDOG endif # WATCHDOG
...@@ -3,9 +3,15 @@ ...@@ -3,9 +3,15 @@
# #
# The WatchDog Timer Driver Core. # The WatchDog Timer Driver Core.
watchdog-objs += watchdog_core.o watchdog_dev.o
obj-$(CONFIG_WATCHDOG_CORE) += watchdog.o obj-$(CONFIG_WATCHDOG_CORE) += watchdog.o
watchdog-objs += watchdog_core.o watchdog_dev.o
watchdog-$(CONFIG_WATCHDOG_PRETIMEOUT_GOV) += watchdog_pretimeout.o
obj-$(CONFIG_WATCHDOG_PRETIMEOUT_GOV_NOOP) += pretimeout_noop.o
obj-$(CONFIG_WATCHDOG_PRETIMEOUT_GOV_PANIC) += pretimeout_panic.o
# Only one watchdog can succeed. We probe the ISA/PCI/USB based # Only one watchdog can succeed. We probe the ISA/PCI/USB based
# watchdog-cards first, then the architecture specific watchdog # watchdog-cards first, then the architecture specific watchdog
# drivers and then the architecture independent "softdog" driver. # drivers and then the architecture independent "softdog" driver.
......
...@@ -389,7 +389,6 @@ MODULE_DEVICE_TABLE(of, asm9260_wdt_of_match); ...@@ -389,7 +389,6 @@ MODULE_DEVICE_TABLE(of, asm9260_wdt_of_match);
static struct platform_driver asm9260_wdt_driver = { static struct platform_driver asm9260_wdt_driver = {
.driver = { .driver = {
.name = "asm9260-wdt", .name = "asm9260-wdt",
.owner = THIS_MODULE,
.of_match_table = asm9260_wdt_of_match, .of_match_table = asm9260_wdt_of_match,
}, },
.probe = asm9260_wdt_probe, .probe = asm9260_wdt_probe,
......
...@@ -107,7 +107,7 @@ static struct watchdog_info bcm7038_wdt_info = { ...@@ -107,7 +107,7 @@ static struct watchdog_info bcm7038_wdt_info = {
WDIOF_MAGICCLOSE WDIOF_MAGICCLOSE
}; };
static struct watchdog_ops bcm7038_wdt_ops = { static const struct watchdog_ops bcm7038_wdt_ops = {
.owner = THIS_MODULE, .owner = THIS_MODULE,
.start = bcm7038_wdt_start, .start = bcm7038_wdt_start,
.stop = bcm7038_wdt_stop, .stop = bcm7038_wdt_stop,
......
...@@ -269,7 +269,7 @@ static struct watchdog_info cdns_wdt_info = { ...@@ -269,7 +269,7 @@ static struct watchdog_info cdns_wdt_info = {
}; };
/* Watchdog Core Ops */ /* Watchdog Core Ops */
static struct watchdog_ops cdns_wdt_ops = { static const struct watchdog_ops cdns_wdt_ops = {
.owner = THIS_MODULE, .owner = THIS_MODULE,
.start = cdns_wdt_start, .start = cdns_wdt_start,
.stop = cdns_wdt_stop, .stop = cdns_wdt_stop,
...@@ -424,8 +424,10 @@ static int __maybe_unused cdns_wdt_suspend(struct device *dev) ...@@ -424,8 +424,10 @@ static int __maybe_unused cdns_wdt_suspend(struct device *dev)
struct platform_device *pdev = to_platform_device(dev); struct platform_device *pdev = to_platform_device(dev);
struct cdns_wdt *wdt = platform_get_drvdata(pdev); struct cdns_wdt *wdt = platform_get_drvdata(pdev);
if (watchdog_active(&wdt->cdns_wdt_device)) {
cdns_wdt_stop(&wdt->cdns_wdt_device); cdns_wdt_stop(&wdt->cdns_wdt_device);
clk_disable_unprepare(wdt->clk); clk_disable_unprepare(wdt->clk);
}
return 0; return 0;
} }
...@@ -442,12 +444,14 @@ static int __maybe_unused cdns_wdt_resume(struct device *dev) ...@@ -442,12 +444,14 @@ static int __maybe_unused cdns_wdt_resume(struct device *dev)
struct platform_device *pdev = to_platform_device(dev); struct platform_device *pdev = to_platform_device(dev);
struct cdns_wdt *wdt = platform_get_drvdata(pdev); struct cdns_wdt *wdt = platform_get_drvdata(pdev);
if (watchdog_active(&wdt->cdns_wdt_device)) {
ret = clk_prepare_enable(wdt->clk); ret = clk_prepare_enable(wdt->clk);
if (ret) { if (ret) {
dev_err(dev, "unable to enable clock\n"); dev_err(dev, "unable to enable clock\n");
return ret; return ret;
} }
cdns_wdt_start(&wdt->cdns_wdt_device); cdns_wdt_start(&wdt->cdns_wdt_device);
}
return 0; return 0;
} }
......
...@@ -54,6 +54,7 @@ MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started " ...@@ -54,6 +54,7 @@ MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started "
struct dw_wdt { struct dw_wdt {
void __iomem *regs; void __iomem *regs;
struct clk *clk; struct clk *clk;
unsigned long rate;
struct notifier_block restart_handler; struct notifier_block restart_handler;
struct watchdog_device wdd; struct watchdog_device wdd;
}; };
...@@ -72,7 +73,7 @@ static inline int dw_wdt_top_in_seconds(struct dw_wdt *dw_wdt, unsigned top) ...@@ -72,7 +73,7 @@ static inline int dw_wdt_top_in_seconds(struct dw_wdt *dw_wdt, unsigned top)
* There are 16 possible timeout values in 0..15 where the number of * There are 16 possible timeout values in 0..15 where the number of
* cycles is 2 ^ (16 + i) and the watchdog counts down. * cycles is 2 ^ (16 + i) and the watchdog counts down.
*/ */
return (1U << (16 + top)) / clk_get_rate(dw_wdt->clk); return (1U << (16 + top)) / dw_wdt->rate;
} }
static int dw_wdt_get_top(struct dw_wdt *dw_wdt) static int dw_wdt_get_top(struct dw_wdt *dw_wdt)
...@@ -163,7 +164,7 @@ static unsigned int dw_wdt_get_timeleft(struct watchdog_device *wdd) ...@@ -163,7 +164,7 @@ static unsigned int dw_wdt_get_timeleft(struct watchdog_device *wdd)
struct dw_wdt *dw_wdt = to_dw_wdt(wdd); struct dw_wdt *dw_wdt = to_dw_wdt(wdd);
return readl(dw_wdt->regs + WDOG_CURRENT_COUNT_REG_OFFSET) / return readl(dw_wdt->regs + WDOG_CURRENT_COUNT_REG_OFFSET) /
clk_get_rate(dw_wdt->clk); dw_wdt->rate;
} }
static const struct watchdog_info dw_wdt_ident = { static const struct watchdog_info dw_wdt_ident = {
...@@ -231,6 +232,12 @@ static int dw_wdt_drv_probe(struct platform_device *pdev) ...@@ -231,6 +232,12 @@ static int dw_wdt_drv_probe(struct platform_device *pdev)
if (ret) if (ret)
return ret; return ret;
dw_wdt->rate = clk_get_rate(dw_wdt->clk);
if (dw_wdt->rate == 0) {
ret = -EINVAL;
goto out_disable_clk;
}
wdd = &dw_wdt->wdd; wdd = &dw_wdt->wdd;
wdd->info = &dw_wdt_ident; wdd->info = &dw_wdt_ident;
wdd->ops = &dw_wdt_ops; wdd->ops = &dw_wdt_ops;
......
...@@ -39,7 +39,7 @@ ...@@ -39,7 +39,7 @@
#include <asm/nmi.h> #include <asm/nmi.h>
#include <asm/frame.h> #include <asm/frame.h>
#define HPWDT_VERSION "1.3.3" #define HPWDT_VERSION "1.4.0"
#define SECS_TO_TICKS(secs) ((secs) * 1000 / 128) #define SECS_TO_TICKS(secs) ((secs) * 1000 / 128)
#define TICKS_TO_SECS(ticks) ((ticks) * 128 / 1000) #define TICKS_TO_SECS(ticks) ((ticks) * 128 / 1000)
#define HPWDT_MAX_TIMER TICKS_TO_SECS(65535) #define HPWDT_MAX_TIMER TICKS_TO_SECS(65535)
...@@ -814,7 +814,8 @@ static int hpwdt_init_one(struct pci_dev *dev, ...@@ -814,7 +814,8 @@ static int hpwdt_init_one(struct pci_dev *dev,
* not run on a legacy ASM box. * not run on a legacy ASM box.
* So we only support the G5 ProLiant servers and higher. * So we only support the G5 ProLiant servers and higher.
*/ */
if (dev->subsystem_vendor != PCI_VENDOR_ID_HP) { if (dev->subsystem_vendor != PCI_VENDOR_ID_HP &&
dev->subsystem_vendor != PCI_VENDOR_ID_HP_3PAR) {
dev_warn(&dev->dev, dev_warn(&dev->dev,
"This server does not have an iLO2+ ASIC.\n"); "This server does not have an iLO2+ ASIC.\n");
return -ENODEV; return -ENODEV;
...@@ -823,7 +824,8 @@ static int hpwdt_init_one(struct pci_dev *dev, ...@@ -823,7 +824,8 @@ static int hpwdt_init_one(struct pci_dev *dev,
/* /*
* Ignore all auxilary iLO devices with the following PCI ID * Ignore all auxilary iLO devices with the following PCI ID
*/ */
if (dev->subsystem_device == 0x1979) if (dev->subsystem_vendor == PCI_VENDOR_ID_HP &&
dev->subsystem_device == 0x1979)
return -ENODEV; return -ENODEV;
if (pci_enable_device(dev)) { if (pci_enable_device(dev)) {
......
...@@ -629,7 +629,7 @@ static int iTCO_wdt_resume_noirq(struct device *dev) ...@@ -629,7 +629,7 @@ static int iTCO_wdt_resume_noirq(struct device *dev)
return 0; return 0;
} }
static struct dev_pm_ops iTCO_wdt_pm = { static const struct dev_pm_ops iTCO_wdt_pm = {
.suspend_noirq = iTCO_wdt_suspend_noirq, .suspend_noirq = iTCO_wdt_suspend_noirq,
.resume_noirq = iTCO_wdt_resume_noirq, .resume_noirq = iTCO_wdt_resume_noirq,
}; };
......
...@@ -24,6 +24,7 @@ ...@@ -24,6 +24,7 @@
#include <linux/clk.h> #include <linux/clk.h>
#include <linux/delay.h> #include <linux/delay.h>
#include <linux/init.h> #include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/io.h> #include <linux/io.h>
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/module.h> #include <linux/module.h>
...@@ -37,18 +38,23 @@ ...@@ -37,18 +38,23 @@
#define IMX2_WDT_WCR 0x00 /* Control Register */ #define IMX2_WDT_WCR 0x00 /* Control Register */
#define IMX2_WDT_WCR_WT (0xFF << 8) /* -> Watchdog Timeout Field */ #define IMX2_WDT_WCR_WT (0xFF << 8) /* -> Watchdog Timeout Field */
#define IMX2_WDT_WCR_WDA (1 << 5) /* -> External Reset WDOG_B */ #define IMX2_WDT_WCR_WDA BIT(5) /* -> External Reset WDOG_B */
#define IMX2_WDT_WCR_SRS (1 << 4) /* -> Software Reset Signal */ #define IMX2_WDT_WCR_SRS BIT(4) /* -> Software Reset Signal */
#define IMX2_WDT_WCR_WRE (1 << 3) /* -> WDOG Reset Enable */ #define IMX2_WDT_WCR_WRE BIT(3) /* -> WDOG Reset Enable */
#define IMX2_WDT_WCR_WDE (1 << 2) /* -> Watchdog Enable */ #define IMX2_WDT_WCR_WDE BIT(2) /* -> Watchdog Enable */
#define IMX2_WDT_WCR_WDZST (1 << 0) /* -> Watchdog timer Suspend */ #define IMX2_WDT_WCR_WDZST BIT(0) /* -> Watchdog timer Suspend */
#define IMX2_WDT_WSR 0x02 /* Service Register */ #define IMX2_WDT_WSR 0x02 /* Service Register */
#define IMX2_WDT_SEQ1 0x5555 /* -> service sequence 1 */ #define IMX2_WDT_SEQ1 0x5555 /* -> service sequence 1 */
#define IMX2_WDT_SEQ2 0xAAAA /* -> service sequence 2 */ #define IMX2_WDT_SEQ2 0xAAAA /* -> service sequence 2 */
#define IMX2_WDT_WRSR 0x04 /* Reset Status Register */ #define IMX2_WDT_WRSR 0x04 /* Reset Status Register */
#define IMX2_WDT_WRSR_TOUT (1 << 1) /* -> Reset due to Timeout */ #define IMX2_WDT_WRSR_TOUT BIT(1) /* -> Reset due to Timeout */
#define IMX2_WDT_WICR 0x06 /* Interrupt Control Register */
#define IMX2_WDT_WICR_WIE BIT(15) /* -> Interrupt Enable */
#define IMX2_WDT_WICR_WTIS BIT(14) /* -> Interrupt Status */
#define IMX2_WDT_WICR_WICT 0xFF /* -> Interrupt Count Timeout */
#define IMX2_WDT_WMCR 0x08 /* Misc Register */ #define IMX2_WDT_WMCR 0x08 /* Misc Register */
...@@ -80,6 +86,12 @@ static const struct watchdog_info imx2_wdt_info = { ...@@ -80,6 +86,12 @@ static const struct watchdog_info imx2_wdt_info = {
.options = WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT | WDIOF_MAGICCLOSE, .options = WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT | WDIOF_MAGICCLOSE,
}; };
static const struct watchdog_info imx2_wdt_pretimeout_info = {
.identity = "imx2+ watchdog",
.options = WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT | WDIOF_MAGICCLOSE |
WDIOF_PRETIMEOUT,
};
static int imx2_wdt_restart(struct watchdog_device *wdog, unsigned long action, static int imx2_wdt_restart(struct watchdog_device *wdog, unsigned long action,
void *data) void *data)
{ {
...@@ -169,6 +181,35 @@ static int imx2_wdt_set_timeout(struct watchdog_device *wdog, ...@@ -169,6 +181,35 @@ static int imx2_wdt_set_timeout(struct watchdog_device *wdog,
return 0; return 0;
} }
static int imx2_wdt_set_pretimeout(struct watchdog_device *wdog,
unsigned int new_pretimeout)
{
struct imx2_wdt_device *wdev = watchdog_get_drvdata(wdog);
if (new_pretimeout >= IMX2_WDT_MAX_TIME)
return -EINVAL;
wdog->pretimeout = new_pretimeout;
regmap_update_bits(wdev->regmap, IMX2_WDT_WICR,
IMX2_WDT_WICR_WIE | IMX2_WDT_WICR_WICT,
IMX2_WDT_WICR_WIE | (new_pretimeout << 1));
return 0;
}
static irqreturn_t imx2_wdt_isr(int irq, void *wdog_arg)
{
struct watchdog_device *wdog = wdog_arg;
struct imx2_wdt_device *wdev = watchdog_get_drvdata(wdog);
regmap_write_bits(wdev->regmap, IMX2_WDT_WICR,
IMX2_WDT_WICR_WTIS, IMX2_WDT_WICR_WTIS);
watchdog_notify_pretimeout(wdog);
return IRQ_HANDLED;
}
static int imx2_wdt_start(struct watchdog_device *wdog) static int imx2_wdt_start(struct watchdog_device *wdog)
{ {
struct imx2_wdt_device *wdev = watchdog_get_drvdata(wdog); struct imx2_wdt_device *wdev = watchdog_get_drvdata(wdog);
...@@ -188,6 +229,7 @@ static const struct watchdog_ops imx2_wdt_ops = { ...@@ -188,6 +229,7 @@ static const struct watchdog_ops imx2_wdt_ops = {
.start = imx2_wdt_start, .start = imx2_wdt_start,
.ping = imx2_wdt_ping, .ping = imx2_wdt_ping,
.set_timeout = imx2_wdt_set_timeout, .set_timeout = imx2_wdt_set_timeout,
.set_pretimeout = imx2_wdt_set_pretimeout,
.restart = imx2_wdt_restart, .restart = imx2_wdt_restart,
}; };
...@@ -236,6 +278,12 @@ static int __init imx2_wdt_probe(struct platform_device *pdev) ...@@ -236,6 +278,12 @@ static int __init imx2_wdt_probe(struct platform_device *pdev)
wdog->max_hw_heartbeat_ms = IMX2_WDT_MAX_TIME * 1000; wdog->max_hw_heartbeat_ms = IMX2_WDT_MAX_TIME * 1000;
wdog->parent = &pdev->dev; wdog->parent = &pdev->dev;
ret = platform_get_irq(pdev, 0);
if (ret > 0)
if (!devm_request_irq(&pdev->dev, ret, imx2_wdt_isr, 0,
dev_name(&pdev->dev), wdog))
wdog->info = &imx2_wdt_pretimeout_info;
ret = clk_prepare_enable(wdev->clk); ret = clk_prepare_enable(wdev->clk);
if (ret) if (ret)
return ret; return ret;
......
...@@ -430,7 +430,7 @@ static struct watchdog_info kempld_wdt_info = { ...@@ -430,7 +430,7 @@ static struct watchdog_info kempld_wdt_info = {
WDIOF_PRETIMEOUT WDIOF_PRETIMEOUT
}; };
static struct watchdog_ops kempld_wdt_ops = { static const struct watchdog_ops kempld_wdt_ops = {
.owner = THIS_MODULE, .owner = THIS_MODULE,
.start = kempld_wdt_start, .start = kempld_wdt_start,
.stop = kempld_wdt_stop, .stop = kempld_wdt_stop,
......
...@@ -139,7 +139,6 @@ static int mt7621_wdt_probe(struct platform_device *pdev) ...@@ -139,7 +139,6 @@ static int mt7621_wdt_probe(struct platform_device *pdev)
if (!IS_ERR(mt7621_wdt_reset)) if (!IS_ERR(mt7621_wdt_reset))
reset_control_deassert(mt7621_wdt_reset); reset_control_deassert(mt7621_wdt_reset);
mt7621_wdt_dev.dev = &pdev->dev;
mt7621_wdt_dev.bootstatus = mt7621_wdt_bootcause(); mt7621_wdt_dev.bootstatus = mt7621_wdt_bootcause();
watchdog_init_timeout(&mt7621_wdt_dev, mt7621_wdt_dev.max_timeout, watchdog_init_timeout(&mt7621_wdt_dev, mt7621_wdt_dev.max_timeout,
......
...@@ -10,6 +10,7 @@ ...@@ -10,6 +10,7 @@
* 2 of the License, or (at your option) any later version. * 2 of the License, or (at your option) any later version.
*/ */
#include <linux/clk.h>
#include <linux/err.h> #include <linux/err.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/types.h> #include <linux/types.h>
...@@ -45,6 +46,7 @@ struct xwdt_device { ...@@ -45,6 +46,7 @@ struct xwdt_device {
u32 wdt_interval; u32 wdt_interval;
spinlock_t spinlock; spinlock_t spinlock;
struct watchdog_device xilinx_wdt_wdd; struct watchdog_device xilinx_wdt_wdd;
struct clk *clk;
}; };
static int xilinx_wdt_start(struct watchdog_device *wdd) static int xilinx_wdt_start(struct watchdog_device *wdd)
...@@ -195,16 +197,30 @@ static int xwdt_probe(struct platform_device *pdev) ...@@ -195,16 +197,30 @@ static int xwdt_probe(struct platform_device *pdev)
spin_lock_init(&xdev->spinlock); spin_lock_init(&xdev->spinlock);
watchdog_set_drvdata(xilinx_wdt_wdd, xdev); watchdog_set_drvdata(xilinx_wdt_wdd, xdev);
xdev->clk = devm_clk_get(&pdev->dev, NULL);
if (IS_ERR(xdev->clk)) {
if (PTR_ERR(xdev->clk) == -ENOENT)
xdev->clk = NULL;
else
return PTR_ERR(xdev->clk);
}
rc = clk_prepare_enable(xdev->clk);
if (rc) {
dev_err(&pdev->dev, "unable to enable clock\n");
return rc;
}
rc = xwdt_selftest(xdev); rc = xwdt_selftest(xdev);
if (rc == XWT_TIMER_FAILED) { if (rc == XWT_TIMER_FAILED) {
dev_err(&pdev->dev, "SelfTest routine error\n"); dev_err(&pdev->dev, "SelfTest routine error\n");
return rc; goto err_clk_disable;
} }
rc = watchdog_register_device(xilinx_wdt_wdd); rc = watchdog_register_device(xilinx_wdt_wdd);
if (rc) { if (rc) {
dev_err(&pdev->dev, "Cannot register watchdog (err=%d)\n", rc); dev_err(&pdev->dev, "Cannot register watchdog (err=%d)\n", rc);
return rc; goto err_clk_disable;
} }
dev_info(&pdev->dev, "Xilinx Watchdog Timer at %p with timeout %ds\n", dev_info(&pdev->dev, "Xilinx Watchdog Timer at %p with timeout %ds\n",
...@@ -213,6 +229,10 @@ static int xwdt_probe(struct platform_device *pdev) ...@@ -213,6 +229,10 @@ static int xwdt_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, xdev); platform_set_drvdata(pdev, xdev);
return 0; return 0;
err_clk_disable:
clk_disable_unprepare(xdev->clk);
return rc;
} }
static int xwdt_remove(struct platform_device *pdev) static int xwdt_remove(struct platform_device *pdev)
...@@ -220,6 +240,7 @@ static int xwdt_remove(struct platform_device *pdev) ...@@ -220,6 +240,7 @@ static int xwdt_remove(struct platform_device *pdev)
struct xwdt_device *xdev = platform_get_drvdata(pdev); struct xwdt_device *xdev = platform_get_drvdata(pdev);
watchdog_unregister_device(&xdev->xilinx_wdt_wdd); watchdog_unregister_device(&xdev->xilinx_wdt_wdd);
clk_disable_unprepare(xdev->clk);
return 0; return 0;
} }
......
/*
* Copyright (C) 2015-2016 Mentor Graphics
*
* 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/module.h>
#include <linux/printk.h>
#include <linux/watchdog.h>
#include "watchdog_pretimeout.h"
/**
* pretimeout_noop - No operation on watchdog pretimeout event
* @wdd - watchdog_device
*
* This function prints a message about pretimeout to kernel log.
*/
static void pretimeout_noop(struct watchdog_device *wdd)
{
pr_alert("watchdog%d: pretimeout event\n", wdd->id);
}
static struct watchdog_governor watchdog_gov_noop = {
.name = "noop",
.pretimeout = pretimeout_noop,
};
static int __init watchdog_gov_noop_register(void)
{
return watchdog_register_governor(&watchdog_gov_noop);
}
static void __exit watchdog_gov_noop_unregister(void)
{
watchdog_unregister_governor(&watchdog_gov_noop);
}
module_init(watchdog_gov_noop_register);
module_exit(watchdog_gov_noop_unregister);
MODULE_AUTHOR("Vladimir Zapolskiy <vladimir_zapolskiy@mentor.com>");
MODULE_DESCRIPTION("Panic watchdog pretimeout governor");
MODULE_LICENSE("GPL");
/*
* Copyright (C) 2015-2016 Mentor Graphics
*
* 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/kernel.h>
#include <linux/module.h>
#include <linux/watchdog.h>
#include "watchdog_pretimeout.h"
/**
* pretimeout_panic - Panic on watchdog pretimeout event
* @wdd - watchdog_device
*
* Panic, watchdog has not been fed till pretimeout event.
*/
static void pretimeout_panic(struct watchdog_device *wdd)
{
panic("watchdog pretimeout event\n");
}
static struct watchdog_governor watchdog_gov_panic = {
.name = "panic",
.pretimeout = pretimeout_panic,
};
static int __init watchdog_gov_panic_register(void)
{
return watchdog_register_governor(&watchdog_gov_panic);
}
static void __exit watchdog_gov_panic_unregister(void)
{
watchdog_unregister_governor(&watchdog_gov_panic);
}
module_init(watchdog_gov_panic_register);
module_exit(watchdog_gov_panic_unregister);
MODULE_AUTHOR("Vladimir Zapolskiy <vladimir_zapolskiy@mentor.com>");
MODULE_DESCRIPTION("Panic watchdog pretimeout governor");
MODULE_LICENSE("GPL");
...@@ -136,7 +136,7 @@ static struct watchdog_info rn5t618_wdt_info = { ...@@ -136,7 +136,7 @@ static struct watchdog_info rn5t618_wdt_info = {
.identity = DRIVER_NAME, .identity = DRIVER_NAME,
}; };
static struct watchdog_ops rn5t618_wdt_ops = { static const struct watchdog_ops rn5t618_wdt_ops = {
.owner = THIS_MODULE, .owner = THIS_MODULE,
.start = rn5t618_wdt_start, .start = rn5t618_wdt_start,
.stop = rn5t618_wdt_stop, .stop = rn5t618_wdt_stop,
......
...@@ -158,7 +158,6 @@ static int rt288x_wdt_probe(struct platform_device *pdev) ...@@ -158,7 +158,6 @@ static int rt288x_wdt_probe(struct platform_device *pdev)
rt288x_wdt_freq = clk_get_rate(rt288x_wdt_clk) / RALINK_WDT_PRESCALE; rt288x_wdt_freq = clk_get_rate(rt288x_wdt_clk) / RALINK_WDT_PRESCALE;
rt288x_wdt_dev.dev = &pdev->dev;
rt288x_wdt_dev.bootstatus = rt288x_wdt_bootcause(); rt288x_wdt_dev.bootstatus = rt288x_wdt_bootcause();
rt288x_wdt_dev.max_timeout = (0xfffful / rt288x_wdt_freq); rt288x_wdt_dev.max_timeout = (0xfffful / rt288x_wdt_freq);
rt288x_wdt_dev.parent = &pdev->dev; rt288x_wdt_dev.parent = &pdev->dev;
......
...@@ -72,10 +72,27 @@ static void softdog_fire(unsigned long data) ...@@ -72,10 +72,27 @@ static void softdog_fire(unsigned long data)
static struct timer_list softdog_ticktock = static struct timer_list softdog_ticktock =
TIMER_INITIALIZER(softdog_fire, 0, 0); TIMER_INITIALIZER(softdog_fire, 0, 0);
static struct watchdog_device softdog_dev;
static void softdog_pretimeout(unsigned long data)
{
watchdog_notify_pretimeout(&softdog_dev);
}
static struct timer_list softdog_preticktock =
TIMER_INITIALIZER(softdog_pretimeout, 0, 0);
static int softdog_ping(struct watchdog_device *w) static int softdog_ping(struct watchdog_device *w)
{ {
if (!mod_timer(&softdog_ticktock, jiffies + (w->timeout * HZ))) if (!mod_timer(&softdog_ticktock, jiffies + (w->timeout * HZ)))
__module_get(THIS_MODULE); __module_get(THIS_MODULE);
if (w->pretimeout)
mod_timer(&softdog_preticktock, jiffies +
(w->timeout - w->pretimeout) * HZ);
else
del_timer(&softdog_preticktock);
return 0; return 0;
} }
...@@ -84,15 +101,18 @@ static int softdog_stop(struct watchdog_device *w) ...@@ -84,15 +101,18 @@ static int softdog_stop(struct watchdog_device *w)
if (del_timer(&softdog_ticktock)) if (del_timer(&softdog_ticktock))
module_put(THIS_MODULE); module_put(THIS_MODULE);
del_timer(&softdog_preticktock);
return 0; return 0;
} }
static struct watchdog_info softdog_info = { static struct watchdog_info softdog_info = {
.identity = "Software Watchdog", .identity = "Software Watchdog",
.options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE, .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE |
WDIOF_PRETIMEOUT,
}; };
static struct watchdog_ops softdog_ops = { static const struct watchdog_ops softdog_ops = {
.owner = THIS_MODULE, .owner = THIS_MODULE,
.start = softdog_ping, .start = softdog_ping,
.stop = softdog_stop, .stop = softdog_stop,
......
...@@ -52,27 +52,6 @@ struct st_wdog { ...@@ -52,27 +52,6 @@ struct st_wdog {
bool warm_reset; bool warm_reset;
}; };
static struct st_wdog_syscfg stid127_syscfg = {
.reset_type_reg = 0x004,
.reset_type_mask = BIT(2),
.enable_reg = 0x000,
.enable_mask = BIT(2),
};
static struct st_wdog_syscfg stih415_syscfg = {
.reset_type_reg = 0x0B8,
.reset_type_mask = BIT(6),
.enable_reg = 0x0B4,
.enable_mask = BIT(7),
};
static struct st_wdog_syscfg stih416_syscfg = {
.reset_type_reg = 0x88C,
.reset_type_mask = BIT(6),
.enable_reg = 0x888,
.enable_mask = BIT(7),
};
static struct st_wdog_syscfg stih407_syscfg = { static struct st_wdog_syscfg stih407_syscfg = {
.enable_reg = 0x204, .enable_reg = 0x204,
.enable_mask = BIT(19), .enable_mask = BIT(19),
...@@ -83,18 +62,6 @@ static const struct of_device_id st_wdog_match[] = { ...@@ -83,18 +62,6 @@ static const struct of_device_id st_wdog_match[] = {
.compatible = "st,stih407-lpc", .compatible = "st,stih407-lpc",
.data = &stih407_syscfg, .data = &stih407_syscfg,
}, },
{
.compatible = "st,stih416-lpc",
.data = &stih416_syscfg,
},
{
.compatible = "st,stih415-lpc",
.data = &stih415_syscfg,
},
{
.compatible = "st,stid127-lpc",
.data = &stid127_syscfg,
},
{}, {},
}; };
MODULE_DEVICE_TABLE(of, st_wdog_match); MODULE_DEVICE_TABLE(of, st_wdog_match);
......
...@@ -178,7 +178,7 @@ static const struct watchdog_info tegra_wdt_info = { ...@@ -178,7 +178,7 @@ static const struct watchdog_info tegra_wdt_info = {
.identity = "Tegra Watchdog", .identity = "Tegra Watchdog",
}; };
static struct watchdog_ops tegra_wdt_ops = { static const struct watchdog_ops tegra_wdt_ops = {
.owner = THIS_MODULE, .owner = THIS_MODULE,
.start = tegra_wdt_start, .start = tegra_wdt_start,
.stop = tegra_wdt_stop, .stop = tegra_wdt_stop,
......
...@@ -112,7 +112,7 @@ static int __init txx9wdt_probe(struct platform_device *dev) ...@@ -112,7 +112,7 @@ static int __init txx9wdt_probe(struct platform_device *dev)
txx9_imclk = NULL; txx9_imclk = NULL;
goto exit; goto exit;
} }
ret = clk_enable(txx9_imclk); ret = clk_prepare_enable(txx9_imclk);
if (ret) { if (ret) {
clk_put(txx9_imclk); clk_put(txx9_imclk);
txx9_imclk = NULL; txx9_imclk = NULL;
...@@ -144,7 +144,7 @@ static int __init txx9wdt_probe(struct platform_device *dev) ...@@ -144,7 +144,7 @@ static int __init txx9wdt_probe(struct platform_device *dev)
return 0; return 0;
exit: exit:
if (txx9_imclk) { if (txx9_imclk) {
clk_disable(txx9_imclk); clk_disable_unprepare(txx9_imclk);
clk_put(txx9_imclk); clk_put(txx9_imclk);
} }
return ret; return ret;
...@@ -153,7 +153,7 @@ static int __init txx9wdt_probe(struct platform_device *dev) ...@@ -153,7 +153,7 @@ static int __init txx9wdt_probe(struct platform_device *dev)
static int __exit txx9wdt_remove(struct platform_device *dev) static int __exit txx9wdt_remove(struct platform_device *dev)
{ {
watchdog_unregister_device(&txx9wdt); watchdog_unregister_device(&txx9wdt);
clk_disable(txx9_imclk); clk_disable_unprepare(txx9_imclk);
clk_put(txx9_imclk); clk_put(txx9_imclk);
return 0; return 0;
} }
......
...@@ -302,7 +302,7 @@ static struct watchdog_info wdt_info = { ...@@ -302,7 +302,7 @@ static struct watchdog_info wdt_info = {
.identity = "W83627HF Watchdog", .identity = "W83627HF Watchdog",
}; };
static struct watchdog_ops wdt_ops = { static const struct watchdog_ops wdt_ops = {
.owner = THIS_MODULE, .owner = THIS_MODULE,
.start = wdt_start, .start = wdt_start,
.stop = wdt_stop, .stop = wdt_stop,
......
...@@ -349,7 +349,7 @@ int devm_watchdog_register_device(struct device *dev, ...@@ -349,7 +349,7 @@ int devm_watchdog_register_device(struct device *dev,
struct watchdog_device **rcwdd; struct watchdog_device **rcwdd;
int ret; int ret;
rcwdd = devres_alloc(devm_watchdog_unregister_device, sizeof(*wdd), rcwdd = devres_alloc(devm_watchdog_unregister_device, sizeof(*rcwdd),
GFP_KERNEL); GFP_KERNEL);
if (!rcwdd) if (!rcwdd)
return -ENOMEM; return -ENOMEM;
......
...@@ -49,6 +49,7 @@ ...@@ -49,6 +49,7 @@
#include <linux/uaccess.h> /* For copy_to_user/put_user/... */ #include <linux/uaccess.h> /* For copy_to_user/put_user/... */
#include "watchdog_core.h" #include "watchdog_core.h"
#include "watchdog_pretimeout.h"
/* /*
* struct watchdog_core_data - watchdog core internal data * struct watchdog_core_data - watchdog core internal data
...@@ -335,16 +336,45 @@ static int watchdog_set_timeout(struct watchdog_device *wdd, ...@@ -335,16 +336,45 @@ static int watchdog_set_timeout(struct watchdog_device *wdd,
if (watchdog_timeout_invalid(wdd, timeout)) if (watchdog_timeout_invalid(wdd, timeout))
return -EINVAL; return -EINVAL;
if (wdd->ops->set_timeout) if (wdd->ops->set_timeout) {
err = wdd->ops->set_timeout(wdd, timeout); err = wdd->ops->set_timeout(wdd, timeout);
else } else {
wdd->timeout = timeout; wdd->timeout = timeout;
/* Disable pretimeout if it doesn't fit the new timeout */
if (wdd->pretimeout >= wdd->timeout)
wdd->pretimeout = 0;
}
watchdog_update_worker(wdd); watchdog_update_worker(wdd);
return err; return err;
} }
/*
* watchdog_set_pretimeout: set the watchdog timer pretimeout
* @wdd: the watchdog device to set the timeout for
* @timeout: pretimeout to set in seconds
*/
static int watchdog_set_pretimeout(struct watchdog_device *wdd,
unsigned int timeout)
{
int err = 0;
if (!(wdd->info->options & WDIOF_PRETIMEOUT))
return -EOPNOTSUPP;
if (watchdog_pretimeout_invalid(wdd, timeout))
return -EINVAL;
if (wdd->ops->set_pretimeout)
err = wdd->ops->set_pretimeout(wdd, timeout);
else
wdd->pretimeout = timeout;
return err;
}
/* /*
* watchdog_get_timeleft: wrapper to get the time left before a reboot * watchdog_get_timeleft: wrapper to get the time left before a reboot
* @wdd: the watchdog device to get the remaining time from * @wdd: the watchdog device to get the remaining time from
...@@ -429,6 +459,15 @@ static ssize_t timeout_show(struct device *dev, struct device_attribute *attr, ...@@ -429,6 +459,15 @@ static ssize_t timeout_show(struct device *dev, struct device_attribute *attr,
} }
static DEVICE_ATTR_RO(timeout); static DEVICE_ATTR_RO(timeout);
static ssize_t pretimeout_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct watchdog_device *wdd = dev_get_drvdata(dev);
return sprintf(buf, "%u\n", wdd->pretimeout);
}
static DEVICE_ATTR_RO(pretimeout);
static ssize_t identity_show(struct device *dev, struct device_attribute *attr, static ssize_t identity_show(struct device *dev, struct device_attribute *attr,
char *buf) char *buf)
{ {
...@@ -450,6 +489,36 @@ static ssize_t state_show(struct device *dev, struct device_attribute *attr, ...@@ -450,6 +489,36 @@ static ssize_t state_show(struct device *dev, struct device_attribute *attr,
} }
static DEVICE_ATTR_RO(state); static DEVICE_ATTR_RO(state);
static ssize_t pretimeout_available_governors_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
return watchdog_pretimeout_available_governors_get(buf);
}
static DEVICE_ATTR_RO(pretimeout_available_governors);
static ssize_t pretimeout_governor_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
struct watchdog_device *wdd = dev_get_drvdata(dev);
return watchdog_pretimeout_governor_get(wdd, buf);
}
static ssize_t pretimeout_governor_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
struct watchdog_device *wdd = dev_get_drvdata(dev);
int ret = watchdog_pretimeout_governor_set(wdd, buf);
if (!ret)
ret = count;
return ret;
}
static DEVICE_ATTR_RW(pretimeout_governor);
static umode_t wdt_is_visible(struct kobject *kobj, struct attribute *attr, static umode_t wdt_is_visible(struct kobject *kobj, struct attribute *attr,
int n) int n)
{ {
...@@ -459,6 +528,14 @@ static umode_t wdt_is_visible(struct kobject *kobj, struct attribute *attr, ...@@ -459,6 +528,14 @@ static umode_t wdt_is_visible(struct kobject *kobj, struct attribute *attr,
if (attr == &dev_attr_timeleft.attr && !wdd->ops->get_timeleft) if (attr == &dev_attr_timeleft.attr && !wdd->ops->get_timeleft)
mode = 0; mode = 0;
else if (attr == &dev_attr_pretimeout.attr &&
!(wdd->info->options & WDIOF_PRETIMEOUT))
mode = 0;
else if ((attr == &dev_attr_pretimeout_governor.attr ||
attr == &dev_attr_pretimeout_available_governors.attr) &&
(!(wdd->info->options & WDIOF_PRETIMEOUT) ||
!IS_ENABLED(CONFIG_WATCHDOG_PRETIMEOUT_GOV)))
mode = 0;
return mode; return mode;
} }
...@@ -466,10 +543,13 @@ static struct attribute *wdt_attrs[] = { ...@@ -466,10 +543,13 @@ static struct attribute *wdt_attrs[] = {
&dev_attr_state.attr, &dev_attr_state.attr,
&dev_attr_identity.attr, &dev_attr_identity.attr,
&dev_attr_timeout.attr, &dev_attr_timeout.attr,
&dev_attr_pretimeout.attr,
&dev_attr_timeleft.attr, &dev_attr_timeleft.attr,
&dev_attr_bootstatus.attr, &dev_attr_bootstatus.attr,
&dev_attr_status.attr, &dev_attr_status.attr,
&dev_attr_nowayout.attr, &dev_attr_nowayout.attr,
&dev_attr_pretimeout_governor.attr,
&dev_attr_pretimeout_available_governors.attr,
NULL, NULL,
}; };
...@@ -646,6 +726,16 @@ static long watchdog_ioctl(struct file *file, unsigned int cmd, ...@@ -646,6 +726,16 @@ static long watchdog_ioctl(struct file *file, unsigned int cmd,
break; break;
err = put_user(val, p); err = put_user(val, p);
break; break;
case WDIOC_SETPRETIMEOUT:
if (get_user(val, p)) {
err = -EFAULT;
break;
}
err = watchdog_set_pretimeout(wdd, val);
break;
case WDIOC_GETPRETIMEOUT:
err = put_user(wdd->pretimeout, p);
break;
default: default:
err = -ENOTTY; err = -ENOTTY;
break; break;
...@@ -937,6 +1027,12 @@ int watchdog_dev_register(struct watchdog_device *wdd) ...@@ -937,6 +1027,12 @@ int watchdog_dev_register(struct watchdog_device *wdd)
return PTR_ERR(dev); return PTR_ERR(dev);
} }
ret = watchdog_register_pretimeout(wdd);
if (ret) {
device_destroy(&watchdog_class, devno);
watchdog_cdev_unregister(wdd);
}
return ret; return ret;
} }
...@@ -950,6 +1046,7 @@ int watchdog_dev_register(struct watchdog_device *wdd) ...@@ -950,6 +1046,7 @@ int watchdog_dev_register(struct watchdog_device *wdd)
void watchdog_dev_unregister(struct watchdog_device *wdd) void watchdog_dev_unregister(struct watchdog_device *wdd)
{ {
watchdog_unregister_pretimeout(wdd);
device_destroy(&watchdog_class, wdd->wd_data->cdev.dev); device_destroy(&watchdog_class, wdd->wd_data->cdev.dev);
watchdog_cdev_unregister(wdd); watchdog_cdev_unregister(wdd);
} }
......
/*
* Copyright (C) 2015-2016 Mentor Graphics
*
* 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/list.h>
#include <linux/slab.h>
#include <linux/spinlock.h>
#include <linux/string.h>
#include <linux/watchdog.h>
#include "watchdog_pretimeout.h"
/* Default watchdog pretimeout governor */
static struct watchdog_governor *default_gov;
/* The spinlock protects default_gov, wdd->gov and pretimeout_list */
static DEFINE_SPINLOCK(pretimeout_lock);
/* List of watchdog devices, which can generate a pretimeout event */
static LIST_HEAD(pretimeout_list);
struct watchdog_pretimeout {
struct watchdog_device *wdd;
struct list_head entry;
};
/* The mutex protects governor list and serializes external interfaces */
static DEFINE_MUTEX(governor_lock);
/* List of the registered watchdog pretimeout governors */
static LIST_HEAD(governor_list);
struct governor_priv {
struct watchdog_governor *gov;
struct list_head entry;
};
static struct governor_priv *find_governor_by_name(const char *gov_name)
{
struct governor_priv *priv;
list_for_each_entry(priv, &governor_list, entry)
if (sysfs_streq(gov_name, priv->gov->name))
return priv;
return NULL;
}
int watchdog_pretimeout_available_governors_get(char *buf)
{
struct governor_priv *priv;
int count = 0;
mutex_lock(&governor_lock);
list_for_each_entry(priv, &governor_list, entry)
count += sprintf(buf + count, "%s\n", priv->gov->name);
mutex_unlock(&governor_lock);
return count;
}
int watchdog_pretimeout_governor_get(struct watchdog_device *wdd, char *buf)
{
int count = 0;
spin_lock_irq(&pretimeout_lock);
if (wdd->gov)
count = sprintf(buf, "%s\n", wdd->gov->name);
spin_unlock_irq(&pretimeout_lock);
return count;
}
int watchdog_pretimeout_governor_set(struct watchdog_device *wdd,
const char *buf)
{
struct governor_priv *priv;
mutex_lock(&governor_lock);
priv = find_governor_by_name(buf);
if (!priv) {
mutex_unlock(&governor_lock);
return -EINVAL;
}
spin_lock_irq(&pretimeout_lock);
wdd->gov = priv->gov;
spin_unlock_irq(&pretimeout_lock);
mutex_unlock(&governor_lock);
return 0;
}
void watchdog_notify_pretimeout(struct watchdog_device *wdd)
{
unsigned long flags;
spin_lock_irqsave(&pretimeout_lock, flags);
if (!wdd->gov) {
spin_unlock_irqrestore(&pretimeout_lock, flags);
return;
}
wdd->gov->pretimeout(wdd);
spin_unlock_irqrestore(&pretimeout_lock, flags);
}
EXPORT_SYMBOL_GPL(watchdog_notify_pretimeout);
int watchdog_register_governor(struct watchdog_governor *gov)
{
struct watchdog_pretimeout *p;
struct governor_priv *priv;
priv = kzalloc(sizeof(*priv), GFP_KERNEL);
if (!priv)
return -ENOMEM;
mutex_lock(&governor_lock);
if (find_governor_by_name(gov->name)) {
mutex_unlock(&governor_lock);
kfree(priv);
return -EBUSY;
}
priv->gov = gov;
list_add(&priv->entry, &governor_list);
if (!strncmp(gov->name, WATCHDOG_PRETIMEOUT_DEFAULT_GOV,
WATCHDOG_GOV_NAME_MAXLEN)) {
spin_lock_irq(&pretimeout_lock);
default_gov = gov;
list_for_each_entry(p, &pretimeout_list, entry)
if (!p->wdd->gov)
p->wdd->gov = default_gov;
spin_unlock_irq(&pretimeout_lock);
}
mutex_unlock(&governor_lock);
return 0;
}
EXPORT_SYMBOL(watchdog_register_governor);
void watchdog_unregister_governor(struct watchdog_governor *gov)
{
struct watchdog_pretimeout *p;
struct governor_priv *priv, *t;
mutex_lock(&governor_lock);
list_for_each_entry_safe(priv, t, &governor_list, entry) {
if (priv->gov == gov) {
list_del(&priv->entry);
kfree(priv);
break;
}
}
spin_lock_irq(&pretimeout_lock);
list_for_each_entry(p, &pretimeout_list, entry)
if (p->wdd->gov == gov)
p->wdd->gov = default_gov;
spin_unlock_irq(&pretimeout_lock);
mutex_unlock(&governor_lock);
}
EXPORT_SYMBOL(watchdog_unregister_governor);
int watchdog_register_pretimeout(struct watchdog_device *wdd)
{
struct watchdog_pretimeout *p;
if (!(wdd->info->options & WDIOF_PRETIMEOUT))
return 0;
p = kzalloc(sizeof(*p), GFP_KERNEL);
if (!p)
return -ENOMEM;
spin_lock_irq(&pretimeout_lock);
list_add(&p->entry, &pretimeout_list);
p->wdd = wdd;
wdd->gov = default_gov;
spin_unlock_irq(&pretimeout_lock);
return 0;
}
void watchdog_unregister_pretimeout(struct watchdog_device *wdd)
{
struct watchdog_pretimeout *p, *t;
if (!(wdd->info->options & WDIOF_PRETIMEOUT))
return;
spin_lock_irq(&pretimeout_lock);
wdd->gov = NULL;
list_for_each_entry_safe(p, t, &pretimeout_list, entry) {
if (p->wdd == wdd) {
list_del(&p->entry);
break;
}
}
spin_unlock_irq(&pretimeout_lock);
kfree(p);
}
#ifndef __WATCHDOG_PRETIMEOUT_H
#define __WATCHDOG_PRETIMEOUT_H
#define WATCHDOG_GOV_NAME_MAXLEN 20
struct watchdog_device;
struct watchdog_governor {
const char name[WATCHDOG_GOV_NAME_MAXLEN];
void (*pretimeout)(struct watchdog_device *wdd);
};
#if IS_ENABLED(CONFIG_WATCHDOG_PRETIMEOUT_GOV)
/* Interfaces to watchdog pretimeout governors */
int watchdog_register_governor(struct watchdog_governor *gov);
void watchdog_unregister_governor(struct watchdog_governor *gov);
/* Interfaces to watchdog_dev.c */
int watchdog_register_pretimeout(struct watchdog_device *wdd);
void watchdog_unregister_pretimeout(struct watchdog_device *wdd);
int watchdog_pretimeout_available_governors_get(char *buf);
int watchdog_pretimeout_governor_get(struct watchdog_device *wdd, char *buf);
int watchdog_pretimeout_governor_set(struct watchdog_device *wdd,
const char *buf);
#if IS_ENABLED(CONFIG_WATCHDOG_PRETIMEOUT_DEFAULT_GOV_NOOP)
#define WATCHDOG_PRETIMEOUT_DEFAULT_GOV "noop"
#elif IS_ENABLED(CONFIG_WATCHDOG_PRETIMEOUT_DEFAULT_GOV_PANIC)
#define WATCHDOG_PRETIMEOUT_DEFAULT_GOV "panic"
#endif
#else
static inline int watchdog_register_pretimeout(struct watchdog_device *wdd)
{
return 0;
}
static inline void watchdog_unregister_pretimeout(struct watchdog_device *wdd)
{
}
static inline int watchdog_pretimeout_available_governors_get(char *buf)
{
return -EINVAL;
}
static inline int watchdog_pretimeout_governor_get(struct watchdog_device *wdd,
char *buf)
{
return -EINVAL;
}
static inline int watchdog_pretimeout_governor_set(struct watchdog_device *wdd,
const char *buf)
{
return -EINVAL;
}
#endif
#endif
...@@ -18,7 +18,10 @@ ...@@ -18,7 +18,10 @@
* GNU General Public License for more details. * GNU General Public License for more details.
*/ */
#include <linux/delay.h>
#include <linux/i2c.h> #include <linux/i2c.h>
#include <linux/ihex.h>
#include <linux/firmware.h>
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/slab.h> #include <linux/slab.h>
...@@ -36,6 +39,8 @@ ...@@ -36,6 +39,8 @@
#define ZIIRAVE_STATE_OFF 0x1 #define ZIIRAVE_STATE_OFF 0x1
#define ZIIRAVE_STATE_ON 0x2 #define ZIIRAVE_STATE_ON 0x2
#define ZIIRAVE_FW_NAME "ziirave_wdt.fw"
static char *ziirave_reasons[] = {"power cycle", "hw watchdog", NULL, NULL, static char *ziirave_reasons[] = {"power cycle", "hw watchdog", NULL, NULL,
"host request", NULL, "illegal configuration", "host request", NULL, "illegal configuration",
"illegal instruction", "illegal trap", "illegal instruction", "illegal trap",
...@@ -50,12 +55,35 @@ static char *ziirave_reasons[] = {"power cycle", "hw watchdog", NULL, NULL, ...@@ -50,12 +55,35 @@ static char *ziirave_reasons[] = {"power cycle", "hw watchdog", NULL, NULL,
#define ZIIRAVE_WDT_PING 0x9 #define ZIIRAVE_WDT_PING 0x9
#define ZIIRAVE_WDT_RESET_DURATION 0xa #define ZIIRAVE_WDT_RESET_DURATION 0xa
#define ZIIRAVE_FIRM_PKT_TOTAL_SIZE 20
#define ZIIRAVE_FIRM_PKT_DATA_SIZE 16
#define ZIIRAVE_FIRM_FLASH_MEMORY_START 0x1600
#define ZIIRAVE_FIRM_FLASH_MEMORY_END 0x2bbf
/* Received and ready for next Download packet. */
#define ZIIRAVE_FIRM_DOWNLOAD_ACK 1
/* Currently writing to flash. Retry Download status in a moment! */
#define ZIIRAVE_FIRM_DOWNLOAD_BUSY 2
/* Wait for ACK timeout in ms */
#define ZIIRAVE_FIRM_WAIT_FOR_ACK_TIMEOUT 50
/* Firmware commands */
#define ZIIRAVE_CMD_DOWNLOAD_START 0x10
#define ZIIRAVE_CMD_DOWNLOAD_END 0x11
#define ZIIRAVE_CMD_DOWNLOAD_SET_READ_ADDR 0x12
#define ZIIRAVE_CMD_DOWNLOAD_READ_BYTE 0x13
#define ZIIRAVE_CMD_RESET_PROCESSOR 0x0b
#define ZIIRAVE_CMD_JUMP_TO_BOOTLOADER 0x0c
#define ZIIRAVE_CMD_DOWNLOAD_PACKET 0x0e
struct ziirave_wdt_rev { struct ziirave_wdt_rev {
unsigned char major; unsigned char major;
unsigned char minor; unsigned char minor;
}; };
struct ziirave_wdt_data { struct ziirave_wdt_data {
struct mutex sysfs_mutex;
struct watchdog_device wdd; struct watchdog_device wdd;
struct ziirave_wdt_rev bootloader_rev; struct ziirave_wdt_rev bootloader_rev;
struct ziirave_wdt_rev firmware_rev; struct ziirave_wdt_rev firmware_rev;
...@@ -146,6 +174,293 @@ static unsigned int ziirave_wdt_get_timeleft(struct watchdog_device *wdd) ...@@ -146,6 +174,293 @@ static unsigned int ziirave_wdt_get_timeleft(struct watchdog_device *wdd)
return ret; return ret;
} }
static int ziirave_firm_wait_for_ack(struct watchdog_device *wdd)
{
struct i2c_client *client = to_i2c_client(wdd->parent);
int ret;
unsigned long timeout;
timeout = jiffies + msecs_to_jiffies(ZIIRAVE_FIRM_WAIT_FOR_ACK_TIMEOUT);
do {
if (time_after(jiffies, timeout))
return -ETIMEDOUT;
usleep_range(5000, 10000);
ret = i2c_smbus_read_byte(client);
if (ret < 0) {
dev_err(&client->dev, "Failed to read byte\n");
return ret;
}
} while (ret == ZIIRAVE_FIRM_DOWNLOAD_BUSY);
return ret == ZIIRAVE_FIRM_DOWNLOAD_ACK ? 0 : -EIO;
}
static int ziirave_firm_set_read_addr(struct watchdog_device *wdd, u16 addr)
{
struct i2c_client *client = to_i2c_client(wdd->parent);
u8 address[2];
address[0] = addr & 0xff;
address[1] = (addr >> 8) & 0xff;
return i2c_smbus_write_block_data(client,
ZIIRAVE_CMD_DOWNLOAD_SET_READ_ADDR,
ARRAY_SIZE(address), address);
}
static int ziirave_firm_write_block_data(struct watchdog_device *wdd,
u8 command, u8 length, const u8 *data,
bool wait_for_ack)
{
struct i2c_client *client = to_i2c_client(wdd->parent);
int ret;
ret = i2c_smbus_write_block_data(client, command, length, data);
if (ret) {
dev_err(&client->dev,
"Failed to send command 0x%02x: %d\n", command, ret);
return ret;
}
if (wait_for_ack)
ret = ziirave_firm_wait_for_ack(wdd);
return ret;
}
static int ziirave_firm_write_byte(struct watchdog_device *wdd, u8 command,
u8 byte, bool wait_for_ack)
{
return ziirave_firm_write_block_data(wdd, command, 1, &byte,
wait_for_ack);
}
/*
* ziirave_firm_write_pkt() - Build and write a firmware packet
*
* A packet to send to the firmware is composed by following bytes:
* Length | Addr0 | Addr1 | Data0 .. Data15 | Checksum |
* Where,
* Length: A data byte containing the length of the data.
* Addr0: Low byte of the address.
* Addr1: High byte of the address.
* Data0 .. Data15: Array of 16 bytes of data.
* Checksum: Checksum byte to verify data integrity.
*/
static int ziirave_firm_write_pkt(struct watchdog_device *wdd,
const struct ihex_binrec *rec)
{
struct i2c_client *client = to_i2c_client(wdd->parent);
u8 i, checksum = 0, packet[ZIIRAVE_FIRM_PKT_TOTAL_SIZE];
int ret;
u16 addr;
memset(packet, 0, ARRAY_SIZE(packet));
/* Packet length */
packet[0] = (u8)be16_to_cpu(rec->len);
/* Packet address */
addr = (be32_to_cpu(rec->addr) & 0xffff) >> 1;
packet[1] = addr & 0xff;
packet[2] = (addr & 0xff00) >> 8;
/* Packet data */
if (be16_to_cpu(rec->len) > ZIIRAVE_FIRM_PKT_DATA_SIZE)
return -EMSGSIZE;
memcpy(packet + 3, rec->data, be16_to_cpu(rec->len));
/* Packet checksum */
for (i = 0; i < ZIIRAVE_FIRM_PKT_TOTAL_SIZE - 1; i++)
checksum += packet[i];
packet[ZIIRAVE_FIRM_PKT_TOTAL_SIZE - 1] = checksum;
ret = ziirave_firm_write_block_data(wdd, ZIIRAVE_CMD_DOWNLOAD_PACKET,
ARRAY_SIZE(packet), packet, true);
if (ret)
dev_err(&client->dev,
"Failed to write firmware packet at address 0x%04x: %d\n",
addr, ret);
return ret;
}
static int ziirave_firm_verify(struct watchdog_device *wdd,
const struct firmware *fw)
{
struct i2c_client *client = to_i2c_client(wdd->parent);
const struct ihex_binrec *rec;
int i, ret;
u8 data[ZIIRAVE_FIRM_PKT_DATA_SIZE];
u16 addr;
for (rec = (void *)fw->data; rec; rec = ihex_next_binrec(rec)) {
/* Zero length marks end of records */
if (!be16_to_cpu(rec->len))
break;
addr = (be32_to_cpu(rec->addr) & 0xffff) >> 1;
if (addr < ZIIRAVE_FIRM_FLASH_MEMORY_START ||
addr > ZIIRAVE_FIRM_FLASH_MEMORY_END)
continue;
ret = ziirave_firm_set_read_addr(wdd, addr);
if (ret) {
dev_err(&client->dev,
"Failed to send SET_READ_ADDR command: %d\n",
ret);
return ret;
}
for (i = 0; i < ARRAY_SIZE(data); i++) {
ret = i2c_smbus_read_byte_data(client,
ZIIRAVE_CMD_DOWNLOAD_READ_BYTE);
if (ret < 0) {
dev_err(&client->dev,
"Failed to READ DATA: %d\n", ret);
return ret;
}
data[i] = ret;
}
if (memcmp(data, rec->data, be16_to_cpu(rec->len))) {
dev_err(&client->dev,
"Firmware mismatch at address 0x%04x\n", addr);
return -EINVAL;
}
}
return 0;
}
static int ziirave_firm_upload(struct watchdog_device *wdd,
const struct firmware *fw)
{
struct i2c_client *client = to_i2c_client(wdd->parent);
int ret, words_till_page_break;
const struct ihex_binrec *rec;
struct ihex_binrec *rec_new;
ret = ziirave_firm_write_byte(wdd, ZIIRAVE_CMD_JUMP_TO_BOOTLOADER, 1,
false);
if (ret)
return ret;
msleep(500);
ret = ziirave_firm_write_byte(wdd, ZIIRAVE_CMD_DOWNLOAD_START, 1, true);
if (ret)
return ret;
msleep(500);
for (rec = (void *)fw->data; rec; rec = ihex_next_binrec(rec)) {
/* Zero length marks end of records */
if (!be16_to_cpu(rec->len))
break;
/* Check max data size */
if (be16_to_cpu(rec->len) > ZIIRAVE_FIRM_PKT_DATA_SIZE) {
dev_err(&client->dev, "Firmware packet too long (%d)\n",
be16_to_cpu(rec->len));
return -EMSGSIZE;
}
/* Calculate words till page break */
words_till_page_break = (64 - ((be32_to_cpu(rec->addr) >> 1) &
0x3f));
if ((be16_to_cpu(rec->len) >> 1) > words_till_page_break) {
/*
* Data in passes page boundary, so we need to split in
* two blocks of data. Create a packet with the first
* block of data.
*/
rec_new = kzalloc(sizeof(struct ihex_binrec) +
(words_till_page_break << 1),
GFP_KERNEL);
if (!rec_new)
return -ENOMEM;
rec_new->len = cpu_to_be16(words_till_page_break << 1);
rec_new->addr = rec->addr;
memcpy(rec_new->data, rec->data,
be16_to_cpu(rec_new->len));
ret = ziirave_firm_write_pkt(wdd, rec_new);
kfree(rec_new);
if (ret)
return ret;
/* Create a packet with the second block of data */
rec_new = kzalloc(sizeof(struct ihex_binrec) +
be16_to_cpu(rec->len) -
(words_till_page_break << 1),
GFP_KERNEL);
if (!rec_new)
return -ENOMEM;
/* Remaining bytes */
rec_new->len = rec->len -
cpu_to_be16(words_till_page_break << 1);
rec_new->addr = cpu_to_be32(be32_to_cpu(rec->addr) +
(words_till_page_break << 1));
memcpy(rec_new->data,
rec->data + (words_till_page_break << 1),
be16_to_cpu(rec_new->len));
ret = ziirave_firm_write_pkt(wdd, rec_new);
kfree(rec_new);
if (ret)
return ret;
} else {
ret = ziirave_firm_write_pkt(wdd, rec);
if (ret)
return ret;
}
}
/* For end of download, the length field will be set to 0 */
rec_new = kzalloc(sizeof(struct ihex_binrec) + 1, GFP_KERNEL);
if (!rec_new)
return -ENOMEM;
ret = ziirave_firm_write_pkt(wdd, rec_new);
kfree(rec_new);
if (ret) {
dev_err(&client->dev, "Failed to send EMPTY packet: %d\n", ret);
return ret;
}
/* This sleep seems to be required */
msleep(20);
/* Start firmware verification */
ret = ziirave_firm_verify(wdd, fw);
if (ret) {
dev_err(&client->dev,
"Failed to verify firmware: %d\n", ret);
return ret;
}
/* End download operation */
ret = ziirave_firm_write_byte(wdd, ZIIRAVE_CMD_DOWNLOAD_END, 1, false);
if (ret)
return ret;
/* Reset the processor */
ret = ziirave_firm_write_byte(wdd, ZIIRAVE_CMD_RESET_PROCESSOR, 1,
false);
if (ret)
return ret;
msleep(500);
return 0;
}
static const struct watchdog_info ziirave_wdt_info = { static const struct watchdog_info ziirave_wdt_info = {
.options = WDIOF_SETTIMEOUT | WDIOF_MAGICCLOSE | WDIOF_KEEPALIVEPING, .options = WDIOF_SETTIMEOUT | WDIOF_MAGICCLOSE | WDIOF_KEEPALIVEPING,
.identity = "Zodiac RAVE Watchdog", .identity = "Zodiac RAVE Watchdog",
...@@ -166,9 +481,18 @@ static ssize_t ziirave_wdt_sysfs_show_firm(struct device *dev, ...@@ -166,9 +481,18 @@ static ssize_t ziirave_wdt_sysfs_show_firm(struct device *dev,
{ {
struct i2c_client *client = to_i2c_client(dev->parent); struct i2c_client *client = to_i2c_client(dev->parent);
struct ziirave_wdt_data *w_priv = i2c_get_clientdata(client); struct ziirave_wdt_data *w_priv = i2c_get_clientdata(client);
int ret;
ret = mutex_lock_interruptible(&w_priv->sysfs_mutex);
if (ret)
return ret;
return sprintf(buf, "02.%02u.%02u", w_priv->firmware_rev.major, ret = sprintf(buf, "02.%02u.%02u", w_priv->firmware_rev.major,
w_priv->firmware_rev.minor); w_priv->firmware_rev.minor);
mutex_unlock(&w_priv->sysfs_mutex);
return ret;
} }
static DEVICE_ATTR(firmware_version, S_IRUGO, ziirave_wdt_sysfs_show_firm, static DEVICE_ATTR(firmware_version, S_IRUGO, ziirave_wdt_sysfs_show_firm,
...@@ -180,9 +504,18 @@ static ssize_t ziirave_wdt_sysfs_show_boot(struct device *dev, ...@@ -180,9 +504,18 @@ static ssize_t ziirave_wdt_sysfs_show_boot(struct device *dev,
{ {
struct i2c_client *client = to_i2c_client(dev->parent); struct i2c_client *client = to_i2c_client(dev->parent);
struct ziirave_wdt_data *w_priv = i2c_get_clientdata(client); struct ziirave_wdt_data *w_priv = i2c_get_clientdata(client);
int ret;
ret = mutex_lock_interruptible(&w_priv->sysfs_mutex);
if (ret)
return ret;
return sprintf(buf, "01.%02u.%02u", w_priv->bootloader_rev.major, ret = sprintf(buf, "01.%02u.%02u", w_priv->bootloader_rev.major,
w_priv->bootloader_rev.minor); w_priv->bootloader_rev.minor);
mutex_unlock(&w_priv->sysfs_mutex);
return ret;
} }
static DEVICE_ATTR(bootloader_version, S_IRUGO, ziirave_wdt_sysfs_show_boot, static DEVICE_ATTR(bootloader_version, S_IRUGO, ziirave_wdt_sysfs_show_boot,
...@@ -194,17 +527,81 @@ static ssize_t ziirave_wdt_sysfs_show_reason(struct device *dev, ...@@ -194,17 +527,81 @@ static ssize_t ziirave_wdt_sysfs_show_reason(struct device *dev,
{ {
struct i2c_client *client = to_i2c_client(dev->parent); struct i2c_client *client = to_i2c_client(dev->parent);
struct ziirave_wdt_data *w_priv = i2c_get_clientdata(client); struct ziirave_wdt_data *w_priv = i2c_get_clientdata(client);
int ret;
ret = mutex_lock_interruptible(&w_priv->sysfs_mutex);
if (ret)
return ret;
ret = sprintf(buf, "%s", ziirave_reasons[w_priv->reset_reason]);
return sprintf(buf, "%s", ziirave_reasons[w_priv->reset_reason]); mutex_unlock(&w_priv->sysfs_mutex);
return ret;
} }
static DEVICE_ATTR(reset_reason, S_IRUGO, ziirave_wdt_sysfs_show_reason, static DEVICE_ATTR(reset_reason, S_IRUGO, ziirave_wdt_sysfs_show_reason,
NULL); NULL);
static ssize_t ziirave_wdt_sysfs_store_firm(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
struct i2c_client *client = to_i2c_client(dev->parent);
struct ziirave_wdt_data *w_priv = i2c_get_clientdata(client);
const struct firmware *fw;
int err;
err = request_ihex_firmware(&fw, ZIIRAVE_FW_NAME, dev);
if (err) {
dev_err(&client->dev, "Failed to request ihex firmware\n");
return err;
}
err = mutex_lock_interruptible(&w_priv->sysfs_mutex);
if (err)
goto release_firmware;
err = ziirave_firm_upload(&w_priv->wdd, fw);
if (err) {
dev_err(&client->dev, "The firmware update failed: %d\n", err);
goto unlock_mutex;
}
/* Update firmware version */
err = ziirave_wdt_revision(client, &w_priv->firmware_rev,
ZIIRAVE_WDT_FIRM_VER_MAJOR);
if (err) {
dev_err(&client->dev, "Failed to read firmware version: %d\n",
err);
goto unlock_mutex;
}
dev_info(&client->dev, "Firmware updated to version 02.%02u.%02u\n",
w_priv->firmware_rev.major, w_priv->firmware_rev.minor);
/* Restore the watchdog timeout */
err = ziirave_wdt_set_timeout(&w_priv->wdd, w_priv->wdd.timeout);
if (err)
dev_err(&client->dev, "Failed to set timeout: %d\n", err);
unlock_mutex:
mutex_unlock(&w_priv->sysfs_mutex);
release_firmware:
release_firmware(fw);
return err ? err : count;
}
static DEVICE_ATTR(update_firmware, S_IWUSR, NULL,
ziirave_wdt_sysfs_store_firm);
static struct attribute *ziirave_wdt_attrs[] = { static struct attribute *ziirave_wdt_attrs[] = {
&dev_attr_firmware_version.attr, &dev_attr_firmware_version.attr,
&dev_attr_bootloader_version.attr, &dev_attr_bootloader_version.attr,
&dev_attr_reset_reason.attr, &dev_attr_reset_reason.attr,
&dev_attr_update_firmware.attr,
NULL NULL
}; };
ATTRIBUTE_GROUPS(ziirave_wdt); ATTRIBUTE_GROUPS(ziirave_wdt);
...@@ -252,6 +649,8 @@ static int ziirave_wdt_probe(struct i2c_client *client, ...@@ -252,6 +649,8 @@ static int ziirave_wdt_probe(struct i2c_client *client,
if (!w_priv) if (!w_priv)
return -ENOMEM; return -ENOMEM;
mutex_init(&w_priv->sysfs_mutex);
w_priv->wdd.info = &ziirave_wdt_info; w_priv->wdd.info = &ziirave_wdt_info;
w_priv->wdd.ops = &ziirave_wdt_ops; w_priv->wdd.ops = &ziirave_wdt_ops;
w_priv->wdd.min_timeout = ZIIRAVE_TIMEOUT_MIN; w_priv->wdd.min_timeout = ZIIRAVE_TIMEOUT_MIN;
......
...@@ -1209,6 +1209,8 @@ COMPATIBLE_IOCTL(WDIOC_SETOPTIONS) ...@@ -1209,6 +1209,8 @@ COMPATIBLE_IOCTL(WDIOC_SETOPTIONS)
COMPATIBLE_IOCTL(WDIOC_KEEPALIVE) COMPATIBLE_IOCTL(WDIOC_KEEPALIVE)
COMPATIBLE_IOCTL(WDIOC_SETTIMEOUT) COMPATIBLE_IOCTL(WDIOC_SETTIMEOUT)
COMPATIBLE_IOCTL(WDIOC_GETTIMEOUT) COMPATIBLE_IOCTL(WDIOC_GETTIMEOUT)
COMPATIBLE_IOCTL(WDIOC_SETPRETIMEOUT)
COMPATIBLE_IOCTL(WDIOC_GETPRETIMEOUT)
/* Big R */ /* Big R */
COMPATIBLE_IOCTL(RNDGETENTCNT) COMPATIBLE_IOCTL(RNDGETENTCNT)
COMPATIBLE_IOCTL(RNDADDTOENTCNT) COMPATIBLE_IOCTL(RNDADDTOENTCNT)
......
...@@ -19,6 +19,7 @@ ...@@ -19,6 +19,7 @@
struct watchdog_ops; struct watchdog_ops;
struct watchdog_device; struct watchdog_device;
struct watchdog_core_data; struct watchdog_core_data;
struct watchdog_governor;
/** struct watchdog_ops - The watchdog-devices operations /** struct watchdog_ops - The watchdog-devices operations
* *
...@@ -28,6 +29,7 @@ struct watchdog_core_data; ...@@ -28,6 +29,7 @@ struct watchdog_core_data;
* @ping: The routine that sends a keepalive ping to the watchdog device. * @ping: The routine that sends a keepalive ping to the watchdog device.
* @status: The routine that shows the status of the watchdog device. * @status: The routine that shows the status of the watchdog device.
* @set_timeout:The routine for setting the watchdog devices timeout value (in seconds). * @set_timeout:The routine for setting the watchdog devices timeout value (in seconds).
* @set_pretimeout:The routine for setting the watchdog devices pretimeout.
* @get_timeleft:The routine that gets the time left before a reset (in seconds). * @get_timeleft:The routine that gets the time left before a reset (in seconds).
* @restart: The routine for restarting the machine. * @restart: The routine for restarting the machine.
* @ioctl: The routines that handles extra ioctl calls. * @ioctl: The routines that handles extra ioctl calls.
...@@ -46,6 +48,7 @@ struct watchdog_ops { ...@@ -46,6 +48,7 @@ struct watchdog_ops {
int (*ping)(struct watchdog_device *); int (*ping)(struct watchdog_device *);
unsigned int (*status)(struct watchdog_device *); unsigned int (*status)(struct watchdog_device *);
int (*set_timeout)(struct watchdog_device *, unsigned int); int (*set_timeout)(struct watchdog_device *, unsigned int);
int (*set_pretimeout)(struct watchdog_device *, unsigned int);
unsigned int (*get_timeleft)(struct watchdog_device *); unsigned int (*get_timeleft)(struct watchdog_device *);
int (*restart)(struct watchdog_device *, unsigned long, void *); int (*restart)(struct watchdog_device *, unsigned long, void *);
long (*ioctl)(struct watchdog_device *, unsigned int, unsigned long); long (*ioctl)(struct watchdog_device *, unsigned int, unsigned long);
...@@ -59,8 +62,10 @@ struct watchdog_ops { ...@@ -59,8 +62,10 @@ struct watchdog_ops {
* watchdog device. * watchdog device.
* @info: Pointer to a watchdog_info structure. * @info: Pointer to a watchdog_info structure.
* @ops: Pointer to the list of watchdog operations. * @ops: Pointer to the list of watchdog operations.
* @gov: Pointer to watchdog pretimeout governor.
* @bootstatus: Status of the watchdog device at boot. * @bootstatus: Status of the watchdog device at boot.
* @timeout: The watchdog devices timeout value (in seconds). * @timeout: The watchdog devices timeout value (in seconds).
* @pretimeout: The watchdog devices pre_timeout value.
* @min_timeout:The watchdog devices minimum timeout value (in seconds). * @min_timeout:The watchdog devices minimum timeout value (in seconds).
* @max_timeout:The watchdog devices maximum timeout value (in seconds) * @max_timeout:The watchdog devices maximum timeout value (in seconds)
* as configurable from user space. Only relevant if * as configurable from user space. Only relevant if
...@@ -94,8 +99,10 @@ struct watchdog_device { ...@@ -94,8 +99,10 @@ struct watchdog_device {
const struct attribute_group **groups; const struct attribute_group **groups;
const struct watchdog_info *info; const struct watchdog_info *info;
const struct watchdog_ops *ops; const struct watchdog_ops *ops;
const struct watchdog_governor *gov;
unsigned int bootstatus; unsigned int bootstatus;
unsigned int timeout; unsigned int timeout;
unsigned int pretimeout;
unsigned int min_timeout; unsigned int min_timeout;
unsigned int max_timeout; unsigned int max_timeout;
unsigned int min_hw_heartbeat_ms; unsigned int min_hw_heartbeat_ms;
...@@ -163,6 +170,13 @@ static inline bool watchdog_timeout_invalid(struct watchdog_device *wdd, unsigne ...@@ -163,6 +170,13 @@ static inline bool watchdog_timeout_invalid(struct watchdog_device *wdd, unsigne
t > wdd->max_timeout); t > wdd->max_timeout);
} }
/* Use the following function to check if a pretimeout value is invalid */
static inline bool watchdog_pretimeout_invalid(struct watchdog_device *wdd,
unsigned int t)
{
return t && wdd->timeout && t >= wdd->timeout;
}
/* Use the following functions to manipulate watchdog driver specific data */ /* Use the following functions to manipulate watchdog driver specific data */
static inline void watchdog_set_drvdata(struct watchdog_device *wdd, void *data) static inline void watchdog_set_drvdata(struct watchdog_device *wdd, void *data)
{ {
...@@ -174,6 +188,16 @@ static inline void *watchdog_get_drvdata(struct watchdog_device *wdd) ...@@ -174,6 +188,16 @@ static inline void *watchdog_get_drvdata(struct watchdog_device *wdd)
return wdd->driver_data; return wdd->driver_data;
} }
/* Use the following functions to report watchdog pretimeout event */
#if IS_ENABLED(CONFIG_WATCHDOG_PRETIMEOUT_GOV)
void watchdog_notify_pretimeout(struct watchdog_device *wdd);
#else
static inline void watchdog_notify_pretimeout(struct watchdog_device *wdd)
{
pr_alert("watchdog%d: pretimeout event\n", wdd->id);
}
#endif
/* drivers/watchdog/watchdog_core.c */ /* drivers/watchdog/watchdog_core.c */
void watchdog_set_restart_priority(struct watchdog_device *wdd, int priority); void watchdog_set_restart_priority(struct watchdog_device *wdd, int priority);
extern int watchdog_init_timeout(struct watchdog_device *wdd, extern int watchdog_init_timeout(struct watchdog_device *wdd,
......
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