diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2017-05-20 11:35:27 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2017-05-20 11:35:27 -0400 |
commit | ec53c027f3e6b0ee82a5d18de7b0d8bfae3ec374 (patch) | |
tree | aacd536cf8c7562c8a0464821cd3e53c4ce27399 | |
parent | cf80a6fbca377516628ea50507eded0a51e88d8c (diff) | |
parent | fedf266f9955d9a019643cde199a2fd9a0259f6f (diff) |
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
-rw-r--r-- | Documentation/watchdog/watchdog-parameters.txt | 2 | ||||
-rw-r--r-- | drivers/watchdog/Kconfig | 2 | ||||
-rw-r--r-- | drivers/watchdog/bcm_kona_wdt.c | 3 | ||||
-rw-r--r-- | drivers/watchdog/cadence_wdt.c | 2 | ||||
-rw-r--r-- | drivers/watchdog/iTCO_wdt.c | 22 | ||||
-rw-r--r-- | drivers/watchdog/pcwd_usb.c | 3 | ||||
-rw-r--r-- | drivers/watchdog/sama5d4_wdt.c | 77 | ||||
-rw-r--r-- | drivers/watchdog/wdt_pci.c | 2 | ||||
-rw-r--r-- | drivers/watchdog/zx2967_wdt.c | 4 |
9 files changed, 77 insertions, 40 deletions
diff --git a/Documentation/watchdog/watchdog-parameters.txt b/Documentation/watchdog/watchdog-parameters.txt index 4f7d86dd0a5d..914518aeb972 100644 --- a/Documentation/watchdog/watchdog-parameters.txt +++ b/Documentation/watchdog/watchdog-parameters.txt | |||
@@ -117,7 +117,7 @@ nowayout: Watchdog cannot be stopped once started | |||
117 | ------------------------------------------------- | 117 | ------------------------------------------------- |
118 | iTCO_wdt: | 118 | iTCO_wdt: |
119 | heartbeat: Watchdog heartbeat in seconds. | 119 | heartbeat: Watchdog heartbeat in seconds. |
120 | (2<heartbeat<39 (TCO v1) or 613 (TCO v2), default=30) | 120 | (5<=heartbeat<=74 (TCO v1) or 1226 (TCO v2), default=30) |
121 | nowayout: Watchdog cannot be stopped once started | 121 | nowayout: Watchdog cannot be stopped once started |
122 | (default=kernel config parameter) | 122 | (default=kernel config parameter) |
123 | ------------------------------------------------- | 123 | ------------------------------------------------- |
diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig index 52a70ee6014f..8b9049dac094 100644 --- a/drivers/watchdog/Kconfig +++ b/drivers/watchdog/Kconfig | |||
@@ -452,7 +452,7 @@ config DAVINCI_WATCHDOG | |||
452 | 452 | ||
453 | config ORION_WATCHDOG | 453 | config ORION_WATCHDOG |
454 | tristate "Orion watchdog" | 454 | tristate "Orion watchdog" |
455 | depends on ARCH_ORION5X || ARCH_DOVE || MACH_DOVE || ARCH_MVEBU || COMPILE_TEST | 455 | depends on ARCH_ORION5X || ARCH_DOVE || MACH_DOVE || ARCH_MVEBU || (COMPILE_TEST && !ARCH_EBSA110) |
456 | depends on ARM | 456 | depends on ARM |
457 | select WATCHDOG_CORE | 457 | select WATCHDOG_CORE |
458 | help | 458 | help |
diff --git a/drivers/watchdog/bcm_kona_wdt.c b/drivers/watchdog/bcm_kona_wdt.c index 6fce17d5b9f1..a5775dfd8d5f 100644 --- a/drivers/watchdog/bcm_kona_wdt.c +++ b/drivers/watchdog/bcm_kona_wdt.c | |||
@@ -304,6 +304,8 @@ static int bcm_kona_wdt_probe(struct platform_device *pdev) | |||
304 | if (!wdt) | 304 | if (!wdt) |
305 | return -ENOMEM; | 305 | return -ENOMEM; |
306 | 306 | ||
307 | spin_lock_init(&wdt->lock); | ||
308 | |||
307 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | 309 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); |
308 | wdt->base = devm_ioremap_resource(dev, res); | 310 | wdt->base = devm_ioremap_resource(dev, res); |
309 | if (IS_ERR(wdt->base)) | 311 | if (IS_ERR(wdt->base)) |
@@ -316,7 +318,6 @@ static int bcm_kona_wdt_probe(struct platform_device *pdev) | |||
316 | return ret; | 318 | return ret; |
317 | } | 319 | } |
318 | 320 | ||
319 | spin_lock_init(&wdt->lock); | ||
320 | platform_set_drvdata(pdev, wdt); | 321 | platform_set_drvdata(pdev, wdt); |
321 | watchdog_set_drvdata(&bcm_kona_wdt_wdd, wdt); | 322 | watchdog_set_drvdata(&bcm_kona_wdt_wdd, wdt); |
322 | bcm_kona_wdt_wdd.parent = &pdev->dev; | 323 | bcm_kona_wdt_wdd.parent = &pdev->dev; |
diff --git a/drivers/watchdog/cadence_wdt.c b/drivers/watchdog/cadence_wdt.c index 8d61e8bfe60b..86e0b5d2e761 100644 --- a/drivers/watchdog/cadence_wdt.c +++ b/drivers/watchdog/cadence_wdt.c | |||
@@ -49,7 +49,7 @@ | |||
49 | /* Counter maximum value */ | 49 | /* Counter maximum value */ |
50 | #define CDNS_WDT_COUNTER_MAX 0xFFF | 50 | #define CDNS_WDT_COUNTER_MAX 0xFFF |
51 | 51 | ||
52 | static int wdt_timeout = CDNS_WDT_DEFAULT_TIMEOUT; | 52 | static int wdt_timeout; |
53 | static int nowayout = WATCHDOG_NOWAYOUT; | 53 | static int nowayout = WATCHDOG_NOWAYOUT; |
54 | 54 | ||
55 | module_param(wdt_timeout, int, 0); | 55 | module_param(wdt_timeout, int, 0); |
diff --git a/drivers/watchdog/iTCO_wdt.c b/drivers/watchdog/iTCO_wdt.c index 347f0389b089..c4f65873bfa4 100644 --- a/drivers/watchdog/iTCO_wdt.c +++ b/drivers/watchdog/iTCO_wdt.c | |||
@@ -306,16 +306,15 @@ static int iTCO_wdt_ping(struct watchdog_device *wd_dev) | |||
306 | 306 | ||
307 | iTCO_vendor_pre_keepalive(p->smi_res, wd_dev->timeout); | 307 | iTCO_vendor_pre_keepalive(p->smi_res, wd_dev->timeout); |
308 | 308 | ||
309 | /* Reset the timeout status bit so that the timer | ||
310 | * needs to count down twice again before rebooting */ | ||
311 | outw(0x0008, TCO1_STS(p)); /* write 1 to clear bit */ | ||
312 | |||
309 | /* Reload the timer by writing to the TCO Timer Counter register */ | 313 | /* Reload the timer by writing to the TCO Timer Counter register */ |
310 | if (p->iTCO_version >= 2) { | 314 | if (p->iTCO_version >= 2) |
311 | outw(0x01, TCO_RLD(p)); | 315 | outw(0x01, TCO_RLD(p)); |
312 | } else if (p->iTCO_version == 1) { | 316 | else if (p->iTCO_version == 1) |
313 | /* Reset the timeout status bit so that the timer | ||
314 | * needs to count down twice again before rebooting */ | ||
315 | outw(0x0008, TCO1_STS(p)); /* write 1 to clear bit */ | ||
316 | |||
317 | outb(0x01, TCO_RLD(p)); | 317 | outb(0x01, TCO_RLD(p)); |
318 | } | ||
319 | 318 | ||
320 | spin_unlock(&p->io_lock); | 319 | spin_unlock(&p->io_lock); |
321 | return 0; | 320 | return 0; |
@@ -328,11 +327,8 @@ static int iTCO_wdt_set_timeout(struct watchdog_device *wd_dev, unsigned int t) | |||
328 | unsigned char val8; | 327 | unsigned char val8; |
329 | unsigned int tmrval; | 328 | unsigned int tmrval; |
330 | 329 | ||
331 | tmrval = seconds_to_ticks(p, t); | 330 | /* The timer counts down twice before rebooting */ |
332 | 331 | tmrval = seconds_to_ticks(p, t) / 2; | |
333 | /* For TCO v1 the timer counts down twice before rebooting */ | ||
334 | if (p->iTCO_version == 1) | ||
335 | tmrval /= 2; | ||
336 | 332 | ||
337 | /* from the specs: */ | 333 | /* from the specs: */ |
338 | /* "Values of 0h-3h are ignored and should not be attempted" */ | 334 | /* "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 | spin_lock(&p->io_lock); | 381 | spin_lock(&p->io_lock); |
386 | val16 = inw(TCO_RLD(p)); | 382 | val16 = inw(TCO_RLD(p)); |
387 | val16 &= 0x3ff; | 383 | val16 &= 0x3ff; |
384 | if (!(inw(TCO1_STS(p)) & 0x0008)) | ||
385 | val16 += (inw(TCOv2_TMR(p)) & 0x3ff); | ||
388 | spin_unlock(&p->io_lock); | 386 | spin_unlock(&p->io_lock); |
389 | 387 | ||
390 | time_left = ticks_to_seconds(p, val16); | 388 | time_left = ticks_to_seconds(p, val16); |
diff --git a/drivers/watchdog/pcwd_usb.c b/drivers/watchdog/pcwd_usb.c index 99ebf6ea3de6..5615f4013924 100644 --- a/drivers/watchdog/pcwd_usb.c +++ b/drivers/watchdog/pcwd_usb.c | |||
@@ -630,6 +630,9 @@ static int usb_pcwd_probe(struct usb_interface *interface, | |||
630 | return -ENODEV; | 630 | return -ENODEV; |
631 | } | 631 | } |
632 | 632 | ||
633 | if (iface_desc->desc.bNumEndpoints < 1) | ||
634 | return -ENODEV; | ||
635 | |||
633 | /* check out the endpoint: it has to be Interrupt & IN */ | 636 | /* check out the endpoint: it has to be Interrupt & IN */ |
634 | endpoint = &iface_desc->endpoint[0].desc; | 637 | endpoint = &iface_desc->endpoint[0].desc; |
635 | 638 | ||
diff --git a/drivers/watchdog/sama5d4_wdt.c b/drivers/watchdog/sama5d4_wdt.c index f709962018ac..362fd229786d 100644 --- a/drivers/watchdog/sama5d4_wdt.c +++ b/drivers/watchdog/sama5d4_wdt.c | |||
@@ -6,6 +6,7 @@ | |||
6 | * Licensed under GPLv2. | 6 | * Licensed under GPLv2. |
7 | */ | 7 | */ |
8 | 8 | ||
9 | #include <linux/delay.h> | ||
9 | #include <linux/interrupt.h> | 10 | #include <linux/interrupt.h> |
10 | #include <linux/io.h> | 11 | #include <linux/io.h> |
11 | #include <linux/kernel.h> | 12 | #include <linux/kernel.h> |
@@ -29,6 +30,7 @@ struct sama5d4_wdt { | |||
29 | struct watchdog_device wdd; | 30 | struct watchdog_device wdd; |
30 | void __iomem *reg_base; | 31 | void __iomem *reg_base; |
31 | u32 mr; | 32 | u32 mr; |
33 | unsigned long last_ping; | ||
32 | }; | 34 | }; |
33 | 35 | ||
34 | static int wdt_timeout = WDT_DEFAULT_TIMEOUT; | 36 | static int wdt_timeout = WDT_DEFAULT_TIMEOUT; |
@@ -44,11 +46,34 @@ MODULE_PARM_DESC(nowayout, | |||
44 | "Watchdog cannot be stopped once started (default=" | 46 | "Watchdog cannot be stopped once started (default=" |
45 | __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); | 47 | __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); |
46 | 48 | ||
49 | #define wdt_enabled (!(wdt->mr & AT91_WDT_WDDIS)) | ||
50 | |||
47 | #define wdt_read(wdt, field) \ | 51 | #define wdt_read(wdt, field) \ |
48 | readl_relaxed((wdt)->reg_base + (field)) | 52 | readl_relaxed((wdt)->reg_base + (field)) |
49 | 53 | ||
50 | #define wdt_write(wtd, field, val) \ | 54 | /* 4 slow clock periods is 4/32768 = 122.07µs*/ |
51 | writel_relaxed((val), (wdt)->reg_base + (field)) | 55 | #define WDT_DELAY usecs_to_jiffies(123) |
56 | |||
57 | static void wdt_write(struct sama5d4_wdt *wdt, u32 field, u32 val) | ||
58 | { | ||
59 | /* | ||
60 | * WDT_CR and WDT_MR must not be modified within three slow clock | ||
61 | * periods following a restart of the watchdog performed by a write | ||
62 | * access in WDT_CR. | ||
63 | */ | ||
64 | while (time_before(jiffies, wdt->last_ping + WDT_DELAY)) | ||
65 | usleep_range(30, 125); | ||
66 | writel_relaxed(val, wdt->reg_base + field); | ||
67 | wdt->last_ping = jiffies; | ||
68 | } | ||
69 | |||
70 | static void wdt_write_nosleep(struct sama5d4_wdt *wdt, u32 field, u32 val) | ||
71 | { | ||
72 | if (time_before(jiffies, wdt->last_ping + WDT_DELAY)) | ||
73 | udelay(123); | ||
74 | writel_relaxed(val, wdt->reg_base + field); | ||
75 | wdt->last_ping = jiffies; | ||
76 | } | ||
52 | 77 | ||
53 | static int sama5d4_wdt_start(struct watchdog_device *wdd) | 78 | static int sama5d4_wdt_start(struct watchdog_device *wdd) |
54 | { | 79 | { |
@@ -89,7 +114,16 @@ static int sama5d4_wdt_set_timeout(struct watchdog_device *wdd, | |||
89 | wdt->mr &= ~AT91_WDT_WDD; | 114 | wdt->mr &= ~AT91_WDT_WDD; |
90 | wdt->mr |= AT91_WDT_SET_WDV(value); | 115 | wdt->mr |= AT91_WDT_SET_WDV(value); |
91 | wdt->mr |= AT91_WDT_SET_WDD(value); | 116 | wdt->mr |= AT91_WDT_SET_WDD(value); |
92 | wdt_write(wdt, AT91_WDT_MR, wdt->mr); | 117 | |
118 | /* | ||
119 | * WDDIS has to be 0 when updating WDD/WDV. The datasheet states: When | ||
120 | * setting the WDDIS bit, and while it is set, the fields WDV and WDD | ||
121 | * must not be modified. | ||
122 | * If the watchdog is enabled, then the timeout can be updated. Else, | ||
123 | * wait that the user enables it. | ||
124 | */ | ||
125 | if (wdt_enabled) | ||
126 | wdt_write(wdt, AT91_WDT_MR, wdt->mr & ~AT91_WDT_WDDIS); | ||
93 | 127 | ||
94 | wdd->timeout = timeout; | 128 | wdd->timeout = timeout; |
95 | 129 | ||
@@ -145,23 +179,21 @@ static int of_sama5d4_wdt_init(struct device_node *np, struct sama5d4_wdt *wdt) | |||
145 | 179 | ||
146 | static int sama5d4_wdt_init(struct sama5d4_wdt *wdt) | 180 | static int sama5d4_wdt_init(struct sama5d4_wdt *wdt) |
147 | { | 181 | { |
148 | struct watchdog_device *wdd = &wdt->wdd; | ||
149 | u32 value = WDT_SEC2TICKS(wdd->timeout); | ||
150 | u32 reg; | 182 | u32 reg; |
151 | |||
152 | /* | 183 | /* |
153 | * Because the fields WDV and WDD must not be modified when the WDDIS | 184 | * When booting and resuming, the bootloader may have changed the |
154 | * bit is set, so clear the WDDIS bit before writing the WDT_MR. | 185 | * watchdog configuration. |
186 | * If the watchdog is already running, we can safely update it. | ||
187 | * Else, we have to disable it properly. | ||
155 | */ | 188 | */ |
156 | reg = wdt_read(wdt, AT91_WDT_MR); | 189 | if (wdt_enabled) { |
157 | reg &= ~AT91_WDT_WDDIS; | 190 | wdt_write_nosleep(wdt, AT91_WDT_MR, wdt->mr); |
158 | wdt_write(wdt, AT91_WDT_MR, reg); | 191 | } else { |
159 | 192 | reg = wdt_read(wdt, AT91_WDT_MR); | |
160 | wdt->mr |= AT91_WDT_SET_WDD(value); | 193 | if (!(reg & AT91_WDT_WDDIS)) |
161 | wdt->mr |= AT91_WDT_SET_WDV(value); | 194 | wdt_write_nosleep(wdt, AT91_WDT_MR, |
162 | 195 | reg | AT91_WDT_WDDIS); | |
163 | wdt_write(wdt, AT91_WDT_MR, wdt->mr); | 196 | } |
164 | |||
165 | return 0; | 197 | return 0; |
166 | } | 198 | } |
167 | 199 | ||
@@ -172,6 +204,7 @@ static int sama5d4_wdt_probe(struct platform_device *pdev) | |||
172 | struct resource *res; | 204 | struct resource *res; |
173 | void __iomem *regs; | 205 | void __iomem *regs; |
174 | u32 irq = 0; | 206 | u32 irq = 0; |
207 | u32 timeout; | ||
175 | int ret; | 208 | int ret; |
176 | 209 | ||
177 | wdt = devm_kzalloc(&pdev->dev, sizeof(*wdt), GFP_KERNEL); | 210 | wdt = devm_kzalloc(&pdev->dev, sizeof(*wdt), GFP_KERNEL); |
@@ -184,6 +217,7 @@ static int sama5d4_wdt_probe(struct platform_device *pdev) | |||
184 | wdd->ops = &sama5d4_wdt_ops; | 217 | wdd->ops = &sama5d4_wdt_ops; |
185 | wdd->min_timeout = MIN_WDT_TIMEOUT; | 218 | wdd->min_timeout = MIN_WDT_TIMEOUT; |
186 | wdd->max_timeout = MAX_WDT_TIMEOUT; | 219 | wdd->max_timeout = MAX_WDT_TIMEOUT; |
220 | wdt->last_ping = jiffies; | ||
187 | 221 | ||
188 | watchdog_set_drvdata(wdd, wdt); | 222 | watchdog_set_drvdata(wdd, wdt); |
189 | 223 | ||
@@ -221,6 +255,11 @@ static int sama5d4_wdt_probe(struct platform_device *pdev) | |||
221 | return ret; | 255 | return ret; |
222 | } | 256 | } |
223 | 257 | ||
258 | timeout = WDT_SEC2TICKS(wdd->timeout); | ||
259 | |||
260 | wdt->mr |= AT91_WDT_SET_WDD(timeout); | ||
261 | wdt->mr |= AT91_WDT_SET_WDV(timeout); | ||
262 | |||
224 | ret = sama5d4_wdt_init(wdt); | 263 | ret = sama5d4_wdt_init(wdt); |
225 | if (ret) | 264 | if (ret) |
226 | return ret; | 265 | return ret; |
@@ -263,9 +302,7 @@ static int sama5d4_wdt_resume(struct device *dev) | |||
263 | { | 302 | { |
264 | struct sama5d4_wdt *wdt = dev_get_drvdata(dev); | 303 | struct sama5d4_wdt *wdt = dev_get_drvdata(dev); |
265 | 304 | ||
266 | wdt_write(wdt, AT91_WDT_MR, wdt->mr & ~AT91_WDT_WDDIS); | 305 | sama5d4_wdt_init(wdt); |
267 | if (wdt->mr & AT91_WDT_WDDIS) | ||
268 | wdt_write(wdt, AT91_WDT_MR, wdt->mr); | ||
269 | 306 | ||
270 | return 0; | 307 | return 0; |
271 | } | 308 | } |
diff --git a/drivers/watchdog/wdt_pci.c b/drivers/watchdog/wdt_pci.c index 48b2c058b009..bc7addc2dc06 100644 --- a/drivers/watchdog/wdt_pci.c +++ b/drivers/watchdog/wdt_pci.c | |||
@@ -332,7 +332,7 @@ static irqreturn_t wdtpci_interrupt(int irq, void *dev_id) | |||
332 | pr_crit("Would Reboot\n"); | 332 | pr_crit("Would Reboot\n"); |
333 | #else | 333 | #else |
334 | pr_crit("Initiating system reboot\n"); | 334 | pr_crit("Initiating system reboot\n"); |
335 | emergency_restart(NULL); | 335 | emergency_restart(); |
336 | #endif | 336 | #endif |
337 | #else | 337 | #else |
338 | pr_crit("Reset in 5ms\n"); | 338 | pr_crit("Reset in 5ms\n"); |
diff --git a/drivers/watchdog/zx2967_wdt.c b/drivers/watchdog/zx2967_wdt.c index e290d5a13a6d..c98252733c30 100644 --- a/drivers/watchdog/zx2967_wdt.c +++ b/drivers/watchdog/zx2967_wdt.c | |||
@@ -211,10 +211,8 @@ static int zx2967_wdt_probe(struct platform_device *pdev) | |||
211 | 211 | ||
212 | base = platform_get_resource(pdev, IORESOURCE_MEM, 0); | 212 | base = platform_get_resource(pdev, IORESOURCE_MEM, 0); |
213 | wdt->reg_base = devm_ioremap_resource(dev, base); | 213 | wdt->reg_base = devm_ioremap_resource(dev, base); |
214 | if (IS_ERR(wdt->reg_base)) { | 214 | if (IS_ERR(wdt->reg_base)) |
215 | dev_err(dev, "ioremap failed\n"); | ||
216 | return PTR_ERR(wdt->reg_base); | 215 | return PTR_ERR(wdt->reg_base); |
217 | } | ||
218 | 216 | ||
219 | zx2967_wdt_reset_sysctrl(dev); | 217 | zx2967_wdt_reset_sysctrl(dev); |
220 | 218 | ||