diff options
| -rw-r--r-- | Documentation/devicetree/bindings/watchdog/digicolor-wdt.txt | 25 | ||||
| -rw-r--r-- | Documentation/devicetree/bindings/watchdog/omap-wdt.txt | 9 | ||||
| -rw-r--r-- | Documentation/watchdog/watchdog-kernel-api.txt | 7 | ||||
| -rw-r--r-- | Documentation/watchdog/watchdog-parameters.txt | 3 | ||||
| -rw-r--r-- | drivers/watchdog/Kconfig | 36 | ||||
| -rw-r--r-- | drivers/watchdog/Makefile | 2 | ||||
| -rw-r--r-- | drivers/watchdog/at91sam9_wdt.c | 4 | ||||
| -rw-r--r-- | drivers/watchdog/da9062_wdt.c | 253 | ||||
| -rw-r--r-- | drivers/watchdog/digicolor_wdt.c | 205 | ||||
| -rw-r--r-- | drivers/watchdog/dw_wdt.c | 8 | ||||
| -rw-r--r-- | drivers/watchdog/gpio_wdt.c | 9 | ||||
| -rw-r--r-- | drivers/watchdog/hpwdt.c | 16 | ||||
| -rw-r--r-- | drivers/watchdog/imgpdc_wdt.c | 84 | ||||
| -rw-r--r-- | drivers/watchdog/imx2_wdt.c | 18 | ||||
| -rw-r--r-- | drivers/watchdog/max63xx_wdt.c | 172 | ||||
| -rw-r--r-- | drivers/watchdog/mena21_wdt.c | 5 | ||||
| -rw-r--r-- | drivers/watchdog/omap_wdt.c | 92 | ||||
| -rw-r--r-- | drivers/watchdog/omap_wdt.h | 1 | ||||
| -rw-r--r-- | drivers/watchdog/st_lpc_wdt.c | 2 | ||||
| -rw-r--r-- | drivers/watchdog/watchdog_core.c | 118 | ||||
| -rw-r--r-- | include/linux/watchdog.h | 3 |
21 files changed, 913 insertions, 159 deletions
diff --git a/Documentation/devicetree/bindings/watchdog/digicolor-wdt.txt b/Documentation/devicetree/bindings/watchdog/digicolor-wdt.txt new file mode 100644 index 000000000000..a882967e17d4 --- /dev/null +++ b/Documentation/devicetree/bindings/watchdog/digicolor-wdt.txt | |||
| @@ -0,0 +1,25 @@ | |||
| 1 | Conexant Digicolor SoCs Watchdog timer | ||
| 2 | |||
| 3 | The watchdog functionality in Conexant Digicolor SoCs relies on the so called | ||
| 4 | "Agent Communication" block. This block includes the eight programmable system | ||
| 5 | timer counters. The first timer (called "Timer A") is the only one that can be | ||
| 6 | used as watchdog. | ||
| 7 | |||
| 8 | Required properties: | ||
| 9 | |||
| 10 | - compatible : Should be "cnxt,cx92755-wdt" | ||
| 11 | - reg : Specifies base physical address and size of the registers | ||
| 12 | - clocks : phandle; specifies the clock that drives the timer | ||
| 13 | |||
| 14 | Optional properties: | ||
| 15 | |||
| 16 | - timeout-sec : Contains the watchdog timeout in seconds | ||
| 17 | |||
| 18 | Example: | ||
| 19 | |||
| 20 | watchdog@f0000fc0 { | ||
| 21 | compatible = "cnxt,cx92755-wdt"; | ||
| 22 | reg = <0xf0000fc0 0x8>; | ||
| 23 | clocks = <&main_clk>; | ||
| 24 | timeout-sec = <15>; | ||
| 25 | }; | ||
diff --git a/Documentation/devicetree/bindings/watchdog/omap-wdt.txt b/Documentation/devicetree/bindings/watchdog/omap-wdt.txt index c227970671ea..1fa20e453a2d 100644 --- a/Documentation/devicetree/bindings/watchdog/omap-wdt.txt +++ b/Documentation/devicetree/bindings/watchdog/omap-wdt.txt | |||
| @@ -1,10 +1,11 @@ | |||
| 1 | TI Watchdog Timer (WDT) Controller for OMAP | 1 | TI Watchdog Timer (WDT) Controller for OMAP |
| 2 | 2 | ||
| 3 | Required properties: | 3 | Required properties: |
| 4 | compatible: | 4 | - compatible : "ti,omap3-wdt" for OMAP3 or "ti,omap4-wdt" for OMAP4 |
| 5 | - "ti,omap3-wdt" for OMAP3 | 5 | - ti,hwmods : Name of the hwmod associated to the WDT |
| 6 | - "ti,omap4-wdt" for OMAP4 | 6 | |
| 7 | - ti,hwmods: Name of the hwmod associated to the WDT | 7 | Optional properties: |
| 8 | - timeout-sec : default watchdog timeout in seconds | ||
| 8 | 9 | ||
| 9 | Examples: | 10 | Examples: |
| 10 | 11 | ||
diff --git a/Documentation/watchdog/watchdog-kernel-api.txt b/Documentation/watchdog/watchdog-kernel-api.txt index a0438f3957ca..d8b0d3367706 100644 --- a/Documentation/watchdog/watchdog-kernel-api.txt +++ b/Documentation/watchdog/watchdog-kernel-api.txt | |||
| @@ -36,6 +36,10 @@ The watchdog_unregister_device routine deregisters a registered watchdog timer | |||
| 36 | device. The parameter of this routine is the pointer to the registered | 36 | device. The parameter of this routine is the pointer to the registered |
| 37 | watchdog_device structure. | 37 | watchdog_device structure. |
| 38 | 38 | ||
| 39 | The watchdog subsystem includes an registration deferral mechanism, | ||
| 40 | which allows you to register an watchdog as early as you wish during | ||
| 41 | the boot process. | ||
| 42 | |||
| 39 | The watchdog device structure looks like this: | 43 | The watchdog device structure looks like this: |
| 40 | 44 | ||
| 41 | struct watchdog_device { | 45 | struct watchdog_device { |
| @@ -52,6 +56,7 @@ struct watchdog_device { | |||
| 52 | void *driver_data; | 56 | void *driver_data; |
| 53 | struct mutex lock; | 57 | struct mutex lock; |
| 54 | unsigned long status; | 58 | unsigned long status; |
| 59 | struct list_head deferred; | ||
| 55 | }; | 60 | }; |
| 56 | 61 | ||
| 57 | It contains following fields: | 62 | It contains following fields: |
| @@ -80,6 +85,8 @@ It contains following fields: | |||
| 80 | information about the status of the device (Like: is the watchdog timer | 85 | information about the status of the device (Like: is the watchdog timer |
| 81 | running/active, is the nowayout bit set, is the device opened via | 86 | running/active, is the nowayout bit set, is the device opened via |
| 82 | the /dev/watchdog interface or not, ...). | 87 | the /dev/watchdog interface or not, ...). |
| 88 | * deferred: entry in wtd_deferred_reg_list which is used to | ||
| 89 | register early initialized watchdogs. | ||
| 83 | 90 | ||
| 84 | The list of watchdog operations is defined as: | 91 | The list of watchdog operations is defined as: |
| 85 | 92 | ||
diff --git a/Documentation/watchdog/watchdog-parameters.txt b/Documentation/watchdog/watchdog-parameters.txt index 692791cc674c..9f9ec9f76039 100644 --- a/Documentation/watchdog/watchdog-parameters.txt +++ b/Documentation/watchdog/watchdog-parameters.txt | |||
| @@ -208,6 +208,9 @@ nowayout: Watchdog cannot be stopped once started | |||
| 208 | ------------------------------------------------- | 208 | ------------------------------------------------- |
| 209 | omap_wdt: | 209 | omap_wdt: |
| 210 | timer_margin: initial watchdog timeout (in seconds) | 210 | timer_margin: initial watchdog timeout (in seconds) |
| 211 | early_enable: Watchdog is started on module insertion (default=0 | ||
| 212 | nowayout: Watchdog cannot be stopped once started | ||
| 213 | (default=kernel config parameter) | ||
| 211 | ------------------------------------------------- | 214 | ------------------------------------------------- |
| 212 | orion_wdt: | 215 | orion_wdt: |
| 213 | heartbeat: Initial watchdog heartbeat in seconds | 216 | heartbeat: Initial watchdog heartbeat in seconds |
diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig index 262647bbc614..241fafde42cb 100644 --- a/drivers/watchdog/Kconfig +++ b/drivers/watchdog/Kconfig | |||
| @@ -1,3 +1,4 @@ | |||
| 1 | |||
| 1 | # | 2 | # |
| 2 | # Watchdog device configuration | 3 | # Watchdog device configuration |
| 3 | # | 4 | # |
| @@ -96,6 +97,15 @@ config DA9063_WATCHDOG | |||
| 96 | 97 | ||
| 97 | This driver can be built as a module. The module name is da9063_wdt. | 98 | This driver can be built as a module. The module name is da9063_wdt. |
| 98 | 99 | ||
| 100 | config DA9062_WATCHDOG | ||
| 101 | tristate "Dialog DA9062 Watchdog" | ||
| 102 | depends on MFD_DA9062 | ||
| 103 | select WATCHDOG_CORE | ||
| 104 | help | ||
| 105 | Support for the watchdog in the DA9062 PMIC. | ||
| 106 | |||
| 107 | This driver can be built as a module. The module name is da9062_wdt. | ||
| 108 | |||
| 99 | config GPIO_WATCHDOG | 109 | config GPIO_WATCHDOG |
| 100 | tristate "Watchdog device controlled through GPIO-line" | 110 | tristate "Watchdog device controlled through GPIO-line" |
| 101 | depends on OF_GPIO | 111 | depends on OF_GPIO |
| @@ -104,6 +114,17 @@ config GPIO_WATCHDOG | |||
| 104 | If you say yes here you get support for watchdog device | 114 | If you say yes here you get support for watchdog device |
| 105 | controlled through GPIO-line. | 115 | controlled through GPIO-line. |
| 106 | 116 | ||
| 117 | config GPIO_WATCHDOG_ARCH_INITCALL | ||
| 118 | bool "Register the watchdog as early as possible" | ||
| 119 | depends on GPIO_WATCHDOG=y | ||
| 120 | help | ||
| 121 | In some situations, the default initcall level (module_init) | ||
| 122 | in not early enough in the boot process to avoid the watchdog | ||
| 123 | to be triggered. | ||
| 124 | If you say yes here, the initcall level would be raised to | ||
| 125 | arch_initcall. | ||
| 126 | If in doubt, say N. | ||
| 127 | |||
| 107 | config MENF21BMC_WATCHDOG | 128 | config MENF21BMC_WATCHDOG |
| 108 | tristate "MEN 14F021P00 BMC Watchdog" | 129 | tristate "MEN 14F021P00 BMC Watchdog" |
| 109 | depends on MFD_MENF21BMC | 130 | depends on MFD_MENF21BMC |
| @@ -169,6 +190,7 @@ config AT91SAM9X_WATCHDOG | |||
| 169 | 190 | ||
| 170 | config CADENCE_WATCHDOG | 191 | config CADENCE_WATCHDOG |
| 171 | tristate "Cadence Watchdog Timer" | 192 | tristate "Cadence Watchdog Timer" |
| 193 | depends on HAS_IOMEM | ||
| 172 | select WATCHDOG_CORE | 194 | select WATCHDOG_CORE |
| 173 | help | 195 | help |
| 174 | Say Y here if you want to include support for the watchdog | 196 | Say Y here if you want to include support for the watchdog |
| @@ -408,7 +430,7 @@ config TS72XX_WATCHDOG | |||
| 408 | 430 | ||
| 409 | config MAX63XX_WATCHDOG | 431 | config MAX63XX_WATCHDOG |
| 410 | tristate "Max63xx watchdog" | 432 | tristate "Max63xx watchdog" |
| 411 | depends on ARM && HAS_IOMEM | 433 | depends on HAS_IOMEM |
| 412 | select WATCHDOG_CORE | 434 | select WATCHDOG_CORE |
| 413 | help | 435 | help |
| 414 | Support for memory mapped max63{69,70,71,72,73,74} watchdog timer. | 436 | Support for memory mapped max63{69,70,71,72,73,74} watchdog timer. |
| @@ -526,6 +548,16 @@ config MEDIATEK_WATCHDOG | |||
| 526 | To compile this driver as a module, choose M here: the | 548 | To compile this driver as a module, choose M here: the |
| 527 | module will be called mtk_wdt. | 549 | module will be called mtk_wdt. |
| 528 | 550 | ||
| 551 | config DIGICOLOR_WATCHDOG | ||
| 552 | tristate "Conexant Digicolor SoCs watchdog support" | ||
| 553 | depends on ARCH_DIGICOLOR | ||
| 554 | select WATCHDOG_CORE | ||
| 555 | help | ||
| 556 | Say Y here to include support for the watchdog timer | ||
| 557 | in Conexant Digicolor SoCs. | ||
| 558 | To compile this driver as a module, choose M here: the | ||
| 559 | module will be called digicolor_wdt. | ||
| 560 | |||
| 529 | # AVR32 Architecture | 561 | # AVR32 Architecture |
| 530 | 562 | ||
| 531 | config AT32AP700X_WDT | 563 | config AT32AP700X_WDT |
| @@ -1355,7 +1387,7 @@ config BOOKE_WDT_DEFAULT_TIMEOUT | |||
| 1355 | config MEN_A21_WDT | 1387 | config MEN_A21_WDT |
| 1356 | tristate "MEN A21 VME CPU Carrier Board Watchdog Timer" | 1388 | tristate "MEN A21 VME CPU Carrier Board Watchdog Timer" |
| 1357 | select WATCHDOG_CORE | 1389 | select WATCHDOG_CORE |
| 1358 | depends on GPIOLIB | 1390 | depends on GPIOLIB || COMPILE_TEST |
| 1359 | help | 1391 | help |
| 1360 | Watchdog driver for MEN A21 VMEbus CPU Carrier Boards. | 1392 | Watchdog driver for MEN A21 VMEbus CPU Carrier Boards. |
| 1361 | 1393 | ||
diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile index d98768c7d928..59ea9a1b8e76 100644 --- a/drivers/watchdog/Makefile +++ b/drivers/watchdog/Makefile | |||
| @@ -65,6 +65,7 @@ obj-$(CONFIG_BCM_KONA_WDT) += bcm_kona_wdt.o | |||
| 65 | obj-$(CONFIG_TEGRA_WATCHDOG) += tegra_wdt.o | 65 | obj-$(CONFIG_TEGRA_WATCHDOG) += tegra_wdt.o |
| 66 | obj-$(CONFIG_MESON_WATCHDOG) += meson_wdt.o | 66 | obj-$(CONFIG_MESON_WATCHDOG) += meson_wdt.o |
| 67 | obj-$(CONFIG_MEDIATEK_WATCHDOG) += mtk_wdt.o | 67 | obj-$(CONFIG_MEDIATEK_WATCHDOG) += mtk_wdt.o |
| 68 | obj-$(CONFIG_DIGICOLOR_WATCHDOG) += digicolor_wdt.o | ||
| 68 | 69 | ||
| 69 | # AVR32 Architecture | 70 | # AVR32 Architecture |
| 70 | obj-$(CONFIG_AT32AP700X_WDT) += at32ap700x_wdt.o | 71 | obj-$(CONFIG_AT32AP700X_WDT) += at32ap700x_wdt.o |
| @@ -180,6 +181,7 @@ obj-$(CONFIG_XEN_WDT) += xen_wdt.o | |||
| 180 | # Architecture Independent | 181 | # Architecture Independent |
| 181 | obj-$(CONFIG_DA9052_WATCHDOG) += da9052_wdt.o | 182 | obj-$(CONFIG_DA9052_WATCHDOG) += da9052_wdt.o |
| 182 | obj-$(CONFIG_DA9055_WATCHDOG) += da9055_wdt.o | 183 | obj-$(CONFIG_DA9055_WATCHDOG) += da9055_wdt.o |
| 184 | obj-$(CONFIG_DA9062_WATCHDOG) += da9062_wdt.o | ||
| 183 | obj-$(CONFIG_DA9063_WATCHDOG) += da9063_wdt.o | 185 | obj-$(CONFIG_DA9063_WATCHDOG) += da9063_wdt.o |
| 184 | obj-$(CONFIG_GPIO_WATCHDOG) += gpio_wdt.o | 186 | obj-$(CONFIG_GPIO_WATCHDOG) += gpio_wdt.o |
| 185 | obj-$(CONFIG_WM831X_WATCHDOG) += wm831x_wdt.o | 187 | obj-$(CONFIG_WM831X_WATCHDOG) += wm831x_wdt.o |
diff --git a/drivers/watchdog/at91sam9_wdt.c b/drivers/watchdog/at91sam9_wdt.c index 1443b3c391de..e4698f7c5f93 100644 --- a/drivers/watchdog/at91sam9_wdt.c +++ b/drivers/watchdog/at91sam9_wdt.c | |||
| @@ -40,9 +40,9 @@ | |||
| 40 | #define DRV_NAME "AT91SAM9 Watchdog" | 40 | #define DRV_NAME "AT91SAM9 Watchdog" |
| 41 | 41 | ||
| 42 | #define wdt_read(wdt, field) \ | 42 | #define wdt_read(wdt, field) \ |
| 43 | __raw_readl((wdt)->base + (field)) | 43 | readl_relaxed((wdt)->base + (field)) |
| 44 | #define wdt_write(wtd, field, val) \ | 44 | #define wdt_write(wtd, field, val) \ |
| 45 | __raw_writel((val), (wdt)->base + (field)) | 45 | writel_relaxed((val), (wdt)->base + (field)) |
| 46 | 46 | ||
| 47 | /* AT91SAM9 watchdog runs a 12bit counter @ 256Hz, | 47 | /* AT91SAM9 watchdog runs a 12bit counter @ 256Hz, |
| 48 | * use this to convert a watchdog | 48 | * use this to convert a watchdog |
diff --git a/drivers/watchdog/da9062_wdt.c b/drivers/watchdog/da9062_wdt.c new file mode 100644 index 000000000000..b3a870ce85be --- /dev/null +++ b/drivers/watchdog/da9062_wdt.c | |||
| @@ -0,0 +1,253 @@ | |||
| 1 | /* | ||
| 2 | * da9062_wdt.c - WDT device driver for DA9062 | ||
| 3 | * Copyright (C) 2015 Dialog Semiconductor Ltd. | ||
| 4 | * | ||
| 5 | * This program is free software; you can redistribute it and/or | ||
| 6 | * modify it under the terms of the GNU General Public License | ||
| 7 | * as published by the Free Software Foundation; either version 2 | ||
| 8 | * of the License, or (at your option) any later version. | ||
| 9 | * | ||
| 10 | * This program is distributed in the hope that it will be useful, | ||
| 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 13 | * GNU General Public License for more details. | ||
| 14 | */ | ||
| 15 | |||
| 16 | #include <linux/kernel.h> | ||
| 17 | #include <linux/module.h> | ||
| 18 | #include <linux/watchdog.h> | ||
| 19 | #include <linux/platform_device.h> | ||
| 20 | #include <linux/uaccess.h> | ||
| 21 | #include <linux/slab.h> | ||
| 22 | #include <linux/delay.h> | ||
| 23 | #include <linux/jiffies.h> | ||
| 24 | #include <linux/mfd/da9062/registers.h> | ||
| 25 | #include <linux/mfd/da9062/core.h> | ||
| 26 | #include <linux/regmap.h> | ||
| 27 | #include <linux/of.h> | ||
| 28 | |||
| 29 | static const unsigned int wdt_timeout[] = { 0, 2, 4, 8, 16, 32, 65, 131 }; | ||
| 30 | #define DA9062_TWDSCALE_DISABLE 0 | ||
| 31 | #define DA9062_TWDSCALE_MIN 1 | ||
| 32 | #define DA9062_TWDSCALE_MAX (ARRAY_SIZE(wdt_timeout) - 1) | ||
| 33 | #define DA9062_WDT_MIN_TIMEOUT wdt_timeout[DA9062_TWDSCALE_MIN] | ||
| 34 | #define DA9062_WDT_MAX_TIMEOUT wdt_timeout[DA9062_TWDSCALE_MAX] | ||
| 35 | #define DA9062_WDG_DEFAULT_TIMEOUT wdt_timeout[DA9062_TWDSCALE_MAX-1] | ||
| 36 | #define DA9062_RESET_PROTECTION_MS 300 | ||
| 37 | |||
| 38 | struct da9062_watchdog { | ||
| 39 | struct da9062 *hw; | ||
| 40 | struct watchdog_device wdtdev; | ||
| 41 | unsigned long j_time_stamp; | ||
| 42 | }; | ||
| 43 | |||
| 44 | static void da9062_set_window_start(struct da9062_watchdog *wdt) | ||
| 45 | { | ||
| 46 | wdt->j_time_stamp = jiffies; | ||
| 47 | } | ||
| 48 | |||
| 49 | static void da9062_apply_window_protection(struct da9062_watchdog *wdt) | ||
| 50 | { | ||
| 51 | unsigned long delay = msecs_to_jiffies(DA9062_RESET_PROTECTION_MS); | ||
| 52 | unsigned long timeout = wdt->j_time_stamp + delay; | ||
| 53 | unsigned long now = jiffies; | ||
| 54 | unsigned int diff_ms; | ||
| 55 | |||
| 56 | /* if time-limit has not elapsed then wait for remainder */ | ||
| 57 | if (time_before(now, timeout)) { | ||
| 58 | diff_ms = jiffies_to_msecs(timeout-now); | ||
| 59 | dev_dbg(wdt->hw->dev, | ||
| 60 | "Kicked too quickly. Delaying %u msecs\n", diff_ms); | ||
| 61 | msleep(diff_ms); | ||
| 62 | } | ||
| 63 | } | ||
| 64 | |||
| 65 | static unsigned int da9062_wdt_timeout_to_sel(unsigned int secs) | ||
| 66 | { | ||
| 67 | unsigned int i; | ||
| 68 | |||
| 69 | for (i = DA9062_TWDSCALE_MIN; i <= DA9062_TWDSCALE_MAX; i++) { | ||
| 70 | if (wdt_timeout[i] >= secs) | ||
| 71 | return i; | ||
| 72 | } | ||
| 73 | |||
| 74 | return DA9062_TWDSCALE_MAX; | ||
| 75 | } | ||
| 76 | |||
| 77 | static int da9062_reset_watchdog_timer(struct da9062_watchdog *wdt) | ||
| 78 | { | ||
| 79 | int ret; | ||
| 80 | |||
| 81 | da9062_apply_window_protection(wdt); | ||
| 82 | |||
| 83 | ret = regmap_update_bits(wdt->hw->regmap, | ||
| 84 | DA9062AA_CONTROL_F, | ||
| 85 | DA9062AA_WATCHDOG_MASK, | ||
| 86 | DA9062AA_WATCHDOG_MASK); | ||
| 87 | |||
| 88 | da9062_set_window_start(wdt); | ||
| 89 | |||
| 90 | return ret; | ||
| 91 | } | ||
| 92 | |||
| 93 | static int da9062_wdt_update_timeout_register(struct da9062_watchdog *wdt, | ||
| 94 | unsigned int regval) | ||
| 95 | { | ||
| 96 | struct da9062 *chip = wdt->hw; | ||
| 97 | int ret; | ||
| 98 | |||
| 99 | ret = da9062_reset_watchdog_timer(wdt); | ||
| 100 | if (ret) | ||
| 101 | return ret; | ||
| 102 | |||
| 103 | return regmap_update_bits(chip->regmap, | ||
| 104 | DA9062AA_CONTROL_D, | ||
| 105 | DA9062AA_TWDSCALE_MASK, | ||
| 106 | regval); | ||
| 107 | } | ||
| 108 | |||
| 109 | static int da9062_wdt_start(struct watchdog_device *wdd) | ||
| 110 | { | ||
| 111 | struct da9062_watchdog *wdt = watchdog_get_drvdata(wdd); | ||
| 112 | unsigned int selector; | ||
| 113 | int ret; | ||
| 114 | |||
| 115 | selector = da9062_wdt_timeout_to_sel(wdt->wdtdev.timeout); | ||
| 116 | ret = da9062_wdt_update_timeout_register(wdt, selector); | ||
| 117 | if (ret) | ||
| 118 | dev_err(wdt->hw->dev, "Watchdog failed to start (err = %d)\n", | ||
| 119 | ret); | ||
| 120 | |||
| 121 | return ret; | ||
| 122 | } | ||
| 123 | |||
| 124 | static int da9062_wdt_stop(struct watchdog_device *wdd) | ||
| 125 | { | ||
| 126 | struct da9062_watchdog *wdt = watchdog_get_drvdata(wdd); | ||
| 127 | int ret; | ||
| 128 | |||
| 129 | ret = da9062_reset_watchdog_timer(wdt); | ||
| 130 | if (ret) { | ||
| 131 | dev_err(wdt->hw->dev, "Failed to ping the watchdog (err = %d)\n", | ||
| 132 | ret); | ||
| 133 | return ret; | ||
| 134 | } | ||
| 135 | |||
| 136 | ret = regmap_update_bits(wdt->hw->regmap, | ||
| 137 | DA9062AA_CONTROL_D, | ||
| 138 | DA9062AA_TWDSCALE_MASK, | ||
| 139 | DA9062_TWDSCALE_DISABLE); | ||
| 140 | if (ret) | ||
| 141 | dev_err(wdt->hw->dev, "Watchdog failed to stop (err = %d)\n", | ||
| 142 | ret); | ||
| 143 | |||
| 144 | return ret; | ||
| 145 | } | ||
| 146 | |||
| 147 | static int da9062_wdt_ping(struct watchdog_device *wdd) | ||
| 148 | { | ||
| 149 | struct da9062_watchdog *wdt = watchdog_get_drvdata(wdd); | ||
| 150 | int ret; | ||
| 151 | |||
| 152 | ret = da9062_reset_watchdog_timer(wdt); | ||
| 153 | if (ret) | ||
| 154 | dev_err(wdt->hw->dev, "Failed to ping the watchdog (err = %d)\n", | ||
| 155 | ret); | ||
| 156 | |||
| 157 | return ret; | ||
| 158 | } | ||
| 159 | |||
| 160 | static int da9062_wdt_set_timeout(struct watchdog_device *wdd, | ||
| 161 | unsigned int timeout) | ||
| 162 | { | ||
| 163 | struct da9062_watchdog *wdt = watchdog_get_drvdata(wdd); | ||
| 164 | unsigned int selector; | ||
| 165 | int ret; | ||
| 166 | |||
| 167 | selector = da9062_wdt_timeout_to_sel(timeout); | ||
| 168 | ret = da9062_wdt_update_timeout_register(wdt, selector); | ||
| 169 | if (ret) | ||
| 170 | dev_err(wdt->hw->dev, "Failed to set watchdog timeout (err = %d)\n", | ||
| 171 | ret); | ||
| 172 | else | ||
| 173 | wdd->timeout = wdt_timeout[selector]; | ||
| 174 | |||
| 175 | return ret; | ||
| 176 | } | ||
| 177 | |||
| 178 | static const struct watchdog_info da9062_watchdog_info = { | ||
| 179 | .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING, | ||
| 180 | .identity = "DA9062 WDT", | ||
| 181 | }; | ||
| 182 | |||
| 183 | static const struct watchdog_ops da9062_watchdog_ops = { | ||
| 184 | .owner = THIS_MODULE, | ||
| 185 | .start = da9062_wdt_start, | ||
| 186 | .stop = da9062_wdt_stop, | ||
| 187 | .ping = da9062_wdt_ping, | ||
| 188 | .set_timeout = da9062_wdt_set_timeout, | ||
| 189 | }; | ||
| 190 | |||
| 191 | static int da9062_wdt_probe(struct platform_device *pdev) | ||
| 192 | { | ||
| 193 | int ret; | ||
| 194 | struct da9062 *chip; | ||
| 195 | struct da9062_watchdog *wdt; | ||
| 196 | |||
| 197 | chip = dev_get_drvdata(pdev->dev.parent); | ||
| 198 | if (!chip) | ||
| 199 | return -EINVAL; | ||
| 200 | |||
| 201 | wdt = devm_kzalloc(&pdev->dev, sizeof(*wdt), GFP_KERNEL); | ||
| 202 | if (!wdt) | ||
| 203 | return -ENOMEM; | ||
| 204 | |||
| 205 | wdt->hw = chip; | ||
| 206 | |||
| 207 | wdt->wdtdev.info = &da9062_watchdog_info; | ||
| 208 | wdt->wdtdev.ops = &da9062_watchdog_ops; | ||
| 209 | wdt->wdtdev.min_timeout = DA9062_WDT_MIN_TIMEOUT; | ||
| 210 | wdt->wdtdev.max_timeout = DA9062_WDT_MAX_TIMEOUT; | ||
| 211 | wdt->wdtdev.timeout = DA9062_WDG_DEFAULT_TIMEOUT; | ||
| 212 | wdt->wdtdev.status = WATCHDOG_NOWAYOUT_INIT_STATUS; | ||
| 213 | |||
| 214 | watchdog_set_drvdata(&wdt->wdtdev, wdt); | ||
| 215 | dev_set_drvdata(&pdev->dev, wdt); | ||
| 216 | |||
| 217 | ret = watchdog_register_device(&wdt->wdtdev); | ||
| 218 | if (ret < 0) { | ||
| 219 | dev_err(wdt->hw->dev, | ||
| 220 | "watchdog registration failed (%d)\n", ret); | ||
| 221 | return ret; | ||
| 222 | } | ||
| 223 | |||
| 224 | da9062_set_window_start(wdt); | ||
| 225 | |||
| 226 | ret = da9062_wdt_ping(&wdt->wdtdev); | ||
| 227 | if (ret < 0) | ||
| 228 | watchdog_unregister_device(&wdt->wdtdev); | ||
| 229 | |||
| 230 | return ret; | ||
| 231 | } | ||
| 232 | |||
| 233 | static int da9062_wdt_remove(struct platform_device *pdev) | ||
| 234 | { | ||
| 235 | struct da9062_watchdog *wdt = dev_get_drvdata(&pdev->dev); | ||
| 236 | |||
| 237 | watchdog_unregister_device(&wdt->wdtdev); | ||
| 238 | return 0; | ||
| 239 | } | ||
| 240 | |||
| 241 | static struct platform_driver da9062_wdt_driver = { | ||
| 242 | .probe = da9062_wdt_probe, | ||
| 243 | .remove = da9062_wdt_remove, | ||
| 244 | .driver = { | ||
| 245 | .name = "da9062-watchdog", | ||
| 246 | }, | ||
| 247 | }; | ||
| 248 | module_platform_driver(da9062_wdt_driver); | ||
| 249 | |||
| 250 | MODULE_AUTHOR("S Twiss <stwiss.opensource@diasemi.com>"); | ||
| 251 | MODULE_DESCRIPTION("WDT device driver for Dialog DA9062"); | ||
| 252 | MODULE_LICENSE("GPL"); | ||
| 253 | MODULE_ALIAS("platform:da9062-watchdog"); | ||
diff --git a/drivers/watchdog/digicolor_wdt.c b/drivers/watchdog/digicolor_wdt.c new file mode 100644 index 000000000000..31d8e4936611 --- /dev/null +++ b/drivers/watchdog/digicolor_wdt.c | |||
| @@ -0,0 +1,205 @@ | |||
| 1 | /* | ||
| 2 | * Watchdog driver for Conexant Digicolor | ||
| 3 | * | ||
| 4 | * Copyright (C) 2015 Paradox Innovation Ltd. | ||
| 5 | * | ||
| 6 | * This program is free software; you can redistribute it and/or modify it | ||
| 7 | * under the terms of the GNU General Public License as published by the | ||
| 8 | * Free Software Foundation; either version 2 of the License, or (at your | ||
| 9 | * option) any later version. | ||
| 10 | */ | ||
| 11 | |||
| 12 | #include <linux/types.h> | ||
| 13 | #include <linux/module.h> | ||
| 14 | #include <linux/io.h> | ||
| 15 | #include <linux/delay.h> | ||
| 16 | #include <linux/clk.h> | ||
| 17 | #include <linux/watchdog.h> | ||
| 18 | #include <linux/reboot.h> | ||
| 19 | #include <linux/platform_device.h> | ||
| 20 | #include <linux/of_address.h> | ||
| 21 | |||
| 22 | #define TIMER_A_CONTROL 0 | ||
| 23 | #define TIMER_A_COUNT 4 | ||
| 24 | |||
| 25 | #define TIMER_A_ENABLE_COUNT BIT(0) | ||
| 26 | #define TIMER_A_ENABLE_WATCHDOG BIT(1) | ||
| 27 | |||
| 28 | struct dc_wdt { | ||
| 29 | void __iomem *base; | ||
| 30 | struct clk *clk; | ||
| 31 | struct notifier_block restart_handler; | ||
| 32 | spinlock_t lock; | ||
| 33 | }; | ||
| 34 | |||
| 35 | static unsigned timeout; | ||
| 36 | module_param(timeout, uint, 0); | ||
| 37 | MODULE_PARM_DESC(timeout, "Watchdog timeout in seconds"); | ||
| 38 | |||
| 39 | static void dc_wdt_set(struct dc_wdt *wdt, u32 ticks) | ||
| 40 | { | ||
| 41 | unsigned long flags; | ||
| 42 | |||
| 43 | spin_lock_irqsave(&wdt->lock, flags); | ||
| 44 | |||
| 45 | writel_relaxed(0, wdt->base + TIMER_A_CONTROL); | ||
| 46 | writel_relaxed(ticks, wdt->base + TIMER_A_COUNT); | ||
| 47 | writel_relaxed(TIMER_A_ENABLE_COUNT | TIMER_A_ENABLE_WATCHDOG, | ||
| 48 | wdt->base + TIMER_A_CONTROL); | ||
| 49 | |||
| 50 | spin_unlock_irqrestore(&wdt->lock, flags); | ||
| 51 | } | ||
| 52 | |||
| 53 | static int dc_restart_handler(struct notifier_block *this, unsigned long mode, | ||
| 54 | void *cmd) | ||
| 55 | { | ||
| 56 | struct dc_wdt *wdt = container_of(this, struct dc_wdt, restart_handler); | ||
| 57 | |||
| 58 | dc_wdt_set(wdt, 1); | ||
| 59 | /* wait for reset to assert... */ | ||
| 60 | mdelay(500); | ||
| 61 | |||
| 62 | return NOTIFY_DONE; | ||
| 63 | } | ||
| 64 | |||
| 65 | static int dc_wdt_start(struct watchdog_device *wdog) | ||
| 66 | { | ||
| 67 | struct dc_wdt *wdt = watchdog_get_drvdata(wdog); | ||
| 68 | |||
| 69 | dc_wdt_set(wdt, wdog->timeout * clk_get_rate(wdt->clk)); | ||
| 70 | |||
| 71 | return 0; | ||
| 72 | } | ||
| 73 | |||
| 74 | static int dc_wdt_stop(struct watchdog_device *wdog) | ||
| 75 | { | ||
| 76 | struct dc_wdt *wdt = watchdog_get_drvdata(wdog); | ||
| 77 | |||
| 78 | writel_relaxed(0, wdt->base + TIMER_A_CONTROL); | ||
| 79 | |||
| 80 | return 0; | ||
| 81 | } | ||
| 82 | |||
| 83 | static int dc_wdt_set_timeout(struct watchdog_device *wdog, unsigned int t) | ||
| 84 | { | ||
| 85 | struct dc_wdt *wdt = watchdog_get_drvdata(wdog); | ||
| 86 | |||
| 87 | dc_wdt_set(wdt, t * clk_get_rate(wdt->clk)); | ||
| 88 | wdog->timeout = t; | ||
| 89 | |||
| 90 | return 0; | ||
| 91 | } | ||
| 92 | |||
| 93 | static unsigned int dc_wdt_get_timeleft(struct watchdog_device *wdog) | ||
| 94 | { | ||
| 95 | struct dc_wdt *wdt = watchdog_get_drvdata(wdog); | ||
| 96 | uint32_t count = readl_relaxed(wdt->base + TIMER_A_COUNT); | ||
| 97 | |||
| 98 | return count / clk_get_rate(wdt->clk); | ||
| 99 | } | ||
| 100 | |||
| 101 | static struct watchdog_ops dc_wdt_ops = { | ||
| 102 | .owner = THIS_MODULE, | ||
| 103 | .start = dc_wdt_start, | ||
| 104 | .stop = dc_wdt_stop, | ||
| 105 | .set_timeout = dc_wdt_set_timeout, | ||
| 106 | .get_timeleft = dc_wdt_get_timeleft, | ||
| 107 | }; | ||
| 108 | |||
| 109 | static struct watchdog_info dc_wdt_info = { | ||
| 110 | .options = WDIOF_SETTIMEOUT | WDIOF_MAGICCLOSE | ||
| 111 | | WDIOF_KEEPALIVEPING, | ||
| 112 | .identity = "Conexant Digicolor Watchdog", | ||
| 113 | }; | ||
| 114 | |||
| 115 | static struct watchdog_device dc_wdt_wdd = { | ||
| 116 | .info = &dc_wdt_info, | ||
| 117 | .ops = &dc_wdt_ops, | ||
| 118 | .min_timeout = 1, | ||
| 119 | }; | ||
| 120 | |||
| 121 | static int dc_wdt_probe(struct platform_device *pdev) | ||
| 122 | { | ||
| 123 | struct device *dev = &pdev->dev; | ||
| 124 | struct device_node *np = dev->of_node; | ||
| 125 | struct dc_wdt *wdt; | ||
| 126 | int ret; | ||
| 127 | |||
| 128 | wdt = devm_kzalloc(dev, sizeof(struct dc_wdt), GFP_KERNEL); | ||
| 129 | if (!wdt) | ||
| 130 | return -ENOMEM; | ||
| 131 | platform_set_drvdata(pdev, wdt); | ||
| 132 | |||
| 133 | wdt->base = of_iomap(np, 0); | ||
| 134 | if (!wdt->base) { | ||
| 135 | dev_err(dev, "Failed to remap watchdog regs"); | ||
| 136 | return -ENODEV; | ||
| 137 | } | ||
| 138 | |||
| 139 | wdt->clk = devm_clk_get(&pdev->dev, NULL); | ||
| 140 | if (IS_ERR(wdt->clk)) { | ||
| 141 | ret = PTR_ERR(wdt->clk); | ||
| 142 | goto err_iounmap; | ||
| 143 | } | ||
| 144 | dc_wdt_wdd.max_timeout = U32_MAX / clk_get_rate(wdt->clk); | ||
| 145 | dc_wdt_wdd.timeout = dc_wdt_wdd.max_timeout; | ||
| 146 | |||
| 147 | spin_lock_init(&wdt->lock); | ||
| 148 | |||
| 149 | watchdog_set_drvdata(&dc_wdt_wdd, wdt); | ||
| 150 | watchdog_init_timeout(&dc_wdt_wdd, timeout, dev); | ||
| 151 | ret = watchdog_register_device(&dc_wdt_wdd); | ||
| 152 | if (ret) { | ||
| 153 | dev_err(dev, "Failed to register watchdog device"); | ||
| 154 | goto err_iounmap; | ||
| 155 | } | ||
| 156 | |||
| 157 | wdt->restart_handler.notifier_call = dc_restart_handler; | ||
| 158 | wdt->restart_handler.priority = 128; | ||
| 159 | ret = register_restart_handler(&wdt->restart_handler); | ||
| 160 | if (ret) | ||
| 161 | dev_warn(&pdev->dev, "cannot register restart handler\n"); | ||
| 162 | |||
| 163 | return 0; | ||
| 164 | |||
| 165 | err_iounmap: | ||
| 166 | iounmap(wdt->base); | ||
| 167 | return ret; | ||
| 168 | } | ||
| 169 | |||
| 170 | static int dc_wdt_remove(struct platform_device *pdev) | ||
| 171 | { | ||
| 172 | struct dc_wdt *wdt = platform_get_drvdata(pdev); | ||
| 173 | |||
| 174 | unregister_restart_handler(&wdt->restart_handler); | ||
| 175 | watchdog_unregister_device(&dc_wdt_wdd); | ||
| 176 | iounmap(wdt->base); | ||
| 177 | |||
| 178 | return 0; | ||
| 179 | } | ||
| 180 | |||
| 181 | static void dc_wdt_shutdown(struct platform_device *pdev) | ||
| 182 | { | ||
| 183 | dc_wdt_stop(&dc_wdt_wdd); | ||
| 184 | } | ||
| 185 | |||
| 186 | static const struct of_device_id dc_wdt_of_match[] = { | ||
| 187 | { .compatible = "cnxt,cx92755-wdt", }, | ||
| 188 | {}, | ||
| 189 | }; | ||
| 190 | MODULE_DEVICE_TABLE(of, dc_wdt_of_match); | ||
| 191 | |||
| 192 | static struct platform_driver dc_wdt_driver = { | ||
| 193 | .probe = dc_wdt_probe, | ||
| 194 | .remove = dc_wdt_remove, | ||
| 195 | .shutdown = dc_wdt_shutdown, | ||
| 196 | .driver = { | ||
| 197 | .name = "digicolor-wdt", | ||
| 198 | .of_match_table = dc_wdt_of_match, | ||
| 199 | }, | ||
| 200 | }; | ||
| 201 | module_platform_driver(dc_wdt_driver); | ||
| 202 | |||
| 203 | MODULE_AUTHOR("Baruch Siach <baruch@tkos.co.il>"); | ||
| 204 | MODULE_DESCRIPTION("Driver for Conexant Digicolor watchdog timer"); | ||
| 205 | MODULE_LICENSE("GPL"); | ||
diff --git a/drivers/watchdog/dw_wdt.c b/drivers/watchdog/dw_wdt.c index d0bb9499d12c..6ea0634345e9 100644 --- a/drivers/watchdog/dw_wdt.c +++ b/drivers/watchdog/dw_wdt.c | |||
| @@ -35,7 +35,6 @@ | |||
| 35 | #include <linux/pm.h> | 35 | #include <linux/pm.h> |
| 36 | #include <linux/platform_device.h> | 36 | #include <linux/platform_device.h> |
| 37 | #include <linux/reboot.h> | 37 | #include <linux/reboot.h> |
| 38 | #include <linux/spinlock.h> | ||
| 39 | #include <linux/timer.h> | 38 | #include <linux/timer.h> |
| 40 | #include <linux/uaccess.h> | 39 | #include <linux/uaccess.h> |
| 41 | #include <linux/watchdog.h> | 40 | #include <linux/watchdog.h> |
| @@ -61,7 +60,6 @@ MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started " | |||
| 61 | #define WDT_TIMEOUT (HZ / 2) | 60 | #define WDT_TIMEOUT (HZ / 2) |
| 62 | 61 | ||
| 63 | static struct { | 62 | static struct { |
| 64 | spinlock_t lock; | ||
| 65 | void __iomem *regs; | 63 | void __iomem *regs; |
| 66 | struct clk *clk; | 64 | struct clk *clk; |
| 67 | unsigned long in_use; | 65 | unsigned long in_use; |
| @@ -177,7 +175,6 @@ static int dw_wdt_open(struct inode *inode, struct file *filp) | |||
| 177 | /* Make sure we don't get unloaded. */ | 175 | /* Make sure we don't get unloaded. */ |
| 178 | __module_get(THIS_MODULE); | 176 | __module_get(THIS_MODULE); |
| 179 | 177 | ||
| 180 | spin_lock(&dw_wdt.lock); | ||
| 181 | if (!dw_wdt_is_enabled()) { | 178 | if (!dw_wdt_is_enabled()) { |
| 182 | /* | 179 | /* |
| 183 | * The watchdog is not currently enabled. Set the timeout to | 180 | * The watchdog is not currently enabled. Set the timeout to |
| @@ -190,8 +187,6 @@ static int dw_wdt_open(struct inode *inode, struct file *filp) | |||
| 190 | 187 | ||
| 191 | dw_wdt_set_next_heartbeat(); | 188 | dw_wdt_set_next_heartbeat(); |
| 192 | 189 | ||
| 193 | spin_unlock(&dw_wdt.lock); | ||
| 194 | |||
| 195 | return nonseekable_open(inode, filp); | 190 | return nonseekable_open(inode, filp); |
| 196 | } | 191 | } |
| 197 | 192 | ||
| @@ -220,6 +215,7 @@ static ssize_t dw_wdt_write(struct file *filp, const char __user *buf, | |||
| 220 | } | 215 | } |
| 221 | 216 | ||
| 222 | dw_wdt_set_next_heartbeat(); | 217 | dw_wdt_set_next_heartbeat(); |
| 218 | dw_wdt_keepalive(); | ||
| 223 | mod_timer(&dw_wdt.timer, jiffies + WDT_TIMEOUT); | 219 | mod_timer(&dw_wdt.timer, jiffies + WDT_TIMEOUT); |
| 224 | 220 | ||
| 225 | return len; | 221 | return len; |
| @@ -348,8 +344,6 @@ static int dw_wdt_drv_probe(struct platform_device *pdev) | |||
| 348 | if (ret) | 344 | if (ret) |
| 349 | return ret; | 345 | return ret; |
| 350 | 346 | ||
| 351 | spin_lock_init(&dw_wdt.lock); | ||
| 352 | |||
| 353 | ret = misc_register(&dw_wdt_miscdev); | 347 | ret = misc_register(&dw_wdt_miscdev); |
| 354 | if (ret) | 348 | if (ret) |
| 355 | goto out_disable_clk; | 349 | goto out_disable_clk; |
diff --git a/drivers/watchdog/gpio_wdt.c b/drivers/watchdog/gpio_wdt.c index cbc313d37c59..1687cc2d7122 100644 --- a/drivers/watchdog/gpio_wdt.c +++ b/drivers/watchdog/gpio_wdt.c | |||
| @@ -267,7 +267,16 @@ static struct platform_driver gpio_wdt_driver = { | |||
| 267 | .probe = gpio_wdt_probe, | 267 | .probe = gpio_wdt_probe, |
| 268 | .remove = gpio_wdt_remove, | 268 | .remove = gpio_wdt_remove, |
| 269 | }; | 269 | }; |
| 270 | |||
| 271 | #ifdef CONFIG_GPIO_WATCHDOG_ARCH_INITCALL | ||
| 272 | static int __init gpio_wdt_init(void) | ||
| 273 | { | ||
| 274 | return platform_driver_register(&gpio_wdt_driver); | ||
| 275 | } | ||
| 276 | arch_initcall(gpio_wdt_init); | ||
| 277 | #else | ||
| 270 | module_platform_driver(gpio_wdt_driver); | 278 | module_platform_driver(gpio_wdt_driver); |
| 279 | #endif | ||
| 271 | 280 | ||
| 272 | MODULE_AUTHOR("Alexander Shiyan <shc_work@mail.ru>"); | 281 | MODULE_AUTHOR("Alexander Shiyan <shc_work@mail.ru>"); |
| 273 | MODULE_DESCRIPTION("GPIO Watchdog"); | 282 | MODULE_DESCRIPTION("GPIO Watchdog"); |
diff --git a/drivers/watchdog/hpwdt.c b/drivers/watchdog/hpwdt.c index ada3e44f9932..286369d4f0f5 100644 --- a/drivers/watchdog/hpwdt.c +++ b/drivers/watchdog/hpwdt.c | |||
| @@ -588,7 +588,7 @@ static long hpwdt_ioctl(struct file *file, unsigned int cmd, | |||
| 588 | { | 588 | { |
| 589 | void __user *argp = (void __user *)arg; | 589 | void __user *argp = (void __user *)arg; |
| 590 | int __user *p = argp; | 590 | int __user *p = argp; |
| 591 | int new_margin; | 591 | int new_margin, options; |
| 592 | int ret = -ENOTTY; | 592 | int ret = -ENOTTY; |
| 593 | 593 | ||
| 594 | switch (cmd) { | 594 | switch (cmd) { |
| @@ -608,6 +608,20 @@ static long hpwdt_ioctl(struct file *file, unsigned int cmd, | |||
| 608 | ret = 0; | 608 | ret = 0; |
| 609 | break; | 609 | break; |
| 610 | 610 | ||
| 611 | case WDIOC_SETOPTIONS: | ||
| 612 | ret = get_user(options, p); | ||
| 613 | if (ret) | ||
| 614 | break; | ||
| 615 | |||
| 616 | if (options & WDIOS_DISABLECARD) | ||
| 617 | hpwdt_stop(); | ||
| 618 | |||
| 619 | if (options & WDIOS_ENABLECARD) { | ||
| 620 | hpwdt_start(); | ||
| 621 | hpwdt_ping(); | ||
| 622 | } | ||
| 623 | break; | ||
| 624 | |||
| 611 | case WDIOC_SETTIMEOUT: | 625 | case WDIOC_SETTIMEOUT: |
| 612 | ret = get_user(new_margin, p); | 626 | ret = get_user(new_margin, p); |
| 613 | if (ret) | 627 | if (ret) |
diff --git a/drivers/watchdog/imgpdc_wdt.c b/drivers/watchdog/imgpdc_wdt.c index 0deaa4f971f5..0f73621827ab 100644 --- a/drivers/watchdog/imgpdc_wdt.c +++ b/drivers/watchdog/imgpdc_wdt.c | |||
| @@ -9,6 +9,35 @@ | |||
| 9 | * | 9 | * |
| 10 | * Based on drivers/watchdog/sunxi_wdt.c Copyright (c) 2013 Carlo Caione | 10 | * Based on drivers/watchdog/sunxi_wdt.c Copyright (c) 2013 Carlo Caione |
| 11 | * 2012 Henrik Nordstrom | 11 | * 2012 Henrik Nordstrom |
| 12 | * | ||
| 13 | * Notes | ||
| 14 | * ----- | ||
| 15 | * The timeout value is rounded to the next power of two clock cycles. | ||
| 16 | * This is configured using the PDC_WDT_CONFIG register, according to this | ||
| 17 | * formula: | ||
| 18 | * | ||
| 19 | * timeout = 2^(delay + 1) clock cycles | ||
| 20 | * | ||
| 21 | * Where 'delay' is the value written in PDC_WDT_CONFIG register. | ||
| 22 | * | ||
| 23 | * Therefore, the hardware only allows to program watchdog timeouts, expressed | ||
| 24 | * as a power of two number of watchdog clock cycles. The current implementation | ||
| 25 | * guarantees that the actual watchdog timeout will be _at least_ the value | ||
| 26 | * programmed in the imgpdg_wdt driver. | ||
| 27 | * | ||
| 28 | * The following table shows how the user-configured timeout relates | ||
| 29 | * to the actual hardware timeout (watchdog clock @ 40000 Hz): | ||
| 30 | * | ||
| 31 | * input timeout | WD_DELAY | actual timeout | ||
| 32 | * ----------------------------------- | ||
| 33 | * 10 | 18 | 13 seconds | ||
| 34 | * 20 | 19 | 26 seconds | ||
| 35 | * 30 | 20 | 52 seconds | ||
| 36 | * 60 | 21 | 104 seconds | ||
| 37 | * | ||
| 38 | * Albeit coarse, this granularity would suffice most watchdog uses. | ||
| 39 | * If the platform allows it, the user should be able to change the watchdog | ||
| 40 | * clock rate and achieve a finer timeout granularity. | ||
| 12 | */ | 41 | */ |
| 13 | 42 | ||
| 14 | #include <linux/clk.h> | 43 | #include <linux/clk.h> |
| @@ -16,6 +45,7 @@ | |||
| 16 | #include <linux/log2.h> | 45 | #include <linux/log2.h> |
| 17 | #include <linux/module.h> | 46 | #include <linux/module.h> |
| 18 | #include <linux/platform_device.h> | 47 | #include <linux/platform_device.h> |
| 48 | #include <linux/reboot.h> | ||
| 19 | #include <linux/slab.h> | 49 | #include <linux/slab.h> |
| 20 | #include <linux/watchdog.h> | 50 | #include <linux/watchdog.h> |
| 21 | 51 | ||
| @@ -42,7 +72,7 @@ | |||
| 42 | #define PDC_WDT_MIN_TIMEOUT 1 | 72 | #define PDC_WDT_MIN_TIMEOUT 1 |
| 43 | #define PDC_WDT_DEF_TIMEOUT 64 | 73 | #define PDC_WDT_DEF_TIMEOUT 64 |
| 44 | 74 | ||
| 45 | static int heartbeat = PDC_WDT_DEF_TIMEOUT; | 75 | static int heartbeat; |
| 46 | module_param(heartbeat, int, 0); | 76 | module_param(heartbeat, int, 0); |
| 47 | MODULE_PARM_DESC(heartbeat, "Watchdog heartbeats in seconds " | 77 | MODULE_PARM_DESC(heartbeat, "Watchdog heartbeats in seconds " |
| 48 | "(default=" __MODULE_STRING(PDC_WDT_DEF_TIMEOUT) ")"); | 78 | "(default=" __MODULE_STRING(PDC_WDT_DEF_TIMEOUT) ")"); |
| @@ -57,6 +87,7 @@ struct pdc_wdt_dev { | |||
| 57 | struct clk *wdt_clk; | 87 | struct clk *wdt_clk; |
| 58 | struct clk *sys_clk; | 88 | struct clk *sys_clk; |
| 59 | void __iomem *base; | 89 | void __iomem *base; |
| 90 | struct notifier_block restart_handler; | ||
| 60 | }; | 91 | }; |
| 61 | 92 | ||
| 62 | static int pdc_wdt_keepalive(struct watchdog_device *wdt_dev) | 93 | static int pdc_wdt_keepalive(struct watchdog_device *wdt_dev) |
| @@ -84,18 +115,24 @@ static int pdc_wdt_stop(struct watchdog_device *wdt_dev) | |||
| 84 | return 0; | 115 | return 0; |
| 85 | } | 116 | } |
| 86 | 117 | ||
| 118 | static void __pdc_wdt_set_timeout(struct pdc_wdt_dev *wdt) | ||
| 119 | { | ||
| 120 | unsigned long clk_rate = clk_get_rate(wdt->wdt_clk); | ||
| 121 | unsigned int val; | ||
| 122 | |||
| 123 | val = readl(wdt->base + PDC_WDT_CONFIG) & ~PDC_WDT_CONFIG_DELAY_MASK; | ||
| 124 | val |= order_base_2(wdt->wdt_dev.timeout * clk_rate) - 1; | ||
| 125 | writel(val, wdt->base + PDC_WDT_CONFIG); | ||
| 126 | } | ||
| 127 | |||
| 87 | static int pdc_wdt_set_timeout(struct watchdog_device *wdt_dev, | 128 | static int pdc_wdt_set_timeout(struct watchdog_device *wdt_dev, |
| 88 | unsigned int new_timeout) | 129 | unsigned int new_timeout) |
| 89 | { | 130 | { |
| 90 | unsigned int val; | ||
| 91 | struct pdc_wdt_dev *wdt = watchdog_get_drvdata(wdt_dev); | 131 | struct pdc_wdt_dev *wdt = watchdog_get_drvdata(wdt_dev); |
| 92 | unsigned long clk_rate = clk_get_rate(wdt->wdt_clk); | ||
| 93 | 132 | ||
| 94 | wdt->wdt_dev.timeout = new_timeout; | 133 | wdt->wdt_dev.timeout = new_timeout; |
| 95 | 134 | ||
| 96 | val = readl(wdt->base + PDC_WDT_CONFIG) & ~PDC_WDT_CONFIG_DELAY_MASK; | 135 | __pdc_wdt_set_timeout(wdt); |
| 97 | val |= order_base_2(new_timeout * clk_rate) - 1; | ||
| 98 | writel(val, wdt->base + PDC_WDT_CONFIG); | ||
| 99 | 136 | ||
| 100 | return 0; | 137 | return 0; |
| 101 | } | 138 | } |
| @@ -106,6 +143,8 @@ static int pdc_wdt_start(struct watchdog_device *wdt_dev) | |||
| 106 | unsigned int val; | 143 | unsigned int val; |
| 107 | struct pdc_wdt_dev *wdt = watchdog_get_drvdata(wdt_dev); | 144 | struct pdc_wdt_dev *wdt = watchdog_get_drvdata(wdt_dev); |
| 108 | 145 | ||
| 146 | __pdc_wdt_set_timeout(wdt); | ||
| 147 | |||
| 109 | val = readl(wdt->base + PDC_WDT_CONFIG); | 148 | val = readl(wdt->base + PDC_WDT_CONFIG); |
| 110 | val |= PDC_WDT_CONFIG_ENABLE; | 149 | val |= PDC_WDT_CONFIG_ENABLE; |
| 111 | writel(val, wdt->base + PDC_WDT_CONFIG); | 150 | writel(val, wdt->base + PDC_WDT_CONFIG); |
| @@ -128,8 +167,21 @@ static const struct watchdog_ops pdc_wdt_ops = { | |||
| 128 | .set_timeout = pdc_wdt_set_timeout, | 167 | .set_timeout = pdc_wdt_set_timeout, |
| 129 | }; | 168 | }; |
| 130 | 169 | ||
| 170 | static int pdc_wdt_restart(struct notifier_block *this, unsigned long mode, | ||
| 171 | void *cmd) | ||
| 172 | { | ||
| 173 | struct pdc_wdt_dev *wdt = container_of(this, struct pdc_wdt_dev, | ||
| 174 | restart_handler); | ||
| 175 | |||
| 176 | /* Assert SOFT_RESET */ | ||
| 177 | writel(0x1, wdt->base + PDC_WDT_SOFT_RESET); | ||
| 178 | |||
| 179 | return NOTIFY_OK; | ||
| 180 | } | ||
| 181 | |||
| 131 | static int pdc_wdt_probe(struct platform_device *pdev) | 182 | static int pdc_wdt_probe(struct platform_device *pdev) |
| 132 | { | 183 | { |
| 184 | u64 div; | ||
| 133 | int ret, val; | 185 | int ret, val; |
| 134 | unsigned long clk_rate; | 186 | unsigned long clk_rate; |
| 135 | struct resource *res; | 187 | struct resource *res; |
| @@ -189,16 +241,15 @@ static int pdc_wdt_probe(struct platform_device *pdev) | |||
| 189 | 241 | ||
| 190 | pdc_wdt->wdt_dev.info = &pdc_wdt_info; | 242 | pdc_wdt->wdt_dev.info = &pdc_wdt_info; |
| 191 | pdc_wdt->wdt_dev.ops = &pdc_wdt_ops; | 243 | pdc_wdt->wdt_dev.ops = &pdc_wdt_ops; |
| 192 | pdc_wdt->wdt_dev.max_timeout = 1 << PDC_WDT_CONFIG_DELAY_MASK; | 244 | |
| 245 | div = 1ULL << (PDC_WDT_CONFIG_DELAY_MASK + 1); | ||
| 246 | do_div(div, clk_rate); | ||
| 247 | pdc_wdt->wdt_dev.max_timeout = div; | ||
| 248 | pdc_wdt->wdt_dev.timeout = PDC_WDT_DEF_TIMEOUT; | ||
| 193 | pdc_wdt->wdt_dev.parent = &pdev->dev; | 249 | pdc_wdt->wdt_dev.parent = &pdev->dev; |
| 194 | watchdog_set_drvdata(&pdc_wdt->wdt_dev, pdc_wdt); | 250 | watchdog_set_drvdata(&pdc_wdt->wdt_dev, pdc_wdt); |
| 195 | 251 | ||
| 196 | ret = watchdog_init_timeout(&pdc_wdt->wdt_dev, heartbeat, &pdev->dev); | 252 | watchdog_init_timeout(&pdc_wdt->wdt_dev, heartbeat, &pdev->dev); |
| 197 | if (ret < 0) { | ||
| 198 | pdc_wdt->wdt_dev.timeout = pdc_wdt->wdt_dev.max_timeout; | ||
| 199 | dev_warn(&pdev->dev, | ||
| 200 | "Initial timeout out of range! setting max timeout\n"); | ||
| 201 | } | ||
| 202 | 253 | ||
| 203 | pdc_wdt_stop(&pdc_wdt->wdt_dev); | 254 | pdc_wdt_stop(&pdc_wdt->wdt_dev); |
| 204 | 255 | ||
| @@ -238,6 +289,13 @@ static int pdc_wdt_probe(struct platform_device *pdev) | |||
| 238 | if (ret) | 289 | if (ret) |
| 239 | goto disable_wdt_clk; | 290 | goto disable_wdt_clk; |
| 240 | 291 | ||
| 292 | pdc_wdt->restart_handler.notifier_call = pdc_wdt_restart; | ||
| 293 | pdc_wdt->restart_handler.priority = 128; | ||
| 294 | ret = register_restart_handler(&pdc_wdt->restart_handler); | ||
| 295 | if (ret) | ||
| 296 | dev_warn(&pdev->dev, "failed to register restart handler: %d\n", | ||
| 297 | ret); | ||
| 298 | |||
| 241 | return 0; | 299 | return 0; |
| 242 | 300 | ||
| 243 | disable_wdt_clk: | 301 | disable_wdt_clk: |
diff --git a/drivers/watchdog/imx2_wdt.c b/drivers/watchdog/imx2_wdt.c index 5e6d808d358a..0bb1a1d1b170 100644 --- a/drivers/watchdog/imx2_wdt.c +++ b/drivers/watchdog/imx2_wdt.c | |||
| @@ -166,6 +166,8 @@ static int imx2_wdt_set_timeout(struct watchdog_device *wdog, | |||
| 166 | { | 166 | { |
| 167 | struct imx2_wdt_device *wdev = watchdog_get_drvdata(wdog); | 167 | struct imx2_wdt_device *wdev = watchdog_get_drvdata(wdog); |
| 168 | 168 | ||
| 169 | wdog->timeout = new_timeout; | ||
| 170 | |||
| 169 | regmap_update_bits(wdev->regmap, IMX2_WDT_WCR, IMX2_WDT_WCR_WT, | 171 | regmap_update_bits(wdev->regmap, IMX2_WDT_WCR, IMX2_WDT_WCR_WT, |
| 170 | WDOG_SEC_TO_COUNT(new_timeout)); | 172 | WDOG_SEC_TO_COUNT(new_timeout)); |
| 171 | return 0; | 173 | return 0; |
| @@ -256,8 +258,11 @@ static int __init imx2_wdt_probe(struct platform_device *pdev) | |||
| 256 | wdog->ops = &imx2_wdt_ops; | 258 | wdog->ops = &imx2_wdt_ops; |
| 257 | wdog->min_timeout = 1; | 259 | wdog->min_timeout = 1; |
| 258 | wdog->max_timeout = IMX2_WDT_MAX_TIME; | 260 | wdog->max_timeout = IMX2_WDT_MAX_TIME; |
| 261 | wdog->parent = &pdev->dev; | ||
| 259 | 262 | ||
| 260 | clk_prepare_enable(wdev->clk); | 263 | ret = clk_prepare_enable(wdev->clk); |
| 264 | if (ret) | ||
| 265 | return ret; | ||
| 261 | 266 | ||
| 262 | regmap_read(wdev->regmap, IMX2_WDT_WRSR, &val); | 267 | regmap_read(wdev->regmap, IMX2_WDT_WRSR, &val); |
| 263 | wdog->bootstatus = val & IMX2_WDT_WRSR_TOUT ? WDIOF_CARDRESET : 0; | 268 | wdog->bootstatus = val & IMX2_WDT_WRSR_TOUT ? WDIOF_CARDRESET : 0; |
| @@ -286,7 +291,7 @@ static int __init imx2_wdt_probe(struct platform_device *pdev) | |||
| 286 | ret = watchdog_register_device(wdog); | 291 | ret = watchdog_register_device(wdog); |
| 287 | if (ret) { | 292 | if (ret) { |
| 288 | dev_err(&pdev->dev, "cannot register watchdog device\n"); | 293 | dev_err(&pdev->dev, "cannot register watchdog device\n"); |
| 289 | return ret; | 294 | goto disable_clk; |
| 290 | } | 295 | } |
| 291 | 296 | ||
| 292 | wdev->restart_handler.notifier_call = imx2_restart_handler; | 297 | wdev->restart_handler.notifier_call = imx2_restart_handler; |
| @@ -299,6 +304,10 @@ static int __init imx2_wdt_probe(struct platform_device *pdev) | |||
| 299 | wdog->timeout, nowayout); | 304 | wdog->timeout, nowayout); |
| 300 | 305 | ||
| 301 | return 0; | 306 | return 0; |
| 307 | |||
| 308 | disable_clk: | ||
| 309 | clk_disable_unprepare(wdev->clk); | ||
| 310 | return ret; | ||
| 302 | } | 311 | } |
| 303 | 312 | ||
| 304 | static int __exit imx2_wdt_remove(struct platform_device *pdev) | 313 | static int __exit imx2_wdt_remove(struct platform_device *pdev) |
| @@ -362,8 +371,11 @@ static int imx2_wdt_resume(struct device *dev) | |||
| 362 | { | 371 | { |
| 363 | struct watchdog_device *wdog = dev_get_drvdata(dev); | 372 | struct watchdog_device *wdog = dev_get_drvdata(dev); |
| 364 | struct imx2_wdt_device *wdev = watchdog_get_drvdata(wdog); | 373 | struct imx2_wdt_device *wdev = watchdog_get_drvdata(wdog); |
| 374 | int ret; | ||
| 365 | 375 | ||
| 366 | clk_prepare_enable(wdev->clk); | 376 | ret = clk_prepare_enable(wdev->clk); |
| 377 | if (ret) | ||
| 378 | return ret; | ||
| 367 | 379 | ||
| 368 | if (watchdog_active(wdog) && !imx2_wdt_is_running(wdev)) { | 380 | if (watchdog_active(wdog) && !imx2_wdt_is_running(wdev)) { |
| 369 | /* | 381 | /* |
diff --git a/drivers/watchdog/max63xx_wdt.c b/drivers/watchdog/max63xx_wdt.c index 08da3114accb..f36ca4be0720 100644 --- a/drivers/watchdog/max63xx_wdt.c +++ b/drivers/watchdog/max63xx_wdt.c | |||
| @@ -39,10 +39,22 @@ static bool nowayout = WATCHDOG_NOWAYOUT; | |||
| 39 | #define MAX6369_WDSET (7 << 0) | 39 | #define MAX6369_WDSET (7 << 0) |
| 40 | #define MAX6369_WDI (1 << 3) | 40 | #define MAX6369_WDI (1 << 3) |
| 41 | 41 | ||
| 42 | static DEFINE_SPINLOCK(io_lock); | 42 | #define MAX6369_WDSET_DISABLED 3 |
| 43 | 43 | ||
| 44 | static int nodelay; | 44 | static int nodelay; |
| 45 | static void __iomem *wdt_base; | 45 | |
| 46 | struct max63xx_wdt { | ||
| 47 | struct watchdog_device wdd; | ||
| 48 | const struct max63xx_timeout *timeout; | ||
| 49 | |||
| 50 | /* memory mapping */ | ||
| 51 | void __iomem *base; | ||
| 52 | spinlock_t lock; | ||
| 53 | |||
| 54 | /* WDI and WSET bits write access routines */ | ||
| 55 | void (*ping)(struct max63xx_wdt *wdt); | ||
| 56 | void (*set)(struct max63xx_wdt *wdt, u8 set); | ||
| 57 | }; | ||
| 46 | 58 | ||
| 47 | /* | 59 | /* |
| 48 | * The timeout values used are actually the absolute minimum the chip | 60 | * The timeout values used are actually the absolute minimum the chip |
| @@ -59,25 +71,25 @@ static void __iomem *wdt_base; | |||
| 59 | 71 | ||
| 60 | /* Timeouts in second */ | 72 | /* Timeouts in second */ |
| 61 | struct max63xx_timeout { | 73 | struct max63xx_timeout { |
| 62 | u8 wdset; | 74 | const u8 wdset; |
| 63 | u8 tdelay; | 75 | const u8 tdelay; |
| 64 | u8 twd; | 76 | const u8 twd; |
| 65 | }; | 77 | }; |
| 66 | 78 | ||
| 67 | static struct max63xx_timeout max6369_table[] = { | 79 | static const struct max63xx_timeout max6369_table[] = { |
| 68 | { 5, 1, 1 }, | 80 | { 5, 1, 1 }, |
| 69 | { 6, 10, 10 }, | 81 | { 6, 10, 10 }, |
| 70 | { 7, 60, 60 }, | 82 | { 7, 60, 60 }, |
| 71 | { }, | 83 | { }, |
| 72 | }; | 84 | }; |
| 73 | 85 | ||
| 74 | static struct max63xx_timeout max6371_table[] = { | 86 | static const struct max63xx_timeout max6371_table[] = { |
| 75 | { 6, 60, 3 }, | 87 | { 6, 60, 3 }, |
| 76 | { 7, 60, 60 }, | 88 | { 7, 60, 60 }, |
| 77 | { }, | 89 | { }, |
| 78 | }; | 90 | }; |
| 79 | 91 | ||
| 80 | static struct max63xx_timeout max6373_table[] = { | 92 | static const struct max63xx_timeout max6373_table[] = { |
| 81 | { 2, 60, 1 }, | 93 | { 2, 60, 1 }, |
| 82 | { 5, 0, 1 }, | 94 | { 5, 0, 1 }, |
| 83 | { 1, 3, 3 }, | 95 | { 1, 3, 3 }, |
| @@ -86,8 +98,6 @@ static struct max63xx_timeout max6373_table[] = { | |||
| 86 | { }, | 98 | { }, |
| 87 | }; | 99 | }; |
| 88 | 100 | ||
| 89 | static struct max63xx_timeout *current_timeout; | ||
| 90 | |||
| 91 | static struct max63xx_timeout * | 101 | static struct max63xx_timeout * |
| 92 | max63xx_select_timeout(struct max63xx_timeout *table, int value) | 102 | max63xx_select_timeout(struct max63xx_timeout *table, int value) |
| 93 | { | 103 | { |
| @@ -108,59 +118,32 @@ max63xx_select_timeout(struct max63xx_timeout *table, int value) | |||
| 108 | 118 | ||
| 109 | static int max63xx_wdt_ping(struct watchdog_device *wdd) | 119 | static int max63xx_wdt_ping(struct watchdog_device *wdd) |
| 110 | { | 120 | { |
| 111 | u8 val; | 121 | struct max63xx_wdt *wdt = watchdog_get_drvdata(wdd); |
| 112 | |||
| 113 | spin_lock(&io_lock); | ||
| 114 | 122 | ||
| 115 | val = __raw_readb(wdt_base); | 123 | wdt->ping(wdt); |
| 116 | |||
| 117 | __raw_writeb(val | MAX6369_WDI, wdt_base); | ||
| 118 | __raw_writeb(val & ~MAX6369_WDI, wdt_base); | ||
| 119 | |||
| 120 | spin_unlock(&io_lock); | ||
| 121 | return 0; | 124 | return 0; |
| 122 | } | 125 | } |
| 123 | 126 | ||
| 124 | static int max63xx_wdt_start(struct watchdog_device *wdd) | 127 | static int max63xx_wdt_start(struct watchdog_device *wdd) |
| 125 | { | 128 | { |
| 126 | struct max63xx_timeout *entry = watchdog_get_drvdata(wdd); | 129 | struct max63xx_wdt *wdt = watchdog_get_drvdata(wdd); |
| 127 | u8 val; | ||
| 128 | 130 | ||
| 129 | spin_lock(&io_lock); | 131 | wdt->set(wdt, wdt->timeout->wdset); |
| 130 | |||
| 131 | val = __raw_readb(wdt_base); | ||
| 132 | val &= ~MAX6369_WDSET; | ||
| 133 | val |= entry->wdset; | ||
| 134 | __raw_writeb(val, wdt_base); | ||
| 135 | |||
| 136 | spin_unlock(&io_lock); | ||
| 137 | 132 | ||
| 138 | /* check for a edge triggered startup */ | 133 | /* check for a edge triggered startup */ |
| 139 | if (entry->tdelay == 0) | 134 | if (wdt->timeout->tdelay == 0) |
| 140 | max63xx_wdt_ping(wdd); | 135 | wdt->ping(wdt); |
| 141 | return 0; | 136 | return 0; |
| 142 | } | 137 | } |
| 143 | 138 | ||
| 144 | static int max63xx_wdt_stop(struct watchdog_device *wdd) | 139 | static int max63xx_wdt_stop(struct watchdog_device *wdd) |
| 145 | { | 140 | { |
| 146 | u8 val; | 141 | struct max63xx_wdt *wdt = watchdog_get_drvdata(wdd); |
| 147 | 142 | ||
| 148 | spin_lock(&io_lock); | 143 | wdt->set(wdt, MAX6369_WDSET_DISABLED); |
| 149 | |||
| 150 | val = __raw_readb(wdt_base); | ||
| 151 | val &= ~MAX6369_WDSET; | ||
| 152 | val |= 3; | ||
| 153 | __raw_writeb(val, wdt_base); | ||
| 154 | |||
| 155 | spin_unlock(&io_lock); | ||
| 156 | return 0; | 144 | return 0; |
| 157 | } | 145 | } |
| 158 | 146 | ||
| 159 | static const struct watchdog_info max63xx_wdt_info = { | ||
| 160 | .options = WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE, | ||
| 161 | .identity = "max63xx Watchdog", | ||
| 162 | }; | ||
| 163 | |||
| 164 | static const struct watchdog_ops max63xx_wdt_ops = { | 147 | static const struct watchdog_ops max63xx_wdt_ops = { |
| 165 | .owner = THIS_MODULE, | 148 | .owner = THIS_MODULE, |
| 166 | .start = max63xx_wdt_start, | 149 | .start = max63xx_wdt_start, |
| @@ -168,53 +151,108 @@ static const struct watchdog_ops max63xx_wdt_ops = { | |||
| 168 | .ping = max63xx_wdt_ping, | 151 | .ping = max63xx_wdt_ping, |
| 169 | }; | 152 | }; |
| 170 | 153 | ||
| 171 | static struct watchdog_device max63xx_wdt_dev = { | 154 | static const struct watchdog_info max63xx_wdt_info = { |
| 172 | .info = &max63xx_wdt_info, | 155 | .options = WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE, |
| 173 | .ops = &max63xx_wdt_ops, | 156 | .identity = "max63xx Watchdog", |
| 174 | }; | 157 | }; |
| 175 | 158 | ||
| 159 | static void max63xx_mmap_ping(struct max63xx_wdt *wdt) | ||
| 160 | { | ||
| 161 | u8 val; | ||
| 162 | |||
| 163 | spin_lock(&wdt->lock); | ||
| 164 | |||
| 165 | val = __raw_readb(wdt->base); | ||
| 166 | |||
| 167 | __raw_writeb(val | MAX6369_WDI, wdt->base); | ||
| 168 | __raw_writeb(val & ~MAX6369_WDI, wdt->base); | ||
| 169 | |||
| 170 | spin_unlock(&wdt->lock); | ||
| 171 | } | ||
| 172 | |||
| 173 | static void max63xx_mmap_set(struct max63xx_wdt *wdt, u8 set) | ||
| 174 | { | ||
| 175 | u8 val; | ||
| 176 | |||
| 177 | spin_lock(&wdt->lock); | ||
| 178 | |||
| 179 | val = __raw_readb(wdt->base); | ||
| 180 | val &= ~MAX6369_WDSET; | ||
| 181 | val |= set & MAX6369_WDSET; | ||
| 182 | __raw_writeb(val, wdt->base); | ||
| 183 | |||
| 184 | spin_unlock(&wdt->lock); | ||
| 185 | } | ||
| 186 | |||
| 187 | static int max63xx_mmap_init(struct platform_device *p, struct max63xx_wdt *wdt) | ||
| 188 | { | ||
| 189 | struct resource *mem = platform_get_resource(p, IORESOURCE_MEM, 0); | ||
| 190 | |||
| 191 | wdt->base = devm_ioremap_resource(&p->dev, mem); | ||
| 192 | if (IS_ERR(wdt->base)) | ||
| 193 | return PTR_ERR(wdt->base); | ||
| 194 | |||
| 195 | spin_lock_init(&wdt->lock); | ||
| 196 | |||
| 197 | wdt->ping = max63xx_mmap_ping; | ||
| 198 | wdt->set = max63xx_mmap_set; | ||
| 199 | return 0; | ||
| 200 | } | ||
| 201 | |||
| 176 | static int max63xx_wdt_probe(struct platform_device *pdev) | 202 | static int max63xx_wdt_probe(struct platform_device *pdev) |
| 177 | { | 203 | { |
| 178 | struct resource *wdt_mem; | 204 | struct max63xx_wdt *wdt; |
| 179 | struct max63xx_timeout *table; | 205 | struct max63xx_timeout *table; |
| 206 | int err; | ||
| 207 | |||
| 208 | wdt = devm_kzalloc(&pdev->dev, sizeof(*wdt), GFP_KERNEL); | ||
| 209 | if (!wdt) | ||
| 210 | return -ENOMEM; | ||
| 180 | 211 | ||
| 181 | table = (struct max63xx_timeout *)pdev->id_entry->driver_data; | 212 | table = (struct max63xx_timeout *)pdev->id_entry->driver_data; |
| 182 | 213 | ||
| 183 | if (heartbeat < 1 || heartbeat > MAX_HEARTBEAT) | 214 | if (heartbeat < 1 || heartbeat > MAX_HEARTBEAT) |
| 184 | heartbeat = DEFAULT_HEARTBEAT; | 215 | heartbeat = DEFAULT_HEARTBEAT; |
| 185 | 216 | ||
| 186 | dev_info(&pdev->dev, "requesting %ds heartbeat\n", heartbeat); | 217 | wdt->timeout = max63xx_select_timeout(table, heartbeat); |
| 187 | current_timeout = max63xx_select_timeout(table, heartbeat); | 218 | if (!wdt->timeout) { |
| 188 | 219 | dev_err(&pdev->dev, "unable to satisfy %ds heartbeat request\n", | |
| 189 | if (!current_timeout) { | 220 | heartbeat); |
| 190 | dev_err(&pdev->dev, "unable to satisfy heartbeat request\n"); | ||
| 191 | return -EINVAL; | 221 | return -EINVAL; |
| 192 | } | 222 | } |
| 193 | 223 | ||
| 194 | dev_info(&pdev->dev, "using %ds heartbeat with %ds initial delay\n", | 224 | err = max63xx_mmap_init(pdev, wdt); |
| 195 | current_timeout->twd, current_timeout->tdelay); | 225 | if (err) |
| 226 | return err; | ||
| 227 | |||
| 228 | platform_set_drvdata(pdev, &wdt->wdd); | ||
| 229 | watchdog_set_drvdata(&wdt->wdd, wdt); | ||
| 196 | 230 | ||
| 197 | heartbeat = current_timeout->twd; | 231 | wdt->wdd.parent = &pdev->dev; |
| 232 | wdt->wdd.timeout = wdt->timeout->twd; | ||
| 233 | wdt->wdd.info = &max63xx_wdt_info; | ||
| 234 | wdt->wdd.ops = &max63xx_wdt_ops; | ||
| 198 | 235 | ||
| 199 | wdt_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); | 236 | watchdog_set_nowayout(&wdt->wdd, nowayout); |
| 200 | wdt_base = devm_ioremap_resource(&pdev->dev, wdt_mem); | ||
| 201 | if (IS_ERR(wdt_base)) | ||
| 202 | return PTR_ERR(wdt_base); | ||
| 203 | 237 | ||
| 204 | max63xx_wdt_dev.timeout = heartbeat; | 238 | err = watchdog_register_device(&wdt->wdd); |
| 205 | watchdog_set_nowayout(&max63xx_wdt_dev, nowayout); | 239 | if (err) |
| 206 | watchdog_set_drvdata(&max63xx_wdt_dev, current_timeout); | 240 | return err; |
| 207 | 241 | ||
| 208 | return watchdog_register_device(&max63xx_wdt_dev); | 242 | dev_info(&pdev->dev, "using %ds heartbeat with %ds initial delay\n", |
| 243 | wdt->timeout->twd, wdt->timeout->tdelay); | ||
| 244 | return 0; | ||
| 209 | } | 245 | } |
| 210 | 246 | ||
| 211 | static int max63xx_wdt_remove(struct platform_device *pdev) | 247 | static int max63xx_wdt_remove(struct platform_device *pdev) |
| 212 | { | 248 | { |
| 213 | watchdog_unregister_device(&max63xx_wdt_dev); | 249 | struct watchdog_device *wdd = platform_get_drvdata(pdev); |
| 250 | |||
| 251 | watchdog_unregister_device(wdd); | ||
| 214 | return 0; | 252 | return 0; |
| 215 | } | 253 | } |
| 216 | 254 | ||
| 217 | static struct platform_device_id max63xx_id_table[] = { | 255 | static const struct platform_device_id max63xx_id_table[] = { |
| 218 | { "max6369_wdt", (kernel_ulong_t)max6369_table, }, | 256 | { "max6369_wdt", (kernel_ulong_t)max6369_table, }, |
| 219 | { "max6370_wdt", (kernel_ulong_t)max6369_table, }, | 257 | { "max6370_wdt", (kernel_ulong_t)max6369_table, }, |
| 220 | { "max6371_wdt", (kernel_ulong_t)max6371_table, }, | 258 | { "max6371_wdt", (kernel_ulong_t)max6371_table, }, |
diff --git a/drivers/watchdog/mena21_wdt.c b/drivers/watchdog/mena21_wdt.c index 96dbba980579..d193a5e79c38 100644 --- a/drivers/watchdog/mena21_wdt.c +++ b/drivers/watchdog/mena21_wdt.c | |||
| @@ -208,14 +208,15 @@ static int a21_wdt_probe(struct platform_device *pdev) | |||
| 208 | else if (reset == 7) | 208 | else if (reset == 7) |
| 209 | a21_wdt.bootstatus |= WDIOF_EXTERN2; | 209 | a21_wdt.bootstatus |= WDIOF_EXTERN2; |
| 210 | 210 | ||
| 211 | drv->wdt = a21_wdt; | ||
| 212 | dev_set_drvdata(&pdev->dev, drv); | ||
| 213 | |||
| 211 | ret = watchdog_register_device(&a21_wdt); | 214 | ret = watchdog_register_device(&a21_wdt); |
| 212 | if (ret) { | 215 | if (ret) { |
| 213 | dev_err(&pdev->dev, "Cannot register watchdog device\n"); | 216 | dev_err(&pdev->dev, "Cannot register watchdog device\n"); |
| 214 | goto err_register_wd; | 217 | goto err_register_wd; |
| 215 | } | 218 | } |
| 216 | 219 | ||
| 217 | dev_set_drvdata(&pdev->dev, drv); | ||
| 218 | |||
| 219 | dev_info(&pdev->dev, "MEN A21 watchdog timer driver enabled\n"); | 220 | dev_info(&pdev->dev, "MEN A21 watchdog timer driver enabled\n"); |
| 220 | 221 | ||
| 221 | return 0; | 222 | return 0; |
diff --git a/drivers/watchdog/omap_wdt.c b/drivers/watchdog/omap_wdt.c index 1e6be9e40577..de911c7e477c 100644 --- a/drivers/watchdog/omap_wdt.c +++ b/drivers/watchdog/omap_wdt.c | |||
| @@ -53,7 +53,15 @@ static unsigned timer_margin; | |||
| 53 | module_param(timer_margin, uint, 0); | 53 | module_param(timer_margin, uint, 0); |
| 54 | MODULE_PARM_DESC(timer_margin, "initial watchdog timeout (in seconds)"); | 54 | MODULE_PARM_DESC(timer_margin, "initial watchdog timeout (in seconds)"); |
| 55 | 55 | ||
| 56 | #define to_omap_wdt_dev(_wdog) container_of(_wdog, struct omap_wdt_dev, wdog) | ||
| 57 | |||
| 58 | static bool early_enable; | ||
| 59 | module_param(early_enable, bool, 0); | ||
| 60 | MODULE_PARM_DESC(early_enable, | ||
| 61 | "Watchdog is started on module insertion (default=0)"); | ||
| 62 | |||
| 56 | struct omap_wdt_dev { | 63 | struct omap_wdt_dev { |
| 64 | struct watchdog_device wdog; | ||
| 57 | void __iomem *base; /* physical */ | 65 | void __iomem *base; /* physical */ |
| 58 | struct device *dev; | 66 | struct device *dev; |
| 59 | bool omap_wdt_users; | 67 | bool omap_wdt_users; |
| @@ -123,7 +131,7 @@ static void omap_wdt_set_timer(struct omap_wdt_dev *wdev, | |||
| 123 | 131 | ||
| 124 | static int omap_wdt_start(struct watchdog_device *wdog) | 132 | static int omap_wdt_start(struct watchdog_device *wdog) |
| 125 | { | 133 | { |
| 126 | struct omap_wdt_dev *wdev = watchdog_get_drvdata(wdog); | 134 | struct omap_wdt_dev *wdev = to_omap_wdt_dev(wdog); |
| 127 | void __iomem *base = wdev->base; | 135 | void __iomem *base = wdev->base; |
| 128 | 136 | ||
| 129 | mutex_lock(&wdev->lock); | 137 | mutex_lock(&wdev->lock); |
| @@ -132,6 +140,13 @@ static int omap_wdt_start(struct watchdog_device *wdog) | |||
| 132 | 140 | ||
| 133 | pm_runtime_get_sync(wdev->dev); | 141 | pm_runtime_get_sync(wdev->dev); |
| 134 | 142 | ||
| 143 | /* | ||
| 144 | * Make sure the watchdog is disabled. This is unfortunately required | ||
| 145 | * because writing to various registers with the watchdog running has no | ||
| 146 | * effect. | ||
| 147 | */ | ||
| 148 | omap_wdt_disable(wdev); | ||
| 149 | |||
| 135 | /* initialize prescaler */ | 150 | /* initialize prescaler */ |
| 136 | while (readl_relaxed(base + OMAP_WATCHDOG_WPS) & 0x01) | 151 | while (readl_relaxed(base + OMAP_WATCHDOG_WPS) & 0x01) |
| 137 | cpu_relax(); | 152 | cpu_relax(); |
| @@ -151,7 +166,7 @@ static int omap_wdt_start(struct watchdog_device *wdog) | |||
| 151 | 166 | ||
| 152 | static int omap_wdt_stop(struct watchdog_device *wdog) | 167 | static int omap_wdt_stop(struct watchdog_device *wdog) |
| 153 | { | 168 | { |
| 154 | struct omap_wdt_dev *wdev = watchdog_get_drvdata(wdog); | 169 | struct omap_wdt_dev *wdev = to_omap_wdt_dev(wdog); |
| 155 | 170 | ||
| 156 | mutex_lock(&wdev->lock); | 171 | mutex_lock(&wdev->lock); |
| 157 | omap_wdt_disable(wdev); | 172 | omap_wdt_disable(wdev); |
| @@ -163,7 +178,7 @@ static int omap_wdt_stop(struct watchdog_device *wdog) | |||
| 163 | 178 | ||
| 164 | static int omap_wdt_ping(struct watchdog_device *wdog) | 179 | static int omap_wdt_ping(struct watchdog_device *wdog) |
| 165 | { | 180 | { |
| 166 | struct omap_wdt_dev *wdev = watchdog_get_drvdata(wdog); | 181 | struct omap_wdt_dev *wdev = to_omap_wdt_dev(wdog); |
| 167 | 182 | ||
| 168 | mutex_lock(&wdev->lock); | 183 | mutex_lock(&wdev->lock); |
| 169 | omap_wdt_reload(wdev); | 184 | omap_wdt_reload(wdev); |
| @@ -175,7 +190,7 @@ static int omap_wdt_ping(struct watchdog_device *wdog) | |||
| 175 | static int omap_wdt_set_timeout(struct watchdog_device *wdog, | 190 | static int omap_wdt_set_timeout(struct watchdog_device *wdog, |
| 176 | unsigned int timeout) | 191 | unsigned int timeout) |
| 177 | { | 192 | { |
| 178 | struct omap_wdt_dev *wdev = watchdog_get_drvdata(wdog); | 193 | struct omap_wdt_dev *wdev = to_omap_wdt_dev(wdog); |
| 179 | 194 | ||
| 180 | mutex_lock(&wdev->lock); | 195 | mutex_lock(&wdev->lock); |
| 181 | omap_wdt_disable(wdev); | 196 | omap_wdt_disable(wdev); |
| @@ -188,6 +203,16 @@ static int omap_wdt_set_timeout(struct watchdog_device *wdog, | |||
| 188 | return 0; | 203 | return 0; |
| 189 | } | 204 | } |
| 190 | 205 | ||
| 206 | static unsigned int omap_wdt_get_timeleft(struct watchdog_device *wdog) | ||
| 207 | { | ||
| 208 | struct omap_wdt_dev *wdev = watchdog_get_drvdata(wdog); | ||
| 209 | void __iomem *base = wdev->base; | ||
| 210 | u32 value; | ||
| 211 | |||
| 212 | value = readl_relaxed(base + OMAP_WATCHDOG_CRR); | ||
| 213 | return GET_WCCR_SECS(value); | ||
| 214 | } | ||
| 215 | |||
| 191 | static const struct watchdog_info omap_wdt_info = { | 216 | static const struct watchdog_info omap_wdt_info = { |
| 192 | .options = WDIOF_SETTIMEOUT | WDIOF_MAGICCLOSE | WDIOF_KEEPALIVEPING, | 217 | .options = WDIOF_SETTIMEOUT | WDIOF_MAGICCLOSE | WDIOF_KEEPALIVEPING, |
| 193 | .identity = "OMAP Watchdog", | 218 | .identity = "OMAP Watchdog", |
| @@ -199,21 +224,16 @@ static const struct watchdog_ops omap_wdt_ops = { | |||
| 199 | .stop = omap_wdt_stop, | 224 | .stop = omap_wdt_stop, |
| 200 | .ping = omap_wdt_ping, | 225 | .ping = omap_wdt_ping, |
| 201 | .set_timeout = omap_wdt_set_timeout, | 226 | .set_timeout = omap_wdt_set_timeout, |
| 227 | .get_timeleft = omap_wdt_get_timeleft, | ||
| 202 | }; | 228 | }; |
| 203 | 229 | ||
| 204 | static int omap_wdt_probe(struct platform_device *pdev) | 230 | static int omap_wdt_probe(struct platform_device *pdev) |
| 205 | { | 231 | { |
| 206 | struct omap_wd_timer_platform_data *pdata = dev_get_platdata(&pdev->dev); | 232 | struct omap_wd_timer_platform_data *pdata = dev_get_platdata(&pdev->dev); |
| 207 | struct watchdog_device *omap_wdt; | ||
| 208 | struct resource *res; | 233 | struct resource *res; |
| 209 | struct omap_wdt_dev *wdev; | 234 | struct omap_wdt_dev *wdev; |
| 210 | u32 rs; | ||
| 211 | int ret; | 235 | int ret; |
| 212 | 236 | ||
| 213 | omap_wdt = devm_kzalloc(&pdev->dev, sizeof(*omap_wdt), GFP_KERNEL); | ||
| 214 | if (!omap_wdt) | ||
| 215 | return -ENOMEM; | ||
| 216 | |||
| 217 | wdev = devm_kzalloc(&pdev->dev, sizeof(*wdev), GFP_KERNEL); | 237 | wdev = devm_kzalloc(&pdev->dev, sizeof(*wdev), GFP_KERNEL); |
| 218 | if (!wdev) | 238 | if (!wdev) |
| 219 | return -ENOMEM; | 239 | return -ENOMEM; |
| @@ -229,35 +249,30 @@ static int omap_wdt_probe(struct platform_device *pdev) | |||
| 229 | if (IS_ERR(wdev->base)) | 249 | if (IS_ERR(wdev->base)) |
| 230 | return PTR_ERR(wdev->base); | 250 | return PTR_ERR(wdev->base); |
| 231 | 251 | ||
| 232 | omap_wdt->info = &omap_wdt_info; | 252 | wdev->wdog.info = &omap_wdt_info; |
| 233 | omap_wdt->ops = &omap_wdt_ops; | 253 | wdev->wdog.ops = &omap_wdt_ops; |
| 234 | omap_wdt->min_timeout = TIMER_MARGIN_MIN; | 254 | wdev->wdog.min_timeout = TIMER_MARGIN_MIN; |
| 235 | omap_wdt->max_timeout = TIMER_MARGIN_MAX; | 255 | wdev->wdog.max_timeout = TIMER_MARGIN_MAX; |
| 236 | 256 | ||
| 237 | if (timer_margin >= TIMER_MARGIN_MIN && | 257 | if (watchdog_init_timeout(&wdev->wdog, timer_margin, &pdev->dev) < 0) |
| 238 | timer_margin <= TIMER_MARGIN_MAX) | 258 | wdev->wdog.timeout = TIMER_MARGIN_DEFAULT; |
| 239 | omap_wdt->timeout = timer_margin; | ||
| 240 | else | ||
| 241 | omap_wdt->timeout = TIMER_MARGIN_DEFAULT; | ||
| 242 | 259 | ||
| 243 | watchdog_set_drvdata(omap_wdt, wdev); | 260 | watchdog_set_nowayout(&wdev->wdog, nowayout); |
| 244 | watchdog_set_nowayout(omap_wdt, nowayout); | ||
| 245 | 261 | ||
| 246 | platform_set_drvdata(pdev, omap_wdt); | 262 | platform_set_drvdata(pdev, wdev); |
| 247 | 263 | ||
| 248 | pm_runtime_enable(wdev->dev); | 264 | pm_runtime_enable(wdev->dev); |
| 249 | pm_runtime_get_sync(wdev->dev); | 265 | pm_runtime_get_sync(wdev->dev); |
| 250 | 266 | ||
| 251 | if (pdata && pdata->read_reset_sources) | 267 | if (pdata && pdata->read_reset_sources) { |
| 252 | rs = pdata->read_reset_sources(); | 268 | u32 rs = pdata->read_reset_sources(); |
| 253 | else | 269 | if (rs & (1 << OMAP_MPU_WD_RST_SRC_ID_SHIFT)) |
| 254 | rs = 0; | 270 | wdev->wdog.bootstatus = WDIOF_CARDRESET; |
| 255 | omap_wdt->bootstatus = (rs & (1 << OMAP_MPU_WD_RST_SRC_ID_SHIFT)) ? | 271 | } |
| 256 | WDIOF_CARDRESET : 0; | ||
| 257 | 272 | ||
| 258 | omap_wdt_disable(wdev); | 273 | omap_wdt_disable(wdev); |
| 259 | 274 | ||
| 260 | ret = watchdog_register_device(omap_wdt); | 275 | ret = watchdog_register_device(&wdev->wdog); |
| 261 | if (ret) { | 276 | if (ret) { |
| 262 | pm_runtime_disable(wdev->dev); | 277 | pm_runtime_disable(wdev->dev); |
| 263 | return ret; | 278 | return ret; |
| @@ -265,17 +280,19 @@ static int omap_wdt_probe(struct platform_device *pdev) | |||
| 265 | 280 | ||
| 266 | pr_info("OMAP Watchdog Timer Rev 0x%02x: initial timeout %d sec\n", | 281 | pr_info("OMAP Watchdog Timer Rev 0x%02x: initial timeout %d sec\n", |
| 267 | readl_relaxed(wdev->base + OMAP_WATCHDOG_REV) & 0xFF, | 282 | readl_relaxed(wdev->base + OMAP_WATCHDOG_REV) & 0xFF, |
| 268 | omap_wdt->timeout); | 283 | wdev->wdog.timeout); |
| 269 | 284 | ||
| 270 | pm_runtime_put_sync(wdev->dev); | 285 | pm_runtime_put_sync(wdev->dev); |
| 271 | 286 | ||
| 287 | if (early_enable) | ||
| 288 | omap_wdt_start(&wdev->wdog); | ||
| 289 | |||
| 272 | return 0; | 290 | return 0; |
| 273 | } | 291 | } |
| 274 | 292 | ||
| 275 | static void omap_wdt_shutdown(struct platform_device *pdev) | 293 | static void omap_wdt_shutdown(struct platform_device *pdev) |
| 276 | { | 294 | { |
| 277 | struct watchdog_device *wdog = platform_get_drvdata(pdev); | 295 | struct omap_wdt_dev *wdev = platform_get_drvdata(pdev); |
| 278 | struct omap_wdt_dev *wdev = watchdog_get_drvdata(wdog); | ||
| 279 | 296 | ||
| 280 | mutex_lock(&wdev->lock); | 297 | mutex_lock(&wdev->lock); |
| 281 | if (wdev->omap_wdt_users) { | 298 | if (wdev->omap_wdt_users) { |
| @@ -287,11 +304,10 @@ static void omap_wdt_shutdown(struct platform_device *pdev) | |||
| 287 | 304 | ||
| 288 | static int omap_wdt_remove(struct platform_device *pdev) | 305 | static int omap_wdt_remove(struct platform_device *pdev) |
| 289 | { | 306 | { |
| 290 | struct watchdog_device *wdog = platform_get_drvdata(pdev); | 307 | struct omap_wdt_dev *wdev = platform_get_drvdata(pdev); |
| 291 | struct omap_wdt_dev *wdev = watchdog_get_drvdata(wdog); | ||
| 292 | 308 | ||
| 293 | pm_runtime_disable(wdev->dev); | 309 | pm_runtime_disable(wdev->dev); |
| 294 | watchdog_unregister_device(wdog); | 310 | watchdog_unregister_device(&wdev->wdog); |
| 295 | 311 | ||
| 296 | return 0; | 312 | return 0; |
| 297 | } | 313 | } |
| @@ -306,8 +322,7 @@ static int omap_wdt_remove(struct platform_device *pdev) | |||
| 306 | 322 | ||
| 307 | static int omap_wdt_suspend(struct platform_device *pdev, pm_message_t state) | 323 | static int omap_wdt_suspend(struct platform_device *pdev, pm_message_t state) |
| 308 | { | 324 | { |
| 309 | struct watchdog_device *wdog = platform_get_drvdata(pdev); | 325 | struct omap_wdt_dev *wdev = platform_get_drvdata(pdev); |
| 310 | struct omap_wdt_dev *wdev = watchdog_get_drvdata(wdog); | ||
| 311 | 326 | ||
| 312 | mutex_lock(&wdev->lock); | 327 | mutex_lock(&wdev->lock); |
| 313 | if (wdev->omap_wdt_users) { | 328 | if (wdev->omap_wdt_users) { |
| @@ -321,8 +336,7 @@ static int omap_wdt_suspend(struct platform_device *pdev, pm_message_t state) | |||
| 321 | 336 | ||
| 322 | static int omap_wdt_resume(struct platform_device *pdev) | 337 | static int omap_wdt_resume(struct platform_device *pdev) |
| 323 | { | 338 | { |
| 324 | struct watchdog_device *wdog = platform_get_drvdata(pdev); | 339 | struct omap_wdt_dev *wdev = platform_get_drvdata(pdev); |
| 325 | struct omap_wdt_dev *wdev = watchdog_get_drvdata(wdog); | ||
| 326 | 340 | ||
| 327 | mutex_lock(&wdev->lock); | 341 | mutex_lock(&wdev->lock); |
| 328 | if (wdev->omap_wdt_users) { | 342 | if (wdev->omap_wdt_users) { |
diff --git a/drivers/watchdog/omap_wdt.h b/drivers/watchdog/omap_wdt.h index 09b774cf75b9..42f31ec5e90d 100644 --- a/drivers/watchdog/omap_wdt.h +++ b/drivers/watchdog/omap_wdt.h | |||
| @@ -50,5 +50,6 @@ | |||
| 50 | 50 | ||
| 51 | #define PTV 0 /* prescale */ | 51 | #define PTV 0 /* prescale */ |
| 52 | #define GET_WLDR_VAL(secs) (0xffffffff - ((secs) * (32768/(1<<PTV))) + 1) | 52 | #define GET_WLDR_VAL(secs) (0xffffffff - ((secs) * (32768/(1<<PTV))) + 1) |
| 53 | #define GET_WCCR_SECS(val) ((0xffffffff - (val) + 1) / (32768/(1<<PTV))) | ||
| 53 | 54 | ||
| 54 | #endif /* _OMAP_WATCHDOG_H */ | 55 | #endif /* _OMAP_WATCHDOG_H */ |
diff --git a/drivers/watchdog/st_lpc_wdt.c b/drivers/watchdog/st_lpc_wdt.c index f32be155212a..6785afdc0fca 100644 --- a/drivers/watchdog/st_lpc_wdt.c +++ b/drivers/watchdog/st_lpc_wdt.c | |||
| @@ -197,7 +197,7 @@ static int st_wdog_probe(struct platform_device *pdev) | |||
| 197 | return -EINVAL; | 197 | return -EINVAL; |
| 198 | } | 198 | } |
| 199 | 199 | ||
| 200 | /* LPC can either run in RTC or WDT mode */ | 200 | /* LPC can either run as a Clocksource or in RTC or WDT mode */ |
| 201 | if (mode != ST_LPC_MODE_WDT) | 201 | if (mode != ST_LPC_MODE_WDT) |
| 202 | return -ENODEV; | 202 | return -ENODEV; |
| 203 | 203 | ||
diff --git a/drivers/watchdog/watchdog_core.c b/drivers/watchdog/watchdog_core.c index cec9b559647d..1a8059455413 100644 --- a/drivers/watchdog/watchdog_core.c +++ b/drivers/watchdog/watchdog_core.c | |||
| @@ -43,6 +43,45 @@ | |||
| 43 | static DEFINE_IDA(watchdog_ida); | 43 | static DEFINE_IDA(watchdog_ida); |
| 44 | static struct class *watchdog_class; | 44 | static struct class *watchdog_class; |
| 45 | 45 | ||
| 46 | /* | ||
| 47 | * Deferred Registration infrastructure. | ||
| 48 | * | ||
| 49 | * Sometimes watchdog drivers needs to be loaded as soon as possible, | ||
| 50 | * for example when it's impossible to disable it. To do so, | ||
| 51 | * raising the initcall level of the watchdog driver is a solution. | ||
| 52 | * But in such case, the miscdev is maybe not ready (subsys_initcall), and | ||
| 53 | * watchdog_core need miscdev to register the watchdog as a char device. | ||
| 54 | * | ||
| 55 | * The deferred registration infrastructure offer a way for the watchdog | ||
| 56 | * subsystem to register a watchdog properly, even before miscdev is ready. | ||
| 57 | */ | ||
| 58 | |||
| 59 | static DEFINE_MUTEX(wtd_deferred_reg_mutex); | ||
| 60 | static LIST_HEAD(wtd_deferred_reg_list); | ||
| 61 | static bool wtd_deferred_reg_done; | ||
| 62 | |||
| 63 | static int watchdog_deferred_registration_add(struct watchdog_device *wdd) | ||
| 64 | { | ||
| 65 | list_add_tail(&wdd->deferred, | ||
| 66 | &wtd_deferred_reg_list); | ||
| 67 | return 0; | ||
| 68 | } | ||
| 69 | |||
| 70 | static void watchdog_deferred_registration_del(struct watchdog_device *wdd) | ||
| 71 | { | ||
| 72 | struct list_head *p, *n; | ||
| 73 | struct watchdog_device *wdd_tmp; | ||
| 74 | |||
| 75 | list_for_each_safe(p, n, &wtd_deferred_reg_list) { | ||
| 76 | wdd_tmp = list_entry(p, struct watchdog_device, | ||
| 77 | deferred); | ||
| 78 | if (wdd_tmp == wdd) { | ||
| 79 | list_del(&wdd_tmp->deferred); | ||
| 80 | break; | ||
| 81 | } | ||
| 82 | } | ||
| 83 | } | ||
| 84 | |||
| 46 | static void watchdog_check_min_max_timeout(struct watchdog_device *wdd) | 85 | static void watchdog_check_min_max_timeout(struct watchdog_device *wdd) |
| 47 | { | 86 | { |
| 48 | /* | 87 | /* |
| @@ -98,17 +137,7 @@ int watchdog_init_timeout(struct watchdog_device *wdd, | |||
| 98 | } | 137 | } |
| 99 | EXPORT_SYMBOL_GPL(watchdog_init_timeout); | 138 | EXPORT_SYMBOL_GPL(watchdog_init_timeout); |
| 100 | 139 | ||
| 101 | /** | 140 | static int __watchdog_register_device(struct watchdog_device *wdd) |
| 102 | * watchdog_register_device() - register a watchdog device | ||
| 103 | * @wdd: watchdog device | ||
| 104 | * | ||
| 105 | * Register a watchdog device with the kernel so that the | ||
| 106 | * watchdog timer can be accessed from userspace. | ||
| 107 | * | ||
| 108 | * A zero is returned on success and a negative errno code for | ||
| 109 | * failure. | ||
| 110 | */ | ||
| 111 | int watchdog_register_device(struct watchdog_device *wdd) | ||
| 112 | { | 141 | { |
| 113 | int ret, id, devno; | 142 | int ret, id, devno; |
| 114 | 143 | ||
| @@ -164,16 +193,33 @@ int watchdog_register_device(struct watchdog_device *wdd) | |||
| 164 | 193 | ||
| 165 | return 0; | 194 | return 0; |
| 166 | } | 195 | } |
| 167 | EXPORT_SYMBOL_GPL(watchdog_register_device); | ||
| 168 | 196 | ||
| 169 | /** | 197 | /** |
| 170 | * watchdog_unregister_device() - unregister a watchdog device | 198 | * watchdog_register_device() - register a watchdog device |
| 171 | * @wdd: watchdog device to unregister | 199 | * @wdd: watchdog device |
| 172 | * | 200 | * |
| 173 | * Unregister a watchdog device that was previously successfully | 201 | * Register a watchdog device with the kernel so that the |
| 174 | * registered with watchdog_register_device(). | 202 | * watchdog timer can be accessed from userspace. |
| 203 | * | ||
| 204 | * A zero is returned on success and a negative errno code for | ||
| 205 | * failure. | ||
| 175 | */ | 206 | */ |
| 176 | void watchdog_unregister_device(struct watchdog_device *wdd) | 207 | |
| 208 | int watchdog_register_device(struct watchdog_device *wdd) | ||
| 209 | { | ||
| 210 | int ret; | ||
| 211 | |||
| 212 | mutex_lock(&wtd_deferred_reg_mutex); | ||
| 213 | if (wtd_deferred_reg_done) | ||
| 214 | ret = __watchdog_register_device(wdd); | ||
| 215 | else | ||
| 216 | ret = watchdog_deferred_registration_add(wdd); | ||
| 217 | mutex_unlock(&wtd_deferred_reg_mutex); | ||
| 218 | return ret; | ||
| 219 | } | ||
| 220 | EXPORT_SYMBOL_GPL(watchdog_register_device); | ||
| 221 | |||
| 222 | static void __watchdog_unregister_device(struct watchdog_device *wdd) | ||
| 177 | { | 223 | { |
| 178 | int ret; | 224 | int ret; |
| 179 | int devno; | 225 | int devno; |
| @@ -189,8 +235,43 @@ void watchdog_unregister_device(struct watchdog_device *wdd) | |||
| 189 | ida_simple_remove(&watchdog_ida, wdd->id); | 235 | ida_simple_remove(&watchdog_ida, wdd->id); |
| 190 | wdd->dev = NULL; | 236 | wdd->dev = NULL; |
| 191 | } | 237 | } |
| 238 | |||
| 239 | /** | ||
| 240 | * watchdog_unregister_device() - unregister a watchdog device | ||
| 241 | * @wdd: watchdog device to unregister | ||
| 242 | * | ||
| 243 | * Unregister a watchdog device that was previously successfully | ||
| 244 | * registered with watchdog_register_device(). | ||
| 245 | */ | ||
| 246 | |||
| 247 | void watchdog_unregister_device(struct watchdog_device *wdd) | ||
| 248 | { | ||
| 249 | mutex_lock(&wtd_deferred_reg_mutex); | ||
| 250 | if (wtd_deferred_reg_done) | ||
| 251 | __watchdog_unregister_device(wdd); | ||
| 252 | else | ||
| 253 | watchdog_deferred_registration_del(wdd); | ||
| 254 | mutex_unlock(&wtd_deferred_reg_mutex); | ||
| 255 | } | ||
| 256 | |||
| 192 | EXPORT_SYMBOL_GPL(watchdog_unregister_device); | 257 | EXPORT_SYMBOL_GPL(watchdog_unregister_device); |
| 193 | 258 | ||
| 259 | static int __init watchdog_deferred_registration(void) | ||
| 260 | { | ||
| 261 | mutex_lock(&wtd_deferred_reg_mutex); | ||
| 262 | wtd_deferred_reg_done = true; | ||
| 263 | while (!list_empty(&wtd_deferred_reg_list)) { | ||
| 264 | struct watchdog_device *wdd; | ||
| 265 | |||
| 266 | wdd = list_first_entry(&wtd_deferred_reg_list, | ||
| 267 | struct watchdog_device, deferred); | ||
| 268 | list_del(&wdd->deferred); | ||
| 269 | __watchdog_register_device(wdd); | ||
| 270 | } | ||
| 271 | mutex_unlock(&wtd_deferred_reg_mutex); | ||
| 272 | return 0; | ||
| 273 | } | ||
| 274 | |||
| 194 | static int __init watchdog_init(void) | 275 | static int __init watchdog_init(void) |
| 195 | { | 276 | { |
| 196 | int err; | 277 | int err; |
| @@ -207,6 +288,7 @@ static int __init watchdog_init(void) | |||
| 207 | return err; | 288 | return err; |
| 208 | } | 289 | } |
| 209 | 290 | ||
| 291 | watchdog_deferred_registration(); | ||
| 210 | return 0; | 292 | return 0; |
| 211 | } | 293 | } |
| 212 | 294 | ||
| @@ -217,7 +299,7 @@ static void __exit watchdog_exit(void) | |||
| 217 | ida_destroy(&watchdog_ida); | 299 | ida_destroy(&watchdog_ida); |
| 218 | } | 300 | } |
| 219 | 301 | ||
| 220 | subsys_initcall(watchdog_init); | 302 | subsys_initcall_sync(watchdog_init); |
| 221 | module_exit(watchdog_exit); | 303 | module_exit(watchdog_exit); |
| 222 | 304 | ||
| 223 | MODULE_AUTHOR("Alan Cox <alan@lxorguk.ukuu.org.uk>"); | 305 | MODULE_AUTHOR("Alan Cox <alan@lxorguk.ukuu.org.uk>"); |
diff --git a/include/linux/watchdog.h b/include/linux/watchdog.h index a746bf5216f8..f47feada5b42 100644 --- a/include/linux/watchdog.h +++ b/include/linux/watchdog.h | |||
| @@ -65,6 +65,8 @@ struct watchdog_ops { | |||
| 65 | * @driver-data:Pointer to the drivers private data. | 65 | * @driver-data:Pointer to the drivers private data. |
| 66 | * @lock: Lock for watchdog core internal use only. | 66 | * @lock: Lock for watchdog core internal use only. |
| 67 | * @status: Field that contains the devices internal status bits. | 67 | * @status: Field that contains the devices internal status bits. |
| 68 | * @deferred: entry in wtd_deferred_reg_list which is used to | ||
| 69 | * register early initialized watchdogs. | ||
| 68 | * | 70 | * |
| 69 | * The watchdog_device structure contains all information about a | 71 | * The watchdog_device structure contains all information about a |
| 70 | * watchdog timer device. | 72 | * watchdog timer device. |
| @@ -95,6 +97,7 @@ struct watchdog_device { | |||
| 95 | #define WDOG_ALLOW_RELEASE 2 /* Did we receive the magic char ? */ | 97 | #define WDOG_ALLOW_RELEASE 2 /* Did we receive the magic char ? */ |
| 96 | #define WDOG_NO_WAY_OUT 3 /* Is 'nowayout' feature set ? */ | 98 | #define WDOG_NO_WAY_OUT 3 /* Is 'nowayout' feature set ? */ |
| 97 | #define WDOG_UNREGISTERED 4 /* Has the device been unregistered */ | 99 | #define WDOG_UNREGISTERED 4 /* Has the device been unregistered */ |
| 100 | struct list_head deferred; | ||
| 98 | }; | 101 | }; |
| 99 | 102 | ||
| 100 | #define WATCHDOG_NOWAYOUT IS_BUILTIN(CONFIG_WATCHDOG_NOWAYOUT) | 103 | #define WATCHDOG_NOWAYOUT IS_BUILTIN(CONFIG_WATCHDOG_NOWAYOUT) |
