mirror of
https://mirrors.bfsu.edu.cn/git/linux.git
synced 2024-11-24 12:44:11 +08:00
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 ...
This commit is contained in:
commit
e3799a210d
@ -7,6 +7,8 @@ Required properties:
|
||||
- reg : Physical base address and size
|
||||
|
||||
Optional properties:
|
||||
- clocks : Input clock specifier. Refer to common clock
|
||||
bindings.
|
||||
- clock-frequency : Frequency of clock in Hz
|
||||
- xlnx,wdt-enable-once : 0 - Watchdog can be restarted
|
||||
1 - Watchdog can be enabled just once
|
||||
@ -17,6 +19,7 @@ Example:
|
||||
axi-timebase-wdt@40100000 {
|
||||
clock-frequency = <50000000>;
|
||||
compatible = "xlnx,xps-timebase-wdt-1.00.a";
|
||||
clocks = <&clkc 15>;
|
||||
reg = <0x40100000 0x10000>;
|
||||
xlnx,wdt-enable-once = <0x0>;
|
||||
xlnx,wdt-interval = <0x1b>;
|
||||
|
@ -9,8 +9,7 @@ functionality.
|
||||
|
||||
Required properties
|
||||
|
||||
- compatible : Must be one of: "st,stih407-lpc" "st,stih416-lpc"
|
||||
"st,stih415-lpc" "st,stid127-lpc"
|
||||
- compatible : Should be: "st,stih407-lpc"
|
||||
- reg : LPC registers base address + size
|
||||
- interrupts : LPC interrupt line number and associated flags
|
||||
- clocks : Clock used by LPC device (See: ../clock/clock-bindings.txt)
|
||||
|
@ -48,8 +48,10 @@ struct watchdog_device {
|
||||
const struct attribute_group **groups;
|
||||
const struct watchdog_info *info;
|
||||
const struct watchdog_ops *ops;
|
||||
const struct watchdog_governor *gov;
|
||||
unsigned int bootstatus;
|
||||
unsigned int timeout;
|
||||
unsigned int pretimeout;
|
||||
unsigned int min_timeout;
|
||||
unsigned int max_timeout;
|
||||
unsigned int min_hw_heartbeat_ms;
|
||||
@ -74,9 +76,11 @@ It contains following fields:
|
||||
* info: a pointer to a watchdog_info structure. This structure gives some
|
||||
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.
|
||||
* gov: a pointer to the assigned watchdog device pretimeout governor or NULL.
|
||||
* timeout: the watchdog timer's timeout value (in seconds).
|
||||
This is the time after which the system will reboot if user space does
|
||||
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).
|
||||
If set, the minimum configurable value for 'timeout'.
|
||||
* max_timeout: the watchdog timer's maximum timeout value (in seconds),
|
||||
@ -121,6 +125,7 @@ struct watchdog_ops {
|
||||
int (*ping)(struct watchdog_device *);
|
||||
unsigned int (*status)(struct watchdog_device *);
|
||||
int (*set_timeout)(struct watchdog_device *, unsigned int);
|
||||
int (*set_pretimeout)(struct watchdog_device *, unsigned int);
|
||||
unsigned int (*get_timeleft)(struct watchdog_device *);
|
||||
int (*restart)(struct watchdog_device *);
|
||||
void (*ref)(struct watchdog_device *) __deprecated;
|
||||
@ -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
|
||||
infrastructure updates the timeout value of the watchdog_device internally
|
||||
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.
|
||||
* restart: this routine restarts the machine. It returns 0 on success or a
|
||||
negative errno code for failure.
|
||||
@ -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
|
||||
available, and/or if restart is sufficient to restart the entire system
|
||||
* 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
|
||||
|
||||
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
|
||||
|
@ -3,9 +3,15 @@
|
||||
#
|
||||
|
||||
# The WatchDog Timer Driver Core.
|
||||
watchdog-objs += watchdog_core.o watchdog_dev.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
|
||||
# watchdog-cards first, then the architecture specific watchdog
|
||||
# drivers and then the architecture independent "softdog" driver.
|
||||
|
@ -389,7 +389,6 @@ MODULE_DEVICE_TABLE(of, asm9260_wdt_of_match);
|
||||
static struct platform_driver asm9260_wdt_driver = {
|
||||
.driver = {
|
||||
.name = "asm9260-wdt",
|
||||
.owner = THIS_MODULE,
|
||||
.of_match_table = asm9260_wdt_of_match,
|
||||
},
|
||||
.probe = asm9260_wdt_probe,
|
||||
|
@ -107,7 +107,7 @@ static struct watchdog_info bcm7038_wdt_info = {
|
||||
WDIOF_MAGICCLOSE
|
||||
};
|
||||
|
||||
static struct watchdog_ops bcm7038_wdt_ops = {
|
||||
static const struct watchdog_ops bcm7038_wdt_ops = {
|
||||
.owner = THIS_MODULE,
|
||||
.start = bcm7038_wdt_start,
|
||||
.stop = bcm7038_wdt_stop,
|
||||
|
@ -269,7 +269,7 @@ static struct watchdog_info cdns_wdt_info = {
|
||||
};
|
||||
|
||||
/* Watchdog Core Ops */
|
||||
static struct watchdog_ops cdns_wdt_ops = {
|
||||
static const struct watchdog_ops cdns_wdt_ops = {
|
||||
.owner = THIS_MODULE,
|
||||
.start = cdns_wdt_start,
|
||||
.stop = cdns_wdt_stop,
|
||||
@ -424,8 +424,10 @@ static int __maybe_unused cdns_wdt_suspend(struct device *dev)
|
||||
struct platform_device *pdev = to_platform_device(dev);
|
||||
struct cdns_wdt *wdt = platform_get_drvdata(pdev);
|
||||
|
||||
cdns_wdt_stop(&wdt->cdns_wdt_device);
|
||||
clk_disable_unprepare(wdt->clk);
|
||||
if (watchdog_active(&wdt->cdns_wdt_device)) {
|
||||
cdns_wdt_stop(&wdt->cdns_wdt_device);
|
||||
clk_disable_unprepare(wdt->clk);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -442,12 +444,14 @@ static int __maybe_unused cdns_wdt_resume(struct device *dev)
|
||||
struct platform_device *pdev = to_platform_device(dev);
|
||||
struct cdns_wdt *wdt = platform_get_drvdata(pdev);
|
||||
|
||||
ret = clk_prepare_enable(wdt->clk);
|
||||
if (ret) {
|
||||
dev_err(dev, "unable to enable clock\n");
|
||||
return ret;
|
||||
if (watchdog_active(&wdt->cdns_wdt_device)) {
|
||||
ret = clk_prepare_enable(wdt->clk);
|
||||
if (ret) {
|
||||
dev_err(dev, "unable to enable clock\n");
|
||||
return ret;
|
||||
}
|
||||
cdns_wdt_start(&wdt->cdns_wdt_device);
|
||||
}
|
||||
cdns_wdt_start(&wdt->cdns_wdt_device);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -54,6 +54,7 @@ MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started "
|
||||
struct dw_wdt {
|
||||
void __iomem *regs;
|
||||
struct clk *clk;
|
||||
unsigned long rate;
|
||||
struct notifier_block restart_handler;
|
||||
struct watchdog_device wdd;
|
||||
};
|
||||
@ -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
|
||||
* 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)
|
||||
@ -163,7 +164,7 @@ static unsigned int dw_wdt_get_timeleft(struct watchdog_device *wdd)
|
||||
struct dw_wdt *dw_wdt = to_dw_wdt(wdd);
|
||||
|
||||
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 = {
|
||||
@ -231,6 +232,12 @@ static int dw_wdt_drv_probe(struct platform_device *pdev)
|
||||
if (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->info = &dw_wdt_ident;
|
||||
wdd->ops = &dw_wdt_ops;
|
||||
|
@ -39,7 +39,7 @@
|
||||
#include <asm/nmi.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 TICKS_TO_SECS(ticks) ((ticks) * 128 / 1000)
|
||||
#define HPWDT_MAX_TIMER TICKS_TO_SECS(65535)
|
||||
@ -814,7 +814,8 @@ static int hpwdt_init_one(struct pci_dev *dev,
|
||||
* not run on a legacy ASM box.
|
||||
* 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,
|
||||
"This server does not have an iLO2+ ASIC.\n");
|
||||
return -ENODEV;
|
||||
@ -823,7 +824,8 @@ static int hpwdt_init_one(struct pci_dev *dev,
|
||||
/*
|
||||
* 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;
|
||||
|
||||
if (pci_enable_device(dev)) {
|
||||
|
@ -629,7 +629,7 @@ static int iTCO_wdt_resume_noirq(struct device *dev)
|
||||
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,
|
||||
.resume_noirq = iTCO_wdt_resume_noirq,
|
||||
};
|
||||
|
@ -24,6 +24,7 @@
|
||||
#include <linux/clk.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
@ -37,18 +38,23 @@
|
||||
|
||||
#define IMX2_WDT_WCR 0x00 /* Control Register */
|
||||
#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_SRS (1 << 4) /* -> Software Reset Signal */
|
||||
#define IMX2_WDT_WCR_WRE (1 << 3) /* -> WDOG Reset Enable */
|
||||
#define IMX2_WDT_WCR_WDE (1 << 2) /* -> Watchdog Enable */
|
||||
#define IMX2_WDT_WCR_WDZST (1 << 0) /* -> Watchdog timer Suspend */
|
||||
#define IMX2_WDT_WCR_WDA BIT(5) /* -> External Reset WDOG_B */
|
||||
#define IMX2_WDT_WCR_SRS BIT(4) /* -> Software Reset Signal */
|
||||
#define IMX2_WDT_WCR_WRE BIT(3) /* -> WDOG Reset Enable */
|
||||
#define IMX2_WDT_WCR_WDE BIT(2) /* -> Watchdog Enable */
|
||||
#define IMX2_WDT_WCR_WDZST BIT(0) /* -> Watchdog timer Suspend */
|
||||
|
||||
#define IMX2_WDT_WSR 0x02 /* Service Register */
|
||||
#define IMX2_WDT_SEQ1 0x5555 /* -> service sequence 1 */
|
||||
#define IMX2_WDT_SEQ2 0xAAAA /* -> service sequence 2 */
|
||||
|
||||
#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 */
|
||||
|
||||
@ -80,6 +86,12 @@ static const struct watchdog_info imx2_wdt_info = {
|
||||
.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,
|
||||
void *data)
|
||||
{
|
||||
@ -169,6 +181,35 @@ static int imx2_wdt_set_timeout(struct watchdog_device *wdog,
|
||||
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)
|
||||
{
|
||||
struct imx2_wdt_device *wdev = watchdog_get_drvdata(wdog);
|
||||
@ -188,6 +229,7 @@ static const struct watchdog_ops imx2_wdt_ops = {
|
||||
.start = imx2_wdt_start,
|
||||
.ping = imx2_wdt_ping,
|
||||
.set_timeout = imx2_wdt_set_timeout,
|
||||
.set_pretimeout = imx2_wdt_set_pretimeout,
|
||||
.restart = imx2_wdt_restart,
|
||||
};
|
||||
|
||||
@ -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->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);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
@ -430,7 +430,7 @@ static struct watchdog_info kempld_wdt_info = {
|
||||
WDIOF_PRETIMEOUT
|
||||
};
|
||||
|
||||
static struct watchdog_ops kempld_wdt_ops = {
|
||||
static const struct watchdog_ops kempld_wdt_ops = {
|
||||
.owner = THIS_MODULE,
|
||||
.start = kempld_wdt_start,
|
||||
.stop = kempld_wdt_stop,
|
||||
|
@ -139,7 +139,6 @@ static int mt7621_wdt_probe(struct platform_device *pdev)
|
||||
if (!IS_ERR(mt7621_wdt_reset))
|
||||
reset_control_deassert(mt7621_wdt_reset);
|
||||
|
||||
mt7621_wdt_dev.dev = &pdev->dev;
|
||||
mt7621_wdt_dev.bootstatus = mt7621_wdt_bootcause();
|
||||
|
||||
watchdog_init_timeout(&mt7621_wdt_dev, mt7621_wdt_dev.max_timeout,
|
||||
|
@ -10,6 +10,7 @@
|
||||
* 2 of the License, or (at your option) any later version.
|
||||
*/
|
||||
|
||||
#include <linux/clk.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/types.h>
|
||||
@ -45,6 +46,7 @@ struct xwdt_device {
|
||||
u32 wdt_interval;
|
||||
spinlock_t spinlock;
|
||||
struct watchdog_device xilinx_wdt_wdd;
|
||||
struct clk *clk;
|
||||
};
|
||||
|
||||
static int xilinx_wdt_start(struct watchdog_device *wdd)
|
||||
@ -195,16 +197,30 @@ static int xwdt_probe(struct platform_device *pdev)
|
||||
spin_lock_init(&xdev->spinlock);
|
||||
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);
|
||||
if (rc == XWT_TIMER_FAILED) {
|
||||
dev_err(&pdev->dev, "SelfTest routine error\n");
|
||||
return rc;
|
||||
goto err_clk_disable;
|
||||
}
|
||||
|
||||
rc = watchdog_register_device(xilinx_wdt_wdd);
|
||||
if (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",
|
||||
@ -213,6 +229,10 @@ static int xwdt_probe(struct platform_device *pdev)
|
||||
platform_set_drvdata(pdev, xdev);
|
||||
|
||||
return 0;
|
||||
err_clk_disable:
|
||||
clk_disable_unprepare(xdev->clk);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
watchdog_unregister_device(&xdev->xilinx_wdt_wdd);
|
||||
clk_disable_unprepare(xdev->clk);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
47
drivers/watchdog/pretimeout_noop.c
Normal file
47
drivers/watchdog/pretimeout_noop.c
Normal file
@ -0,0 +1,47 @@
|
||||
/*
|
||||
* 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");
|
47
drivers/watchdog/pretimeout_panic.c
Normal file
47
drivers/watchdog/pretimeout_panic.c
Normal file
@ -0,0 +1,47 @@
|
||||
/*
|
||||
* 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 = {
|
||||
.identity = DRIVER_NAME,
|
||||
};
|
||||
|
||||
static struct watchdog_ops rn5t618_wdt_ops = {
|
||||
static const struct watchdog_ops rn5t618_wdt_ops = {
|
||||
.owner = THIS_MODULE,
|
||||
.start = rn5t618_wdt_start,
|
||||
.stop = rn5t618_wdt_stop,
|
||||
|
@ -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_dev.dev = &pdev->dev;
|
||||
rt288x_wdt_dev.bootstatus = rt288x_wdt_bootcause();
|
||||
rt288x_wdt_dev.max_timeout = (0xfffful / rt288x_wdt_freq);
|
||||
rt288x_wdt_dev.parent = &pdev->dev;
|
||||
|
@ -72,10 +72,27 @@ static void softdog_fire(unsigned long data)
|
||||
static struct timer_list softdog_ticktock =
|
||||
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)
|
||||
{
|
||||
if (!mod_timer(&softdog_ticktock, jiffies + (w->timeout * HZ)))
|
||||
__module_get(THIS_MODULE);
|
||||
|
||||
if (w->pretimeout)
|
||||
mod_timer(&softdog_preticktock, jiffies +
|
||||
(w->timeout - w->pretimeout) * HZ);
|
||||
else
|
||||
del_timer(&softdog_preticktock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -84,15 +101,18 @@ static int softdog_stop(struct watchdog_device *w)
|
||||
if (del_timer(&softdog_ticktock))
|
||||
module_put(THIS_MODULE);
|
||||
|
||||
del_timer(&softdog_preticktock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct watchdog_info softdog_info = {
|
||||
.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,
|
||||
.start = softdog_ping,
|
||||
.stop = softdog_stop,
|
||||
|
@ -52,27 +52,6 @@ struct st_wdog {
|
||||
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 = {
|
||||
.enable_reg = 0x204,
|
||||
.enable_mask = BIT(19),
|
||||
@ -83,18 +62,6 @@ static const struct of_device_id st_wdog_match[] = {
|
||||
.compatible = "st,stih407-lpc",
|
||||
.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);
|
||||
|
@ -178,7 +178,7 @@ static const struct watchdog_info tegra_wdt_info = {
|
||||
.identity = "Tegra Watchdog",
|
||||
};
|
||||
|
||||
static struct watchdog_ops tegra_wdt_ops = {
|
||||
static const struct watchdog_ops tegra_wdt_ops = {
|
||||
.owner = THIS_MODULE,
|
||||
.start = tegra_wdt_start,
|
||||
.stop = tegra_wdt_stop,
|
||||
|
@ -112,7 +112,7 @@ static int __init txx9wdt_probe(struct platform_device *dev)
|
||||
txx9_imclk = NULL;
|
||||
goto exit;
|
||||
}
|
||||
ret = clk_enable(txx9_imclk);
|
||||
ret = clk_prepare_enable(txx9_imclk);
|
||||
if (ret) {
|
||||
clk_put(txx9_imclk);
|
||||
txx9_imclk = NULL;
|
||||
@ -144,7 +144,7 @@ static int __init txx9wdt_probe(struct platform_device *dev)
|
||||
return 0;
|
||||
exit:
|
||||
if (txx9_imclk) {
|
||||
clk_disable(txx9_imclk);
|
||||
clk_disable_unprepare(txx9_imclk);
|
||||
clk_put(txx9_imclk);
|
||||
}
|
||||
return ret;
|
||||
@ -153,7 +153,7 @@ exit:
|
||||
static int __exit txx9wdt_remove(struct platform_device *dev)
|
||||
{
|
||||
watchdog_unregister_device(&txx9wdt);
|
||||
clk_disable(txx9_imclk);
|
||||
clk_disable_unprepare(txx9_imclk);
|
||||
clk_put(txx9_imclk);
|
||||
return 0;
|
||||
}
|
||||
|
@ -302,7 +302,7 @@ static struct watchdog_info wdt_info = {
|
||||
.identity = "W83627HF Watchdog",
|
||||
};
|
||||
|
||||
static struct watchdog_ops wdt_ops = {
|
||||
static const struct watchdog_ops wdt_ops = {
|
||||
.owner = THIS_MODULE,
|
||||
.start = wdt_start,
|
||||
.stop = wdt_stop,
|
||||
|
@ -349,7 +349,7 @@ int devm_watchdog_register_device(struct device *dev,
|
||||
struct watchdog_device **rcwdd;
|
||||
int ret;
|
||||
|
||||
rcwdd = devres_alloc(devm_watchdog_unregister_device, sizeof(*wdd),
|
||||
rcwdd = devres_alloc(devm_watchdog_unregister_device, sizeof(*rcwdd),
|
||||
GFP_KERNEL);
|
||||
if (!rcwdd)
|
||||
return -ENOMEM;
|
||||
|
@ -49,6 +49,7 @@
|
||||
#include <linux/uaccess.h> /* For copy_to_user/put_user/... */
|
||||
|
||||
#include "watchdog_core.h"
|
||||
#include "watchdog_pretimeout.h"
|
||||
|
||||
/*
|
||||
* struct watchdog_core_data - watchdog core internal data
|
||||
@ -335,16 +336,45 @@ static int watchdog_set_timeout(struct watchdog_device *wdd,
|
||||
if (watchdog_timeout_invalid(wdd, timeout))
|
||||
return -EINVAL;
|
||||
|
||||
if (wdd->ops->set_timeout)
|
||||
if (wdd->ops->set_timeout) {
|
||||
err = wdd->ops->set_timeout(wdd, timeout);
|
||||
else
|
||||
} else {
|
||||
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);
|
||||
|
||||
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
|
||||
* @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,
|
||||
}
|
||||
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,
|
||||
char *buf)
|
||||
{
|
||||
@ -450,6 +489,36 @@ static ssize_t state_show(struct device *dev, struct device_attribute *attr,
|
||||
}
|
||||
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,
|
||||
int n)
|
||||
{
|
||||
@ -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)
|
||||
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;
|
||||
}
|
||||
@ -466,10 +543,13 @@ static struct attribute *wdt_attrs[] = {
|
||||
&dev_attr_state.attr,
|
||||
&dev_attr_identity.attr,
|
||||
&dev_attr_timeout.attr,
|
||||
&dev_attr_pretimeout.attr,
|
||||
&dev_attr_timeleft.attr,
|
||||
&dev_attr_bootstatus.attr,
|
||||
&dev_attr_status.attr,
|
||||
&dev_attr_nowayout.attr,
|
||||
&dev_attr_pretimeout_governor.attr,
|
||||
&dev_attr_pretimeout_available_governors.attr,
|
||||
NULL,
|
||||
};
|
||||
|
||||
@ -646,6 +726,16 @@ static long watchdog_ioctl(struct file *file, unsigned int cmd,
|
||||
break;
|
||||
err = put_user(val, p);
|
||||
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:
|
||||
err = -ENOTTY;
|
||||
break;
|
||||
@ -937,6 +1027,12 @@ int watchdog_dev_register(struct watchdog_device *wdd)
|
||||
return PTR_ERR(dev);
|
||||
}
|
||||
|
||||
ret = watchdog_register_pretimeout(wdd);
|
||||
if (ret) {
|
||||
device_destroy(&watchdog_class, devno);
|
||||
watchdog_cdev_unregister(wdd);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -950,6 +1046,7 @@ int watchdog_dev_register(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);
|
||||
watchdog_cdev_unregister(wdd);
|
||||
}
|
||||
|
220
drivers/watchdog/watchdog_pretimeout.c
Normal file
220
drivers/watchdog/watchdog_pretimeout.c
Normal file
@ -0,0 +1,220 @@
|
||||
/*
|
||||
* 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);
|
||||
}
|
60
drivers/watchdog/watchdog_pretimeout.h
Normal file
60
drivers/watchdog/watchdog_pretimeout.h
Normal file
@ -0,0 +1,60 @@
|
||||
#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 @@
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#include <linux/delay.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/ihex.h>
|
||||
#include <linux/firmware.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/slab.h>
|
||||
@ -36,6 +39,8 @@
|
||||
#define ZIIRAVE_STATE_OFF 0x1
|
||||
#define ZIIRAVE_STATE_ON 0x2
|
||||
|
||||
#define ZIIRAVE_FW_NAME "ziirave_wdt.fw"
|
||||
|
||||
static char *ziirave_reasons[] = {"power cycle", "hw watchdog", NULL, NULL,
|
||||
"host request", NULL, "illegal configuration",
|
||||
"illegal instruction", "illegal trap",
|
||||
@ -50,12 +55,35 @@ static char *ziirave_reasons[] = {"power cycle", "hw watchdog", NULL, NULL,
|
||||
#define ZIIRAVE_WDT_PING 0x9
|
||||
#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 {
|
||||
unsigned char major;
|
||||
unsigned char minor;
|
||||
};
|
||||
|
||||
struct ziirave_wdt_data {
|
||||
struct mutex sysfs_mutex;
|
||||
struct watchdog_device wdd;
|
||||
struct ziirave_wdt_rev bootloader_rev;
|
||||
struct ziirave_wdt_rev firmware_rev;
|
||||
@ -146,6 +174,293 @@ static unsigned int ziirave_wdt_get_timeleft(struct watchdog_device *wdd)
|
||||
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 = {
|
||||
.options = WDIOF_SETTIMEOUT | WDIOF_MAGICCLOSE | WDIOF_KEEPALIVEPING,
|
||||
.identity = "Zodiac RAVE Watchdog",
|
||||
@ -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 ziirave_wdt_data *w_priv = i2c_get_clientdata(client);
|
||||
int ret;
|
||||
|
||||
return sprintf(buf, "02.%02u.%02u", w_priv->firmware_rev.major,
|
||||
w_priv->firmware_rev.minor);
|
||||
ret = mutex_lock_interruptible(&w_priv->sysfs_mutex);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = sprintf(buf, "02.%02u.%02u", w_priv->firmware_rev.major,
|
||||
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,
|
||||
@ -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 ziirave_wdt_data *w_priv = i2c_get_clientdata(client);
|
||||
int ret;
|
||||
|
||||
return sprintf(buf, "01.%02u.%02u", w_priv->bootloader_rev.major,
|
||||
w_priv->bootloader_rev.minor);
|
||||
ret = mutex_lock_interruptible(&w_priv->sysfs_mutex);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = sprintf(buf, "01.%02u.%02u", w_priv->bootloader_rev.major,
|
||||
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,
|
||||
@ -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 ziirave_wdt_data *w_priv = i2c_get_clientdata(client);
|
||||
int ret;
|
||||
|
||||
return sprintf(buf, "%s", ziirave_reasons[w_priv->reset_reason]);
|
||||
ret = mutex_lock_interruptible(&w_priv->sysfs_mutex);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = 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,
|
||||
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[] = {
|
||||
&dev_attr_firmware_version.attr,
|
||||
&dev_attr_bootloader_version.attr,
|
||||
&dev_attr_reset_reason.attr,
|
||||
&dev_attr_update_firmware.attr,
|
||||
NULL
|
||||
};
|
||||
ATTRIBUTE_GROUPS(ziirave_wdt);
|
||||
@ -252,6 +649,8 @@ static int ziirave_wdt_probe(struct i2c_client *client,
|
||||
if (!w_priv)
|
||||
return -ENOMEM;
|
||||
|
||||
mutex_init(&w_priv->sysfs_mutex);
|
||||
|
||||
w_priv->wdd.info = &ziirave_wdt_info;
|
||||
w_priv->wdd.ops = &ziirave_wdt_ops;
|
||||
w_priv->wdd.min_timeout = ZIIRAVE_TIMEOUT_MIN;
|
||||
|
@ -1209,6 +1209,8 @@ COMPATIBLE_IOCTL(WDIOC_SETOPTIONS)
|
||||
COMPATIBLE_IOCTL(WDIOC_KEEPALIVE)
|
||||
COMPATIBLE_IOCTL(WDIOC_SETTIMEOUT)
|
||||
COMPATIBLE_IOCTL(WDIOC_GETTIMEOUT)
|
||||
COMPATIBLE_IOCTL(WDIOC_SETPRETIMEOUT)
|
||||
COMPATIBLE_IOCTL(WDIOC_GETPRETIMEOUT)
|
||||
/* Big R */
|
||||
COMPATIBLE_IOCTL(RNDGETENTCNT)
|
||||
COMPATIBLE_IOCTL(RNDADDTOENTCNT)
|
||||
|
@ -19,6 +19,7 @@
|
||||
struct watchdog_ops;
|
||||
struct watchdog_device;
|
||||
struct watchdog_core_data;
|
||||
struct watchdog_governor;
|
||||
|
||||
/** struct watchdog_ops - The watchdog-devices operations
|
||||
*
|
||||
@ -28,6 +29,7 @@ struct watchdog_core_data;
|
||||
* @ping: The routine that sends a keepalive ping to 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_pretimeout:The routine for setting the watchdog devices pretimeout.
|
||||
* @get_timeleft:The routine that gets the time left before a reset (in seconds).
|
||||
* @restart: The routine for restarting the machine.
|
||||
* @ioctl: The routines that handles extra ioctl calls.
|
||||
@ -46,6 +48,7 @@ struct watchdog_ops {
|
||||
int (*ping)(struct watchdog_device *);
|
||||
unsigned int (*status)(struct watchdog_device *);
|
||||
int (*set_timeout)(struct watchdog_device *, unsigned int);
|
||||
int (*set_pretimeout)(struct watchdog_device *, unsigned int);
|
||||
unsigned int (*get_timeleft)(struct watchdog_device *);
|
||||
int (*restart)(struct watchdog_device *, unsigned long, void *);
|
||||
long (*ioctl)(struct watchdog_device *, unsigned int, unsigned long);
|
||||
@ -59,8 +62,10 @@ struct watchdog_ops {
|
||||
* watchdog device.
|
||||
* @info: Pointer to a watchdog_info structure.
|
||||
* @ops: Pointer to the list of watchdog operations.
|
||||
* @gov: Pointer to watchdog pretimeout governor.
|
||||
* @bootstatus: Status of the watchdog device at boot.
|
||||
* @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).
|
||||
* @max_timeout:The watchdog devices maximum timeout value (in seconds)
|
||||
* as configurable from user space. Only relevant if
|
||||
@ -94,8 +99,10 @@ struct watchdog_device {
|
||||
const struct attribute_group **groups;
|
||||
const struct watchdog_info *info;
|
||||
const struct watchdog_ops *ops;
|
||||
const struct watchdog_governor *gov;
|
||||
unsigned int bootstatus;
|
||||
unsigned int timeout;
|
||||
unsigned int pretimeout;
|
||||
unsigned int min_timeout;
|
||||
unsigned int max_timeout;
|
||||
unsigned int min_hw_heartbeat_ms;
|
||||
@ -163,6 +170,13 @@ static inline bool watchdog_timeout_invalid(struct watchdog_device *wdd, unsigne
|
||||
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 */
|
||||
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)
|
||||
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 */
|
||||
void watchdog_set_restart_priority(struct watchdog_device *wdd, int priority);
|
||||
extern int watchdog_init_timeout(struct watchdog_device *wdd,
|
||||
|
Loading…
Reference in New Issue
Block a user