Commit ec53c027 authored by Linus Torvalds's avatar Linus Torvalds

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

Pull watchdog fixes from Wim Van Sebroeck:
 - orion_wdt compile-test dependencies
 - sama5d4_wdt: WDDIS handling and a race confition
 - pcwd_usb: fix NULL-deref at probe
 - cadence_wdt: fix timeout setting
 - wdt_pci: fix build error if SOFTWARE_REBOOT is defined
 - iTCO_wdt: all versions count down twice
 - zx2967: remove redundant dev_err call in zx2967_wdt_probe()
 - bcm281xx: Fix use of uninitialized spinlock

* git://www.linux-watchdog.org/linux-watchdog:
  watchdog: bcm281xx: Fix use of uninitialized spinlock.
  watchdog: zx2967: remove redundant dev_err call in zx2967_wdt_probe()
  iTCO_wdt: all versions count down twice
  watchdog: wdt_pci: fix build error if define SOFTWARE_REBOOT
  watchdog: cadence_wdt: fix timeout setting
  watchdog: pcwd_usb: fix NULL-deref at probe
  watchdog: sama5d4: fix race condition
  watchdog: sama5d4: fix WDDIS handling
  watchdog: orion: fix compile-test dependencies
parents cf80a6fb fedf266f
...@@ -117,7 +117,7 @@ nowayout: Watchdog cannot be stopped once started ...@@ -117,7 +117,7 @@ nowayout: Watchdog cannot be stopped once started
------------------------------------------------- -------------------------------------------------
iTCO_wdt: iTCO_wdt:
heartbeat: Watchdog heartbeat in seconds. heartbeat: Watchdog heartbeat in seconds.
(2<heartbeat<39 (TCO v1) or 613 (TCO v2), default=30) (5<=heartbeat<=74 (TCO v1) or 1226 (TCO v2), default=30)
nowayout: Watchdog cannot be stopped once started nowayout: Watchdog cannot be stopped once started
(default=kernel config parameter) (default=kernel config parameter)
------------------------------------------------- -------------------------------------------------
......
...@@ -452,7 +452,7 @@ config DAVINCI_WATCHDOG ...@@ -452,7 +452,7 @@ config DAVINCI_WATCHDOG
config ORION_WATCHDOG config ORION_WATCHDOG
tristate "Orion watchdog" tristate "Orion watchdog"
depends on ARCH_ORION5X || ARCH_DOVE || MACH_DOVE || ARCH_MVEBU || COMPILE_TEST depends on ARCH_ORION5X || ARCH_DOVE || MACH_DOVE || ARCH_MVEBU || (COMPILE_TEST && !ARCH_EBSA110)
depends on ARM depends on ARM
select WATCHDOG_CORE select WATCHDOG_CORE
help help
......
...@@ -304,6 +304,8 @@ static int bcm_kona_wdt_probe(struct platform_device *pdev) ...@@ -304,6 +304,8 @@ static int bcm_kona_wdt_probe(struct platform_device *pdev)
if (!wdt) if (!wdt)
return -ENOMEM; return -ENOMEM;
spin_lock_init(&wdt->lock);
res = platform_get_resource(pdev, IORESOURCE_MEM, 0); res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
wdt->base = devm_ioremap_resource(dev, res); wdt->base = devm_ioremap_resource(dev, res);
if (IS_ERR(wdt->base)) if (IS_ERR(wdt->base))
...@@ -316,7 +318,6 @@ static int bcm_kona_wdt_probe(struct platform_device *pdev) ...@@ -316,7 +318,6 @@ static int bcm_kona_wdt_probe(struct platform_device *pdev)
return ret; return ret;
} }
spin_lock_init(&wdt->lock);
platform_set_drvdata(pdev, wdt); platform_set_drvdata(pdev, wdt);
watchdog_set_drvdata(&bcm_kona_wdt_wdd, wdt); watchdog_set_drvdata(&bcm_kona_wdt_wdd, wdt);
bcm_kona_wdt_wdd.parent = &pdev->dev; bcm_kona_wdt_wdd.parent = &pdev->dev;
......
...@@ -49,7 +49,7 @@ ...@@ -49,7 +49,7 @@
/* Counter maximum value */ /* Counter maximum value */
#define CDNS_WDT_COUNTER_MAX 0xFFF #define CDNS_WDT_COUNTER_MAX 0xFFF
static int wdt_timeout = CDNS_WDT_DEFAULT_TIMEOUT; static int wdt_timeout;
static int nowayout = WATCHDOG_NOWAYOUT; static int nowayout = WATCHDOG_NOWAYOUT;
module_param(wdt_timeout, int, 0); module_param(wdt_timeout, int, 0);
......
...@@ -306,16 +306,15 @@ static int iTCO_wdt_ping(struct watchdog_device *wd_dev) ...@@ -306,16 +306,15 @@ static int iTCO_wdt_ping(struct watchdog_device *wd_dev)
iTCO_vendor_pre_keepalive(p->smi_res, wd_dev->timeout); iTCO_vendor_pre_keepalive(p->smi_res, wd_dev->timeout);
/* Reset the timeout status bit so that the timer
* needs to count down twice again before rebooting */
outw(0x0008, TCO1_STS(p)); /* write 1 to clear bit */
/* Reload the timer by writing to the TCO Timer Counter register */ /* Reload the timer by writing to the TCO Timer Counter register */
if (p->iTCO_version >= 2) { if (p->iTCO_version >= 2)
outw(0x01, TCO_RLD(p)); outw(0x01, TCO_RLD(p));
} else if (p->iTCO_version == 1) { else if (p->iTCO_version == 1)
/* Reset the timeout status bit so that the timer
* needs to count down twice again before rebooting */
outw(0x0008, TCO1_STS(p)); /* write 1 to clear bit */
outb(0x01, TCO_RLD(p)); outb(0x01, TCO_RLD(p));
}
spin_unlock(&p->io_lock); spin_unlock(&p->io_lock);
return 0; return 0;
...@@ -328,11 +327,8 @@ static int iTCO_wdt_set_timeout(struct watchdog_device *wd_dev, unsigned int t) ...@@ -328,11 +327,8 @@ static int iTCO_wdt_set_timeout(struct watchdog_device *wd_dev, unsigned int t)
unsigned char val8; unsigned char val8;
unsigned int tmrval; unsigned int tmrval;
tmrval = seconds_to_ticks(p, t); /* The timer counts down twice before rebooting */
tmrval = seconds_to_ticks(p, t) / 2;
/* For TCO v1 the timer counts down twice before rebooting */
if (p->iTCO_version == 1)
tmrval /= 2;
/* from the specs: */ /* from the specs: */
/* "Values of 0h-3h are ignored and should not be attempted" */ /* "Values of 0h-3h are ignored and should not be attempted" */
...@@ -385,6 +381,8 @@ static unsigned int iTCO_wdt_get_timeleft(struct watchdog_device *wd_dev) ...@@ -385,6 +381,8 @@ static unsigned int iTCO_wdt_get_timeleft(struct watchdog_device *wd_dev)
spin_lock(&p->io_lock); spin_lock(&p->io_lock);
val16 = inw(TCO_RLD(p)); val16 = inw(TCO_RLD(p));
val16 &= 0x3ff; val16 &= 0x3ff;
if (!(inw(TCO1_STS(p)) & 0x0008))
val16 += (inw(TCOv2_TMR(p)) & 0x3ff);
spin_unlock(&p->io_lock); spin_unlock(&p->io_lock);
time_left = ticks_to_seconds(p, val16); time_left = ticks_to_seconds(p, val16);
......
...@@ -630,6 +630,9 @@ static int usb_pcwd_probe(struct usb_interface *interface, ...@@ -630,6 +630,9 @@ static int usb_pcwd_probe(struct usb_interface *interface,
return -ENODEV; return -ENODEV;
} }
if (iface_desc->desc.bNumEndpoints < 1)
return -ENODEV;
/* check out the endpoint: it has to be Interrupt & IN */ /* check out the endpoint: it has to be Interrupt & IN */
endpoint = &iface_desc->endpoint[0].desc; endpoint = &iface_desc->endpoint[0].desc;
......
...@@ -6,6 +6,7 @@ ...@@ -6,6 +6,7 @@
* Licensed under GPLv2. * Licensed under GPLv2.
*/ */
#include <linux/delay.h>
#include <linux/interrupt.h> #include <linux/interrupt.h>
#include <linux/io.h> #include <linux/io.h>
#include <linux/kernel.h> #include <linux/kernel.h>
...@@ -29,6 +30,7 @@ struct sama5d4_wdt { ...@@ -29,6 +30,7 @@ struct sama5d4_wdt {
struct watchdog_device wdd; struct watchdog_device wdd;
void __iomem *reg_base; void __iomem *reg_base;
u32 mr; u32 mr;
unsigned long last_ping;
}; };
static int wdt_timeout = WDT_DEFAULT_TIMEOUT; static int wdt_timeout = WDT_DEFAULT_TIMEOUT;
...@@ -44,11 +46,34 @@ MODULE_PARM_DESC(nowayout, ...@@ -44,11 +46,34 @@ MODULE_PARM_DESC(nowayout,
"Watchdog cannot be stopped once started (default=" "Watchdog cannot be stopped once started (default="
__MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
#define wdt_enabled (!(wdt->mr & AT91_WDT_WDDIS))
#define wdt_read(wdt, field) \ #define wdt_read(wdt, field) \
readl_relaxed((wdt)->reg_base + (field)) readl_relaxed((wdt)->reg_base + (field))
#define wdt_write(wtd, field, val) \ /* 4 slow clock periods is 4/32768 = 122.07µs*/
writel_relaxed((val), (wdt)->reg_base + (field)) #define WDT_DELAY usecs_to_jiffies(123)
static void wdt_write(struct sama5d4_wdt *wdt, u32 field, u32 val)
{
/*
* WDT_CR and WDT_MR must not be modified within three slow clock
* periods following a restart of the watchdog performed by a write
* access in WDT_CR.
*/
while (time_before(jiffies, wdt->last_ping + WDT_DELAY))
usleep_range(30, 125);
writel_relaxed(val, wdt->reg_base + field);
wdt->last_ping = jiffies;
}
static void wdt_write_nosleep(struct sama5d4_wdt *wdt, u32 field, u32 val)
{
if (time_before(jiffies, wdt->last_ping + WDT_DELAY))
udelay(123);
writel_relaxed(val, wdt->reg_base + field);
wdt->last_ping = jiffies;
}
static int sama5d4_wdt_start(struct watchdog_device *wdd) static int sama5d4_wdt_start(struct watchdog_device *wdd)
{ {
...@@ -89,7 +114,16 @@ static int sama5d4_wdt_set_timeout(struct watchdog_device *wdd, ...@@ -89,7 +114,16 @@ static int sama5d4_wdt_set_timeout(struct watchdog_device *wdd,
wdt->mr &= ~AT91_WDT_WDD; wdt->mr &= ~AT91_WDT_WDD;
wdt->mr |= AT91_WDT_SET_WDV(value); wdt->mr |= AT91_WDT_SET_WDV(value);
wdt->mr |= AT91_WDT_SET_WDD(value); wdt->mr |= AT91_WDT_SET_WDD(value);
wdt_write(wdt, AT91_WDT_MR, wdt->mr);
/*
* WDDIS has to be 0 when updating WDD/WDV. The datasheet states: When
* setting the WDDIS bit, and while it is set, the fields WDV and WDD
* must not be modified.
* If the watchdog is enabled, then the timeout can be updated. Else,
* wait that the user enables it.
*/
if (wdt_enabled)
wdt_write(wdt, AT91_WDT_MR, wdt->mr & ~AT91_WDT_WDDIS);
wdd->timeout = timeout; wdd->timeout = timeout;
...@@ -145,23 +179,21 @@ static int of_sama5d4_wdt_init(struct device_node *np, struct sama5d4_wdt *wdt) ...@@ -145,23 +179,21 @@ static int of_sama5d4_wdt_init(struct device_node *np, struct sama5d4_wdt *wdt)
static int sama5d4_wdt_init(struct sama5d4_wdt *wdt) static int sama5d4_wdt_init(struct sama5d4_wdt *wdt)
{ {
struct watchdog_device *wdd = &wdt->wdd;
u32 value = WDT_SEC2TICKS(wdd->timeout);
u32 reg; u32 reg;
/* /*
* Because the fields WDV and WDD must not be modified when the WDDIS * When booting and resuming, the bootloader may have changed the
* bit is set, so clear the WDDIS bit before writing the WDT_MR. * watchdog configuration.
* If the watchdog is already running, we can safely update it.
* Else, we have to disable it properly.
*/ */
reg = wdt_read(wdt, AT91_WDT_MR); if (wdt_enabled) {
reg &= ~AT91_WDT_WDDIS; wdt_write_nosleep(wdt, AT91_WDT_MR, wdt->mr);
wdt_write(wdt, AT91_WDT_MR, reg); } else {
reg = wdt_read(wdt, AT91_WDT_MR);
wdt->mr |= AT91_WDT_SET_WDD(value); if (!(reg & AT91_WDT_WDDIS))
wdt->mr |= AT91_WDT_SET_WDV(value); wdt_write_nosleep(wdt, AT91_WDT_MR,
reg | AT91_WDT_WDDIS);
wdt_write(wdt, AT91_WDT_MR, wdt->mr); }
return 0; return 0;
} }
...@@ -172,6 +204,7 @@ static int sama5d4_wdt_probe(struct platform_device *pdev) ...@@ -172,6 +204,7 @@ static int sama5d4_wdt_probe(struct platform_device *pdev)
struct resource *res; struct resource *res;
void __iomem *regs; void __iomem *regs;
u32 irq = 0; u32 irq = 0;
u32 timeout;
int ret; int ret;
wdt = devm_kzalloc(&pdev->dev, sizeof(*wdt), GFP_KERNEL); wdt = devm_kzalloc(&pdev->dev, sizeof(*wdt), GFP_KERNEL);
...@@ -184,6 +217,7 @@ static int sama5d4_wdt_probe(struct platform_device *pdev) ...@@ -184,6 +217,7 @@ static int sama5d4_wdt_probe(struct platform_device *pdev)
wdd->ops = &sama5d4_wdt_ops; wdd->ops = &sama5d4_wdt_ops;
wdd->min_timeout = MIN_WDT_TIMEOUT; wdd->min_timeout = MIN_WDT_TIMEOUT;
wdd->max_timeout = MAX_WDT_TIMEOUT; wdd->max_timeout = MAX_WDT_TIMEOUT;
wdt->last_ping = jiffies;
watchdog_set_drvdata(wdd, wdt); watchdog_set_drvdata(wdd, wdt);
...@@ -221,6 +255,11 @@ static int sama5d4_wdt_probe(struct platform_device *pdev) ...@@ -221,6 +255,11 @@ static int sama5d4_wdt_probe(struct platform_device *pdev)
return ret; return ret;
} }
timeout = WDT_SEC2TICKS(wdd->timeout);
wdt->mr |= AT91_WDT_SET_WDD(timeout);
wdt->mr |= AT91_WDT_SET_WDV(timeout);
ret = sama5d4_wdt_init(wdt); ret = sama5d4_wdt_init(wdt);
if (ret) if (ret)
return ret; return ret;
...@@ -263,9 +302,7 @@ static int sama5d4_wdt_resume(struct device *dev) ...@@ -263,9 +302,7 @@ static int sama5d4_wdt_resume(struct device *dev)
{ {
struct sama5d4_wdt *wdt = dev_get_drvdata(dev); struct sama5d4_wdt *wdt = dev_get_drvdata(dev);
wdt_write(wdt, AT91_WDT_MR, wdt->mr & ~AT91_WDT_WDDIS); sama5d4_wdt_init(wdt);
if (wdt->mr & AT91_WDT_WDDIS)
wdt_write(wdt, AT91_WDT_MR, wdt->mr);
return 0; return 0;
} }
......
...@@ -332,7 +332,7 @@ static irqreturn_t wdtpci_interrupt(int irq, void *dev_id) ...@@ -332,7 +332,7 @@ static irqreturn_t wdtpci_interrupt(int irq, void *dev_id)
pr_crit("Would Reboot\n"); pr_crit("Would Reboot\n");
#else #else
pr_crit("Initiating system reboot\n"); pr_crit("Initiating system reboot\n");
emergency_restart(NULL); emergency_restart();
#endif #endif
#else #else
pr_crit("Reset in 5ms\n"); pr_crit("Reset in 5ms\n");
......
...@@ -211,10 +211,8 @@ static int zx2967_wdt_probe(struct platform_device *pdev) ...@@ -211,10 +211,8 @@ static int zx2967_wdt_probe(struct platform_device *pdev)
base = platform_get_resource(pdev, IORESOURCE_MEM, 0); base = platform_get_resource(pdev, IORESOURCE_MEM, 0);
wdt->reg_base = devm_ioremap_resource(dev, base); wdt->reg_base = devm_ioremap_resource(dev, base);
if (IS_ERR(wdt->reg_base)) { if (IS_ERR(wdt->reg_base))
dev_err(dev, "ioremap failed\n");
return PTR_ERR(wdt->reg_base); return PTR_ERR(wdt->reg_base);
}
zx2967_wdt_reset_sysctrl(dev); zx2967_wdt_reset_sysctrl(dev);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment