Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
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
  • Loading branch information
torvalds committed May 20, 2017
2 parents cf80a6f + fedf266 commit ec53c02
Show file tree
Hide file tree
Showing 9 changed files with 77 additions and 40 deletions.
2 changes: 1 addition & 1 deletion Documentation/watchdog/watchdog-parameters.txt
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@ nowayout: Watchdog cannot be stopped once started
-------------------------------------------------
iTCO_wdt:
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
(default=kernel config parameter)
-------------------------------------------------
Expand Down
2 changes: 1 addition & 1 deletion drivers/watchdog/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -452,7 +452,7 @@ config DAVINCI_WATCHDOG

config 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
select WATCHDOG_CORE
help
Expand Down
3 changes: 2 additions & 1 deletion drivers/watchdog/bcm_kona_wdt.c
Original file line number Diff line number Diff line change
Expand Up @@ -304,6 +304,8 @@ static int bcm_kona_wdt_probe(struct platform_device *pdev)
if (!wdt)
return -ENOMEM;

spin_lock_init(&wdt->lock);

res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
wdt->base = devm_ioremap_resource(dev, res);
if (IS_ERR(wdt->base))
Expand All @@ -316,7 +318,6 @@ static int bcm_kona_wdt_probe(struct platform_device *pdev)
return ret;
}

spin_lock_init(&wdt->lock);
platform_set_drvdata(pdev, wdt);
watchdog_set_drvdata(&bcm_kona_wdt_wdd, wdt);
bcm_kona_wdt_wdd.parent = &pdev->dev;
Expand Down
2 changes: 1 addition & 1 deletion drivers/watchdog/cadence_wdt.c
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@
/* Counter maximum value */
#define CDNS_WDT_COUNTER_MAX 0xFFF

static int wdt_timeout = CDNS_WDT_DEFAULT_TIMEOUT;
static int wdt_timeout;
static int nowayout = WATCHDOG_NOWAYOUT;

module_param(wdt_timeout, int, 0);
Expand Down
22 changes: 10 additions & 12 deletions drivers/watchdog/iTCO_wdt.c
Original file line number Diff line number Diff line change
Expand Up @@ -306,16 +306,15 @@ static int iTCO_wdt_ping(struct watchdog_device *wd_dev)

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 */
if (p->iTCO_version >= 2) {
if (p->iTCO_version >= 2)
outw(0x01, TCO_RLD(p));
} 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 */

else if (p->iTCO_version == 1)
outb(0x01, TCO_RLD(p));
}

spin_unlock(&p->io_lock);
return 0;
Expand All @@ -328,11 +327,8 @@ static int iTCO_wdt_set_timeout(struct watchdog_device *wd_dev, unsigned int t)
unsigned char val8;
unsigned int tmrval;

tmrval = seconds_to_ticks(p, t);

/* For TCO v1 the timer counts down twice before rebooting */
if (p->iTCO_version == 1)
tmrval /= 2;
/* The timer counts down twice before rebooting */
tmrval = seconds_to_ticks(p, t) / 2;

/* from the specs: */
/* "Values of 0h-3h are ignored and should not be attempted" */
Expand Down Expand Up @@ -385,6 +381,8 @@ static unsigned int iTCO_wdt_get_timeleft(struct watchdog_device *wd_dev)
spin_lock(&p->io_lock);
val16 = inw(TCO_RLD(p));
val16 &= 0x3ff;
if (!(inw(TCO1_STS(p)) & 0x0008))
val16 += (inw(TCOv2_TMR(p)) & 0x3ff);
spin_unlock(&p->io_lock);

time_left = ticks_to_seconds(p, val16);
Expand Down
3 changes: 3 additions & 0 deletions drivers/watchdog/pcwd_usb.c
Original file line number Diff line number Diff line change
Expand Up @@ -630,6 +630,9 @@ static int usb_pcwd_probe(struct usb_interface *interface,
return -ENODEV;
}

if (iface_desc->desc.bNumEndpoints < 1)
return -ENODEV;

/* check out the endpoint: it has to be Interrupt & IN */
endpoint = &iface_desc->endpoint[0].desc;

Expand Down
77 changes: 57 additions & 20 deletions drivers/watchdog/sama5d4_wdt.c
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
* Licensed under GPLv2.
*/

#include <linux/delay.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/kernel.h>
Expand All @@ -29,6 +30,7 @@ struct sama5d4_wdt {
struct watchdog_device wdd;
void __iomem *reg_base;
u32 mr;
unsigned long last_ping;
};

static int wdt_timeout = WDT_DEFAULT_TIMEOUT;
Expand All @@ -44,11 +46,34 @@ MODULE_PARM_DESC(nowayout,
"Watchdog cannot be stopped once started (default="
__MODULE_STRING(WATCHDOG_NOWAYOUT) ")");

#define wdt_enabled (!(wdt->mr & AT91_WDT_WDDIS))

#define wdt_read(wdt, field) \
readl_relaxed((wdt)->reg_base + (field))

#define wdt_write(wtd, field, val) \
writel_relaxed((val), (wdt)->reg_base + (field))
/* 4 slow clock periods is 4/32768 = 122.07µs*/
#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)
{
Expand Down Expand Up @@ -89,7 +114,16 @@ static int sama5d4_wdt_set_timeout(struct watchdog_device *wdd,
wdt->mr &= ~AT91_WDT_WDD;
wdt->mr |= AT91_WDT_SET_WDV(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;

Expand Down Expand Up @@ -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)
{
struct watchdog_device *wdd = &wdt->wdd;
u32 value = WDT_SEC2TICKS(wdd->timeout);
u32 reg;

/*
* Because the fields WDV and WDD must not be modified when the WDDIS
* bit is set, so clear the WDDIS bit before writing the WDT_MR.
* When booting and resuming, the bootloader may have changed the
* 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);
reg &= ~AT91_WDT_WDDIS;
wdt_write(wdt, AT91_WDT_MR, reg);

wdt->mr |= AT91_WDT_SET_WDD(value);
wdt->mr |= AT91_WDT_SET_WDV(value);

wdt_write(wdt, AT91_WDT_MR, wdt->mr);

if (wdt_enabled) {
wdt_write_nosleep(wdt, AT91_WDT_MR, wdt->mr);
} else {
reg = wdt_read(wdt, AT91_WDT_MR);
if (!(reg & AT91_WDT_WDDIS))
wdt_write_nosleep(wdt, AT91_WDT_MR,
reg | AT91_WDT_WDDIS);
}
return 0;
}

Expand All @@ -172,6 +204,7 @@ static int sama5d4_wdt_probe(struct platform_device *pdev)
struct resource *res;
void __iomem *regs;
u32 irq = 0;
u32 timeout;
int ret;

wdt = devm_kzalloc(&pdev->dev, sizeof(*wdt), GFP_KERNEL);
Expand All @@ -184,6 +217,7 @@ static int sama5d4_wdt_probe(struct platform_device *pdev)
wdd->ops = &sama5d4_wdt_ops;
wdd->min_timeout = MIN_WDT_TIMEOUT;
wdd->max_timeout = MAX_WDT_TIMEOUT;
wdt->last_ping = jiffies;

watchdog_set_drvdata(wdd, wdt);

Expand Down Expand Up @@ -221,6 +255,11 @@ static int sama5d4_wdt_probe(struct platform_device *pdev)
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);
if (ret)
return ret;
Expand Down Expand Up @@ -263,9 +302,7 @@ static int sama5d4_wdt_resume(struct device *dev)
{
struct sama5d4_wdt *wdt = dev_get_drvdata(dev);

wdt_write(wdt, AT91_WDT_MR, wdt->mr & ~AT91_WDT_WDDIS);
if (wdt->mr & AT91_WDT_WDDIS)
wdt_write(wdt, AT91_WDT_MR, wdt->mr);
sama5d4_wdt_init(wdt);

return 0;
}
Expand Down
2 changes: 1 addition & 1 deletion drivers/watchdog/wdt_pci.c
Original file line number Diff line number Diff line change
Expand Up @@ -332,7 +332,7 @@ static irqreturn_t wdtpci_interrupt(int irq, void *dev_id)
pr_crit("Would Reboot\n");
#else
pr_crit("Initiating system reboot\n");
emergency_restart(NULL);
emergency_restart();
#endif
#else
pr_crit("Reset in 5ms\n");
Expand Down
4 changes: 1 addition & 3 deletions drivers/watchdog/zx2967_wdt.c
Original file line number Diff line number Diff line change
Expand Up @@ -211,10 +211,8 @@ static int zx2967_wdt_probe(struct platform_device *pdev)

base = platform_get_resource(pdev, IORESOURCE_MEM, 0);
wdt->reg_base = devm_ioremap_resource(dev, base);
if (IS_ERR(wdt->reg_base)) {
dev_err(dev, "ioremap failed\n");
if (IS_ERR(wdt->reg_base))
return PTR_ERR(wdt->reg_base);
}

zx2967_wdt_reset_sysctrl(dev);

Expand Down

0 comments on commit ec53c02

Please sign in to comment.