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:
Linus Torvalds 2016-10-13 16:44:20 -07:00
commit e3799a210d
31 changed files with 1131 additions and 79 deletions

View file

@ -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

View file

@ -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.

View file

@ -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,

View file

@ -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,

View file

@ -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;
}

View file

@ -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;

View file

@ -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)) {

View file

@ -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,
};

View file

@ -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;

View file

@ -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,

View file

@ -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,

View file

@ -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;
}

View 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 <[email protected]>");
MODULE_DESCRIPTION("Panic watchdog pretimeout governor");
MODULE_LICENSE("GPL");

View 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 <[email protected]>");
MODULE_DESCRIPTION("Panic watchdog pretimeout governor");
MODULE_LICENSE("GPL");

View file

@ -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,

View file

@ -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;

View file

@ -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,

View file

@ -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);

View file

@ -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,

View file

@ -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;
}

View file

@ -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,

View file

@ -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;

View file

@ -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);
}

View 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);
}

View 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

View file

@ -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;