diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2014-10-21 11:24:55 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2014-10-21 11:24:55 -0400 |
commit | 21d2271fd0812ebe3716cab0b48356837485a74d (patch) | |
tree | d2f009276c26028707a1912d9d26149dde32a90b | |
parent | 045aaedab67bc3f2f01fe46917e0e17a6b5a7d5d (diff) | |
parent | 06980b24cf9bfcc753a07ee362976169bb869869 (diff) |
Merge git://www.linux-watchdog.org/linux-watchdog
Pull watchdog updates from Wim Van Sebroeck:
- new Cadence WDT driver
- new Ricoh RN5T618 watchdog
- new DA9063 PMIC watchdog driver
- new Meson WDT driver
- add restart handling code
- fixes and improvements
* git://www.linux-watchdog.org/linux-watchdog: (25 commits)
watchdog: meson: remove magic value for reboot
watchdog: Let XILINX_WATCHDOG and TEGRA_WATCHDOG depend on HAS_IOMEM
watchdog: sunxi: Add A31 watchdog support
watchdog: sunxi: support parameterized compatible strings
watchdog: imx2_wdt: add restart handler support
watchdog: qcom: register a restart notifier
watchdog: s3c2410: add restart handler
watchdog: dw_wdt: add restart handler support
ARM: defconfig: update multi_v7_defconfig
ARM: meson: add watchdog driver
ARM: docs: add documentation binding for meson watchdog
stmp3xxx_rtc_wdt: Add suspend/resume PM support
watchdog: Add DA9063 PMIC watchdog driver.
watchdog: add driver for Ricoh RN5T618 watchdog
watchdog: s3c2410_wdt: Add support for Watchdog device on Exynos7
watchdog: qcom: document device tree bindings
watchdog: qcom: add support for KPSS WDT
watchdog: dw_wdt: initialise TOP_INIT in dw_wdt_set_top()
devicetree: Add Cadence WDT devicetree bindings documentation
watchdog: Add Cadence WDT driver
...
22 files changed, 1725 insertions, 70 deletions
diff --git a/Documentation/devicetree/bindings/watchdog/cadence-wdt.txt b/Documentation/devicetree/bindings/watchdog/cadence-wdt.txt new file mode 100644 index 000000000000..c3a36ee45552 --- /dev/null +++ b/Documentation/devicetree/bindings/watchdog/cadence-wdt.txt | |||
@@ -0,0 +1,24 @@ | |||
1 | Zynq Watchdog Device Tree Bindings | ||
2 | ------------------------------------------- | ||
3 | |||
4 | Required properties: | ||
5 | - compatible : Should be "cdns,wdt-r1p2". | ||
6 | - clocks : This is pclk (APB clock). | ||
7 | - interrupts : This is wd_irq - watchdog timeout interrupt. | ||
8 | - interrupt-parent : Must be core interrupt controller. | ||
9 | |||
10 | Optional properties | ||
11 | - reset-on-timeout : If this property exists, then a reset is done | ||
12 | when watchdog times out. | ||
13 | - timeout-sec : Watchdog timeout value (in seconds). | ||
14 | |||
15 | Example: | ||
16 | watchdog@f8005000 { | ||
17 | compatible = "cdns,wdt-r1p2"; | ||
18 | clocks = <&clkc 45>; | ||
19 | interrupt-parent = <&intc>; | ||
20 | interrupts = <0 9 1>; | ||
21 | reg = <0xf8005000 0x1000>; | ||
22 | reset-on-timeout; | ||
23 | timeout-sec = <10>; | ||
24 | }; | ||
diff --git a/Documentation/devicetree/bindings/watchdog/fsl-imx-wdt.txt b/Documentation/devicetree/bindings/watchdog/fsl-imx-wdt.txt index e52ba2da868c..8dab6fd024aa 100644 --- a/Documentation/devicetree/bindings/watchdog/fsl-imx-wdt.txt +++ b/Documentation/devicetree/bindings/watchdog/fsl-imx-wdt.txt | |||
@@ -7,7 +7,8 @@ Required properties: | |||
7 | 7 | ||
8 | Optional property: | 8 | Optional property: |
9 | - big-endian: If present the watchdog device's registers are implemented | 9 | - big-endian: If present the watchdog device's registers are implemented |
10 | in big endian mode, otherwise in little mode. | 10 | in big endian mode, otherwise in native mode(same with CPU), for more |
11 | detail please see: Documentation/devicetree/bindings/regmap/regmap.txt. | ||
11 | 12 | ||
12 | Examples: | 13 | Examples: |
13 | 14 | ||
diff --git a/Documentation/devicetree/bindings/watchdog/meson6-wdt.txt b/Documentation/devicetree/bindings/watchdog/meson6-wdt.txt new file mode 100644 index 000000000000..9200fc2d508c --- /dev/null +++ b/Documentation/devicetree/bindings/watchdog/meson6-wdt.txt | |||
@@ -0,0 +1,13 @@ | |||
1 | Meson SoCs Watchdog timer | ||
2 | |||
3 | Required properties: | ||
4 | |||
5 | - compatible : should be "amlogic,meson6-wdt" | ||
6 | - reg : Specifies base physical address and size of the registers. | ||
7 | |||
8 | Example: | ||
9 | |||
10 | wdt: watchdog@c1109900 { | ||
11 | compatible = "amlogic,meson6-wdt"; | ||
12 | reg = <0xc1109900 0x8>; | ||
13 | }; | ||
diff --git a/Documentation/devicetree/bindings/watchdog/qcom-wdt.txt b/Documentation/devicetree/bindings/watchdog/qcom-wdt.txt new file mode 100644 index 000000000000..4726924d034e --- /dev/null +++ b/Documentation/devicetree/bindings/watchdog/qcom-wdt.txt | |||
@@ -0,0 +1,24 @@ | |||
1 | Qualcomm Krait Processor Sub-system (KPSS) Watchdog | ||
2 | --------------------------------------------------- | ||
3 | |||
4 | Required properties : | ||
5 | - compatible : shall contain only one of the following: | ||
6 | |||
7 | "qcom,kpss-wdt-msm8960" | ||
8 | "qcom,kpss-wdt-apq8064" | ||
9 | "qcom,kpss-wdt-ipq8064" | ||
10 | |||
11 | - reg : shall contain base register location and length | ||
12 | - clocks : shall contain the input clock | ||
13 | |||
14 | Optional properties : | ||
15 | - timeout-sec : shall contain the default watchdog timeout in seconds, | ||
16 | if unset, the default timeout is 30 seconds | ||
17 | |||
18 | Example: | ||
19 | watchdog@208a038 { | ||
20 | compatible = "qcom,kpss-wdt-ipq8064"; | ||
21 | reg = <0x0208a038 0x40>; | ||
22 | clocks = <&sleep_clk>; | ||
23 | timeout-sec = <10>; | ||
24 | }; | ||
diff --git a/Documentation/devicetree/bindings/watchdog/samsung-wdt.txt b/Documentation/devicetree/bindings/watchdog/samsung-wdt.txt index cfff37511aac..8f3d96af81d7 100644 --- a/Documentation/devicetree/bindings/watchdog/samsung-wdt.txt +++ b/Documentation/devicetree/bindings/watchdog/samsung-wdt.txt | |||
@@ -9,6 +9,7 @@ Required properties: | |||
9 | (a) "samsung,s3c2410-wdt" for Exynos4 and previous SoCs | 9 | (a) "samsung,s3c2410-wdt" for Exynos4 and previous SoCs |
10 | (b) "samsung,exynos5250-wdt" for Exynos5250 | 10 | (b) "samsung,exynos5250-wdt" for Exynos5250 |
11 | (c) "samsung,exynos5420-wdt" for Exynos5420 | 11 | (c) "samsung,exynos5420-wdt" for Exynos5420 |
12 | (c) "samsung,exynos7-wdt" for Exynos7 | ||
12 | 13 | ||
13 | - reg : base physical address of the controller and length of memory mapped | 14 | - reg : base physical address of the controller and length of memory mapped |
14 | region. | 15 | region. |
diff --git a/arch/arm/configs/multi_v7_defconfig b/arch/arm/configs/multi_v7_defconfig index 491b7d5523bf..9702b140ae04 100644 --- a/arch/arm/configs/multi_v7_defconfig +++ b/arch/arm/configs/multi_v7_defconfig | |||
@@ -261,6 +261,7 @@ CONFIG_WATCHDOG=y | |||
261 | CONFIG_XILINX_WATCHDOG=y | 261 | CONFIG_XILINX_WATCHDOG=y |
262 | CONFIG_ORION_WATCHDOG=y | 262 | CONFIG_ORION_WATCHDOG=y |
263 | CONFIG_SUNXI_WATCHDOG=y | 263 | CONFIG_SUNXI_WATCHDOG=y |
264 | CONFIG_MESON_WATCHDOG=y | ||
264 | CONFIG_MFD_AS3722=y | 265 | CONFIG_MFD_AS3722=y |
265 | CONFIG_MFD_BCM590XX=y | 266 | CONFIG_MFD_BCM590XX=y |
266 | CONFIG_MFD_CROS_EC=y | 267 | CONFIG_MFD_CROS_EC=y |
diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig index e3d5bf0a5021..d0107d424ee4 100644 --- a/drivers/watchdog/Kconfig +++ b/drivers/watchdog/Kconfig | |||
@@ -87,6 +87,15 @@ config DA9055_WATCHDOG | |||
87 | This driver can also be built as a module. If so, the module | 87 | This driver can also be built as a module. If so, the module |
88 | will be called da9055_wdt. | 88 | will be called da9055_wdt. |
89 | 89 | ||
90 | config DA9063_WATCHDOG | ||
91 | tristate "Dialog DA9063 Watchdog" | ||
92 | depends on MFD_DA9063 | ||
93 | select WATCHDOG_CORE | ||
94 | help | ||
95 | Support for the watchdog in the DA9063 PMIC. | ||
96 | |||
97 | This driver can be built as a module. The module name is da9063_wdt. | ||
98 | |||
90 | config GPIO_WATCHDOG | 99 | config GPIO_WATCHDOG |
91 | tristate "Watchdog device controlled through GPIO-line" | 100 | tristate "Watchdog device controlled through GPIO-line" |
92 | depends on OF_GPIO | 101 | depends on OF_GPIO |
@@ -123,6 +132,7 @@ config WM8350_WATCHDOG | |||
123 | 132 | ||
124 | config XILINX_WATCHDOG | 133 | config XILINX_WATCHDOG |
125 | tristate "Xilinx Watchdog timer" | 134 | tristate "Xilinx Watchdog timer" |
135 | depends on HAS_IOMEM | ||
126 | select WATCHDOG_CORE | 136 | select WATCHDOG_CORE |
127 | help | 137 | help |
128 | Watchdog driver for the xps_timebase_wdt ip core. | 138 | Watchdog driver for the xps_timebase_wdt ip core. |
@@ -157,6 +167,14 @@ config AT91SAM9X_WATCHDOG | |||
157 | Watchdog timer embedded into AT91SAM9X and AT91CAP9 chips. This will | 167 | Watchdog timer embedded into AT91SAM9X and AT91CAP9 chips. This will |
158 | reboot your system when the timeout is reached. | 168 | reboot your system when the timeout is reached. |
159 | 169 | ||
170 | config CADENCE_WATCHDOG | ||
171 | tristate "Cadence Watchdog Timer" | ||
172 | depends on ARM | ||
173 | select WATCHDOG_CORE | ||
174 | help | ||
175 | Say Y here if you want to include support for the watchdog | ||
176 | timer in the Xilinx Zynq. | ||
177 | |||
160 | config 21285_WATCHDOG | 178 | config 21285_WATCHDOG |
161 | tristate "DC21285 watchdog" | 179 | tristate "DC21285 watchdog" |
162 | depends on FOOTBRIDGE | 180 | depends on FOOTBRIDGE |
@@ -319,6 +337,17 @@ config ORION_WATCHDOG | |||
319 | To compile this driver as a module, choose M here: the | 337 | To compile this driver as a module, choose M here: the |
320 | module will be called orion_wdt. | 338 | module will be called orion_wdt. |
321 | 339 | ||
340 | config RN5T618_WATCHDOG | ||
341 | tristate "Ricoh RN5T618 watchdog" | ||
342 | depends on MFD_RN5T618 | ||
343 | select WATCHDOG_CORE | ||
344 | help | ||
345 | If you say yes here you get support for watchdog on the Ricoh | ||
346 | RN5T618 PMIC. | ||
347 | |||
348 | This driver can also be built as a module. If so, the module | ||
349 | will be called rn5t618_wdt. | ||
350 | |||
322 | config SUNXI_WATCHDOG | 351 | config SUNXI_WATCHDOG |
323 | tristate "Allwinner SoCs watchdog support" | 352 | tristate "Allwinner SoCs watchdog support" |
324 | depends on ARCH_SUNXI | 353 | depends on ARCH_SUNXI |
@@ -444,7 +473,7 @@ config SIRFSOC_WATCHDOG | |||
444 | 473 | ||
445 | config TEGRA_WATCHDOG | 474 | config TEGRA_WATCHDOG |
446 | tristate "Tegra watchdog" | 475 | tristate "Tegra watchdog" |
447 | depends on ARCH_TEGRA || COMPILE_TEST | 476 | depends on (ARCH_TEGRA || COMPILE_TEST) && HAS_IOMEM |
448 | select WATCHDOG_CORE | 477 | select WATCHDOG_CORE |
449 | help | 478 | help |
450 | Say Y here to include support for the watchdog timer | 479 | Say Y here to include support for the watchdog timer |
@@ -453,6 +482,29 @@ config TEGRA_WATCHDOG | |||
453 | To compile this driver as a module, choose M here: the | 482 | To compile this driver as a module, choose M here: the |
454 | module will be called tegra_wdt. | 483 | module will be called tegra_wdt. |
455 | 484 | ||
485 | config QCOM_WDT | ||
486 | tristate "QCOM watchdog" | ||
487 | depends on HAS_IOMEM | ||
488 | depends on ARCH_QCOM | ||
489 | select WATCHDOG_CORE | ||
490 | help | ||
491 | Say Y here to include Watchdog timer support for the watchdog found | ||
492 | on QCOM chipsets. Currently supported targets are the MSM8960, | ||
493 | APQ8064, and IPQ8064. | ||
494 | |||
495 | To compile this driver as a module, choose M here: the | ||
496 | module will be called qcom_wdt. | ||
497 | |||
498 | config MESON_WATCHDOG | ||
499 | tristate "Amlogic Meson SoCs watchdog support" | ||
500 | depends on ARCH_MESON | ||
501 | select WATCHDOG_CORE | ||
502 | help | ||
503 | Say Y here to include support for the watchdog timer | ||
504 | in Amlogic Meson SoCs. | ||
505 | To compile this driver as a module, choose M here: the | ||
506 | module will be called meson_wdt. | ||
507 | |||
456 | # AVR32 Architecture | 508 | # AVR32 Architecture |
457 | 509 | ||
458 | config AT32AP700X_WDT | 510 | config AT32AP700X_WDT |
diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile index de1701470c14..c569ec8f8a76 100644 --- a/drivers/watchdog/Makefile +++ b/drivers/watchdog/Makefile | |||
@@ -32,6 +32,7 @@ obj-$(CONFIG_USBPCWATCHDOG) += pcwd_usb.o | |||
32 | obj-$(CONFIG_ARM_SP805_WATCHDOG) += sp805_wdt.o | 32 | obj-$(CONFIG_ARM_SP805_WATCHDOG) += sp805_wdt.o |
33 | obj-$(CONFIG_AT91RM9200_WATCHDOG) += at91rm9200_wdt.o | 33 | obj-$(CONFIG_AT91RM9200_WATCHDOG) += at91rm9200_wdt.o |
34 | obj-$(CONFIG_AT91SAM9X_WATCHDOG) += at91sam9_wdt.o | 34 | obj-$(CONFIG_AT91SAM9X_WATCHDOG) += at91sam9_wdt.o |
35 | obj-$(CONFIG_CADENCE_WATCHDOG) += cadence_wdt.o | ||
35 | obj-$(CONFIG_OMAP_WATCHDOG) += omap_wdt.o | 36 | obj-$(CONFIG_OMAP_WATCHDOG) += omap_wdt.o |
36 | obj-$(CONFIG_TWL4030_WATCHDOG) += twl4030_wdt.o | 37 | obj-$(CONFIG_TWL4030_WATCHDOG) += twl4030_wdt.o |
37 | obj-$(CONFIG_21285_WATCHDOG) += wdt285.o | 38 | obj-$(CONFIG_21285_WATCHDOG) += wdt285.o |
@@ -47,6 +48,7 @@ obj-$(CONFIG_IOP_WATCHDOG) += iop_wdt.o | |||
47 | obj-$(CONFIG_DAVINCI_WATCHDOG) += davinci_wdt.o | 48 | obj-$(CONFIG_DAVINCI_WATCHDOG) += davinci_wdt.o |
48 | obj-$(CONFIG_ORION_WATCHDOG) += orion_wdt.o | 49 | obj-$(CONFIG_ORION_WATCHDOG) += orion_wdt.o |
49 | obj-$(CONFIG_SUNXI_WATCHDOG) += sunxi_wdt.o | 50 | obj-$(CONFIG_SUNXI_WATCHDOG) += sunxi_wdt.o |
51 | obj-$(CONFIG_RN5T618_WATCHDOG) += rn5t618_wdt.o | ||
50 | obj-$(CONFIG_COH901327_WATCHDOG) += coh901327_wdt.o | 52 | obj-$(CONFIG_COH901327_WATCHDOG) += coh901327_wdt.o |
51 | obj-$(CONFIG_STMP3XXX_RTC_WATCHDOG) += stmp3xxx_rtc_wdt.o | 53 | obj-$(CONFIG_STMP3XXX_RTC_WATCHDOG) += stmp3xxx_rtc_wdt.o |
52 | obj-$(CONFIG_NUC900_WATCHDOG) += nuc900_wdt.o | 54 | obj-$(CONFIG_NUC900_WATCHDOG) += nuc900_wdt.o |
@@ -57,8 +59,10 @@ obj-$(CONFIG_RETU_WATCHDOG) += retu_wdt.o | |||
57 | obj-$(CONFIG_BCM2835_WDT) += bcm2835_wdt.o | 59 | obj-$(CONFIG_BCM2835_WDT) += bcm2835_wdt.o |
58 | obj-$(CONFIG_MOXART_WDT) += moxart_wdt.o | 60 | obj-$(CONFIG_MOXART_WDT) += moxart_wdt.o |
59 | obj-$(CONFIG_SIRFSOC_WATCHDOG) += sirfsoc_wdt.o | 61 | obj-$(CONFIG_SIRFSOC_WATCHDOG) += sirfsoc_wdt.o |
62 | obj-$(CONFIG_QCOM_WDT) += qcom-wdt.o | ||
60 | obj-$(CONFIG_BCM_KONA_WDT) += bcm_kona_wdt.o | 63 | obj-$(CONFIG_BCM_KONA_WDT) += bcm_kona_wdt.o |
61 | obj-$(CONFIG_TEGRA_WATCHDOG) += tegra_wdt.o | 64 | obj-$(CONFIG_TEGRA_WATCHDOG) += tegra_wdt.o |
65 | obj-$(CONFIG_MESON_WATCHDOG) += meson_wdt.o | ||
62 | 66 | ||
63 | # AVR32 Architecture | 67 | # AVR32 Architecture |
64 | obj-$(CONFIG_AT32AP700X_WDT) += at32ap700x_wdt.o | 68 | obj-$(CONFIG_AT32AP700X_WDT) += at32ap700x_wdt.o |
@@ -173,6 +177,7 @@ obj-$(CONFIG_XEN_WDT) += xen_wdt.o | |||
173 | # Architecture Independent | 177 | # Architecture Independent |
174 | obj-$(CONFIG_DA9052_WATCHDOG) += da9052_wdt.o | 178 | obj-$(CONFIG_DA9052_WATCHDOG) += da9052_wdt.o |
175 | obj-$(CONFIG_DA9055_WATCHDOG) += da9055_wdt.o | 179 | obj-$(CONFIG_DA9055_WATCHDOG) += da9055_wdt.o |
180 | obj-$(CONFIG_DA9063_WATCHDOG) += da9063_wdt.o | ||
176 | obj-$(CONFIG_GPIO_WATCHDOG) += gpio_wdt.o | 181 | obj-$(CONFIG_GPIO_WATCHDOG) += gpio_wdt.o |
177 | obj-$(CONFIG_WM831X_WATCHDOG) += wm831x_wdt.o | 182 | obj-$(CONFIG_WM831X_WATCHDOG) += wm831x_wdt.o |
178 | obj-$(CONFIG_WM8350_WATCHDOG) += wm8350_wdt.o | 183 | obj-$(CONFIG_WM8350_WATCHDOG) += wm8350_wdt.o |
diff --git a/drivers/watchdog/booke_wdt.c b/drivers/watchdog/booke_wdt.c index 08a785398eac..e96b09b135c8 100644 --- a/drivers/watchdog/booke_wdt.c +++ b/drivers/watchdog/booke_wdt.c | |||
@@ -30,8 +30,6 @@ | |||
30 | * occur, and the final time the board will reset. | 30 | * occur, and the final time the board will reset. |
31 | */ | 31 | */ |
32 | 32 | ||
33 | u32 booke_wdt_enabled; | ||
34 | u32 booke_wdt_period = CONFIG_BOOKE_WDT_DEFAULT_TIMEOUT; | ||
35 | 33 | ||
36 | #ifdef CONFIG_PPC_FSL_BOOK3E | 34 | #ifdef CONFIG_PPC_FSL_BOOK3E |
37 | #define WDTP(x) ((((x)&0x3)<<30)|(((x)&0x3c)<<15)) | 35 | #define WDTP(x) ((((x)&0x3)<<30)|(((x)&0x3c)<<15)) |
@@ -41,27 +39,10 @@ u32 booke_wdt_period = CONFIG_BOOKE_WDT_DEFAULT_TIMEOUT; | |||
41 | #define WDTP_MASK (TCR_WP_MASK) | 39 | #define WDTP_MASK (TCR_WP_MASK) |
42 | #endif | 40 | #endif |
43 | 41 | ||
44 | /* Checks wdt=x and wdt_period=xx command-line option */ | 42 | static bool booke_wdt_enabled; |
45 | notrace int __init early_parse_wdt(char *p) | 43 | module_param(booke_wdt_enabled, bool, 0); |
46 | { | 44 | static int booke_wdt_period = CONFIG_BOOKE_WDT_DEFAULT_TIMEOUT; |
47 | if (p && strncmp(p, "0", 1) != 0) | 45 | module_param(booke_wdt_period, int, 0); |
48 | booke_wdt_enabled = 1; | ||
49 | |||
50 | return 0; | ||
51 | } | ||
52 | early_param("wdt", early_parse_wdt); | ||
53 | |||
54 | int __init early_parse_wdt_period(char *p) | ||
55 | { | ||
56 | unsigned long ret; | ||
57 | if (p) { | ||
58 | if (!kstrtol(p, 0, &ret)) | ||
59 | booke_wdt_period = ret; | ||
60 | } | ||
61 | |||
62 | return 0; | ||
63 | } | ||
64 | early_param("wdt_period", early_parse_wdt_period); | ||
65 | 46 | ||
66 | #ifdef CONFIG_PPC_FSL_BOOK3E | 47 | #ifdef CONFIG_PPC_FSL_BOOK3E |
67 | 48 | ||
@@ -259,5 +240,6 @@ static int __init booke_wdt_init(void) | |||
259 | module_init(booke_wdt_init); | 240 | module_init(booke_wdt_init); |
260 | module_exit(booke_wdt_exit); | 241 | module_exit(booke_wdt_exit); |
261 | 242 | ||
243 | MODULE_ALIAS("booke_wdt"); | ||
262 | MODULE_DESCRIPTION("PowerPC Book-E watchdog driver"); | 244 | MODULE_DESCRIPTION("PowerPC Book-E watchdog driver"); |
263 | MODULE_LICENSE("GPL"); | 245 | MODULE_LICENSE("GPL"); |
diff --git a/drivers/watchdog/cadence_wdt.c b/drivers/watchdog/cadence_wdt.c new file mode 100644 index 000000000000..5927c0a98a74 --- /dev/null +++ b/drivers/watchdog/cadence_wdt.c | |||
@@ -0,0 +1,516 @@ | |||
1 | /* | ||
2 | * Cadence WDT driver - Used by Xilinx Zynq | ||
3 | * | ||
4 | * Copyright (C) 2010 - 2014 Xilinx, Inc. | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or | ||
7 | * modify it under the terms of the GNU General Public License | ||
8 | * as published by the Free Software Foundation; either version | ||
9 | * 2 of the License, or (at your option) any later version. | ||
10 | */ | ||
11 | |||
12 | #include <linux/clk.h> | ||
13 | #include <linux/init.h> | ||
14 | #include <linux/interrupt.h> | ||
15 | #include <linux/io.h> | ||
16 | #include <linux/irq.h> | ||
17 | #include <linux/kernel.h> | ||
18 | #include <linux/module.h> | ||
19 | #include <linux/of.h> | ||
20 | #include <linux/platform_device.h> | ||
21 | #include <linux/reboot.h> | ||
22 | #include <linux/watchdog.h> | ||
23 | |||
24 | #define CDNS_WDT_DEFAULT_TIMEOUT 10 | ||
25 | /* Supports 1 - 516 sec */ | ||
26 | #define CDNS_WDT_MIN_TIMEOUT 1 | ||
27 | #define CDNS_WDT_MAX_TIMEOUT 516 | ||
28 | |||
29 | /* Restart key */ | ||
30 | #define CDNS_WDT_RESTART_KEY 0x00001999 | ||
31 | |||
32 | /* Counter register access key */ | ||
33 | #define CDNS_WDT_REGISTER_ACCESS_KEY 0x00920000 | ||
34 | |||
35 | /* Counter value divisor */ | ||
36 | #define CDNS_WDT_COUNTER_VALUE_DIVISOR 0x1000 | ||
37 | |||
38 | /* Clock prescaler value and selection */ | ||
39 | #define CDNS_WDT_PRESCALE_64 64 | ||
40 | #define CDNS_WDT_PRESCALE_512 512 | ||
41 | #define CDNS_WDT_PRESCALE_4096 4096 | ||
42 | #define CDNS_WDT_PRESCALE_SELECT_64 1 | ||
43 | #define CDNS_WDT_PRESCALE_SELECT_512 2 | ||
44 | #define CDNS_WDT_PRESCALE_SELECT_4096 3 | ||
45 | |||
46 | /* Input clock frequency */ | ||
47 | #define CDNS_WDT_CLK_10MHZ 10000000 | ||
48 | #define CDNS_WDT_CLK_75MHZ 75000000 | ||
49 | |||
50 | /* Counter maximum value */ | ||
51 | #define CDNS_WDT_COUNTER_MAX 0xFFF | ||
52 | |||
53 | static int wdt_timeout = CDNS_WDT_DEFAULT_TIMEOUT; | ||
54 | static int nowayout = WATCHDOG_NOWAYOUT; | ||
55 | |||
56 | module_param(wdt_timeout, int, 0); | ||
57 | MODULE_PARM_DESC(wdt_timeout, | ||
58 | "Watchdog time in seconds. (default=" | ||
59 | __MODULE_STRING(CDNS_WDT_DEFAULT_TIMEOUT) ")"); | ||
60 | |||
61 | module_param(nowayout, int, 0); | ||
62 | MODULE_PARM_DESC(nowayout, | ||
63 | "Watchdog cannot be stopped once started (default=" | ||
64 | __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); | ||
65 | |||
66 | /** | ||
67 | * struct cdns_wdt - Watchdog device structure | ||
68 | * @regs: baseaddress of device | ||
69 | * @rst: reset flag | ||
70 | * @clk: struct clk * of a clock source | ||
71 | * @prescaler: for saving prescaler value | ||
72 | * @ctrl_clksel: counter clock prescaler selection | ||
73 | * @io_lock: spinlock for IO register access | ||
74 | * @cdns_wdt_device: watchdog device structure | ||
75 | * @cdns_wdt_notifier: notifier structure | ||
76 | * | ||
77 | * Structure containing parameters specific to cadence watchdog. | ||
78 | */ | ||
79 | struct cdns_wdt { | ||
80 | void __iomem *regs; | ||
81 | bool rst; | ||
82 | struct clk *clk; | ||
83 | u32 prescaler; | ||
84 | u32 ctrl_clksel; | ||
85 | spinlock_t io_lock; | ||
86 | struct watchdog_device cdns_wdt_device; | ||
87 | struct notifier_block cdns_wdt_notifier; | ||
88 | }; | ||
89 | |||
90 | /* Write access to Registers */ | ||
91 | static inline void cdns_wdt_writereg(struct cdns_wdt *wdt, u32 offset, u32 val) | ||
92 | { | ||
93 | writel_relaxed(val, wdt->regs + offset); | ||
94 | } | ||
95 | |||
96 | /*************************Register Map**************************************/ | ||
97 | |||
98 | /* Register Offsets for the WDT */ | ||
99 | #define CDNS_WDT_ZMR_OFFSET 0x0 /* Zero Mode Register */ | ||
100 | #define CDNS_WDT_CCR_OFFSET 0x4 /* Counter Control Register */ | ||
101 | #define CDNS_WDT_RESTART_OFFSET 0x8 /* Restart Register */ | ||
102 | #define CDNS_WDT_SR_OFFSET 0xC /* Status Register */ | ||
103 | |||
104 | /* | ||
105 | * Zero Mode Register - This register controls how the time out is indicated | ||
106 | * and also contains the access code to allow writes to the register (0xABC). | ||
107 | */ | ||
108 | #define CDNS_WDT_ZMR_WDEN_MASK 0x00000001 /* Enable the WDT */ | ||
109 | #define CDNS_WDT_ZMR_RSTEN_MASK 0x00000002 /* Enable the reset output */ | ||
110 | #define CDNS_WDT_ZMR_IRQEN_MASK 0x00000004 /* Enable IRQ output */ | ||
111 | #define CDNS_WDT_ZMR_RSTLEN_16 0x00000030 /* Reset pulse of 16 pclk cycles */ | ||
112 | #define CDNS_WDT_ZMR_ZKEY_VAL 0x00ABC000 /* Access key, 0xABC << 12 */ | ||
113 | /* | ||
114 | * Counter Control register - This register controls how fast the timer runs | ||
115 | * and the reset value and also contains the access code to allow writes to | ||
116 | * the register. | ||
117 | */ | ||
118 | #define CDNS_WDT_CCR_CRV_MASK 0x00003FFC /* Counter reset value */ | ||
119 | |||
120 | /** | ||
121 | * cdns_wdt_stop - Stop the watchdog. | ||
122 | * | ||
123 | * @wdd: watchdog device | ||
124 | * | ||
125 | * Read the contents of the ZMR register, clear the WDEN bit | ||
126 | * in the register and set the access key for successful write. | ||
127 | * | ||
128 | * Return: always 0 | ||
129 | */ | ||
130 | static int cdns_wdt_stop(struct watchdog_device *wdd) | ||
131 | { | ||
132 | struct cdns_wdt *wdt = watchdog_get_drvdata(wdd); | ||
133 | |||
134 | spin_lock(&wdt->io_lock); | ||
135 | cdns_wdt_writereg(wdt, CDNS_WDT_ZMR_OFFSET, | ||
136 | CDNS_WDT_ZMR_ZKEY_VAL & (~CDNS_WDT_ZMR_WDEN_MASK)); | ||
137 | spin_unlock(&wdt->io_lock); | ||
138 | |||
139 | return 0; | ||
140 | } | ||
141 | |||
142 | /** | ||
143 | * cdns_wdt_reload - Reload the watchdog timer (i.e. pat the watchdog). | ||
144 | * | ||
145 | * @wdd: watchdog device | ||
146 | * | ||
147 | * Write the restart key value (0x00001999) to the restart register. | ||
148 | * | ||
149 | * Return: always 0 | ||
150 | */ | ||
151 | static int cdns_wdt_reload(struct watchdog_device *wdd) | ||
152 | { | ||
153 | struct cdns_wdt *wdt = watchdog_get_drvdata(wdd); | ||
154 | |||
155 | spin_lock(&wdt->io_lock); | ||
156 | cdns_wdt_writereg(wdt, CDNS_WDT_RESTART_OFFSET, | ||
157 | CDNS_WDT_RESTART_KEY); | ||
158 | spin_unlock(&wdt->io_lock); | ||
159 | |||
160 | return 0; | ||
161 | } | ||
162 | |||
163 | /** | ||
164 | * cdns_wdt_start - Enable and start the watchdog. | ||
165 | * | ||
166 | * @wdd: watchdog device | ||
167 | * | ||
168 | * The counter value is calculated according to the formula: | ||
169 | * calculated count = (timeout * clock) / prescaler + 1. | ||
170 | * The calculated count is divided by 0x1000 to obtain the field value | ||
171 | * to write to counter control register. | ||
172 | * Clears the contents of prescaler and counter reset value. Sets the | ||
173 | * prescaler to 4096 and the calculated count and access key | ||
174 | * to write to CCR Register. | ||
175 | * Sets the WDT (WDEN bit) and either the Reset signal(RSTEN bit) | ||
176 | * or Interrupt signal(IRQEN) with a specified cycles and the access | ||
177 | * key to write to ZMR Register. | ||
178 | * | ||
179 | * Return: always 0 | ||
180 | */ | ||
181 | static int cdns_wdt_start(struct watchdog_device *wdd) | ||
182 | { | ||
183 | struct cdns_wdt *wdt = watchdog_get_drvdata(wdd); | ||
184 | unsigned int data = 0; | ||
185 | unsigned short count; | ||
186 | unsigned long clock_f = clk_get_rate(wdt->clk); | ||
187 | |||
188 | /* | ||
189 | * Counter value divisor to obtain the value of | ||
190 | * counter reset to be written to control register. | ||
191 | */ | ||
192 | count = (wdd->timeout * (clock_f / wdt->prescaler)) / | ||
193 | CDNS_WDT_COUNTER_VALUE_DIVISOR + 1; | ||
194 | |||
195 | if (count > CDNS_WDT_COUNTER_MAX) | ||
196 | count = CDNS_WDT_COUNTER_MAX; | ||
197 | |||
198 | spin_lock(&wdt->io_lock); | ||
199 | cdns_wdt_writereg(wdt, CDNS_WDT_ZMR_OFFSET, | ||
200 | CDNS_WDT_ZMR_ZKEY_VAL); | ||
201 | |||
202 | count = (count << 2) & CDNS_WDT_CCR_CRV_MASK; | ||
203 | |||
204 | /* Write counter access key first to be able write to register */ | ||
205 | data = count | CDNS_WDT_REGISTER_ACCESS_KEY | wdt->ctrl_clksel; | ||
206 | cdns_wdt_writereg(wdt, CDNS_WDT_CCR_OFFSET, data); | ||
207 | data = CDNS_WDT_ZMR_WDEN_MASK | CDNS_WDT_ZMR_RSTLEN_16 | | ||
208 | CDNS_WDT_ZMR_ZKEY_VAL; | ||
209 | |||
210 | /* Reset on timeout if specified in device tree. */ | ||
211 | if (wdt->rst) { | ||
212 | data |= CDNS_WDT_ZMR_RSTEN_MASK; | ||
213 | data &= ~CDNS_WDT_ZMR_IRQEN_MASK; | ||
214 | } else { | ||
215 | data &= ~CDNS_WDT_ZMR_RSTEN_MASK; | ||
216 | data |= CDNS_WDT_ZMR_IRQEN_MASK; | ||
217 | } | ||
218 | cdns_wdt_writereg(wdt, CDNS_WDT_ZMR_OFFSET, data); | ||
219 | cdns_wdt_writereg(wdt, CDNS_WDT_RESTART_OFFSET, | ||
220 | CDNS_WDT_RESTART_KEY); | ||
221 | spin_unlock(&wdt->io_lock); | ||
222 | |||
223 | return 0; | ||
224 | } | ||
225 | |||
226 | /** | ||
227 | * cdns_wdt_settimeout - Set a new timeout value for the watchdog device. | ||
228 | * | ||
229 | * @wdd: watchdog device | ||
230 | * @new_time: new timeout value that needs to be set | ||
231 | * Return: 0 on success | ||
232 | * | ||
233 | * Update the watchdog_device timeout with new value which is used when | ||
234 | * cdns_wdt_start is called. | ||
235 | */ | ||
236 | static int cdns_wdt_settimeout(struct watchdog_device *wdd, | ||
237 | unsigned int new_time) | ||
238 | { | ||
239 | wdd->timeout = new_time; | ||
240 | |||
241 | return cdns_wdt_start(wdd); | ||
242 | } | ||
243 | |||
244 | /** | ||
245 | * cdns_wdt_irq_handler - Notifies of watchdog timeout. | ||
246 | * | ||
247 | * @irq: interrupt number | ||
248 | * @dev_id: pointer to a platform device structure | ||
249 | * Return: IRQ_HANDLED | ||
250 | * | ||
251 | * The handler is invoked when the watchdog times out and a | ||
252 | * reset on timeout has not been enabled. | ||
253 | */ | ||
254 | static irqreturn_t cdns_wdt_irq_handler(int irq, void *dev_id) | ||
255 | { | ||
256 | struct platform_device *pdev = dev_id; | ||
257 | |||
258 | dev_info(&pdev->dev, | ||
259 | "Watchdog timed out. Internal reset not enabled\n"); | ||
260 | |||
261 | return IRQ_HANDLED; | ||
262 | } | ||
263 | |||
264 | /* | ||
265 | * Info structure used to indicate the features supported by the device | ||
266 | * to the upper layers. This is defined in watchdog.h header file. | ||
267 | */ | ||
268 | static struct watchdog_info cdns_wdt_info = { | ||
269 | .identity = "cdns_wdt watchdog", | ||
270 | .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | | ||
271 | WDIOF_MAGICCLOSE, | ||
272 | }; | ||
273 | |||
274 | /* Watchdog Core Ops */ | ||
275 | static struct watchdog_ops cdns_wdt_ops = { | ||
276 | .owner = THIS_MODULE, | ||
277 | .start = cdns_wdt_start, | ||
278 | .stop = cdns_wdt_stop, | ||
279 | .ping = cdns_wdt_reload, | ||
280 | .set_timeout = cdns_wdt_settimeout, | ||
281 | }; | ||
282 | |||
283 | /** | ||
284 | * cdns_wdt_notify_sys - Notifier for reboot or shutdown. | ||
285 | * | ||
286 | * @this: handle to notifier block | ||
287 | * @code: turn off indicator | ||
288 | * @unused: unused | ||
289 | * Return: NOTIFY_DONE | ||
290 | * | ||
291 | * This notifier is invoked whenever the system reboot or shutdown occur | ||
292 | * because we need to disable the WDT before system goes down as WDT might | ||
293 | * reset on the next boot. | ||
294 | */ | ||
295 | static int cdns_wdt_notify_sys(struct notifier_block *this, unsigned long code, | ||
296 | void *unused) | ||
297 | { | ||
298 | struct cdns_wdt *wdt = container_of(this, struct cdns_wdt, | ||
299 | cdns_wdt_notifier); | ||
300 | if (code == SYS_DOWN || code == SYS_HALT) | ||
301 | cdns_wdt_stop(&wdt->cdns_wdt_device); | ||
302 | |||
303 | return NOTIFY_DONE; | ||
304 | } | ||
305 | |||
306 | /************************Platform Operations*****************************/ | ||
307 | /** | ||
308 | * cdns_wdt_probe - Probe call for the device. | ||
309 | * | ||
310 | * @pdev: handle to the platform device structure. | ||
311 | * Return: 0 on success, negative error otherwise. | ||
312 | * | ||
313 | * It does all the memory allocation and registration for the device. | ||
314 | */ | ||
315 | static int cdns_wdt_probe(struct platform_device *pdev) | ||
316 | { | ||
317 | struct resource *res; | ||
318 | int ret, irq; | ||
319 | unsigned long clock_f; | ||
320 | struct cdns_wdt *wdt; | ||
321 | struct watchdog_device *cdns_wdt_device; | ||
322 | |||
323 | wdt = devm_kzalloc(&pdev->dev, sizeof(*wdt), GFP_KERNEL); | ||
324 | if (!wdt) | ||
325 | return -ENOMEM; | ||
326 | |||
327 | cdns_wdt_device = &wdt->cdns_wdt_device; | ||
328 | cdns_wdt_device->info = &cdns_wdt_info; | ||
329 | cdns_wdt_device->ops = &cdns_wdt_ops; | ||
330 | cdns_wdt_device->timeout = CDNS_WDT_DEFAULT_TIMEOUT; | ||
331 | cdns_wdt_device->min_timeout = CDNS_WDT_MIN_TIMEOUT; | ||
332 | cdns_wdt_device->max_timeout = CDNS_WDT_MAX_TIMEOUT; | ||
333 | |||
334 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||
335 | wdt->regs = devm_ioremap_resource(&pdev->dev, res); | ||
336 | if (IS_ERR(wdt->regs)) | ||
337 | return PTR_ERR(wdt->regs); | ||
338 | |||
339 | /* Register the interrupt */ | ||
340 | wdt->rst = of_property_read_bool(pdev->dev.of_node, "reset-on-timeout"); | ||
341 | irq = platform_get_irq(pdev, 0); | ||
342 | if (!wdt->rst && irq >= 0) { | ||
343 | ret = devm_request_irq(&pdev->dev, irq, cdns_wdt_irq_handler, 0, | ||
344 | pdev->name, pdev); | ||
345 | if (ret) { | ||
346 | dev_err(&pdev->dev, | ||
347 | "cannot register interrupt handler err=%d\n", | ||
348 | ret); | ||
349 | return ret; | ||
350 | } | ||
351 | } | ||
352 | |||
353 | /* Initialize the members of cdns_wdt structure */ | ||
354 | cdns_wdt_device->parent = &pdev->dev; | ||
355 | |||
356 | ret = watchdog_init_timeout(cdns_wdt_device, wdt_timeout, &pdev->dev); | ||
357 | if (ret) { | ||
358 | dev_err(&pdev->dev, "unable to set timeout value\n"); | ||
359 | return ret; | ||
360 | } | ||
361 | |||
362 | watchdog_set_nowayout(cdns_wdt_device, nowayout); | ||
363 | watchdog_set_drvdata(cdns_wdt_device, wdt); | ||
364 | |||
365 | wdt->clk = devm_clk_get(&pdev->dev, NULL); | ||
366 | if (IS_ERR(wdt->clk)) { | ||
367 | dev_err(&pdev->dev, "input clock not found\n"); | ||
368 | ret = PTR_ERR(wdt->clk); | ||
369 | return ret; | ||
370 | } | ||
371 | |||
372 | ret = clk_prepare_enable(wdt->clk); | ||
373 | if (ret) { | ||
374 | dev_err(&pdev->dev, "unable to enable clock\n"); | ||
375 | return ret; | ||
376 | } | ||
377 | |||
378 | clock_f = clk_get_rate(wdt->clk); | ||
379 | if (clock_f <= CDNS_WDT_CLK_75MHZ) { | ||
380 | wdt->prescaler = CDNS_WDT_PRESCALE_512; | ||
381 | wdt->ctrl_clksel = CDNS_WDT_PRESCALE_SELECT_512; | ||
382 | } else { | ||
383 | wdt->prescaler = CDNS_WDT_PRESCALE_4096; | ||
384 | wdt->ctrl_clksel = CDNS_WDT_PRESCALE_SELECT_4096; | ||
385 | } | ||
386 | |||
387 | spin_lock_init(&wdt->io_lock); | ||
388 | |||
389 | wdt->cdns_wdt_notifier.notifier_call = &cdns_wdt_notify_sys; | ||
390 | ret = register_reboot_notifier(&wdt->cdns_wdt_notifier); | ||
391 | if (ret != 0) { | ||
392 | dev_err(&pdev->dev, "cannot register reboot notifier err=%d)\n", | ||
393 | ret); | ||
394 | goto err_clk_disable; | ||
395 | } | ||
396 | |||
397 | ret = watchdog_register_device(cdns_wdt_device); | ||
398 | if (ret) { | ||
399 | dev_err(&pdev->dev, "Failed to register wdt device\n"); | ||
400 | goto err_clk_disable; | ||
401 | } | ||
402 | platform_set_drvdata(pdev, wdt); | ||
403 | |||
404 | dev_dbg(&pdev->dev, "Xilinx Watchdog Timer at %p with timeout %ds%s\n", | ||
405 | wdt->regs, cdns_wdt_device->timeout, | ||
406 | nowayout ? ", nowayout" : ""); | ||
407 | |||
408 | return 0; | ||
409 | |||
410 | err_clk_disable: | ||
411 | clk_disable_unprepare(wdt->clk); | ||
412 | |||
413 | return ret; | ||
414 | } | ||
415 | |||
416 | /** | ||
417 | * cdns_wdt_remove - Probe call for the device. | ||
418 | * | ||
419 | * @pdev: handle to the platform device structure. | ||
420 | * Return: 0 on success, otherwise negative error. | ||
421 | * | ||
422 | * Unregister the device after releasing the resources. | ||
423 | */ | ||
424 | static int cdns_wdt_remove(struct platform_device *pdev) | ||
425 | { | ||
426 | struct cdns_wdt *wdt = platform_get_drvdata(pdev); | ||
427 | |||
428 | cdns_wdt_stop(&wdt->cdns_wdt_device); | ||
429 | watchdog_unregister_device(&wdt->cdns_wdt_device); | ||
430 | unregister_reboot_notifier(&wdt->cdns_wdt_notifier); | ||
431 | clk_disable_unprepare(wdt->clk); | ||
432 | |||
433 | return 0; | ||
434 | } | ||
435 | |||
436 | /** | ||
437 | * cdns_wdt_shutdown - Stop the device. | ||
438 | * | ||
439 | * @pdev: handle to the platform structure. | ||
440 | * | ||
441 | */ | ||
442 | static void cdns_wdt_shutdown(struct platform_device *pdev) | ||
443 | { | ||
444 | struct cdns_wdt *wdt = platform_get_drvdata(pdev); | ||
445 | |||
446 | cdns_wdt_stop(&wdt->cdns_wdt_device); | ||
447 | clk_disable_unprepare(wdt->clk); | ||
448 | } | ||
449 | |||
450 | /** | ||
451 | * cdns_wdt_suspend - Stop the device. | ||
452 | * | ||
453 | * @dev: handle to the device structure. | ||
454 | * Return: 0 always. | ||
455 | */ | ||
456 | static int __maybe_unused cdns_wdt_suspend(struct device *dev) | ||
457 | { | ||
458 | struct platform_device *pdev = container_of(dev, | ||
459 | struct platform_device, dev); | ||
460 | struct cdns_wdt *wdt = platform_get_drvdata(pdev); | ||
461 | |||
462 | cdns_wdt_stop(&wdt->cdns_wdt_device); | ||
463 | clk_disable_unprepare(wdt->clk); | ||
464 | |||
465 | return 0; | ||
466 | } | ||
467 | |||
468 | /** | ||
469 | * cdns_wdt_resume - Resume the device. | ||
470 | * | ||
471 | * @dev: handle to the device structure. | ||
472 | * Return: 0 on success, errno otherwise. | ||
473 | */ | ||
474 | static int __maybe_unused cdns_wdt_resume(struct device *dev) | ||
475 | { | ||
476 | int ret; | ||
477 | struct platform_device *pdev = container_of(dev, | ||
478 | struct platform_device, dev); | ||
479 | struct cdns_wdt *wdt = platform_get_drvdata(pdev); | ||
480 | |||
481 | ret = clk_prepare_enable(wdt->clk); | ||
482 | if (ret) { | ||
483 | dev_err(dev, "unable to enable clock\n"); | ||
484 | return ret; | ||
485 | } | ||
486 | cdns_wdt_start(&wdt->cdns_wdt_device); | ||
487 | |||
488 | return 0; | ||
489 | } | ||
490 | |||
491 | static SIMPLE_DEV_PM_OPS(cdns_wdt_pm_ops, cdns_wdt_suspend, cdns_wdt_resume); | ||
492 | |||
493 | static struct of_device_id cdns_wdt_of_match[] = { | ||
494 | { .compatible = "cdns,wdt-r1p2", }, | ||
495 | { /* end of table */ } | ||
496 | }; | ||
497 | MODULE_DEVICE_TABLE(of, cdns_wdt_of_match); | ||
498 | |||
499 | /* Driver Structure */ | ||
500 | static struct platform_driver cdns_wdt_driver = { | ||
501 | .probe = cdns_wdt_probe, | ||
502 | .remove = cdns_wdt_remove, | ||
503 | .shutdown = cdns_wdt_shutdown, | ||
504 | .driver = { | ||
505 | .name = "cdns-wdt", | ||
506 | .owner = THIS_MODULE, | ||
507 | .of_match_table = cdns_wdt_of_match, | ||
508 | .pm = &cdns_wdt_pm_ops, | ||
509 | }, | ||
510 | }; | ||
511 | |||
512 | module_platform_driver(cdns_wdt_driver); | ||
513 | |||
514 | MODULE_AUTHOR("Xilinx, Inc."); | ||
515 | MODULE_DESCRIPTION("Watchdog driver for Cadence WDT"); | ||
516 | MODULE_LICENSE("GPL"); | ||
diff --git a/drivers/watchdog/da9063_wdt.c b/drivers/watchdog/da9063_wdt.c new file mode 100644 index 000000000000..2cd6b2c2dd2a --- /dev/null +++ b/drivers/watchdog/da9063_wdt.c | |||
@@ -0,0 +1,191 @@ | |||
1 | /* | ||
2 | * Watchdog driver for DA9063 PMICs. | ||
3 | * | ||
4 | * Copyright(c) 2012 Dialog Semiconductor Ltd. | ||
5 | * | ||
6 | * Author: Mariusz Wojtasik <mariusz.wojtasik@diasemi.com> | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or modify it | ||
9 | * under the terms of the GNU General Public License as published by the | ||
10 | * Free Software Foundation; either version 2 of the License, or (at your | ||
11 | * option) any later version. | ||
12 | */ | ||
13 | |||
14 | #include <linux/kernel.h> | ||
15 | #include <linux/module.h> | ||
16 | #include <linux/watchdog.h> | ||
17 | #include <linux/platform_device.h> | ||
18 | #include <linux/uaccess.h> | ||
19 | #include <linux/slab.h> | ||
20 | #include <linux/delay.h> | ||
21 | #include <linux/mfd/da9063/registers.h> | ||
22 | #include <linux/mfd/da9063/core.h> | ||
23 | #include <linux/regmap.h> | ||
24 | |||
25 | /* | ||
26 | * Watchdog selector to timeout in seconds. | ||
27 | * 0: WDT disabled; | ||
28 | * others: timeout = 2048 ms * 2^(TWDSCALE-1). | ||
29 | */ | ||
30 | static const unsigned int wdt_timeout[] = { 0, 2, 4, 8, 16, 32, 65, 131 }; | ||
31 | #define DA9063_TWDSCALE_DISABLE 0 | ||
32 | #define DA9063_TWDSCALE_MIN 1 | ||
33 | #define DA9063_TWDSCALE_MAX (ARRAY_SIZE(wdt_timeout) - 1) | ||
34 | #define DA9063_WDT_MIN_TIMEOUT wdt_timeout[DA9063_TWDSCALE_MIN] | ||
35 | #define DA9063_WDT_MAX_TIMEOUT wdt_timeout[DA9063_TWDSCALE_MAX] | ||
36 | #define DA9063_WDG_TIMEOUT wdt_timeout[3] | ||
37 | |||
38 | struct da9063_watchdog { | ||
39 | struct da9063 *da9063; | ||
40 | struct watchdog_device wdtdev; | ||
41 | }; | ||
42 | |||
43 | static unsigned int da9063_wdt_timeout_to_sel(unsigned int secs) | ||
44 | { | ||
45 | unsigned int i; | ||
46 | |||
47 | for (i = DA9063_TWDSCALE_MIN; i <= DA9063_TWDSCALE_MAX; i++) { | ||
48 | if (wdt_timeout[i] >= secs) | ||
49 | return i; | ||
50 | } | ||
51 | |||
52 | return DA9063_TWDSCALE_MAX; | ||
53 | } | ||
54 | |||
55 | static int _da9063_wdt_set_timeout(struct da9063 *da9063, unsigned int regval) | ||
56 | { | ||
57 | return regmap_update_bits(da9063->regmap, DA9063_REG_CONTROL_D, | ||
58 | DA9063_TWDSCALE_MASK, regval); | ||
59 | } | ||
60 | |||
61 | static int da9063_wdt_start(struct watchdog_device *wdd) | ||
62 | { | ||
63 | struct da9063_watchdog *wdt = watchdog_get_drvdata(wdd); | ||
64 | unsigned int selector; | ||
65 | int ret; | ||
66 | |||
67 | selector = da9063_wdt_timeout_to_sel(wdt->wdtdev.timeout); | ||
68 | ret = _da9063_wdt_set_timeout(wdt->da9063, selector); | ||
69 | if (ret) | ||
70 | dev_err(wdt->da9063->dev, "Watchdog failed to start (err = %d)\n", | ||
71 | ret); | ||
72 | |||
73 | return ret; | ||
74 | } | ||
75 | |||
76 | static int da9063_wdt_stop(struct watchdog_device *wdd) | ||
77 | { | ||
78 | struct da9063_watchdog *wdt = watchdog_get_drvdata(wdd); | ||
79 | int ret; | ||
80 | |||
81 | ret = regmap_update_bits(wdt->da9063->regmap, DA9063_REG_CONTROL_D, | ||
82 | DA9063_TWDSCALE_MASK, DA9063_TWDSCALE_DISABLE); | ||
83 | if (ret) | ||
84 | dev_alert(wdt->da9063->dev, "Watchdog failed to stop (err = %d)\n", | ||
85 | ret); | ||
86 | |||
87 | return ret; | ||
88 | } | ||
89 | |||
90 | static int da9063_wdt_ping(struct watchdog_device *wdd) | ||
91 | { | ||
92 | struct da9063_watchdog *wdt = watchdog_get_drvdata(wdd); | ||
93 | int ret; | ||
94 | |||
95 | ret = regmap_write(wdt->da9063->regmap, DA9063_REG_CONTROL_F, | ||
96 | DA9063_WATCHDOG); | ||
97 | if (ret) | ||
98 | dev_alert(wdt->da9063->dev, "Failed to ping the watchdog (err = %d)\n", | ||
99 | ret); | ||
100 | |||
101 | return ret; | ||
102 | } | ||
103 | |||
104 | static int da9063_wdt_set_timeout(struct watchdog_device *wdd, | ||
105 | unsigned int timeout) | ||
106 | { | ||
107 | struct da9063_watchdog *wdt = watchdog_get_drvdata(wdd); | ||
108 | unsigned int selector; | ||
109 | int ret; | ||
110 | |||
111 | selector = da9063_wdt_timeout_to_sel(timeout); | ||
112 | ret = _da9063_wdt_set_timeout(wdt->da9063, selector); | ||
113 | if (ret) | ||
114 | dev_err(wdt->da9063->dev, "Failed to set watchdog timeout (err = %d)\n", | ||
115 | ret); | ||
116 | else | ||
117 | wdd->timeout = wdt_timeout[selector]; | ||
118 | |||
119 | return ret; | ||
120 | } | ||
121 | |||
122 | static const struct watchdog_info da9063_watchdog_info = { | ||
123 | .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING, | ||
124 | .identity = "DA9063 Watchdog", | ||
125 | }; | ||
126 | |||
127 | static const struct watchdog_ops da9063_watchdog_ops = { | ||
128 | .owner = THIS_MODULE, | ||
129 | .start = da9063_wdt_start, | ||
130 | .stop = da9063_wdt_stop, | ||
131 | .ping = da9063_wdt_ping, | ||
132 | .set_timeout = da9063_wdt_set_timeout, | ||
133 | }; | ||
134 | |||
135 | static int da9063_wdt_probe(struct platform_device *pdev) | ||
136 | { | ||
137 | int ret; | ||
138 | struct da9063 *da9063; | ||
139 | struct da9063_watchdog *wdt; | ||
140 | |||
141 | if (!pdev->dev.parent) | ||
142 | return -EINVAL; | ||
143 | |||
144 | da9063 = dev_get_drvdata(pdev->dev.parent); | ||
145 | if (!da9063) | ||
146 | return -EINVAL; | ||
147 | |||
148 | wdt = devm_kzalloc(&pdev->dev, sizeof(*wdt), GFP_KERNEL); | ||
149 | if (!wdt) | ||
150 | return -ENOMEM; | ||
151 | |||
152 | wdt->da9063 = da9063; | ||
153 | |||
154 | wdt->wdtdev.info = &da9063_watchdog_info; | ||
155 | wdt->wdtdev.ops = &da9063_watchdog_ops; | ||
156 | wdt->wdtdev.min_timeout = DA9063_WDT_MIN_TIMEOUT; | ||
157 | wdt->wdtdev.max_timeout = DA9063_WDT_MAX_TIMEOUT; | ||
158 | wdt->wdtdev.timeout = DA9063_WDG_TIMEOUT; | ||
159 | |||
160 | wdt->wdtdev.status = WATCHDOG_NOWAYOUT_INIT_STATUS; | ||
161 | |||
162 | watchdog_set_drvdata(&wdt->wdtdev, wdt); | ||
163 | dev_set_drvdata(&pdev->dev, wdt); | ||
164 | |||
165 | ret = watchdog_register_device(&wdt->wdtdev); | ||
166 | |||
167 | return ret; | ||
168 | } | ||
169 | |||
170 | static int da9063_wdt_remove(struct platform_device *pdev) | ||
171 | { | ||
172 | struct da9063_watchdog *wdt = dev_get_drvdata(&pdev->dev); | ||
173 | |||
174 | watchdog_unregister_device(&wdt->wdtdev); | ||
175 | |||
176 | return 0; | ||
177 | } | ||
178 | |||
179 | static struct platform_driver da9063_wdt_driver = { | ||
180 | .probe = da9063_wdt_probe, | ||
181 | .remove = da9063_wdt_remove, | ||
182 | .driver = { | ||
183 | .name = DA9063_DRVNAME_WATCHDOG, | ||
184 | }, | ||
185 | }; | ||
186 | module_platform_driver(da9063_wdt_driver); | ||
187 | |||
188 | MODULE_AUTHOR("Mariusz Wojtasik <mariusz.wojtasik@diasemi.com>"); | ||
189 | MODULE_DESCRIPTION("Watchdog driver for Dialog DA9063"); | ||
190 | MODULE_LICENSE("GPL"); | ||
191 | MODULE_ALIAS("platform:" DA9063_DRVNAME_WATCHDOG); | ||
diff --git a/drivers/watchdog/dw_wdt.c b/drivers/watchdog/dw_wdt.c index 9f210299de24..9e577a64ec9e 100644 --- a/drivers/watchdog/dw_wdt.c +++ b/drivers/watchdog/dw_wdt.c | |||
@@ -21,6 +21,7 @@ | |||
21 | 21 | ||
22 | #include <linux/bitops.h> | 22 | #include <linux/bitops.h> |
23 | #include <linux/clk.h> | 23 | #include <linux/clk.h> |
24 | #include <linux/delay.h> | ||
24 | #include <linux/device.h> | 25 | #include <linux/device.h> |
25 | #include <linux/err.h> | 26 | #include <linux/err.h> |
26 | #include <linux/fs.h> | 27 | #include <linux/fs.h> |
@@ -29,9 +30,11 @@ | |||
29 | #include <linux/miscdevice.h> | 30 | #include <linux/miscdevice.h> |
30 | #include <linux/module.h> | 31 | #include <linux/module.h> |
31 | #include <linux/moduleparam.h> | 32 | #include <linux/moduleparam.h> |
33 | #include <linux/notifier.h> | ||
32 | #include <linux/of.h> | 34 | #include <linux/of.h> |
33 | #include <linux/pm.h> | 35 | #include <linux/pm.h> |
34 | #include <linux/platform_device.h> | 36 | #include <linux/platform_device.h> |
37 | #include <linux/reboot.h> | ||
35 | #include <linux/spinlock.h> | 38 | #include <linux/spinlock.h> |
36 | #include <linux/timer.h> | 39 | #include <linux/timer.h> |
37 | #include <linux/uaccess.h> | 40 | #include <linux/uaccess.h> |
@@ -40,6 +43,7 @@ | |||
40 | #define WDOG_CONTROL_REG_OFFSET 0x00 | 43 | #define WDOG_CONTROL_REG_OFFSET 0x00 |
41 | #define WDOG_CONTROL_REG_WDT_EN_MASK 0x01 | 44 | #define WDOG_CONTROL_REG_WDT_EN_MASK 0x01 |
42 | #define WDOG_TIMEOUT_RANGE_REG_OFFSET 0x04 | 45 | #define WDOG_TIMEOUT_RANGE_REG_OFFSET 0x04 |
46 | #define WDOG_TIMEOUT_RANGE_TOPINIT_SHIFT 4 | ||
43 | #define WDOG_CURRENT_COUNT_REG_OFFSET 0x08 | 47 | #define WDOG_CURRENT_COUNT_REG_OFFSET 0x08 |
44 | #define WDOG_COUNTER_RESTART_REG_OFFSET 0x0c | 48 | #define WDOG_COUNTER_RESTART_REG_OFFSET 0x0c |
45 | #define WDOG_COUNTER_RESTART_KICK_VALUE 0x76 | 49 | #define WDOG_COUNTER_RESTART_KICK_VALUE 0x76 |
@@ -62,6 +66,7 @@ static struct { | |||
62 | unsigned long next_heartbeat; | 66 | unsigned long next_heartbeat; |
63 | struct timer_list timer; | 67 | struct timer_list timer; |
64 | int expect_close; | 68 | int expect_close; |
69 | struct notifier_block restart_handler; | ||
65 | } dw_wdt; | 70 | } dw_wdt; |
66 | 71 | ||
67 | static inline int dw_wdt_is_enabled(void) | 72 | static inline int dw_wdt_is_enabled(void) |
@@ -106,7 +111,8 @@ static int dw_wdt_set_top(unsigned top_s) | |||
106 | } | 111 | } |
107 | 112 | ||
108 | /* Set the new value in the watchdog. */ | 113 | /* Set the new value in the watchdog. */ |
109 | writel(top_val, dw_wdt.regs + WDOG_TIMEOUT_RANGE_REG_OFFSET); | 114 | writel(top_val | top_val << WDOG_TIMEOUT_RANGE_TOPINIT_SHIFT, |
115 | dw_wdt.regs + WDOG_TIMEOUT_RANGE_REG_OFFSET); | ||
110 | 116 | ||
111 | dw_wdt_set_next_heartbeat(); | 117 | dw_wdt_set_next_heartbeat(); |
112 | 118 | ||
@@ -119,6 +125,26 @@ static void dw_wdt_keepalive(void) | |||
119 | WDOG_COUNTER_RESTART_REG_OFFSET); | 125 | WDOG_COUNTER_RESTART_REG_OFFSET); |
120 | } | 126 | } |
121 | 127 | ||
128 | static int dw_wdt_restart_handle(struct notifier_block *this, | ||
129 | unsigned long mode, void *cmd) | ||
130 | { | ||
131 | u32 val; | ||
132 | |||
133 | writel(0, dw_wdt.regs + WDOG_TIMEOUT_RANGE_REG_OFFSET); | ||
134 | val = readl(dw_wdt.regs + WDOG_CONTROL_REG_OFFSET); | ||
135 | if (val & WDOG_CONTROL_REG_WDT_EN_MASK) | ||
136 | writel(WDOG_COUNTER_RESTART_KICK_VALUE, dw_wdt.regs + | ||
137 | WDOG_COUNTER_RESTART_REG_OFFSET); | ||
138 | else | ||
139 | writel(WDOG_CONTROL_REG_WDT_EN_MASK, | ||
140 | dw_wdt.regs + WDOG_CONTROL_REG_OFFSET); | ||
141 | |||
142 | /* wait for reset to assert... */ | ||
143 | mdelay(500); | ||
144 | |||
145 | return NOTIFY_DONE; | ||
146 | } | ||
147 | |||
122 | static void dw_wdt_ping(unsigned long data) | 148 | static void dw_wdt_ping(unsigned long data) |
123 | { | 149 | { |
124 | if (time_before(jiffies, dw_wdt.next_heartbeat) || | 150 | if (time_before(jiffies, dw_wdt.next_heartbeat) || |
@@ -314,6 +340,12 @@ static int dw_wdt_drv_probe(struct platform_device *pdev) | |||
314 | if (ret) | 340 | if (ret) |
315 | goto out_disable_clk; | 341 | goto out_disable_clk; |
316 | 342 | ||
343 | dw_wdt.restart_handler.notifier_call = dw_wdt_restart_handle; | ||
344 | dw_wdt.restart_handler.priority = 128; | ||
345 | ret = register_restart_handler(&dw_wdt.restart_handler); | ||
346 | if (ret) | ||
347 | pr_warn("cannot register restart handler\n"); | ||
348 | |||
317 | dw_wdt_set_next_heartbeat(); | 349 | dw_wdt_set_next_heartbeat(); |
318 | setup_timer(&dw_wdt.timer, dw_wdt_ping, 0); | 350 | setup_timer(&dw_wdt.timer, dw_wdt_ping, 0); |
319 | mod_timer(&dw_wdt.timer, jiffies + WDT_TIMEOUT); | 351 | mod_timer(&dw_wdt.timer, jiffies + WDT_TIMEOUT); |
@@ -328,6 +360,8 @@ out_disable_clk: | |||
328 | 360 | ||
329 | static int dw_wdt_drv_remove(struct platform_device *pdev) | 361 | static int dw_wdt_drv_remove(struct platform_device *pdev) |
330 | { | 362 | { |
363 | unregister_restart_handler(&dw_wdt.restart_handler); | ||
364 | |||
331 | misc_deregister(&dw_wdt_miscdev); | 365 | misc_deregister(&dw_wdt_miscdev); |
332 | 366 | ||
333 | clk_disable_unprepare(dw_wdt.clk); | 367 | clk_disable_unprepare(dw_wdt.clk); |
diff --git a/drivers/watchdog/imx2_wdt.c b/drivers/watchdog/imx2_wdt.c index 68c3d379ffa8..7e12f88bb4a6 100644 --- a/drivers/watchdog/imx2_wdt.c +++ b/drivers/watchdog/imx2_wdt.c | |||
@@ -22,14 +22,17 @@ | |||
22 | */ | 22 | */ |
23 | 23 | ||
24 | #include <linux/clk.h> | 24 | #include <linux/clk.h> |
25 | #include <linux/delay.h> | ||
25 | #include <linux/init.h> | 26 | #include <linux/init.h> |
26 | #include <linux/io.h> | 27 | #include <linux/io.h> |
27 | #include <linux/jiffies.h> | 28 | #include <linux/jiffies.h> |
28 | #include <linux/kernel.h> | 29 | #include <linux/kernel.h> |
29 | #include <linux/module.h> | 30 | #include <linux/module.h> |
30 | #include <linux/moduleparam.h> | 31 | #include <linux/moduleparam.h> |
32 | #include <linux/notifier.h> | ||
31 | #include <linux/of_address.h> | 33 | #include <linux/of_address.h> |
32 | #include <linux/platform_device.h> | 34 | #include <linux/platform_device.h> |
35 | #include <linux/reboot.h> | ||
33 | #include <linux/regmap.h> | 36 | #include <linux/regmap.h> |
34 | #include <linux/timer.h> | 37 | #include <linux/timer.h> |
35 | #include <linux/watchdog.h> | 38 | #include <linux/watchdog.h> |
@@ -59,6 +62,7 @@ struct imx2_wdt_device { | |||
59 | struct regmap *regmap; | 62 | struct regmap *regmap; |
60 | struct timer_list timer; /* Pings the watchdog when closed */ | 63 | struct timer_list timer; /* Pings the watchdog when closed */ |
61 | struct watchdog_device wdog; | 64 | struct watchdog_device wdog; |
65 | struct notifier_block restart_handler; | ||
62 | }; | 66 | }; |
63 | 67 | ||
64 | static bool nowayout = WATCHDOG_NOWAYOUT; | 68 | static bool nowayout = WATCHDOG_NOWAYOUT; |
@@ -77,6 +81,31 @@ static const struct watchdog_info imx2_wdt_info = { | |||
77 | .options = WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT | WDIOF_MAGICCLOSE, | 81 | .options = WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT | WDIOF_MAGICCLOSE, |
78 | }; | 82 | }; |
79 | 83 | ||
84 | static int imx2_restart_handler(struct notifier_block *this, unsigned long mode, | ||
85 | void *cmd) | ||
86 | { | ||
87 | unsigned int wcr_enable = IMX2_WDT_WCR_WDE; | ||
88 | struct imx2_wdt_device *wdev = container_of(this, | ||
89 | struct imx2_wdt_device, | ||
90 | restart_handler); | ||
91 | /* Assert SRS signal */ | ||
92 | regmap_write(wdev->regmap, 0, wcr_enable); | ||
93 | /* | ||
94 | * Due to imx6q errata ERR004346 (WDOG: WDOG SRS bit requires to be | ||
95 | * written twice), we add another two writes to ensure there must be at | ||
96 | * least two writes happen in the same one 32kHz clock period. We save | ||
97 | * the target check here, since the writes shouldn't be a huge burden | ||
98 | * for other platforms. | ||
99 | */ | ||
100 | regmap_write(wdev->regmap, 0, wcr_enable); | ||
101 | regmap_write(wdev->regmap, 0, wcr_enable); | ||
102 | |||
103 | /* wait for reset to assert... */ | ||
104 | mdelay(500); | ||
105 | |||
106 | return NOTIFY_DONE; | ||
107 | } | ||
108 | |||
80 | static inline void imx2_wdt_setup(struct watchdog_device *wdog) | 109 | static inline void imx2_wdt_setup(struct watchdog_device *wdog) |
81 | { | 110 | { |
82 | struct imx2_wdt_device *wdev = watchdog_get_drvdata(wdog); | 111 | struct imx2_wdt_device *wdev = watchdog_get_drvdata(wdog); |
@@ -191,12 +220,10 @@ static struct regmap_config imx2_wdt_regmap_config = { | |||
191 | 220 | ||
192 | static int __init imx2_wdt_probe(struct platform_device *pdev) | 221 | static int __init imx2_wdt_probe(struct platform_device *pdev) |
193 | { | 222 | { |
194 | struct device_node *np = pdev->dev.of_node; | ||
195 | struct imx2_wdt_device *wdev; | 223 | struct imx2_wdt_device *wdev; |
196 | struct watchdog_device *wdog; | 224 | struct watchdog_device *wdog; |
197 | struct resource *res; | 225 | struct resource *res; |
198 | void __iomem *base; | 226 | void __iomem *base; |
199 | bool big_endian; | ||
200 | int ret; | 227 | int ret; |
201 | u32 val; | 228 | u32 val; |
202 | 229 | ||
@@ -204,10 +231,6 @@ static int __init imx2_wdt_probe(struct platform_device *pdev) | |||
204 | if (!wdev) | 231 | if (!wdev) |
205 | return -ENOMEM; | 232 | return -ENOMEM; |
206 | 233 | ||
207 | big_endian = of_property_read_bool(np, "big-endian"); | ||
208 | if (big_endian) | ||
209 | imx2_wdt_regmap_config.val_format_endian = REGMAP_ENDIAN_BIG; | ||
210 | |||
211 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | 234 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); |
212 | base = devm_ioremap_resource(&pdev->dev, res); | 235 | base = devm_ioremap_resource(&pdev->dev, res); |
213 | if (IS_ERR(base)) | 236 | if (IS_ERR(base)) |
@@ -257,6 +280,12 @@ static int __init imx2_wdt_probe(struct platform_device *pdev) | |||
257 | return ret; | 280 | return ret; |
258 | } | 281 | } |
259 | 282 | ||
283 | wdev->restart_handler.notifier_call = imx2_restart_handler; | ||
284 | wdev->restart_handler.priority = 128; | ||
285 | ret = register_restart_handler(&wdev->restart_handler); | ||
286 | if (ret) | ||
287 | dev_err(&pdev->dev, "cannot register restart handler\n"); | ||
288 | |||
260 | dev_info(&pdev->dev, "timeout %d sec (nowayout=%d)\n", | 289 | dev_info(&pdev->dev, "timeout %d sec (nowayout=%d)\n", |
261 | wdog->timeout, nowayout); | 290 | wdog->timeout, nowayout); |
262 | 291 | ||
@@ -268,6 +297,8 @@ static int __exit imx2_wdt_remove(struct platform_device *pdev) | |||
268 | struct watchdog_device *wdog = platform_get_drvdata(pdev); | 297 | struct watchdog_device *wdog = platform_get_drvdata(pdev); |
269 | struct imx2_wdt_device *wdev = watchdog_get_drvdata(wdog); | 298 | struct imx2_wdt_device *wdev = watchdog_get_drvdata(wdog); |
270 | 299 | ||
300 | unregister_restart_handler(&wdev->restart_handler); | ||
301 | |||
271 | watchdog_unregister_device(wdog); | 302 | watchdog_unregister_device(wdog); |
272 | 303 | ||
273 | if (imx2_wdt_is_running(wdev)) { | 304 | if (imx2_wdt_is_running(wdev)) { |
diff --git a/drivers/watchdog/meson_wdt.c b/drivers/watchdog/meson_wdt.c new file mode 100644 index 000000000000..ef6a298e8c45 --- /dev/null +++ b/drivers/watchdog/meson_wdt.c | |||
@@ -0,0 +1,236 @@ | |||
1 | /* | ||
2 | * Meson Watchdog Driver | ||
3 | * | ||
4 | * Copyright (c) 2014 Carlo Caione | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or | ||
7 | * modify it under the terms of the GNU General Public License | ||
8 | * as published by the Free Software Foundation; either version | ||
9 | * 2 of the License, or (at your option) any later version. | ||
10 | */ | ||
11 | |||
12 | #include <linux/clk.h> | ||
13 | #include <linux/delay.h> | ||
14 | #include <linux/err.h> | ||
15 | #include <linux/init.h> | ||
16 | #include <linux/io.h> | ||
17 | #include <linux/kernel.h> | ||
18 | #include <linux/module.h> | ||
19 | #include <linux/moduleparam.h> | ||
20 | #include <linux/notifier.h> | ||
21 | #include <linux/of.h> | ||
22 | #include <linux/platform_device.h> | ||
23 | #include <linux/reboot.h> | ||
24 | #include <linux/types.h> | ||
25 | #include <linux/watchdog.h> | ||
26 | |||
27 | #define DRV_NAME "meson_wdt" | ||
28 | |||
29 | #define MESON_WDT_TC 0x00 | ||
30 | #define MESON_WDT_TC_EN BIT(22) | ||
31 | #define MESON_WDT_TC_TM_MASK 0x3fffff | ||
32 | #define MESON_WDT_DC_RESET (3 << 24) | ||
33 | |||
34 | #define MESON_WDT_RESET 0x04 | ||
35 | |||
36 | #define MESON_WDT_TIMEOUT 30 | ||
37 | #define MESON_WDT_MIN_TIMEOUT 1 | ||
38 | #define MESON_WDT_MAX_TIMEOUT (MESON_WDT_TC_TM_MASK / 100000) | ||
39 | |||
40 | #define MESON_SEC_TO_TC(s) ((s) * 100000) | ||
41 | |||
42 | static bool nowayout = WATCHDOG_NOWAYOUT; | ||
43 | static unsigned int timeout = MESON_WDT_TIMEOUT; | ||
44 | |||
45 | struct meson_wdt_dev { | ||
46 | struct watchdog_device wdt_dev; | ||
47 | void __iomem *wdt_base; | ||
48 | struct notifier_block restart_handler; | ||
49 | }; | ||
50 | |||
51 | static int meson_restart_handle(struct notifier_block *this, unsigned long mode, | ||
52 | void *cmd) | ||
53 | { | ||
54 | u32 tc_reboot = MESON_WDT_DC_RESET | MESON_WDT_TC_EN; | ||
55 | struct meson_wdt_dev *meson_wdt = container_of(this, | ||
56 | struct meson_wdt_dev, | ||
57 | restart_handler); | ||
58 | |||
59 | while (1) { | ||
60 | writel(tc_reboot, meson_wdt->wdt_base + MESON_WDT_TC); | ||
61 | mdelay(5); | ||
62 | } | ||
63 | |||
64 | return NOTIFY_DONE; | ||
65 | } | ||
66 | |||
67 | static int meson_wdt_ping(struct watchdog_device *wdt_dev) | ||
68 | { | ||
69 | struct meson_wdt_dev *meson_wdt = watchdog_get_drvdata(wdt_dev); | ||
70 | |||
71 | writel(0, meson_wdt->wdt_base + MESON_WDT_RESET); | ||
72 | |||
73 | return 0; | ||
74 | } | ||
75 | |||
76 | static void meson_wdt_change_timeout(struct watchdog_device *wdt_dev, | ||
77 | unsigned int timeout) | ||
78 | { | ||
79 | struct meson_wdt_dev *meson_wdt = watchdog_get_drvdata(wdt_dev); | ||
80 | u32 reg; | ||
81 | |||
82 | reg = readl(meson_wdt->wdt_base + MESON_WDT_TC); | ||
83 | reg &= ~MESON_WDT_TC_TM_MASK; | ||
84 | reg |= MESON_SEC_TO_TC(timeout); | ||
85 | writel(reg, meson_wdt->wdt_base + MESON_WDT_TC); | ||
86 | } | ||
87 | |||
88 | static int meson_wdt_set_timeout(struct watchdog_device *wdt_dev, | ||
89 | unsigned int timeout) | ||
90 | { | ||
91 | wdt_dev->timeout = timeout; | ||
92 | |||
93 | meson_wdt_change_timeout(wdt_dev, timeout); | ||
94 | meson_wdt_ping(wdt_dev); | ||
95 | |||
96 | return 0; | ||
97 | } | ||
98 | |||
99 | static int meson_wdt_stop(struct watchdog_device *wdt_dev) | ||
100 | { | ||
101 | struct meson_wdt_dev *meson_wdt = watchdog_get_drvdata(wdt_dev); | ||
102 | u32 reg; | ||
103 | |||
104 | reg = readl(meson_wdt->wdt_base + MESON_WDT_TC); | ||
105 | reg &= ~MESON_WDT_TC_EN; | ||
106 | writel(reg, meson_wdt->wdt_base + MESON_WDT_TC); | ||
107 | |||
108 | return 0; | ||
109 | } | ||
110 | |||
111 | static int meson_wdt_start(struct watchdog_device *wdt_dev) | ||
112 | { | ||
113 | struct meson_wdt_dev *meson_wdt = watchdog_get_drvdata(wdt_dev); | ||
114 | u32 reg; | ||
115 | |||
116 | meson_wdt_change_timeout(wdt_dev, meson_wdt->wdt_dev.timeout); | ||
117 | meson_wdt_ping(wdt_dev); | ||
118 | |||
119 | reg = readl(meson_wdt->wdt_base + MESON_WDT_TC); | ||
120 | reg |= MESON_WDT_TC_EN; | ||
121 | writel(reg, meson_wdt->wdt_base + MESON_WDT_TC); | ||
122 | |||
123 | return 0; | ||
124 | } | ||
125 | |||
126 | static const struct watchdog_info meson_wdt_info = { | ||
127 | .identity = DRV_NAME, | ||
128 | .options = WDIOF_SETTIMEOUT | | ||
129 | WDIOF_KEEPALIVEPING | | ||
130 | WDIOF_MAGICCLOSE, | ||
131 | }; | ||
132 | |||
133 | static const struct watchdog_ops meson_wdt_ops = { | ||
134 | .owner = THIS_MODULE, | ||
135 | .start = meson_wdt_start, | ||
136 | .stop = meson_wdt_stop, | ||
137 | .ping = meson_wdt_ping, | ||
138 | .set_timeout = meson_wdt_set_timeout, | ||
139 | }; | ||
140 | |||
141 | static int meson_wdt_probe(struct platform_device *pdev) | ||
142 | { | ||
143 | struct resource *res; | ||
144 | struct meson_wdt_dev *meson_wdt; | ||
145 | int err; | ||
146 | |||
147 | meson_wdt = devm_kzalloc(&pdev->dev, sizeof(*meson_wdt), GFP_KERNEL); | ||
148 | if (!meson_wdt) | ||
149 | return -ENOMEM; | ||
150 | |||
151 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||
152 | meson_wdt->wdt_base = devm_ioremap_resource(&pdev->dev, res); | ||
153 | if (IS_ERR(meson_wdt->wdt_base)) | ||
154 | return PTR_ERR(meson_wdt->wdt_base); | ||
155 | |||
156 | meson_wdt->wdt_dev.parent = &pdev->dev; | ||
157 | meson_wdt->wdt_dev.info = &meson_wdt_info; | ||
158 | meson_wdt->wdt_dev.ops = &meson_wdt_ops; | ||
159 | meson_wdt->wdt_dev.timeout = MESON_WDT_TIMEOUT; | ||
160 | meson_wdt->wdt_dev.max_timeout = MESON_WDT_MAX_TIMEOUT; | ||
161 | meson_wdt->wdt_dev.min_timeout = MESON_WDT_MIN_TIMEOUT; | ||
162 | |||
163 | watchdog_set_drvdata(&meson_wdt->wdt_dev, meson_wdt); | ||
164 | |||
165 | watchdog_init_timeout(&meson_wdt->wdt_dev, timeout, &pdev->dev); | ||
166 | watchdog_set_nowayout(&meson_wdt->wdt_dev, nowayout); | ||
167 | |||
168 | meson_wdt_stop(&meson_wdt->wdt_dev); | ||
169 | |||
170 | err = watchdog_register_device(&meson_wdt->wdt_dev); | ||
171 | if (err) | ||
172 | return err; | ||
173 | |||
174 | platform_set_drvdata(pdev, meson_wdt); | ||
175 | |||
176 | meson_wdt->restart_handler.notifier_call = meson_restart_handle; | ||
177 | meson_wdt->restart_handler.priority = 128; | ||
178 | err = register_restart_handler(&meson_wdt->restart_handler); | ||
179 | if (err) | ||
180 | dev_err(&pdev->dev, | ||
181 | "cannot register restart handler (err=%d)\n", err); | ||
182 | |||
183 | dev_info(&pdev->dev, "Watchdog enabled (timeout=%d sec, nowayout=%d)", | ||
184 | meson_wdt->wdt_dev.timeout, nowayout); | ||
185 | |||
186 | return 0; | ||
187 | } | ||
188 | |||
189 | static int meson_wdt_remove(struct platform_device *pdev) | ||
190 | { | ||
191 | struct meson_wdt_dev *meson_wdt = platform_get_drvdata(pdev); | ||
192 | |||
193 | unregister_restart_handler(&meson_wdt->restart_handler); | ||
194 | |||
195 | watchdog_unregister_device(&meson_wdt->wdt_dev); | ||
196 | |||
197 | return 0; | ||
198 | } | ||
199 | |||
200 | static void meson_wdt_shutdown(struct platform_device *pdev) | ||
201 | { | ||
202 | struct meson_wdt_dev *meson_wdt = platform_get_drvdata(pdev); | ||
203 | |||
204 | meson_wdt_stop(&meson_wdt->wdt_dev); | ||
205 | } | ||
206 | |||
207 | static const struct of_device_id meson_wdt_dt_ids[] = { | ||
208 | { .compatible = "amlogic,meson6-wdt" }, | ||
209 | { /* sentinel */ } | ||
210 | }; | ||
211 | MODULE_DEVICE_TABLE(of, meson_wdt_dt_ids); | ||
212 | |||
213 | static struct platform_driver meson_wdt_driver = { | ||
214 | .probe = meson_wdt_probe, | ||
215 | .remove = meson_wdt_remove, | ||
216 | .shutdown = meson_wdt_shutdown, | ||
217 | .driver = { | ||
218 | .owner = THIS_MODULE, | ||
219 | .name = DRV_NAME, | ||
220 | .of_match_table = meson_wdt_dt_ids, | ||
221 | }, | ||
222 | }; | ||
223 | |||
224 | module_platform_driver(meson_wdt_driver); | ||
225 | |||
226 | module_param(timeout, uint, 0); | ||
227 | MODULE_PARM_DESC(timeout, "Watchdog heartbeat in seconds"); | ||
228 | |||
229 | module_param(nowayout, bool, 0); | ||
230 | MODULE_PARM_DESC(nowayout, | ||
231 | "Watchdog cannot be stopped once started (default=" | ||
232 | __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); | ||
233 | |||
234 | MODULE_LICENSE("GPL"); | ||
235 | MODULE_AUTHOR("Carlo Caione <carlo@caione.org>"); | ||
236 | MODULE_DESCRIPTION("Meson Watchdog Timer Driver"); | ||
diff --git a/drivers/watchdog/of_xilinx_wdt.c b/drivers/watchdog/of_xilinx_wdt.c index 1e6e28df5d7b..b2e1b4cbbdc1 100644 --- a/drivers/watchdog/of_xilinx_wdt.c +++ b/drivers/watchdog/of_xilinx_wdt.c | |||
@@ -236,7 +236,6 @@ static struct platform_driver xwdt_driver = { | |||
236 | .probe = xwdt_probe, | 236 | .probe = xwdt_probe, |
237 | .remove = xwdt_remove, | 237 | .remove = xwdt_remove, |
238 | .driver = { | 238 | .driver = { |
239 | .owner = THIS_MODULE, | ||
240 | .name = WATCHDOG_NAME, | 239 | .name = WATCHDOG_NAME, |
241 | .of_match_table = xwdt_of_match, | 240 | .of_match_table = xwdt_of_match, |
242 | }, | 241 | }, |
diff --git a/drivers/watchdog/qcom-wdt.c b/drivers/watchdog/qcom-wdt.c new file mode 100644 index 000000000000..aa85618c4d03 --- /dev/null +++ b/drivers/watchdog/qcom-wdt.c | |||
@@ -0,0 +1,224 @@ | |||
1 | /* Copyright (c) 2014, The Linux Foundation. All rights reserved. | ||
2 | * | ||
3 | * This program is free software; you can redistribute it and/or modify | ||
4 | * it under the terms of the GNU General Public License version 2 and | ||
5 | * only version 2 as published by the Free Software Foundation. | ||
6 | * | ||
7 | * This program is distributed in the hope that it will be useful, | ||
8 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
9 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
10 | * GNU General Public License for more details. | ||
11 | * | ||
12 | */ | ||
13 | #include <linux/clk.h> | ||
14 | #include <linux/delay.h> | ||
15 | #include <linux/io.h> | ||
16 | #include <linux/kernel.h> | ||
17 | #include <linux/module.h> | ||
18 | #include <linux/of.h> | ||
19 | #include <linux/platform_device.h> | ||
20 | #include <linux/reboot.h> | ||
21 | #include <linux/watchdog.h> | ||
22 | |||
23 | #define WDT_RST 0x0 | ||
24 | #define WDT_EN 0x8 | ||
25 | #define WDT_BITE_TIME 0x24 | ||
26 | |||
27 | struct qcom_wdt { | ||
28 | struct watchdog_device wdd; | ||
29 | struct clk *clk; | ||
30 | unsigned long rate; | ||
31 | struct notifier_block restart_nb; | ||
32 | void __iomem *base; | ||
33 | }; | ||
34 | |||
35 | static inline | ||
36 | struct qcom_wdt *to_qcom_wdt(struct watchdog_device *wdd) | ||
37 | { | ||
38 | return container_of(wdd, struct qcom_wdt, wdd); | ||
39 | } | ||
40 | |||
41 | static int qcom_wdt_start(struct watchdog_device *wdd) | ||
42 | { | ||
43 | struct qcom_wdt *wdt = to_qcom_wdt(wdd); | ||
44 | |||
45 | writel(0, wdt->base + WDT_EN); | ||
46 | writel(1, wdt->base + WDT_RST); | ||
47 | writel(wdd->timeout * wdt->rate, wdt->base + WDT_BITE_TIME); | ||
48 | writel(1, wdt->base + WDT_EN); | ||
49 | return 0; | ||
50 | } | ||
51 | |||
52 | static int qcom_wdt_stop(struct watchdog_device *wdd) | ||
53 | { | ||
54 | struct qcom_wdt *wdt = to_qcom_wdt(wdd); | ||
55 | |||
56 | writel(0, wdt->base + WDT_EN); | ||
57 | return 0; | ||
58 | } | ||
59 | |||
60 | static int qcom_wdt_ping(struct watchdog_device *wdd) | ||
61 | { | ||
62 | struct qcom_wdt *wdt = to_qcom_wdt(wdd); | ||
63 | |||
64 | writel(1, wdt->base + WDT_RST); | ||
65 | return 0; | ||
66 | } | ||
67 | |||
68 | static int qcom_wdt_set_timeout(struct watchdog_device *wdd, | ||
69 | unsigned int timeout) | ||
70 | { | ||
71 | wdd->timeout = timeout; | ||
72 | return qcom_wdt_start(wdd); | ||
73 | } | ||
74 | |||
75 | static const struct watchdog_ops qcom_wdt_ops = { | ||
76 | .start = qcom_wdt_start, | ||
77 | .stop = qcom_wdt_stop, | ||
78 | .ping = qcom_wdt_ping, | ||
79 | .set_timeout = qcom_wdt_set_timeout, | ||
80 | .owner = THIS_MODULE, | ||
81 | }; | ||
82 | |||
83 | static const struct watchdog_info qcom_wdt_info = { | ||
84 | .options = WDIOF_KEEPALIVEPING | ||
85 | | WDIOF_MAGICCLOSE | ||
86 | | WDIOF_SETTIMEOUT, | ||
87 | .identity = KBUILD_MODNAME, | ||
88 | }; | ||
89 | |||
90 | static int qcom_wdt_restart(struct notifier_block *nb, unsigned long action, | ||
91 | void *data) | ||
92 | { | ||
93 | struct qcom_wdt *wdt = container_of(nb, struct qcom_wdt, restart_nb); | ||
94 | u32 timeout; | ||
95 | |||
96 | /* | ||
97 | * Trigger watchdog bite: | ||
98 | * Setup BITE_TIME to be 128ms, and enable WDT. | ||
99 | */ | ||
100 | timeout = 128 * wdt->rate / 1000; | ||
101 | |||
102 | writel(0, wdt->base + WDT_EN); | ||
103 | writel(1, wdt->base + WDT_RST); | ||
104 | writel(timeout, wdt->base + WDT_BITE_TIME); | ||
105 | writel(1, wdt->base + WDT_EN); | ||
106 | |||
107 | /* | ||
108 | * Actually make sure the above sequence hits hardware before sleeping. | ||
109 | */ | ||
110 | wmb(); | ||
111 | |||
112 | msleep(150); | ||
113 | return NOTIFY_DONE; | ||
114 | } | ||
115 | |||
116 | static int qcom_wdt_probe(struct platform_device *pdev) | ||
117 | { | ||
118 | struct qcom_wdt *wdt; | ||
119 | struct resource *res; | ||
120 | int ret; | ||
121 | |||
122 | wdt = devm_kzalloc(&pdev->dev, sizeof(*wdt), GFP_KERNEL); | ||
123 | if (!wdt) | ||
124 | return -ENOMEM; | ||
125 | |||
126 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||
127 | wdt->base = devm_ioremap_resource(&pdev->dev, res); | ||
128 | if (IS_ERR(wdt->base)) | ||
129 | return PTR_ERR(wdt->base); | ||
130 | |||
131 | wdt->clk = devm_clk_get(&pdev->dev, NULL); | ||
132 | if (IS_ERR(wdt->clk)) { | ||
133 | dev_err(&pdev->dev, "failed to get input clock\n"); | ||
134 | return PTR_ERR(wdt->clk); | ||
135 | } | ||
136 | |||
137 | ret = clk_prepare_enable(wdt->clk); | ||
138 | if (ret) { | ||
139 | dev_err(&pdev->dev, "failed to setup clock\n"); | ||
140 | return ret; | ||
141 | } | ||
142 | |||
143 | /* | ||
144 | * We use the clock rate to calculate the max timeout, so ensure it's | ||
145 | * not zero to avoid a divide-by-zero exception. | ||
146 | * | ||
147 | * WATCHDOG_CORE assumes units of seconds, if the WDT is clocked such | ||
148 | * that it would bite before a second elapses it's usefulness is | ||
149 | * limited. Bail if this is the case. | ||
150 | */ | ||
151 | wdt->rate = clk_get_rate(wdt->clk); | ||
152 | if (wdt->rate == 0 || | ||
153 | wdt->rate > 0x10000000U) { | ||
154 | dev_err(&pdev->dev, "invalid clock rate\n"); | ||
155 | ret = -EINVAL; | ||
156 | goto err_clk_unprepare; | ||
157 | } | ||
158 | |||
159 | wdt->wdd.dev = &pdev->dev; | ||
160 | wdt->wdd.info = &qcom_wdt_info; | ||
161 | wdt->wdd.ops = &qcom_wdt_ops; | ||
162 | wdt->wdd.min_timeout = 1; | ||
163 | wdt->wdd.max_timeout = 0x10000000U / wdt->rate; | ||
164 | |||
165 | /* | ||
166 | * If 'timeout-sec' unspecified in devicetree, assume a 30 second | ||
167 | * default, unless the max timeout is less than 30 seconds, then use | ||
168 | * the max instead. | ||
169 | */ | ||
170 | wdt->wdd.timeout = min(wdt->wdd.max_timeout, 30U); | ||
171 | watchdog_init_timeout(&wdt->wdd, 0, &pdev->dev); | ||
172 | |||
173 | ret = watchdog_register_device(&wdt->wdd); | ||
174 | if (ret) { | ||
175 | dev_err(&pdev->dev, "failed to register watchdog\n"); | ||
176 | goto err_clk_unprepare; | ||
177 | } | ||
178 | |||
179 | /* | ||
180 | * WDT restart notifier has priority 0 (use as a last resort) | ||
181 | */ | ||
182 | wdt->restart_nb.notifier_call = qcom_wdt_restart; | ||
183 | ret = register_restart_handler(&wdt->restart_nb); | ||
184 | if (ret) | ||
185 | dev_err(&pdev->dev, "failed to setup restart handler\n"); | ||
186 | |||
187 | platform_set_drvdata(pdev, wdt); | ||
188 | return 0; | ||
189 | |||
190 | err_clk_unprepare: | ||
191 | clk_disable_unprepare(wdt->clk); | ||
192 | return ret; | ||
193 | } | ||
194 | |||
195 | static int qcom_wdt_remove(struct platform_device *pdev) | ||
196 | { | ||
197 | struct qcom_wdt *wdt = platform_get_drvdata(pdev); | ||
198 | |||
199 | unregister_restart_handler(&wdt->restart_nb); | ||
200 | watchdog_unregister_device(&wdt->wdd); | ||
201 | clk_disable_unprepare(wdt->clk); | ||
202 | return 0; | ||
203 | } | ||
204 | |||
205 | static const struct of_device_id qcom_wdt_of_table[] = { | ||
206 | { .compatible = "qcom,kpss-wdt-msm8960", }, | ||
207 | { .compatible = "qcom,kpss-wdt-apq8064", }, | ||
208 | { .compatible = "qcom,kpss-wdt-ipq8064", }, | ||
209 | { }, | ||
210 | }; | ||
211 | MODULE_DEVICE_TABLE(of, qcom_wdt_of_table); | ||
212 | |||
213 | static struct platform_driver qcom_watchdog_driver = { | ||
214 | .probe = qcom_wdt_probe, | ||
215 | .remove = qcom_wdt_remove, | ||
216 | .driver = { | ||
217 | .name = KBUILD_MODNAME, | ||
218 | .of_match_table = qcom_wdt_of_table, | ||
219 | }, | ||
220 | }; | ||
221 | module_platform_driver(qcom_watchdog_driver); | ||
222 | |||
223 | MODULE_DESCRIPTION("QCOM KPSS Watchdog Driver"); | ||
224 | MODULE_LICENSE("GPL v2"); | ||
diff --git a/drivers/watchdog/rn5t618_wdt.c b/drivers/watchdog/rn5t618_wdt.c new file mode 100644 index 000000000000..d1c12278cb6a --- /dev/null +++ b/drivers/watchdog/rn5t618_wdt.c | |||
@@ -0,0 +1,198 @@ | |||
1 | /* | ||
2 | * Watchdog driver for Ricoh RN5T618 PMIC | ||
3 | * | ||
4 | * Copyright (C) 2014 Beniamino Galvani <b.galvani@gmail.com> | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or | ||
7 | * modify it under the terms of the GNU General Public License | ||
8 | * version 2 as published by the Free Software Foundation. | ||
9 | * | ||
10 | * You should have received a copy of the GNU General Public License | ||
11 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
12 | */ | ||
13 | |||
14 | #include <linux/device.h> | ||
15 | #include <linux/mfd/rn5t618.h> | ||
16 | #include <linux/module.h> | ||
17 | #include <linux/platform_device.h> | ||
18 | #include <linux/watchdog.h> | ||
19 | |||
20 | #define DRIVER_NAME "rn5t618-wdt" | ||
21 | |||
22 | static bool nowayout = WATCHDOG_NOWAYOUT; | ||
23 | static unsigned int timeout; | ||
24 | |||
25 | module_param(timeout, uint, 0); | ||
26 | MODULE_PARM_DESC(timeout, "Initial watchdog timeout in seconds"); | ||
27 | |||
28 | module_param(nowayout, bool, 0); | ||
29 | MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=" | ||
30 | __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); | ||
31 | |||
32 | struct rn5t618_wdt { | ||
33 | struct watchdog_device wdt_dev; | ||
34 | struct rn5t618 *rn5t618; | ||
35 | }; | ||
36 | |||
37 | /* | ||
38 | * This array encodes the values of WDOGTIM field for the supported | ||
39 | * watchdog expiration times. If the watchdog is not accessed before | ||
40 | * the timer expiration, the PMU generates an interrupt and if the CPU | ||
41 | * doesn't clear it within one second the system is restarted. | ||
42 | */ | ||
43 | static const struct { | ||
44 | u8 reg_val; | ||
45 | unsigned int time; | ||
46 | } rn5t618_wdt_map[] = { | ||
47 | { 0, 1 }, | ||
48 | { 1, 8 }, | ||
49 | { 2, 32 }, | ||
50 | { 3, 128 }, | ||
51 | }; | ||
52 | |||
53 | static int rn5t618_wdt_set_timeout(struct watchdog_device *wdt_dev, | ||
54 | unsigned int t) | ||
55 | { | ||
56 | struct rn5t618_wdt *wdt = watchdog_get_drvdata(wdt_dev); | ||
57 | int ret, i; | ||
58 | |||
59 | for (i = 0; i < ARRAY_SIZE(rn5t618_wdt_map); i++) { | ||
60 | if (rn5t618_wdt_map[i].time + 1 >= t) | ||
61 | break; | ||
62 | } | ||
63 | |||
64 | if (i == ARRAY_SIZE(rn5t618_wdt_map)) | ||
65 | return -EINVAL; | ||
66 | |||
67 | ret = regmap_update_bits(wdt->rn5t618->regmap, RN5T618_WATCHDOG, | ||
68 | RN5T618_WATCHDOG_WDOGTIM_M, | ||
69 | rn5t618_wdt_map[i].reg_val); | ||
70 | if (!ret) | ||
71 | wdt_dev->timeout = rn5t618_wdt_map[i].time; | ||
72 | |||
73 | return ret; | ||
74 | } | ||
75 | |||
76 | static int rn5t618_wdt_start(struct watchdog_device *wdt_dev) | ||
77 | { | ||
78 | struct rn5t618_wdt *wdt = watchdog_get_drvdata(wdt_dev); | ||
79 | int ret; | ||
80 | |||
81 | ret = rn5t618_wdt_set_timeout(wdt_dev, wdt_dev->timeout); | ||
82 | if (ret) | ||
83 | return ret; | ||
84 | |||
85 | /* enable repower-on */ | ||
86 | ret = regmap_update_bits(wdt->rn5t618->regmap, RN5T618_REPCNT, | ||
87 | RN5T618_REPCNT_REPWRON, | ||
88 | RN5T618_REPCNT_REPWRON); | ||
89 | if (ret) | ||
90 | return ret; | ||
91 | |||
92 | /* enable watchdog */ | ||
93 | ret = regmap_update_bits(wdt->rn5t618->regmap, RN5T618_WATCHDOG, | ||
94 | RN5T618_WATCHDOG_WDOGEN, | ||
95 | RN5T618_WATCHDOG_WDOGEN); | ||
96 | if (ret) | ||
97 | return ret; | ||
98 | |||
99 | /* enable watchdog interrupt */ | ||
100 | return regmap_update_bits(wdt->rn5t618->regmap, RN5T618_PWRIREN, | ||
101 | RN5T618_PWRIRQ_IR_WDOG, | ||
102 | RN5T618_PWRIRQ_IR_WDOG); | ||
103 | } | ||
104 | |||
105 | static int rn5t618_wdt_stop(struct watchdog_device *wdt_dev) | ||
106 | { | ||
107 | struct rn5t618_wdt *wdt = watchdog_get_drvdata(wdt_dev); | ||
108 | |||
109 | return regmap_update_bits(wdt->rn5t618->regmap, RN5T618_WATCHDOG, | ||
110 | RN5T618_WATCHDOG_WDOGEN, 0); | ||
111 | } | ||
112 | |||
113 | static int rn5t618_wdt_ping(struct watchdog_device *wdt_dev) | ||
114 | { | ||
115 | struct rn5t618_wdt *wdt = watchdog_get_drvdata(wdt_dev); | ||
116 | unsigned int val; | ||
117 | int ret; | ||
118 | |||
119 | /* The counter is restarted after a R/W access to watchdog register */ | ||
120 | ret = regmap_read(wdt->rn5t618->regmap, RN5T618_WATCHDOG, &val); | ||
121 | if (ret) | ||
122 | return ret; | ||
123 | |||
124 | ret = regmap_write(wdt->rn5t618->regmap, RN5T618_WATCHDOG, val); | ||
125 | if (ret) | ||
126 | return ret; | ||
127 | |||
128 | /* Clear pending watchdog interrupt */ | ||
129 | return regmap_update_bits(wdt->rn5t618->regmap, RN5T618_PWRIRQ, | ||
130 | RN5T618_PWRIRQ_IR_WDOG, 0); | ||
131 | } | ||
132 | |||
133 | static struct watchdog_info rn5t618_wdt_info = { | ||
134 | .options = WDIOF_SETTIMEOUT | WDIOF_MAGICCLOSE | | ||
135 | WDIOF_KEEPALIVEPING, | ||
136 | .identity = DRIVER_NAME, | ||
137 | }; | ||
138 | |||
139 | static struct watchdog_ops rn5t618_wdt_ops = { | ||
140 | .owner = THIS_MODULE, | ||
141 | .start = rn5t618_wdt_start, | ||
142 | .stop = rn5t618_wdt_stop, | ||
143 | .ping = rn5t618_wdt_ping, | ||
144 | .set_timeout = rn5t618_wdt_set_timeout, | ||
145 | }; | ||
146 | |||
147 | static int rn5t618_wdt_probe(struct platform_device *pdev) | ||
148 | { | ||
149 | struct rn5t618 *rn5t618 = dev_get_drvdata(pdev->dev.parent); | ||
150 | struct rn5t618_wdt *wdt; | ||
151 | int min_timeout, max_timeout; | ||
152 | |||
153 | wdt = devm_kzalloc(&pdev->dev, sizeof(struct rn5t618_wdt), GFP_KERNEL); | ||
154 | if (!wdt) | ||
155 | return -ENOMEM; | ||
156 | |||
157 | min_timeout = rn5t618_wdt_map[0].time; | ||
158 | max_timeout = rn5t618_wdt_map[ARRAY_SIZE(rn5t618_wdt_map) - 1].time; | ||
159 | |||
160 | wdt->rn5t618 = rn5t618; | ||
161 | wdt->wdt_dev.info = &rn5t618_wdt_info; | ||
162 | wdt->wdt_dev.ops = &rn5t618_wdt_ops; | ||
163 | wdt->wdt_dev.min_timeout = min_timeout; | ||
164 | wdt->wdt_dev.max_timeout = max_timeout; | ||
165 | wdt->wdt_dev.timeout = max_timeout; | ||
166 | wdt->wdt_dev.parent = &pdev->dev; | ||
167 | |||
168 | watchdog_set_drvdata(&wdt->wdt_dev, wdt); | ||
169 | watchdog_init_timeout(&wdt->wdt_dev, timeout, &pdev->dev); | ||
170 | watchdog_set_nowayout(&wdt->wdt_dev, nowayout); | ||
171 | |||
172 | platform_set_drvdata(pdev, wdt); | ||
173 | |||
174 | return watchdog_register_device(&wdt->wdt_dev); | ||
175 | } | ||
176 | |||
177 | static int rn5t618_wdt_remove(struct platform_device *pdev) | ||
178 | { | ||
179 | struct rn5t618_wdt *wdt = platform_get_drvdata(pdev); | ||
180 | |||
181 | watchdog_unregister_device(&wdt->wdt_dev); | ||
182 | |||
183 | return 0; | ||
184 | } | ||
185 | |||
186 | static struct platform_driver rn5t618_wdt_driver = { | ||
187 | .probe = rn5t618_wdt_probe, | ||
188 | .remove = rn5t618_wdt_remove, | ||
189 | .driver = { | ||
190 | .name = DRIVER_NAME, | ||
191 | }, | ||
192 | }; | ||
193 | |||
194 | module_platform_driver(rn5t618_wdt_driver); | ||
195 | |||
196 | MODULE_AUTHOR("Beniamino Galvani <b.galvani@gmail.com>"); | ||
197 | MODULE_DESCRIPTION("RN5T618 watchdog driver"); | ||
198 | MODULE_LICENSE("GPL v2"); | ||
diff --git a/drivers/watchdog/s3c2410_wdt.c b/drivers/watchdog/s3c2410_wdt.c index 7c6ccd071baf..8532c3e2aea7 100644 --- a/drivers/watchdog/s3c2410_wdt.c +++ b/drivers/watchdog/s3c2410_wdt.c | |||
@@ -41,6 +41,8 @@ | |||
41 | #include <linux/of.h> | 41 | #include <linux/of.h> |
42 | #include <linux/mfd/syscon.h> | 42 | #include <linux/mfd/syscon.h> |
43 | #include <linux/regmap.h> | 43 | #include <linux/regmap.h> |
44 | #include <linux/reboot.h> | ||
45 | #include <linux/delay.h> | ||
44 | 46 | ||
45 | #define S3C2410_WTCON 0x00 | 47 | #define S3C2410_WTCON 0x00 |
46 | #define S3C2410_WTDAT 0x04 | 48 | #define S3C2410_WTDAT 0x04 |
@@ -128,6 +130,7 @@ struct s3c2410_wdt { | |||
128 | unsigned long wtdat_save; | 130 | unsigned long wtdat_save; |
129 | struct watchdog_device wdt_device; | 131 | struct watchdog_device wdt_device; |
130 | struct notifier_block freq_transition; | 132 | struct notifier_block freq_transition; |
133 | struct notifier_block restart_handler; | ||
131 | struct s3c2410_wdt_variant *drv_data; | 134 | struct s3c2410_wdt_variant *drv_data; |
132 | struct regmap *pmureg; | 135 | struct regmap *pmureg; |
133 | }; | 136 | }; |
@@ -155,6 +158,15 @@ static const struct s3c2410_wdt_variant drv_data_exynos5420 = { | |||
155 | .quirks = QUIRK_HAS_PMU_CONFIG | QUIRK_HAS_RST_STAT, | 158 | .quirks = QUIRK_HAS_PMU_CONFIG | QUIRK_HAS_RST_STAT, |
156 | }; | 159 | }; |
157 | 160 | ||
161 | static const struct s3c2410_wdt_variant drv_data_exynos7 = { | ||
162 | .disable_reg = EXYNOS5_WDT_DISABLE_REG_OFFSET, | ||
163 | .mask_reset_reg = EXYNOS5_WDT_MASK_RESET_REG_OFFSET, | ||
164 | .mask_bit = 0, | ||
165 | .rst_stat_reg = EXYNOS5_RST_STAT_REG_OFFSET, | ||
166 | .rst_stat_bit = 23, /* A57 WDTRESET */ | ||
167 | .quirks = QUIRK_HAS_PMU_CONFIG | QUIRK_HAS_RST_STAT, | ||
168 | }; | ||
169 | |||
158 | static const struct of_device_id s3c2410_wdt_match[] = { | 170 | static const struct of_device_id s3c2410_wdt_match[] = { |
159 | { .compatible = "samsung,s3c2410-wdt", | 171 | { .compatible = "samsung,s3c2410-wdt", |
160 | .data = &drv_data_s3c2410 }, | 172 | .data = &drv_data_s3c2410 }, |
@@ -162,6 +174,8 @@ static const struct of_device_id s3c2410_wdt_match[] = { | |||
162 | .data = &drv_data_exynos5250 }, | 174 | .data = &drv_data_exynos5250 }, |
163 | { .compatible = "samsung,exynos5420-wdt", | 175 | { .compatible = "samsung,exynos5420-wdt", |
164 | .data = &drv_data_exynos5420 }, | 176 | .data = &drv_data_exynos5420 }, |
177 | { .compatible = "samsung,exynos7-wdt", | ||
178 | .data = &drv_data_exynos7 }, | ||
165 | {}, | 179 | {}, |
166 | }; | 180 | }; |
167 | MODULE_DEVICE_TABLE(of, s3c2410_wdt_match); | 181 | MODULE_DEVICE_TABLE(of, s3c2410_wdt_match); |
@@ -438,6 +452,31 @@ static inline void s3c2410wdt_cpufreq_deregister(struct s3c2410_wdt *wdt) | |||
438 | } | 452 | } |
439 | #endif | 453 | #endif |
440 | 454 | ||
455 | static int s3c2410wdt_restart(struct notifier_block *this, | ||
456 | unsigned long mode, void *cmd) | ||
457 | { | ||
458 | struct s3c2410_wdt *wdt = container_of(this, struct s3c2410_wdt, | ||
459 | restart_handler); | ||
460 | void __iomem *wdt_base = wdt->reg_base; | ||
461 | |||
462 | /* disable watchdog, to be safe */ | ||
463 | writel(0, wdt_base + S3C2410_WTCON); | ||
464 | |||
465 | /* put initial values into count and data */ | ||
466 | writel(0x80, wdt_base + S3C2410_WTCNT); | ||
467 | writel(0x80, wdt_base + S3C2410_WTDAT); | ||
468 | |||
469 | /* set the watchdog to go and reset... */ | ||
470 | writel(S3C2410_WTCON_ENABLE | S3C2410_WTCON_DIV16 | | ||
471 | S3C2410_WTCON_RSTEN | S3C2410_WTCON_PRESCALE(0x20), | ||
472 | wdt_base + S3C2410_WTCON); | ||
473 | |||
474 | /* wait for reset to assert... */ | ||
475 | mdelay(500); | ||
476 | |||
477 | return NOTIFY_DONE; | ||
478 | } | ||
479 | |||
441 | static inline unsigned int s3c2410wdt_get_bootstatus(struct s3c2410_wdt *wdt) | 480 | static inline unsigned int s3c2410wdt_get_bootstatus(struct s3c2410_wdt *wdt) |
442 | { | 481 | { |
443 | unsigned int rst_stat; | 482 | unsigned int rst_stat; |
@@ -592,6 +631,12 @@ static int s3c2410wdt_probe(struct platform_device *pdev) | |||
592 | 631 | ||
593 | platform_set_drvdata(pdev, wdt); | 632 | platform_set_drvdata(pdev, wdt); |
594 | 633 | ||
634 | wdt->restart_handler.notifier_call = s3c2410wdt_restart; | ||
635 | wdt->restart_handler.priority = 128; | ||
636 | ret = register_restart_handler(&wdt->restart_handler); | ||
637 | if (ret) | ||
638 | pr_err("cannot register restart handler, %d\n", ret); | ||
639 | |||
595 | /* print out a statement of readiness */ | 640 | /* print out a statement of readiness */ |
596 | 641 | ||
597 | wtcon = readl(wdt->reg_base + S3C2410_WTCON); | 642 | wtcon = readl(wdt->reg_base + S3C2410_WTCON); |
@@ -621,6 +666,8 @@ static int s3c2410wdt_remove(struct platform_device *dev) | |||
621 | int ret; | 666 | int ret; |
622 | struct s3c2410_wdt *wdt = platform_get_drvdata(dev); | 667 | struct s3c2410_wdt *wdt = platform_get_drvdata(dev); |
623 | 668 | ||
669 | unregister_restart_handler(&wdt->restart_handler); | ||
670 | |||
624 | ret = s3c2410wdt_mask_and_disable_reset(wdt, true); | 671 | ret = s3c2410wdt_mask_and_disable_reset(wdt, true); |
625 | if (ret < 0) | 672 | if (ret < 0) |
626 | return ret; | 673 | return ret; |
diff --git a/drivers/watchdog/stmp3xxx_rtc_wdt.c b/drivers/watchdog/stmp3xxx_rtc_wdt.c index 3804d5e9baea..a62b1b6decf4 100644 --- a/drivers/watchdog/stmp3xxx_rtc_wdt.c +++ b/drivers/watchdog/stmp3xxx_rtc_wdt.c | |||
@@ -94,9 +94,33 @@ static int stmp3xxx_wdt_remove(struct platform_device *pdev) | |||
94 | return 0; | 94 | return 0; |
95 | } | 95 | } |
96 | 96 | ||
97 | static int __maybe_unused stmp3xxx_wdt_suspend(struct device *dev) | ||
98 | { | ||
99 | struct watchdog_device *wdd = &stmp3xxx_wdd; | ||
100 | |||
101 | if (watchdog_active(wdd)) | ||
102 | return wdt_stop(wdd); | ||
103 | |||
104 | return 0; | ||
105 | } | ||
106 | |||
107 | static int __maybe_unused stmp3xxx_wdt_resume(struct device *dev) | ||
108 | { | ||
109 | struct watchdog_device *wdd = &stmp3xxx_wdd; | ||
110 | |||
111 | if (watchdog_active(wdd)) | ||
112 | return wdt_start(wdd); | ||
113 | |||
114 | return 0; | ||
115 | } | ||
116 | |||
117 | static SIMPLE_DEV_PM_OPS(stmp3xxx_wdt_pm_ops, | ||
118 | stmp3xxx_wdt_suspend, stmp3xxx_wdt_resume); | ||
119 | |||
97 | static struct platform_driver stmp3xxx_wdt_driver = { | 120 | static struct platform_driver stmp3xxx_wdt_driver = { |
98 | .driver = { | 121 | .driver = { |
99 | .name = "stmp3xxx_rtc_wdt", | 122 | .name = "stmp3xxx_rtc_wdt", |
123 | .pm = &stmp3xxx_wdt_pm_ops, | ||
100 | }, | 124 | }, |
101 | .probe = stmp3xxx_wdt_probe, | 125 | .probe = stmp3xxx_wdt_probe, |
102 | .remove = stmp3xxx_wdt_remove, | 126 | .remove = stmp3xxx_wdt_remove, |
diff --git a/drivers/watchdog/sunxi_wdt.c b/drivers/watchdog/sunxi_wdt.c index 480bb557f353..b62301e74e5f 100644 --- a/drivers/watchdog/sunxi_wdt.c +++ b/drivers/watchdog/sunxi_wdt.c | |||
@@ -23,6 +23,7 @@ | |||
23 | #include <linux/moduleparam.h> | 23 | #include <linux/moduleparam.h> |
24 | #include <linux/notifier.h> | 24 | #include <linux/notifier.h> |
25 | #include <linux/of.h> | 25 | #include <linux/of.h> |
26 | #include <linux/of_device.h> | ||
26 | #include <linux/platform_device.h> | 27 | #include <linux/platform_device.h> |
27 | #include <linux/reboot.h> | 28 | #include <linux/reboot.h> |
28 | #include <linux/types.h> | 29 | #include <linux/types.h> |
@@ -30,15 +31,11 @@ | |||
30 | 31 | ||
31 | #define WDT_MAX_TIMEOUT 16 | 32 | #define WDT_MAX_TIMEOUT 16 |
32 | #define WDT_MIN_TIMEOUT 1 | 33 | #define WDT_MIN_TIMEOUT 1 |
33 | #define WDT_MODE_TIMEOUT(n) ((n) << 3) | 34 | #define WDT_TIMEOUT_MASK 0x0F |
34 | #define WDT_TIMEOUT_MASK WDT_MODE_TIMEOUT(0x0F) | ||
35 | 35 | ||
36 | #define WDT_CTRL 0x00 | ||
37 | #define WDT_CTRL_RELOAD ((1 << 0) | (0x0a57 << 1)) | 36 | #define WDT_CTRL_RELOAD ((1 << 0) | (0x0a57 << 1)) |
38 | 37 | ||
39 | #define WDT_MODE 0x04 | ||
40 | #define WDT_MODE_EN (1 << 0) | 38 | #define WDT_MODE_EN (1 << 0) |
41 | #define WDT_MODE_RST_EN (1 << 1) | ||
42 | 39 | ||
43 | #define DRV_NAME "sunxi-wdt" | 40 | #define DRV_NAME "sunxi-wdt" |
44 | #define DRV_VERSION "1.0" | 41 | #define DRV_VERSION "1.0" |
@@ -46,15 +43,29 @@ | |||
46 | static bool nowayout = WATCHDOG_NOWAYOUT; | 43 | static bool nowayout = WATCHDOG_NOWAYOUT; |
47 | static unsigned int timeout = WDT_MAX_TIMEOUT; | 44 | static unsigned int timeout = WDT_MAX_TIMEOUT; |
48 | 45 | ||
46 | /* | ||
47 | * This structure stores the register offsets for different variants | ||
48 | * of Allwinner's watchdog hardware. | ||
49 | */ | ||
50 | struct sunxi_wdt_reg { | ||
51 | u8 wdt_ctrl; | ||
52 | u8 wdt_cfg; | ||
53 | u8 wdt_mode; | ||
54 | u8 wdt_timeout_shift; | ||
55 | u8 wdt_reset_mask; | ||
56 | u8 wdt_reset_val; | ||
57 | }; | ||
58 | |||
49 | struct sunxi_wdt_dev { | 59 | struct sunxi_wdt_dev { |
50 | struct watchdog_device wdt_dev; | 60 | struct watchdog_device wdt_dev; |
51 | void __iomem *wdt_base; | 61 | void __iomem *wdt_base; |
62 | const struct sunxi_wdt_reg *wdt_regs; | ||
52 | struct notifier_block restart_handler; | 63 | struct notifier_block restart_handler; |
53 | }; | 64 | }; |
54 | 65 | ||
55 | /* | 66 | /* |
56 | * wdt_timeout_map maps the watchdog timer interval value in seconds to | 67 | * wdt_timeout_map maps the watchdog timer interval value in seconds to |
57 | * the value of the register WDT_MODE bit 3:6 | 68 | * the value of the register WDT_MODE at bits .wdt_timeout_shift ~ +3 |
58 | * | 69 | * |
59 | * [timeout seconds] = register value | 70 | * [timeout seconds] = register value |
60 | * | 71 | * |
@@ -82,19 +93,32 @@ static int sunxi_restart_handle(struct notifier_block *this, unsigned long mode, | |||
82 | struct sunxi_wdt_dev, | 93 | struct sunxi_wdt_dev, |
83 | restart_handler); | 94 | restart_handler); |
84 | void __iomem *wdt_base = sunxi_wdt->wdt_base; | 95 | void __iomem *wdt_base = sunxi_wdt->wdt_base; |
96 | const struct sunxi_wdt_reg *regs = sunxi_wdt->wdt_regs; | ||
97 | u32 val; | ||
98 | |||
99 | /* Set system reset function */ | ||
100 | val = readl(wdt_base + regs->wdt_cfg); | ||
101 | val &= ~(regs->wdt_reset_mask); | ||
102 | val |= regs->wdt_reset_val; | ||
103 | writel(val, wdt_base + regs->wdt_cfg); | ||
85 | 104 | ||
86 | /* Enable timer and set reset bit in the watchdog */ | 105 | /* Set lowest timeout and enable watchdog */ |
87 | writel(WDT_MODE_EN | WDT_MODE_RST_EN, wdt_base + WDT_MODE); | 106 | val = readl(wdt_base + regs->wdt_mode); |
107 | val &= ~(WDT_TIMEOUT_MASK << regs->wdt_timeout_shift); | ||
108 | val |= WDT_MODE_EN; | ||
109 | writel(val, wdt_base + regs->wdt_mode); | ||
88 | 110 | ||
89 | /* | 111 | /* |
90 | * Restart the watchdog. The default (and lowest) interval | 112 | * Restart the watchdog. The default (and lowest) interval |
91 | * value for the watchdog is 0.5s. | 113 | * value for the watchdog is 0.5s. |
92 | */ | 114 | */ |
93 | writel(WDT_CTRL_RELOAD, wdt_base + WDT_CTRL); | 115 | writel(WDT_CTRL_RELOAD, wdt_base + regs->wdt_ctrl); |
94 | 116 | ||
95 | while (1) { | 117 | while (1) { |
96 | mdelay(5); | 118 | mdelay(5); |
97 | writel(WDT_MODE_EN | WDT_MODE_RST_EN, wdt_base + WDT_MODE); | 119 | val = readl(wdt_base + regs->wdt_mode); |
120 | val |= WDT_MODE_EN; | ||
121 | writel(val, wdt_base + regs->wdt_mode); | ||
98 | } | 122 | } |
99 | return NOTIFY_DONE; | 123 | return NOTIFY_DONE; |
100 | } | 124 | } |
@@ -103,8 +127,9 @@ static int sunxi_wdt_ping(struct watchdog_device *wdt_dev) | |||
103 | { | 127 | { |
104 | struct sunxi_wdt_dev *sunxi_wdt = watchdog_get_drvdata(wdt_dev); | 128 | struct sunxi_wdt_dev *sunxi_wdt = watchdog_get_drvdata(wdt_dev); |
105 | void __iomem *wdt_base = sunxi_wdt->wdt_base; | 129 | void __iomem *wdt_base = sunxi_wdt->wdt_base; |
130 | const struct sunxi_wdt_reg *regs = sunxi_wdt->wdt_regs; | ||
106 | 131 | ||
107 | iowrite32(WDT_CTRL_RELOAD, wdt_base + WDT_CTRL); | 132 | writel(WDT_CTRL_RELOAD, wdt_base + regs->wdt_ctrl); |
108 | 133 | ||
109 | return 0; | 134 | return 0; |
110 | } | 135 | } |
@@ -114,6 +139,7 @@ static int sunxi_wdt_set_timeout(struct watchdog_device *wdt_dev, | |||
114 | { | 139 | { |
115 | struct sunxi_wdt_dev *sunxi_wdt = watchdog_get_drvdata(wdt_dev); | 140 | struct sunxi_wdt_dev *sunxi_wdt = watchdog_get_drvdata(wdt_dev); |
116 | void __iomem *wdt_base = sunxi_wdt->wdt_base; | 141 | void __iomem *wdt_base = sunxi_wdt->wdt_base; |
142 | const struct sunxi_wdt_reg *regs = sunxi_wdt->wdt_regs; | ||
117 | u32 reg; | 143 | u32 reg; |
118 | 144 | ||
119 | if (wdt_timeout_map[timeout] == 0) | 145 | if (wdt_timeout_map[timeout] == 0) |
@@ -121,10 +147,10 @@ static int sunxi_wdt_set_timeout(struct watchdog_device *wdt_dev, | |||
121 | 147 | ||
122 | sunxi_wdt->wdt_dev.timeout = timeout; | 148 | sunxi_wdt->wdt_dev.timeout = timeout; |
123 | 149 | ||
124 | reg = ioread32(wdt_base + WDT_MODE); | 150 | reg = readl(wdt_base + regs->wdt_mode); |
125 | reg &= ~WDT_TIMEOUT_MASK; | 151 | reg &= ~(WDT_TIMEOUT_MASK << regs->wdt_timeout_shift); |
126 | reg |= WDT_MODE_TIMEOUT(wdt_timeout_map[timeout]); | 152 | reg |= wdt_timeout_map[timeout] << regs->wdt_timeout_shift; |
127 | iowrite32(reg, wdt_base + WDT_MODE); | 153 | writel(reg, wdt_base + regs->wdt_mode); |
128 | 154 | ||
129 | sunxi_wdt_ping(wdt_dev); | 155 | sunxi_wdt_ping(wdt_dev); |
130 | 156 | ||
@@ -135,8 +161,9 @@ static int sunxi_wdt_stop(struct watchdog_device *wdt_dev) | |||
135 | { | 161 | { |
136 | struct sunxi_wdt_dev *sunxi_wdt = watchdog_get_drvdata(wdt_dev); | 162 | struct sunxi_wdt_dev *sunxi_wdt = watchdog_get_drvdata(wdt_dev); |
137 | void __iomem *wdt_base = sunxi_wdt->wdt_base; | 163 | void __iomem *wdt_base = sunxi_wdt->wdt_base; |
164 | const struct sunxi_wdt_reg *regs = sunxi_wdt->wdt_regs; | ||
138 | 165 | ||
139 | iowrite32(0, wdt_base + WDT_MODE); | 166 | writel(0, wdt_base + regs->wdt_mode); |
140 | 167 | ||
141 | return 0; | 168 | return 0; |
142 | } | 169 | } |
@@ -146,6 +173,7 @@ static int sunxi_wdt_start(struct watchdog_device *wdt_dev) | |||
146 | u32 reg; | 173 | u32 reg; |
147 | struct sunxi_wdt_dev *sunxi_wdt = watchdog_get_drvdata(wdt_dev); | 174 | struct sunxi_wdt_dev *sunxi_wdt = watchdog_get_drvdata(wdt_dev); |
148 | void __iomem *wdt_base = sunxi_wdt->wdt_base; | 175 | void __iomem *wdt_base = sunxi_wdt->wdt_base; |
176 | const struct sunxi_wdt_reg *regs = sunxi_wdt->wdt_regs; | ||
149 | int ret; | 177 | int ret; |
150 | 178 | ||
151 | ret = sunxi_wdt_set_timeout(&sunxi_wdt->wdt_dev, | 179 | ret = sunxi_wdt_set_timeout(&sunxi_wdt->wdt_dev, |
@@ -153,9 +181,16 @@ static int sunxi_wdt_start(struct watchdog_device *wdt_dev) | |||
153 | if (ret < 0) | 181 | if (ret < 0) |
154 | return ret; | 182 | return ret; |
155 | 183 | ||
156 | reg = ioread32(wdt_base + WDT_MODE); | 184 | /* Set system reset function */ |
157 | reg |= (WDT_MODE_RST_EN | WDT_MODE_EN); | 185 | reg = readl(wdt_base + regs->wdt_cfg); |
158 | iowrite32(reg, wdt_base + WDT_MODE); | 186 | reg &= ~(regs->wdt_reset_mask); |
187 | reg |= ~(regs->wdt_reset_val); | ||
188 | writel(reg, wdt_base + regs->wdt_cfg); | ||
189 | |||
190 | /* Enable watchdog */ | ||
191 | reg = readl(wdt_base + regs->wdt_mode); | ||
192 | reg |= WDT_MODE_EN; | ||
193 | writel(reg, wdt_base + regs->wdt_mode); | ||
159 | 194 | ||
160 | return 0; | 195 | return 0; |
161 | } | 196 | } |
@@ -175,9 +210,35 @@ static const struct watchdog_ops sunxi_wdt_ops = { | |||
175 | .set_timeout = sunxi_wdt_set_timeout, | 210 | .set_timeout = sunxi_wdt_set_timeout, |
176 | }; | 211 | }; |
177 | 212 | ||
213 | static const struct sunxi_wdt_reg sun4i_wdt_reg = { | ||
214 | .wdt_ctrl = 0x00, | ||
215 | .wdt_cfg = 0x04, | ||
216 | .wdt_mode = 0x04, | ||
217 | .wdt_timeout_shift = 3, | ||
218 | .wdt_reset_mask = 0x02, | ||
219 | .wdt_reset_val = 0x02, | ||
220 | }; | ||
221 | |||
222 | static const struct sunxi_wdt_reg sun6i_wdt_reg = { | ||
223 | .wdt_ctrl = 0x10, | ||
224 | .wdt_cfg = 0x14, | ||
225 | .wdt_mode = 0x18, | ||
226 | .wdt_timeout_shift = 4, | ||
227 | .wdt_reset_mask = 0x03, | ||
228 | .wdt_reset_val = 0x01, | ||
229 | }; | ||
230 | |||
231 | static const struct of_device_id sunxi_wdt_dt_ids[] = { | ||
232 | { .compatible = "allwinner,sun4i-a10-wdt", .data = &sun4i_wdt_reg }, | ||
233 | { .compatible = "allwinner,sun6i-a31-wdt", .data = &sun6i_wdt_reg }, | ||
234 | { /* sentinel */ } | ||
235 | }; | ||
236 | MODULE_DEVICE_TABLE(of, sunxi_wdt_dt_ids); | ||
237 | |||
178 | static int sunxi_wdt_probe(struct platform_device *pdev) | 238 | static int sunxi_wdt_probe(struct platform_device *pdev) |
179 | { | 239 | { |
180 | struct sunxi_wdt_dev *sunxi_wdt; | 240 | struct sunxi_wdt_dev *sunxi_wdt; |
241 | const struct of_device_id *device; | ||
181 | struct resource *res; | 242 | struct resource *res; |
182 | int err; | 243 | int err; |
183 | 244 | ||
@@ -187,6 +248,12 @@ static int sunxi_wdt_probe(struct platform_device *pdev) | |||
187 | 248 | ||
188 | platform_set_drvdata(pdev, sunxi_wdt); | 249 | platform_set_drvdata(pdev, sunxi_wdt); |
189 | 250 | ||
251 | device = of_match_device(sunxi_wdt_dt_ids, &pdev->dev); | ||
252 | if (!device) | ||
253 | return -ENODEV; | ||
254 | |||
255 | sunxi_wdt->wdt_regs = device->data; | ||
256 | |||
190 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | 257 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); |
191 | sunxi_wdt->wdt_base = devm_ioremap_resource(&pdev->dev, res); | 258 | sunxi_wdt->wdt_base = devm_ioremap_resource(&pdev->dev, res); |
192 | if (IS_ERR(sunxi_wdt->wdt_base)) | 259 | if (IS_ERR(sunxi_wdt->wdt_base)) |
@@ -242,12 +309,6 @@ static void sunxi_wdt_shutdown(struct platform_device *pdev) | |||
242 | sunxi_wdt_stop(&sunxi_wdt->wdt_dev); | 309 | sunxi_wdt_stop(&sunxi_wdt->wdt_dev); |
243 | } | 310 | } |
244 | 311 | ||
245 | static const struct of_device_id sunxi_wdt_dt_ids[] = { | ||
246 | { .compatible = "allwinner,sun4i-a10-wdt" }, | ||
247 | { /* sentinel */ } | ||
248 | }; | ||
249 | MODULE_DEVICE_TABLE(of, sunxi_wdt_dt_ids); | ||
250 | |||
251 | static struct platform_driver sunxi_wdt_driver = { | 312 | static struct platform_driver sunxi_wdt_driver = { |
252 | .probe = sunxi_wdt_probe, | 313 | .probe = sunxi_wdt_probe, |
253 | .remove = sunxi_wdt_remove, | 314 | .remove = sunxi_wdt_remove, |
diff --git a/drivers/watchdog/ts72xx_wdt.c b/drivers/watchdog/ts72xx_wdt.c index afa9d6ef353a..dee9c6cbe6df 100644 --- a/drivers/watchdog/ts72xx_wdt.c +++ b/drivers/watchdog/ts72xx_wdt.c | |||
@@ -428,11 +428,7 @@ static int ts72xx_wdt_probe(struct platform_device *pdev) | |||
428 | 428 | ||
429 | static int ts72xx_wdt_remove(struct platform_device *pdev) | 429 | static int ts72xx_wdt_remove(struct platform_device *pdev) |
430 | { | 430 | { |
431 | int error; | 431 | return misc_deregister(&ts72xx_wdt_miscdev); |
432 | |||
433 | error = misc_deregister(&ts72xx_wdt_miscdev); | ||
434 | |||
435 | return error; | ||
436 | } | 432 | } |
437 | 433 | ||
438 | static struct platform_driver ts72xx_wdt_driver = { | 434 | static struct platform_driver ts72xx_wdt_driver = { |
diff --git a/include/linux/watchdog.h b/include/linux/watchdog.h index 2a3038ee17a3..395b70e0eccf 100644 --- a/include/linux/watchdog.h +++ b/include/linux/watchdog.h | |||
@@ -97,13 +97,8 @@ struct watchdog_device { | |||
97 | #define WDOG_UNREGISTERED 4 /* Has the device been unregistered */ | 97 | #define WDOG_UNREGISTERED 4 /* Has the device been unregistered */ |
98 | }; | 98 | }; |
99 | 99 | ||
100 | #ifdef CONFIG_WATCHDOG_NOWAYOUT | 100 | #define WATCHDOG_NOWAYOUT IS_BUILTIN(CONFIG_WATCHDOG_NOWAYOUT) |
101 | #define WATCHDOG_NOWAYOUT 1 | 101 | #define WATCHDOG_NOWAYOUT_INIT_STATUS (WATCHDOG_NOWAYOUT << WDOG_NO_WAY_OUT) |
102 | #define WATCHDOG_NOWAYOUT_INIT_STATUS (1 << WDOG_NO_WAY_OUT) | ||
103 | #else | ||
104 | #define WATCHDOG_NOWAYOUT 0 | ||
105 | #define WATCHDOG_NOWAYOUT_INIT_STATUS 0 | ||
106 | #endif | ||
107 | 102 | ||
108 | /* Use the following function to check whether or not the watchdog is active */ | 103 | /* Use the following function to check whether or not the watchdog is active */ |
109 | static inline bool watchdog_active(struct watchdog_device *wdd) | 104 | static inline bool watchdog_active(struct watchdog_device *wdd) |