Commit e5d56efc authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'watchdog-for-linus-v4.11' of...

Merge tag 'watchdog-for-linus-v4.11' of git://git.kernel.org/pub/scm/linux/kernel/git/groeck/linux-staging

Pull watchdog updates from Guenter Roeck:
 "Wim asked me to handle the watchdog pull request this time around.

  Key changes:

   - New drivers: Cortina Gemini, ZTE's zx2967 family, NIC7018

   - Convert to use device managed functions: ebc-c384_wdt, tegra_wdt,
     da9063_wdt, da9062_wdt, da9055_wdt, da9052_wdt, bcm2835_wdt,
     mena21_wdt, wm831x_wdt, digicolor_wdt, intel-mid_wdt, meson_wdt,
     sunxi_wdt, aspeed_wdt, coh901327_wdt, iTCO_wdt

   - Use watchdog core to install restart handler: tangox, dw_wdt,
     bcm2835_wdt, asm9260_wdt, bcm47xx_wdt

   - Convert ts72xx_wdt driver to watchdog core

   - Let core handle heartbeat in ep93xx_wdt driver

   - Enable COMPILE_TEST where possible

   - Various other improvements"

* tag 'watchdog-for-linus-v4.11' of git://git.kernel.org/pub/scm/linux/kernel/git/groeck/linux-staging: (54 commits)
  watchdog: s3c2410: Add prefix to local function
  watchdog: s3c2410: Select MFD_SYSCON on all Exynos platforms
  watchdog: s3c2410: Use dev_dbg instead of pr_info
  watchdog: s3c2410: Fix infinite interrupt in soft mode
  watchdog: s3c2410: Remove confusing CONFIG prefix from local defines
  watchdog: softdog: make pretimeout support a compile option
  watchdog: zx2967: add watchdog controller driver for ZTE's zx2967 family
  dt: bindings: add documentation for zx2967 family watchdog controller
  watchdog: sama5d4: Implement resume hook
  watchdog: sama5d4: Cache MR instead of a partial config
  watchdog: ts72xx_wdt: convert driver to watchdog core
  watchdog: ep93xx_wdt: cleanup and let the core handle the heartbeat
  watchdog: RDC321X_WDT always depends on PCI
  watchdog: add driver for Cortina Gemini watchdog
  watchdog: add DT bindings for Cortina Gemini
  watchdog: constify watchdog_ops structures
  watchdog: Introduce watchdog_stop_on_unregister helper
  watchdog: ebc-c384_wdt: Utilize devm_ functions in driver probe callback
  watchdog: tegra_wdt: Convert to use device managed functions
  watchdog: da9063_wdt: Convert to use device managed functions
  ...
parents c4f3f22e e3a60ead
Cortina Systems Gemini SoC Watchdog
Required properties:
- compatible : must be "cortina,gemini-watchdog"
- reg : shall contain base register location and length
- interrupts : shall contain the interrupt for the watchdog
Optional properties:
- timeout-sec : the default watchdog timeout in seconds.
Example:
watchdog@41000000 {
compatible = "cortina,gemini-watchdog";
reg = <0x41000000 0x1000>;
interrupts = <3 IRQ_TYPE_LEVEL_HIGH>;
};
......@@ -6,10 +6,11 @@ occurred.
Required properties:
- compatible : should be one among the following
(a) "samsung,s3c2410-wdt" for Exynos4 and previous SoCs
(b) "samsung,exynos5250-wdt" for Exynos5250
(c) "samsung,exynos5420-wdt" for Exynos5420
(c) "samsung,exynos7-wdt" for Exynos7
- "samsung,s3c2410-wdt" for S3C2410
- "samsung,s3c6410-wdt" for S3C6410, S5PV210 and Exynos4
- "samsung,exynos5250-wdt" for Exynos5250
- "samsung,exynos5420-wdt" for Exynos5420
- "samsung,exynos7-wdt" for Exynos7
- reg : base physical address of the controller and length of memory mapped
region.
......
ZTE zx2967 Watchdog timer
Required properties:
- compatible : should be one of the following.
* zte,zx296718-wdt
- reg : Specifies base physical address and size of the registers.
- clocks : Pairs of phandle and specifier referencing the controller's clocks.
- resets : Reference to the reset controller controlling the watchdog
controller.
Optional properties:
- timeout-sec : Contains the watchdog timeout in seconds.
- zte,wdt-reset-sysctrl : Directs how to reset system by the watchdog.
if we don't want to restart system when watchdog been triggered,
it's not required, vice versa.
It should include following fields.
* phandle of aon-sysctrl.
* offset of register that be written, should be 0xb0.
* configure value that be written to aon-sysctrl.
* bit mask, corresponding bits will be affected.
Example:
wdt: watchdog@1465000 {
compatible = "zte,zx296718-wdt";
reg = <0x1465000 0x1000>;
clocks = <&topcrm WDT_WCLK>;
resets = <&toprst 35>;
zte,wdt-reset-sysctrl = <&aon_sysctrl 0xb0 1 0x115>;
};
......@@ -280,6 +280,12 @@ To disable the watchdog on reboot, the user must call the following helper:
static inline void watchdog_stop_on_reboot(struct watchdog_device *wdd);
To disable the watchdog when unregistering the watchdog, the user must call
the following helper. Note that this will only stop the watchdog if the
nowayout flag is not set.
static inline void watchdog_stop_on_unregister(struct watchdog_device *wdd);
To change the priority of the restart handler the following helper should be
used:
......
......@@ -209,6 +209,11 @@ timeout: Initial watchdog timeout in seconds (0<timeout<516, default=60)
nowayout: Watchdog cannot be stopped once started
(default=kernel config parameter)
-------------------------------------------------
nic7018_wdt:
timeout: Initial watchdog timeout in seconds (0<timeout<464, default=80)
nowayout: Watchdog cannot be stopped once started
(default=kernel config parameter)
-------------------------------------------------
nuc900_wdt:
heartbeat: Watchdog heartbeats in seconds.
(default = 15)
......
......@@ -71,9 +71,17 @@ config SOFT_WATCHDOG
To compile this driver as a module, choose M here: the
module will be called softdog.
config SOFT_WATCHDOG_PRETIMEOUT
bool "Software watchdog pretimeout governor support"
depends on SOFT_WATCHDOG && WATCHDOG_PRETIMEOUT_GOV
help
Enable this if you want to use pretimeout governors with the software
watchdog. Be aware that governors might affect the watchdog because it
is purely software, e.g. the panic governor will stall it!
config DA9052_WATCHDOG
tristate "Dialog DA9052 Watchdog"
depends on PMIC_DA9052
depends on PMIC_DA9052 || COMPILE_TEST
select WATCHDOG_CORE
help
Support for the watchdog in the DA9052 PMIC. Watchdog trigger
......@@ -85,7 +93,7 @@ config DA9052_WATCHDOG
config DA9055_WATCHDOG
tristate "Dialog Semiconductor DA9055 Watchdog"
depends on MFD_DA9055
depends on MFD_DA9055 || COMPILE_TEST
select WATCHDOG_CORE
help
If you say yes here you get support for watchdog on the Dialog
......@@ -96,7 +104,7 @@ config DA9055_WATCHDOG
config DA9063_WATCHDOG
tristate "Dialog DA9063 Watchdog"
depends on MFD_DA9063
depends on MFD_DA9063 || COMPILE_TEST
select WATCHDOG_CORE
help
Support for the watchdog in the DA9063 PMIC.
......@@ -105,7 +113,7 @@ config DA9063_WATCHDOG
config DA9062_WATCHDOG
tristate "Dialog DA9062/61 Watchdog"
depends on MFD_DA9062
depends on MFD_DA9062 || COMPILE_TEST
select WATCHDOG_CORE
help
Support for the watchdog in the DA9062 and DA9061 PMICs.
......@@ -133,7 +141,7 @@ config GPIO_WATCHDOG_ARCH_INITCALL
config MENF21BMC_WATCHDOG
tristate "MEN 14F021P00 BMC Watchdog"
depends on MFD_MENF21BMC
depends on MFD_MENF21BMC || COMPILE_TEST
select WATCHDOG_CORE
help
Say Y here to include support for the MEN 14F021P00 BMC Watchdog.
......@@ -168,7 +176,7 @@ config WDAT_WDT
config WM831X_WATCHDOG
tristate "WM831x watchdog"
depends on MFD_WM831X
depends on MFD_WM831X || COMPILE_TEST
select WATCHDOG_CORE
help
Support for the watchdog in the WM831x AudioPlus PMICs. When
......@@ -209,7 +217,7 @@ config ZIIRAVE_WATCHDOG
config ARM_SP805_WATCHDOG
tristate "ARM SP805 Watchdog"
depends on (ARM || ARM64) && ARM_AMBA
depends on (ARM || ARM64) && (ARM_AMBA || COMPILE_TEST)
select WATCHDOG_CORE
help
ARM Primecell SP805 Watchdog timer. This will reboot your system when
......@@ -237,7 +245,7 @@ config ARM_SBSA_WATCHDOG
config ASM9260_WATCHDOG
tristate "Alphascale ASM9260 watchdog"
depends on MACH_ASM9260
depends on MACH_ASM9260 || COMPILE_TEST
depends on OF
select WATCHDOG_CORE
select RESET_CONTROLLER
......@@ -247,14 +255,14 @@ config ASM9260_WATCHDOG
config AT91RM9200_WATCHDOG
tristate "AT91RM9200 watchdog"
depends on SOC_AT91RM9200 && MFD_SYSCON
depends on (SOC_AT91RM9200 && MFD_SYSCON) || COMPILE_TEST
help
Watchdog timer embedded into AT91RM9200 chips. This will reboot your
system when the timeout is reached.
config AT91SAM9X_WATCHDOG
tristate "AT91SAM9X / AT91CAP9 watchdog"
depends on ARCH_AT91
depends on ARCH_AT91 || COMPILE_TEST
select WATCHDOG_CORE
help
Watchdog timer embedded into AT91SAM9X and AT91CAP9 chips. This will
......@@ -262,7 +270,7 @@ config AT91SAM9X_WATCHDOG
config SAMA5D4_WATCHDOG
tristate "Atmel SAMA5D4 Watchdog Timer"
depends on ARCH_AT91
depends on ARCH_AT91 || COMPILE_TEST
select WATCHDOG_CORE
help
Atmel SAMA5D4 watchdog timer is embedded into SAMA5D4 chips.
......@@ -293,7 +301,7 @@ config 21285_WATCHDOG
config 977_WATCHDOG
tristate "NetWinder WB83C977 watchdog"
depends on FOOTBRIDGE && ARCH_NETWINDER
depends on (FOOTBRIDGE && ARCH_NETWINDER) || (ARM && COMPILE_TEST)
help
Say Y here to include support for the WB977 watchdog included in
NetWinder machines. Alternatively say M to compile the driver as
......@@ -301,6 +309,17 @@ config 977_WATCHDOG
Not sure? It's safe to say N.
config GEMINI_WATCHDOG
tristate "Gemini watchdog"
depends on ARCH_GEMINI
select WATCHDOG_CORE
help
Say Y here if to include support for the watchdog timer
embedded in the Cortina Systems Gemini family of devices.
To compile this driver as a module, choose M here: the
module will be called gemini_wdt.
config IXP4XX_WATCHDOG
tristate "IXP4xx Watchdog"
depends on ARCH_IXP4XX
......@@ -333,9 +352,9 @@ config HAVE_S3C2410_WATCHDOG
config S3C2410_WATCHDOG
tristate "S3C2410 Watchdog"
depends on HAVE_S3C2410_WATCHDOG
depends on HAVE_S3C2410_WATCHDOG || COMPILE_TEST
select WATCHDOG_CORE
select MFD_SYSCON if ARCH_EXYNOS5
select MFD_SYSCON if ARCH_EXYNOS
help
Watchdog timer block in the Samsung SoCs. This will reboot
the system when the timer expires with the watchdog enabled.
......@@ -372,7 +391,7 @@ config DW_WATCHDOG
config EP93XX_WATCHDOG
tristate "EP93xx Watchdog"
depends on ARCH_EP93XX
depends on ARCH_EP93XX || COMPILE_TEST
select WATCHDOG_CORE
help
Say Y here if to include support for the watchdog timer
......@@ -383,7 +402,7 @@ config EP93XX_WATCHDOG
config OMAP_WATCHDOG
tristate "OMAP Watchdog"
depends on ARCH_OMAP16XX || ARCH_OMAP2PLUS
depends on ARCH_OMAP16XX || ARCH_OMAP2PLUS || COMPILE_TEST
select WATCHDOG_CORE
help
Support for TI OMAP1610/OMAP1710/OMAP2420/OMAP3430/OMAP4430 watchdog. Say 'Y'
......@@ -419,7 +438,7 @@ config IOP_WATCHDOG
config DAVINCI_WATCHDOG
tristate "DaVinci watchdog"
depends on ARCH_DAVINCI || ARCH_KEYSTONE
depends on ARCH_DAVINCI || ARCH_KEYSTONE || COMPILE_TEST
select WATCHDOG_CORE
help
Say Y here if to include support for the watchdog timer
......@@ -432,7 +451,7 @@ config DAVINCI_WATCHDOG
config ORION_WATCHDOG
tristate "Orion watchdog"
depends on ARCH_ORION5X || ARCH_DOVE || MACH_DOVE || ARCH_MVEBU
depends on ARCH_ORION5X || ARCH_DOVE || MACH_DOVE || ARCH_MVEBU || COMPILE_TEST
depends on ARM
select WATCHDOG_CORE
help
......@@ -443,7 +462,7 @@ config ORION_WATCHDOG
config RN5T618_WATCHDOG
tristate "Ricoh RN5T618 watchdog"
depends on MFD_RN5T618
depends on MFD_RN5T618 || COMPILE_TEST
select WATCHDOG_CORE
help
If you say yes here you get support for watchdog on the Ricoh
......@@ -454,7 +473,7 @@ config RN5T618_WATCHDOG
config SUNXI_WATCHDOG
tristate "Allwinner SoCs watchdog support"
depends on ARCH_SUNXI
depends on ARCH_SUNXI || COMPILE_TEST
select WATCHDOG_CORE
help
Say Y here to include support for the watchdog timer
......@@ -464,7 +483,7 @@ config SUNXI_WATCHDOG
config COH901327_WATCHDOG
bool "ST-Ericsson COH 901 327 watchdog"
depends on ARCH_U300
depends on ARCH_U300 || (ARM && COMPILE_TEST)
default y if MACH_U300
select WATCHDOG_CORE
help
......@@ -483,7 +502,7 @@ config TWL4030_WATCHDOG
config STMP3XXX_RTC_WATCHDOG
tristate "Freescale STMP3XXX & i.MX23/28 watchdog"
depends on RTC_DRV_STMP
depends on RTC_DRV_STMP || COMPILE_TEST
select WATCHDOG_CORE
help
Say Y here to include support for the watchdog timer inside
......@@ -493,7 +512,7 @@ config STMP3XXX_RTC_WATCHDOG
config NUC900_WATCHDOG
tristate "Nuvoton NUC900 watchdog"
depends on ARCH_W90X900
depends on ARCH_W90X900 || COMPILE_TEST
help
Say Y here if to include support for the watchdog timer
for the Nuvoton NUC900 series SoCs.
......@@ -513,7 +532,7 @@ config TS4800_WATCHDOG
config TS72XX_WATCHDOG
tristate "TS-72XX SBC Watchdog"
depends on MACH_TS72XX
depends on MACH_TS72XX || COMPILE_TEST
help
Technologic Systems TS-7200, TS-7250 and TS-7260 boards have
watchdog timer implemented in a external CPLD chip. Say Y here
......@@ -531,7 +550,7 @@ config MAX63XX_WATCHDOG
config MAX77620_WATCHDOG
tristate "Maxim Max77620 Watchdog Timer"
depends on MFD_MAX77620
depends on MFD_MAX77620 || COMPILE_TEST
help
This is the driver for the Max77620 watchdog timer.
Say 'Y' here to enable the watchdog timer support for
......@@ -540,7 +559,7 @@ config MAX77620_WATCHDOG
config IMX2_WDT
tristate "IMX2+ Watchdog"
depends on ARCH_MXC || ARCH_LAYERSCAPE
depends on ARCH_MXC || ARCH_LAYERSCAPE || COMPILE_TEST
select REGMAP_MMIO
select WATCHDOG_CORE
help
......@@ -554,7 +573,7 @@ config IMX2_WDT
config UX500_WATCHDOG
tristate "ST-Ericsson Ux500 watchdog"
depends on MFD_DB8500_PRCMU
depends on MFD_DB8500_PRCMU || (ARM && COMPILE_TEST)
select WATCHDOG_CORE
default y
help
......@@ -566,7 +585,7 @@ config UX500_WATCHDOG
config RETU_WATCHDOG
tristate "Retu watchdog"
depends on MFD_RETU
depends on MFD_RETU || COMPILE_TEST
select WATCHDOG_CORE
help
Retu watchdog driver for Nokia Internet Tablets (770, N800,
......@@ -578,7 +597,7 @@ config RETU_WATCHDOG
config MOXART_WDT
tristate "MOXART watchdog"
depends on ARCH_MOXART
depends on ARCH_MOXART || COMPILE_TEST
help
Say Y here to include Watchdog timer support for the watchdog
existing on the MOXA ART SoC series platforms.
......@@ -588,7 +607,7 @@ config MOXART_WDT
config SIRFSOC_WATCHDOG
tristate "SiRFSOC watchdog"
depends on ARCH_SIRF
depends on ARCH_SIRF || COMPILE_TEST
select WATCHDOG_CORE
default y
help
......@@ -597,7 +616,7 @@ config SIRFSOC_WATCHDOG
config ST_LPC_WATCHDOG
tristate "STMicroelectronics LPC Watchdog"
depends on ARCH_STI
depends on ARCH_STI || COMPILE_TEST
depends on OF
select WATCHDOG_CORE
help
......@@ -621,7 +640,7 @@ config TEGRA_WATCHDOG
config QCOM_WDT
tristate "QCOM watchdog"
depends on HAS_IOMEM
depends on ARCH_QCOM
depends on ARCH_QCOM || COMPILE_TEST
select WATCHDOG_CORE
help
Say Y here to include Watchdog timer support for the watchdog found
......@@ -633,7 +652,7 @@ config QCOM_WDT
config MESON_GXBB_WATCHDOG
tristate "Amlogic Meson GXBB SoCs watchdog support"
depends on ARCH_MESON
depends on ARCH_MESON || COMPILE_TEST
select WATCHDOG_CORE
help
Say Y here to include support for the watchdog timer
......@@ -643,7 +662,7 @@ config MESON_GXBB_WATCHDOG
config MESON_WATCHDOG
tristate "Amlogic Meson SoCs watchdog support"
depends on ARCH_MESON
depends on ARCH_MESON || COMPILE_TEST
select WATCHDOG_CORE
help
Say Y here to include support for the watchdog timer
......@@ -653,7 +672,7 @@ config MESON_WATCHDOG
config MEDIATEK_WATCHDOG
tristate "Mediatek SoCs watchdog support"
depends on ARCH_MEDIATEK
depends on ARCH_MEDIATEK || COMPILE_TEST
select WATCHDOG_CORE
help
Say Y here to include support for the watchdog timer
......@@ -663,7 +682,7 @@ config MEDIATEK_WATCHDOG
config DIGICOLOR_WATCHDOG
tristate "Conexant Digicolor SoCs watchdog support"
depends on ARCH_DIGICOLOR
depends on ARCH_DIGICOLOR || COMPILE_TEST
select WATCHDOG_CORE
help
Say Y here to include support for the watchdog timer
......@@ -685,7 +704,7 @@ config LPC18XX_WATCHDOG
config ATLAS7_WATCHDOG
tristate "CSRatlas7 watchdog"
depends on ARCH_ATLAS7
depends on ARCH_ATLAS7 || COMPILE_TEST
help
Say Y here to include Watchdog timer support for the watchdog
existing on the CSRatlas7 series platforms.
......@@ -714,11 +733,21 @@ config ASPEED_WATCHDOG
To compile this driver as a module, choose M here: the
module will be called aspeed_wdt.
config ZX2967_WATCHDOG
tristate "ZTE zx2967 SoCs watchdog support"
depends on ARCH_ZX
select WATCHDOG_CORE
help
Say Y here to include support for the watchdog timer
in ZTE zx2967 SoCs.
To compile this driver as a module, choose M here: the
module will be called zx2967_wdt.
# AVR32 Architecture
config AT32AP700X_WDT
tristate "AT32AP700x watchdog"
depends on CPU_AT32AP700X
depends on CPU_AT32AP700X || COMPILE_TEST
help
Watchdog timer embedded into AT32AP700x devices. This will reboot
your system when the timeout is reached.
......@@ -822,7 +851,7 @@ config SP5100_TCO
config GEODE_WDT
tristate "AMD Geode CS5535/CS5536 Watchdog"
depends on CS5535_MFGPT
depends on CS5535_MFGPT || (X86 && COMPILE_TEST)
help
This driver enables a watchdog capability built into the
CS5535/CS5536 companion chips for the AMD Geode GX and LX
......@@ -835,7 +864,7 @@ config GEODE_WDT
config SC520_WDT
tristate "AMD Elan SC520 processor Watchdog"
depends on MELAN
depends on MELAN || COMPILE_TEST
help
This is the driver for the hardware watchdog built in to the
AMD "Elan" SC520 microcomputer commonly used in embedded systems.
......@@ -1034,7 +1063,7 @@ config HP_WATCHDOG
config KEMPLD_WDT
tristate "Kontron COM Watchdog Timer"
depends on MFD_KEMPLD
depends on MFD_KEMPLD || COMPILE_TEST
select WATCHDOG_CORE
help
Support for the PLD watchdog on some Kontron ETX and COMexpress
......@@ -1108,7 +1137,8 @@ config NV_TCO
config RDC321X_WDT
tristate "RDC R-321x SoC watchdog"
depends on X86_RDC321X
depends on X86_RDC321X || COMPILE_TEST
depends on PCI
help
This is the driver for the built in hardware watchdog
in the RDC R-321x SoC.
......@@ -1326,6 +1356,16 @@ config NI903X_WDT
To compile this driver as a module, choose M here: the module will be
called ni903x_wdt.
config NIC7018_WDT
tristate "NIC7018 Watchdog"
depends on X86 && ACPI
select WATCHDOG_CORE
---help---
Support for National Instruments NIC7018 Watchdog.
To compile this driver as a module, choose M here: the module will be
called nic7018_wdt.
# M32R Architecture
# M68K Architecture
......@@ -1343,14 +1383,14 @@ config M54xx_WATCHDOG
config ATH79_WDT
tristate "Atheros AR71XX/AR724X/AR913X hardware watchdog"
depends on ATH79
depends on ATH79 || (ARM && COMPILE_TEST)
help
Hardware driver for the built-in watchdog timer on the Atheros
AR71XX/AR724X/AR913X SoCs.
config BCM47XX_WDT
tristate "Broadcom BCM47xx Watchdog Timer"
depends on BCM47XX || ARCH_BCM_5301X
depends on BCM47XX || ARCH_BCM_5301X || COMPILE_TEST
select WATCHDOG_CORE
help
Hardware driver for the Broadcom BCM47xx Watchdog Timer.
......@@ -1367,7 +1407,7 @@ config RC32434_WDT
config INDYDOG
tristate "Indy/I2 Hardware Watchdog"
depends on SGI_HAS_INDYDOG
depends on SGI_HAS_INDYDOG || (MIPS && COMPILE_TEST)
help
Hardware driver for the Indy's/I2's watchdog. This is a
watchdog timer that will reboot the machine after a 60 second
......@@ -1383,7 +1423,7 @@ config JZ4740_WDT
config WDT_MTX1
tristate "MTX-1 Hardware Watchdog"
depends on MIPS_MTX1
depends on MIPS_MTX1 || (MIPS && COMPILE_TEST)
help
Hardware driver for the MTX-1 boards. This is a watchdog timer that
will reboot the machine after a 100 seconds timer expired.
......@@ -1391,6 +1431,7 @@ config WDT_MTX1
config PNX833X_WDT
tristate "PNX833x Hardware Watchdog"
depends on SOC_PNX8335
depends on BROKEN
help
Hardware driver for the PNX833x's watchdog. This is a
watchdog timer that will reboot the machine after a programmable
......@@ -1399,7 +1440,7 @@ config PNX833X_WDT
config SIBYTE_WDOG
tristate "Sibyte SoC hardware watchdog"
depends on CPU_SB1
depends on CPU_SB1 || (MIPS && COMPILE_TEST)
help
Watchdog driver for the built in watchdog hardware in Sibyte
SoC processors. There are apparently two watchdog timers
......@@ -1412,13 +1453,13 @@ config SIBYTE_WDOG
config AR7_WDT
tristate "TI AR7 Watchdog Timer"
depends on AR7
depends on AR7 || (MIPS && COMPILE_TEST)
help
Hardware driver for the TI AR7 Watchdog Timer.
config TXX9_WDT
tristate "Toshiba TXx9 Watchdog Timer"
depends on CPU_TX39XX || CPU_TX49XX
depends on CPU_TX39XX || CPU_TX49XX || (MIPS && COMPILE_TEST)
select WATCHDOG_CORE
help
Hardware driver for the built-in watchdog timer on TXx9 MIPS SoCs.
......@@ -1454,7 +1495,7 @@ config BCM63XX_WDT
config BCM2835_WDT
tristate "Broadcom BCM2835 hardware watchdog"
depends on ARCH_BCM2835
depends on ARCH_BCM2835 || COMPILE_TEST
select WATCHDOG_CORE
help
Watchdog driver for the built in watchdog hardware in Broadcom
......@@ -1465,7 +1506,7 @@ config BCM2835_WDT
config BCM_KONA_WDT
tristate "BCM Kona Watchdog"
depends on ARCH_BCM_MOBILE
depends on ARCH_BCM_MOBILE || COMPILE_TEST
select WATCHDOG_CORE
help
Support for the watchdog timer on the following Broadcom BCM281xx
......@@ -1477,7 +1518,7 @@ config BCM_KONA_WDT
config BCM_KONA_WDT_DEBUG
bool "DEBUGFS support for BCM Kona Watchdog"
depends on BCM_KONA_WDT
depends on BCM_KONA_WDT || COMPILE_TEST
help
If enabled, adds /sys/kernel/debug/bcm_kona_wdt/info which provides
access to the driver's internal data structures as well as watchdog
......@@ -1538,7 +1579,7 @@ config MT7621_WDT
config PIC32_WDT
tristate "Microchip PIC32 hardware watchdog"
select WATCHDOG_CORE
depends on MACH_PIC32
depends on MACH_PIC32 || (MIPS && COMPILE_TEST)
help
Watchdog driver for the built in watchdog hardware in a PIC32.
......@@ -1551,7 +1592,7 @@ config PIC32_WDT
config PIC32_DMT
tristate "Microchip PIC32 Deadman Timer"
select WATCHDOG_CORE
depends on MACH_PIC32
depends on MACH_PIC32 || (MIPS && COMPILE_TEST)
help
Watchdog driver for PIC32 instruction fetch counting timer. This specific
timer is typically be used in misson critical and safety critical
......@@ -1573,7 +1614,7 @@ config GEF_WDT
config MPC5200_WDT
bool "MPC52xx Watchdog Timer"
depends on PPC_MPC52xx
depends on PPC_MPC52xx || COMPILE_TEST
help
Use General Purpose Timer (GPT) 0 on the MPC5200 as Watchdog.
......@@ -1592,11 +1633,11 @@ config 8xxx_WDT
config MV64X60_WDT
tristate "MV64X60 (Marvell Discovery) Watchdog Timer"
depends on MV64X60
depends on MV64X60 || COMPILE_TEST
config PIKA_WDT
tristate "PIKA FPGA Watchdog"
depends on WARP
depends on WARP || (PPC64 && COMPILE_TEST)
default y
help
This enables the watchdog in the PIKA FPGA. Currently used on
......@@ -1646,7 +1687,7 @@ config MEN_A21_WDT
config WATCHDOG_RTAS
tristate "RTAS watchdog"
depends on PPC_RTAS
depends on PPC_RTAS || (PPC64 && COMPILE_TEST)
help
This driver adds watchdog support for the RTAS watchdog.
......@@ -1674,7 +1715,7 @@ config DIAG288_WATCHDOG
config SH_WDT
tristate "SuperH Watchdog"
depends on SUPERH && (CPU_SH3 || CPU_SH4)
depends on SUPERH && (CPU_SH3 || CPU_SH4 || COMPILE_TEST)
select WATCHDOG_CORE
help
This driver adds watchdog support for the integrated watchdog in the
......@@ -1741,7 +1782,7 @@ config XEN_WDT
config UML_WATCHDOG
tristate "UML watchdog"
depends on UML
depends on UML || COMPILE_TEST
#
# ISA-based Watchdog Cards
......
......@@ -45,6 +45,7 @@ obj-$(CONFIG_OMAP_WATCHDOG) += omap_wdt.o
obj-$(CONFIG_TWL4030_WATCHDOG) += twl4030_wdt.o
obj-$(CONFIG_21285_WATCHDOG) += wdt285.o
obj-$(CONFIG_977_WATCHDOG) += wdt977.o
obj-$(CONFIG_GEMINI_WATCHDOG) += gemini_wdt.o
obj-$(CONFIG_IXP4XX_WATCHDOG) += ixp4xx_wdt.o
obj-$(CONFIG_KS8695_WATCHDOG) += ks8695_wdt.o
obj-$(CONFIG_S3C2410_WATCHDOG) += s3c2410_wdt.o
......@@ -82,6 +83,7 @@ obj-$(CONFIG_BCM7038_WDT) += bcm7038_wdt.o
obj-$(CONFIG_ATLAS7_WATCHDOG) += atlas7_wdt.o
obj-$(CONFIG_RENESAS_WDT) += renesas_wdt.o
obj-$(CONFIG_ASPEED_WATCHDOG) += aspeed_wdt.o
obj-$(CONFIG_ZX2967_WATCHDOG) += zx2967_wdt.o
# AVR32 Architecture
obj-$(CONFIG_AT32AP700X_WDT) += at32ap700x_wdt.o
......@@ -139,6 +141,7 @@ obj-$(CONFIG_INTEL_SCU_WATCHDOG) += intel_scu_watchdog.o
obj-$(CONFIG_INTEL_MID_WATCHDOG) += intel-mid_wdt.o
obj-$(CONFIG_INTEL_MEI_WDT) += mei_wdt.o
obj-$(CONFIG_NI903X_WDT) += ni903x_wdt.o
obj-$(CONFIG_NIC7018_WDT) += nic7018_wdt.o
# M32R Architecture
......
......@@ -14,7 +14,6 @@
#include <linux/module.h>
#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/reboot.h>
#include <linux/reset.h>
#include <linux/watchdog.h>
......@@ -59,7 +58,6 @@ struct asm9260_wdt_priv {
struct clk *clk;
struct clk *clk_ahb;
struct reset_control *rst;
struct notifier_block restart_handler;
void __iomem *iobase;
int irq;
......@@ -172,15 +170,14 @@ static irqreturn_t asm9260_wdt_irq(int irq, void *devid)
return IRQ_HANDLED;
}
static int asm9260_restart_handler(struct notifier_block *this,
unsigned long mode, void *cmd)
static int asm9260_restart(struct watchdog_device *wdd, unsigned long action,
void *data)
{
struct asm9260_wdt_priv *priv =
container_of(this, struct asm9260_wdt_priv, restart_handler);
struct asm9260_wdt_priv *priv = watchdog_get_drvdata(wdd);
asm9260_wdt_sys_reset(priv);
return NOTIFY_DONE;
return 0;
}
static const struct watchdog_info asm9260_wdt_ident = {
......@@ -189,13 +186,14 @@ static const struct watchdog_info asm9260_wdt_ident = {
.identity = "Alphascale asm9260 Watchdog",
};
static struct watchdog_ops asm9260_wdt_ops = {
static const struct watchdog_ops asm9260_wdt_ops = {
.owner = THIS_MODULE,
.start = asm9260_wdt_enable,
.stop = asm9260_wdt_disable,
.get_timeleft = asm9260_wdt_gettimeleft,
.ping = asm9260_wdt_feed,
.set_timeout = asm9260_wdt_settimeout,
.restart = asm9260_restart,
};
static int asm9260_wdt_get_dt_clks(struct asm9260_wdt_priv *priv)
......@@ -335,18 +333,14 @@ static int asm9260_wdt_probe(struct platform_device *pdev)
dev_warn(&pdev->dev, "failed to request IRQ\n");
}
watchdog_set_restart_priority(wdd, 128);
ret = watchdog_register_device(wdd);
if (ret)
goto clk_off;
platform_set_drvdata(pdev, priv);
priv->restart_handler.notifier_call = asm9260_restart_handler;
priv->restart_handler.priority = 128;
ret = register_restart_handler(&priv->restart_handler);
if (ret)
dev_warn(&pdev->dev, "cannot register restart handler\n");
dev_info(&pdev->dev, "Watchdog enabled (timeout: %d sec, mode: %s)\n",
wdd->timeout, mode_name[priv->mode]);
return 0;
......@@ -370,8 +364,6 @@ static int asm9260_wdt_remove(struct platform_device *pdev)
asm9260_wdt_disable(&priv->wdd);
unregister_restart_handler(&priv->restart_handler);
watchdog_unregister_device(&priv->wdd);
clk_disable_unprepare(priv->clk);
......
......@@ -136,15 +136,6 @@ static const struct watchdog_info aspeed_wdt_info = {
.identity = KBUILD_MODNAME,
};
static int aspeed_wdt_remove(struct platform_device *pdev)
{
struct aspeed_wdt *wdt = platform_get_drvdata(pdev);
watchdog_unregister_device(&wdt->wdd);
return 0;
}
static int aspeed_wdt_probe(struct platform_device *pdev)
{
struct aspeed_wdt *wdt;
......@@ -187,20 +178,17 @@ static int aspeed_wdt_probe(struct platform_device *pdev)
set_bit(WDOG_HW_RUNNING, &wdt->wdd.status);
}
ret = watchdog_register_device(&wdt->wdd);
ret = devm_watchdog_register_device(&pdev->dev, &wdt->wdd);
if (ret) {
dev_err(&pdev->dev, "failed to register\n");
return ret;
}
platform_set_drvdata(pdev, wdt);
return 0;
}
static struct platform_driver aspeed_watchdog_driver = {
.probe = aspeed_wdt_probe,
.remove = aspeed_wdt_remove,
.driver = {
.name = KBUILD_MODNAME,
.of_match_table = of_match_ptr(aspeed_wdt_of_table),
......
......@@ -105,7 +105,7 @@ static const struct watchdog_info atlas7_wdt_ident = {
.identity = "atlas7 Watchdog",
};
static struct watchdog_ops atlas7_wdt_ops = {
static const struct watchdog_ops atlas7_wdt_ops = {
.owner = THIS_MODULE,
.start = atlas7_wdt_enable,
.stop = atlas7_wdt_disable,
......
......@@ -14,7 +14,6 @@
*/
#include <linux/delay.h>
#include <linux/reboot.h>
#include <linux/types.h>
#include <linux/module.h>
#include <linux/io.h>
......@@ -49,7 +48,6 @@
struct bcm2835_wdt {
void __iomem *base;
spinlock_t lock;
struct notifier_block restart_handler;
};
static unsigned int heartbeat;
......@@ -99,11 +97,37 @@ static unsigned int bcm2835_wdt_get_timeleft(struct watchdog_device *wdog)
return WDOG_TICKS_TO_SECS(ret & PM_WDOG_TIME_SET);
}
static void __bcm2835_restart(struct bcm2835_wdt *wdt)
{
u32 val;
/* use a timeout of 10 ticks (~150us) */
writel_relaxed(10 | PM_PASSWORD, wdt->base + PM_WDOG);
val = readl_relaxed(wdt->base + PM_RSTC);
val &= PM_RSTC_WRCFG_CLR;
val |= PM_PASSWORD | PM_RSTC_WRCFG_FULL_RESET;
writel_relaxed(val, wdt->base + PM_RSTC);
/* No sleeping, possibly atomic. */
mdelay(1);
}
static int bcm2835_restart(struct watchdog_device *wdog,
unsigned long action, void *data)
{
struct bcm2835_wdt *wdt = watchdog_get_drvdata(wdog);
__bcm2835_restart(wdt);
return 0;
}
static const struct watchdog_ops bcm2835_wdt_ops = {
.owner = THIS_MODULE,
.start = bcm2835_wdt_start,
.stop = bcm2835_wdt_stop,
.get_timeleft = bcm2835_wdt_get_timeleft,
.restart = bcm2835_restart,
};
static const struct watchdog_info bcm2835_wdt_info = {
......@@ -120,26 +144,6 @@ static struct watchdog_device bcm2835_wdt_wdd = {
.timeout = WDOG_TICKS_TO_SECS(PM_WDOG_TIME_SET),
};
static int
bcm2835_restart(struct notifier_block *this, unsigned long mode, void *cmd)
{
struct bcm2835_wdt *wdt = container_of(this, struct bcm2835_wdt,
restart_handler);
u32 val;
/* use a timeout of 10 ticks (~150us) */
writel_relaxed(10 | PM_PASSWORD, wdt->base + PM_WDOG);
val = readl_relaxed(wdt->base + PM_RSTC);
val &= PM_RSTC_WRCFG_CLR;
val |= PM_PASSWORD | PM_RSTC_WRCFG_FULL_RESET;
writel_relaxed(val, wdt->base + PM_RSTC);
/* No sleeping, possibly atomic. */
mdelay(1);
return 0;
}
/*
* We can't really power off, but if we do the normal reset scheme, and
* indicate to bootcode.bin not to reboot, then most of the chip will be
......@@ -163,13 +167,13 @@ static void bcm2835_power_off(void)
writel_relaxed(val, wdt->base + PM_RSTS);
/* Continue with normal reset mechanism */
bcm2835_restart(&wdt->restart_handler, REBOOT_HARD, NULL);
__bcm2835_restart(wdt);
}
static int bcm2835_wdt_probe(struct platform_device *pdev)
{
struct resource *res;
struct device *dev = &pdev->dev;
struct device_node *np = dev->of_node;
struct bcm2835_wdt *wdt;
int err;
......@@ -180,16 +184,15 @@ static int bcm2835_wdt_probe(struct platform_device *pdev)
spin_lock_init(&wdt->lock);
wdt->base = of_iomap(np, 0);
if (!wdt->base) {
dev_err(dev, "Failed to remap watchdog regs");
return -ENODEV;
}
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
wdt->base = devm_ioremap_resource(dev, res);
if (IS_ERR(wdt->base))
return PTR_ERR(wdt->base);
watchdog_set_drvdata(&bcm2835_wdt_wdd, wdt);
watchdog_init_timeout(&bcm2835_wdt_wdd, heartbeat, dev);
watchdog_set_nowayout(&bcm2835_wdt_wdd, nowayout);
bcm2835_wdt_wdd.parent = &pdev->dev;
bcm2835_wdt_wdd.parent = dev;
if (bcm2835_wdt_is_running(wdt)) {
/*
* The currently active timeout value (set by the
......@@ -201,16 +204,16 @@ static int bcm2835_wdt_probe(struct platform_device *pdev)
*/
set_bit(WDOG_HW_RUNNING, &bcm2835_wdt_wdd.status);
}
err = watchdog_register_device(&bcm2835_wdt_wdd);
watchdog_set_restart_priority(&bcm2835_wdt_wdd, 128);
watchdog_stop_on_reboot(&bcm2835_wdt_wdd);
err = devm_watchdog_register_device(dev, &bcm2835_wdt_wdd);
if (err) {
dev_err(dev, "Failed to register watchdog device");
iounmap(wdt->base);
return err;
}
wdt->restart_handler.notifier_call = bcm2835_restart;
wdt->restart_handler.priority = 128;
register_restart_handler(&wdt->restart_handler);
if (pm_power_off == NULL)
pm_power_off = bcm2835_power_off;
......@@ -220,22 +223,12 @@ static int bcm2835_wdt_probe(struct platform_device *pdev)
static int bcm2835_wdt_remove(struct platform_device *pdev)
{
struct bcm2835_wdt *wdt = platform_get_drvdata(pdev);
unregister_restart_handler(&wdt->restart_handler);
if (pm_power_off == bcm2835_power_off)
pm_power_off = NULL;
watchdog_unregister_device(&bcm2835_wdt_wdd);
iounmap(wdt->base);
return 0;
}
static void bcm2835_wdt_shutdown(struct platform_device *pdev)
{
bcm2835_wdt_stop(&bcm2835_wdt_wdd);
}
static const struct of_device_id bcm2835_wdt_of_match[] = {
{ .compatible = "brcm,bcm2835-pm-wdt", },
{},
......@@ -245,7 +238,6 @@ MODULE_DEVICE_TABLE(of, bcm2835_wdt_of_match);
static struct platform_driver bcm2835_wdt_driver = {
.probe = bcm2835_wdt_probe,
.remove = bcm2835_wdt_remove,
.shutdown = bcm2835_wdt_shutdown,
.driver = {
.name = "bcm2835-wdt",
.of_match_table = bcm2835_wdt_of_match,
......
......@@ -226,9 +226,6 @@ static int bcm47xx_wdt_remove(struct platform_device *pdev)
{
struct bcm47xx_wdt *wdt = dev_get_platdata(&pdev->dev);
if (!wdt)
return -ENXIO;
watchdog_unregister_device(&wdt->wdd);
return 0;
......
......@@ -101,7 +101,7 @@ static unsigned int bcm7038_wdt_get_timeleft(struct watchdog_device *wdog)
return time_left / wdt->rate;
}
static struct watchdog_info bcm7038_wdt_info = {
static const struct watchdog_info bcm7038_wdt_info = {
.identity = "Broadcom BCM7038 Watchdog Timer",
.options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING |
WDIOF_MAGICCLOSE
......
......@@ -266,7 +266,7 @@ static int bcm_kona_wdt_stop(struct watchdog_device *wdog)
SECWDOG_SRSTEN_MASK, 0);
}
static struct watchdog_ops bcm_kona_wdt_ops = {
static const struct watchdog_ops bcm_kona_wdt_ops = {
.owner = THIS_MODULE,
.start = bcm_kona_wdt_start,
.stop = bcm_kona_wdt_stop,
......@@ -274,7 +274,7 @@ static struct watchdog_ops bcm_kona_wdt_ops = {
.get_timeleft = bcm_kona_wdt_get_timeleft,
};
static struct watchdog_info bcm_kona_wdt_info = {
static const struct watchdog_info bcm_kona_wdt_info = {
.options = WDIOF_SETTIMEOUT | WDIOF_MAGICCLOSE |
WDIOF_KEEPALIVEPING,
.identity = "Broadcom Kona Watchdog Timer",
......
......@@ -192,12 +192,12 @@ static int booke_wdt_set_timeout(struct watchdog_device *wdt_dev,
return 0;
}
static struct watchdog_info booke_wdt_info = {
static struct watchdog_info booke_wdt_info __ro_after_init = {
.options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING,
.identity = "PowerPC Book-E Watchdog",
};
static struct watchdog_ops booke_wdt_ops = {
static const struct watchdog_ops booke_wdt_ops = {
.owner = THIS_MODULE,
.start = booke_wdt_start,
.stop = booke_wdt_stop,
......
......@@ -262,7 +262,7 @@ static irqreturn_t cdns_wdt_irq_handler(int irq, void *dev_id)
* Info structure used to indicate the features supported by the device
* to the upper layers. This is defined in watchdog.h header file.
*/
static struct watchdog_info cdns_wdt_info = {
static const struct watchdog_info cdns_wdt_info = {
.identity = "cdns_wdt watchdog",
.options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING |
WDIOF_MAGICCLOSE,
......
......@@ -68,17 +68,10 @@
/* Default timeout in seconds = 1 minute */
static unsigned int margin = 60;
static resource_size_t phybase;
static resource_size_t physize;
static int irq;
static void __iomem *virtbase;
static struct device *parent;
/*
* The watchdog block is of course always clocked, the
* clk_enable()/clk_disable() calls are mainly for performing reference
* counting higher up in the clock hierarchy.
*/
static struct clk *clk;
/*
......@@ -90,7 +83,6 @@ static void coh901327_enable(u16 timeout)
unsigned long freq;
unsigned long delay_ns;
clk_enable(clk);
/* Restart timer if it is disabled */
val = readw(virtbase + U300_WDOG_D2R);
if (val == U300_WDOG_D2R_DISABLE_STATUS_DISABLED)
......@@ -118,7 +110,6 @@ static void coh901327_enable(u16 timeout)
*/
(void) readw(virtbase + U300_WDOG_CR);
val = readw(virtbase + U300_WDOG_D2R);
clk_disable(clk);
if (val != U300_WDOG_D2R_DISABLE_STATUS_ENABLED)
dev_err(parent,
"%s(): watchdog not enabled! D2R value %04x\n",
......@@ -129,7 +120,6 @@ static void coh901327_disable(void)
{
u16 val;
clk_enable(clk);
/* Disable the watchdog interrupt if it is active */
writew(0x0000U, virtbase + U300_WDOG_IMR);
/* If the watchdog is currently enabled, attempt to disable it */
......@@ -144,7 +134,6 @@ static void coh901327_disable(void)
virtbase + U300_WDOG_D2R);
}
val = readw(virtbase + U300_WDOG_D2R);
clk_disable(clk);
if (val != U300_WDOG_D2R_DISABLE_STATUS_DISABLED)
dev_err(parent,
"%s(): watchdog not disabled! D2R value %04x\n",
......@@ -165,11 +154,9 @@ static int coh901327_stop(struct watchdog_device *wdt_dev)
static int coh901327_ping(struct watchdog_device *wdd)
{
clk_enable(clk);
/* Feed the watchdog */
writew(U300_WDOG_FR_FEED_RESTART_TIMER,
virtbase + U300_WDOG_FR);
clk_disable(clk);
return 0;
}
......@@ -177,13 +164,11 @@ static int coh901327_settimeout(struct watchdog_device *wdt_dev,
unsigned int time)
{
wdt_dev->timeout = time;
clk_enable(clk);
/* Set new timeout value */
writew(time * 100, virtbase + U300_WDOG_TR);
/* Feed the dog */
writew(U300_WDOG_FR_FEED_RESTART_TIMER,
virtbase + U300_WDOG_FR);
clk_disable(clk);
return 0;
}
......@@ -191,13 +176,11 @@ static unsigned int coh901327_gettimeleft(struct watchdog_device *wdt_dev)
{
u16 val;
clk_enable(clk);
/* Read repeatedly until the value is stable! */
val = readw(virtbase + U300_WDOG_CR);
while (val & U300_WDOG_CR_VALID_IND)
val = readw(virtbase + U300_WDOG_CR);
val &= U300_WDOG_CR_COUNT_VALUE_MASK;
clk_disable(clk);
if (val != 0)
val /= 100;
......@@ -221,13 +204,11 @@ static irqreturn_t coh901327_interrupt(int irq, void *data)
* to prevent a watchdog reset by feeding the watchdog at this
* point.
*/
clk_enable(clk);
val = readw(virtbase + U300_WDOG_IER);
if (val == U300_WDOG_IER_WILL_BARK_IRQ_EVENT_IND)
writew(U300_WDOG_IER_WILL_BARK_IRQ_ACK_ENABLE,
virtbase + U300_WDOG_IER);
writew(0x0000U, virtbase + U300_WDOG_IMR);
clk_disable(clk);
dev_crit(parent, "watchdog is barking!\n");
return IRQ_HANDLED;
}
......@@ -263,81 +244,63 @@ static int __exit coh901327_remove(struct platform_device *pdev)
watchdog_unregister_device(&coh901327_wdt);
coh901327_disable();
free_irq(irq, pdev);
clk_unprepare(clk);
clk_disable_unprepare(clk);
clk_put(clk);
iounmap(virtbase);
release_mem_region(phybase, physize);
return 0;
}
static int __init coh901327_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
int ret;
u16 val;
struct resource *res;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!res)
return -ENOENT;
parent = &pdev->dev;
physize = resource_size(res);
phybase = res->start;
parent = dev;
if (request_mem_region(phybase, physize, DRV_NAME) == NULL) {
ret = -EBUSY;
goto out;
}
virtbase = ioremap(phybase, physize);
if (!virtbase) {
ret = -ENOMEM;
goto out_no_remap;
}
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
virtbase = devm_ioremap_resource(dev, res);
if (IS_ERR(virtbase))
return PTR_ERR(virtbase);
clk = clk_get(&pdev->dev, NULL);
clk = clk_get(dev, NULL);
if (IS_ERR(clk)) {
ret = PTR_ERR(clk);
dev_err(&pdev->dev, "could not get clock\n");
goto out_no_clk;
dev_err(dev, "could not get clock\n");
return ret;
}
ret = clk_prepare_enable(clk);
if (ret) {
dev_err(&pdev->dev, "could not prepare and enable clock\n");
dev_err(dev, "could not prepare and enable clock\n");
goto out_no_clk_enable;
}
val = readw(virtbase + U300_WDOG_SR);
switch (val) {
case U300_WDOG_SR_STATUS_TIMED_OUT:
dev_info(&pdev->dev,
"watchdog timed out since last chip reset!\n");
dev_info(dev, "watchdog timed out since last chip reset!\n");
coh901327_wdt.bootstatus |= WDIOF_CARDRESET;
/* Status will be cleared below */
break;
case U300_WDOG_SR_STATUS_NORMAL:
dev_info(&pdev->dev,
"in normal status, no timeouts have occurred.\n");
dev_info(dev, "in normal status, no timeouts have occurred.\n");
break;
default:
dev_info(&pdev->dev,
"contains an illegal status code (%08x)\n", val);
dev_info(dev, "contains an illegal status code (%08x)\n", val);
break;
}
val = readw(virtbase + U300_WDOG_D2R);
switch (val) {
case U300_WDOG_D2R_DISABLE_STATUS_DISABLED:
dev_info(&pdev->dev, "currently disabled.\n");
dev_info(dev, "currently disabled.\n");
break;
case U300_WDOG_D2R_DISABLE_STATUS_ENABLED:
dev_info(&pdev->dev,
"currently enabled! (disabling it now)\n");
dev_info(dev, "currently enabled! (disabling it now)\n");
coh901327_disable();
break;
default:
dev_err(&pdev->dev,
"contains an illegal enable/disable code (%08x)\n",
dev_err(dev, "contains an illegal enable/disable code (%08x)\n",
val);
break;
}
......@@ -352,20 +315,16 @@ static int __init coh901327_probe(struct platform_device *pdev)
goto out_no_irq;
}
clk_disable(clk);
ret = watchdog_init_timeout(&coh901327_wdt, margin, &pdev->dev);
ret = watchdog_init_timeout(&coh901327_wdt, margin, dev);
if (ret < 0)
coh901327_wdt.timeout = 60;
coh901327_wdt.parent = &pdev->dev;
coh901327_wdt.parent = dev;
ret = watchdog_register_device(&coh901327_wdt);
if (ret == 0)
dev_info(&pdev->dev,
"initialized. timer margin=%d sec\n", margin);
else
if (ret)
goto out_no_wdog;
dev_info(dev, "initialized. timer margin=%d sec\n", margin);
return 0;
out_no_wdog:
......@@ -374,11 +333,6 @@ static int __init coh901327_probe(struct platform_device *pdev)
clk_disable_unprepare(clk);
out_no_clk_enable:
clk_put(clk);
out_no_clk:
iounmap(virtbase);
out_no_remap:
release_mem_region(phybase, SZ_4K);
out:
return ret;
}
......
......@@ -128,19 +128,17 @@ static int da9052_wdt_ping(struct watchdog_device *wdt_dev)
ret = da9052_reg_update(da9052, DA9052_CONTROL_D_REG,
DA9052_CONTROLD_WATCHDOG, 1 << 7);
if (ret < 0)
goto err_strobe;
return ret;
/*
* FIXME: Reset the watchdog core, in general PMIC
* is supposed to do this
*/
ret = da9052_reg_update(da9052, DA9052_CONTROL_D_REG,
DA9052_CONTROLD_WATCHDOG, 0 << 7);
err_strobe:
return ret;
return da9052_reg_update(da9052, DA9052_CONTROL_D_REG,
DA9052_CONTROLD_WATCHDOG, 0 << 7);
}
static struct watchdog_info da9052_wdt_info = {
static const struct watchdog_info da9052_wdt_info = {
.options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING,
.identity = "DA9052 Watchdog",
};
......@@ -163,10 +161,8 @@ static int da9052_wdt_probe(struct platform_device *pdev)
driver_data = devm_kzalloc(&pdev->dev, sizeof(*driver_data),
GFP_KERNEL);
if (!driver_data) {
ret = -ENOMEM;
goto err;
}
if (!driver_data)
return -ENOMEM;
driver_data->da9052 = da9052;
da9052_wdt = &driver_data->wdt;
......@@ -182,33 +178,21 @@ static int da9052_wdt_probe(struct platform_device *pdev)
if (ret < 0) {
dev_err(&pdev->dev, "Failed to disable watchdog bits, %d\n",
ret);
goto err;
return ret;
}
ret = watchdog_register_device(&driver_data->wdt);
ret = devm_watchdog_register_device(&pdev->dev, &driver_data->wdt);
if (ret != 0) {
dev_err(da9052->dev, "watchdog_register_device() failed: %d\n",
ret);
goto err;
return ret;
}
platform_set_drvdata(pdev, driver_data);
err:
return ret;
}
static int da9052_wdt_remove(struct platform_device *pdev)
{
struct da9052_wdt_data *driver_data = platform_get_drvdata(pdev);
watchdog_unregister_device(&driver_data->wdt);
return 0;
}
static struct platform_driver da9052_wdt_driver = {
.probe = da9052_wdt_probe,
.remove = da9052_wdt_remove,
.driver = {
.name = "da9052-watchdog",
},
......
......@@ -108,7 +108,7 @@ static int da9055_wdt_stop(struct watchdog_device *wdt_dev)
return da9055_wdt_set_timeout(wdt_dev, 0);
}
static struct watchdog_info da9055_wdt_info = {
static const struct watchdog_info da9055_wdt_info = {
.options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING,
.identity = "DA9055 Watchdog",
};
......@@ -147,32 +147,19 @@ static int da9055_wdt_probe(struct platform_device *pdev)
ret = da9055_wdt_stop(da9055_wdt);
if (ret < 0) {
dev_err(&pdev->dev, "Failed to stop watchdog, %d\n", ret);
goto err;
return ret;
}
platform_set_drvdata(pdev, driver_data);
ret = watchdog_register_device(&driver_data->wdt);
ret = devm_watchdog_register_device(&pdev->dev, &driver_data->wdt);
if (ret != 0)
dev_err(da9055->dev, "watchdog_register_device() failed: %d\n",
ret);
err:
return ret;
}
static int da9055_wdt_remove(struct platform_device *pdev)
{
struct da9055_wdt_data *driver_data = platform_get_drvdata(pdev);
watchdog_unregister_device(&driver_data->wdt);
return 0;
}
static struct platform_driver da9055_wdt_driver = {
.probe = da9055_wdt_probe,
.remove = da9055_wdt_remove,
.driver = {
.name = "da9055-watchdog",
},
......
......@@ -220,9 +220,8 @@ static int da9062_wdt_probe(struct platform_device *pdev)
wdt->wdtdev.parent = &pdev->dev;
watchdog_set_drvdata(&wdt->wdtdev, wdt);
dev_set_drvdata(&pdev->dev, wdt);
ret = watchdog_register_device(&wdt->wdtdev);
ret = devm_watchdog_register_device(&pdev->dev, &wdt->wdtdev);
if (ret < 0) {
dev_err(wdt->hw->dev,
"watchdog registration failed (%d)\n", ret);
......@@ -231,24 +230,11 @@ static int da9062_wdt_probe(struct platform_device *pdev)
da9062_set_window_start(wdt);
ret = da9062_wdt_ping(&wdt->wdtdev);
if (ret < 0)
watchdog_unregister_device(&wdt->wdtdev);
return ret;
}
static int da9062_wdt_remove(struct platform_device *pdev)
{
struct da9062_watchdog *wdt = dev_get_drvdata(&pdev->dev);
watchdog_unregister_device(&wdt->wdtdev);
return 0;
return da9062_wdt_ping(&wdt->wdtdev);
}
static struct platform_driver da9062_wdt_driver = {
.probe = da9062_wdt_probe,
.remove = da9062_wdt_remove,
.driver = {
.name = "da9062-watchdog",
.of_match_table = da9062_compatible_id_table,
......
......@@ -151,7 +151,6 @@ static const struct watchdog_ops da9063_watchdog_ops = {
static int da9063_wdt_probe(struct platform_device *pdev)
{
int ret;
struct da9063 *da9063;
struct da9063_watchdog *wdt;
......@@ -181,27 +180,12 @@ static int da9063_wdt_probe(struct platform_device *pdev)
watchdog_set_restart_priority(&wdt->wdtdev, 128);
watchdog_set_drvdata(&wdt->wdtdev, wdt);
dev_set_drvdata(&pdev->dev, wdt);
ret = watchdog_register_device(&wdt->wdtdev);
if (ret)
return ret;
return 0;
}
static int da9063_wdt_remove(struct platform_device *pdev)
{
struct da9063_watchdog *wdt = dev_get_drvdata(&pdev->dev);
watchdog_unregister_device(&wdt->wdtdev);
return 0;
return devm_watchdog_register_device(&pdev->dev, &wdt->wdtdev);
}
static struct platform_driver da9063_wdt_driver = {
.probe = da9063_wdt_probe,
.remove = da9063_wdt_remove,
.driver = {
.name = DA9063_DRVNAME_WATCHDOG,
},
......
......@@ -205,7 +205,7 @@ static int wdt_set_timeout(struct watchdog_device * dev, unsigned int new_to)
return wdt_ping(dev);
}
static struct watchdog_ops wdt_ops = {
static const struct watchdog_ops wdt_ops = {
.owner = THIS_MODULE,
.start = wdt_start,
.stop = wdt_stop,
......
......@@ -96,7 +96,7 @@ static unsigned int dc_wdt_get_timeleft(struct watchdog_device *wdog)
return count / clk_get_rate(wdt->clk);
}
static struct watchdog_ops dc_wdt_ops = {
static const struct watchdog_ops dc_wdt_ops = {
.owner = THIS_MODULE,
.start = dc_wdt_start,
.stop = dc_wdt_stop,
......@@ -105,7 +105,7 @@ static struct watchdog_ops dc_wdt_ops = {
.restart = dc_wdt_restart,
};
static struct watchdog_info dc_wdt_info = {
static const struct watchdog_info dc_wdt_info = {
.options = WDIOF_SETTIMEOUT | WDIOF_MAGICCLOSE
| WDIOF_KEEPALIVEPING,
.identity = "Conexant Digicolor Watchdog",
......@@ -119,62 +119,40 @@ static struct watchdog_device dc_wdt_wdd = {
static int dc_wdt_probe(struct platform_device *pdev)
{
struct resource *res;
struct device *dev = &pdev->dev;
struct device_node *np = dev->of_node;
struct dc_wdt *wdt;
int ret;
wdt = devm_kzalloc(dev, sizeof(struct dc_wdt), GFP_KERNEL);
if (!wdt)
return -ENOMEM;
platform_set_drvdata(pdev, wdt);
wdt->base = of_iomap(np, 0);
if (!wdt->base) {
dev_err(dev, "Failed to remap watchdog regs");
return -ENODEV;
}
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
wdt->base = devm_ioremap_resource(dev, res);
if (IS_ERR(wdt->base))
return PTR_ERR(wdt->base);
wdt->clk = devm_clk_get(&pdev->dev, NULL);
if (IS_ERR(wdt->clk)) {
ret = PTR_ERR(wdt->clk);
goto err_iounmap;
}
wdt->clk = devm_clk_get(dev, NULL);
if (IS_ERR(wdt->clk))
return PTR_ERR(wdt->clk);
dc_wdt_wdd.max_timeout = U32_MAX / clk_get_rate(wdt->clk);
dc_wdt_wdd.timeout = dc_wdt_wdd.max_timeout;
dc_wdt_wdd.parent = &pdev->dev;
dc_wdt_wdd.parent = dev;
spin_lock_init(&wdt->lock);
watchdog_set_drvdata(&dc_wdt_wdd, wdt);
watchdog_set_restart_priority(&dc_wdt_wdd, 128);
watchdog_init_timeout(&dc_wdt_wdd, timeout, dev);
ret = watchdog_register_device(&dc_wdt_wdd);
watchdog_stop_on_reboot(&dc_wdt_wdd);
ret = devm_watchdog_register_device(dev, &dc_wdt_wdd);
if (ret) {
dev_err(dev, "Failed to register watchdog device");
goto err_iounmap;
return ret;
}
return 0;
err_iounmap:
iounmap(wdt->base);
return ret;
}
static int dc_wdt_remove(struct platform_device *pdev)
{
struct dc_wdt *wdt = platform_get_drvdata(pdev);
watchdog_unregister_device(&dc_wdt_wdd);
iounmap(wdt->base);
return 0;
}
static void dc_wdt_shutdown(struct platform_device *pdev)
{
dc_wdt_stop(&dc_wdt_wdd);
}
static const struct of_device_id dc_wdt_of_match[] = {
......@@ -185,8 +163,6 @@ MODULE_DEVICE_TABLE(of, dc_wdt_of_match);
static struct platform_driver dc_wdt_driver = {
.probe = dc_wdt_probe,
.remove = dc_wdt_remove,
.shutdown = dc_wdt_shutdown,
.driver = {
.name = "digicolor-wdt",
.of_match_table = dc_wdt_of_match,
......
......@@ -26,11 +26,9 @@
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/notifier.h>
#include <linux/of.h>
#include <linux/pm.h>
#include <linux/platform_device.h>
#include <linux/reboot.h>
#include <linux/watchdog.h>
#define WDOG_CONTROL_REG_OFFSET 0x00
......@@ -55,7 +53,6 @@ struct dw_wdt {
void __iomem *regs;
struct clk *clk;
unsigned long rate;
struct notifier_block restart_handler;
struct watchdog_device wdd;
};
......@@ -136,14 +133,12 @@ static int dw_wdt_start(struct watchdog_device *wdd)
return 0;
}
static int dw_wdt_restart_handle(struct notifier_block *this,
unsigned long mode, void *cmd)
static int dw_wdt_restart(struct watchdog_device *wdd,
unsigned long action, void *data)
{
struct dw_wdt *dw_wdt;
struct dw_wdt *dw_wdt = to_dw_wdt(wdd);
u32 val;
dw_wdt = container_of(this, struct dw_wdt, restart_handler);
writel(0, dw_wdt->regs + WDOG_TIMEOUT_RANGE_REG_OFFSET);
val = readl(dw_wdt->regs + WDOG_CONTROL_REG_OFFSET);
if (val & WDOG_CONTROL_REG_WDT_EN_MASK)
......@@ -156,7 +151,7 @@ static int dw_wdt_restart_handle(struct notifier_block *this,
/* wait for reset to assert... */
mdelay(500);
return NOTIFY_DONE;
return 0;
}
static unsigned int dw_wdt_get_timeleft(struct watchdog_device *wdd)
......@@ -179,6 +174,7 @@ static const struct watchdog_ops dw_wdt_ops = {
.ping = dw_wdt_ping,
.set_timeout = dw_wdt_set_timeout,
.get_timeleft = dw_wdt_get_timeleft,
.restart = dw_wdt_restart,
};
#ifdef CONFIG_PM_SLEEP
......@@ -265,16 +261,12 @@ static int dw_wdt_drv_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, dw_wdt);
watchdog_set_restart_priority(wdd, 128);
ret = watchdog_register_device(wdd);
if (ret)
goto out_disable_clk;
dw_wdt->restart_handler.notifier_call = dw_wdt_restart_handle;
dw_wdt->restart_handler.priority = 128;
ret = register_restart_handler(&dw_wdt->restart_handler);
if (ret)
pr_warn("cannot register restart handler\n");
return 0;
out_disable_clk:
......@@ -286,7 +278,6 @@ static int dw_wdt_drv_remove(struct platform_device *pdev)
{
struct dw_wdt *dw_wdt = platform_get_drvdata(pdev);
unregister_restart_handler(&dw_wdt->restart_handler);
watchdog_unregister_device(&dw_wdt->wdd);
clk_disable_unprepare(dw_wdt->clk);
......
......@@ -121,18 +121,7 @@ static int ebc_c384_wdt_probe(struct device *dev, unsigned int id)
dev_warn(dev, "Invalid timeout (%u seconds), using default (%u seconds)\n",
timeout, WATCHDOG_TIMEOUT);
dev_set_drvdata(dev, wdd);
return watchdog_register_device(wdd);
}
static int ebc_c384_wdt_remove(struct device *dev, unsigned int id)
{
struct watchdog_device *wdd = dev_get_drvdata(dev);
watchdog_unregister_device(wdd);
return 0;
return devm_watchdog_register_device(dev, wdd);
}
static struct isa_driver ebc_c384_wdt_driver = {
......@@ -140,7 +129,6 @@ static struct isa_driver ebc_c384_wdt_driver = {
.driver = {
.name = MODULE_NAME
},
.remove = ebc_c384_wdt_remove
};
static int __init ebc_c384_wdt_init(void)
......
......@@ -19,21 +19,13 @@
* for us to rely on the user space daemon alone. So we ping the
* wdt each ~200msec and eventually stop doing it if the user space
* daemon dies.
*
* TODO:
*
* - Test last reset from watchdog status
* - Add a few missing ioctls
*/
#include <linux/platform_device.h>
#include <linux/module.h>
#include <linux/watchdog.h>
#include <linux/timer.h>
#include <linux/io.h>
#define WDT_VERSION "0.4"
/* default timeout (secs) */
#define WDT_TIMEOUT 30
......@@ -41,117 +33,101 @@ static bool nowayout = WATCHDOG_NOWAYOUT;
module_param(nowayout, bool, 0);
MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started");
static unsigned int timeout = WDT_TIMEOUT;
static unsigned int timeout;
module_param(timeout, uint, 0);
MODULE_PARM_DESC(timeout,
"Watchdog timeout in seconds. (1<=timeout<=3600, default="
__MODULE_STRING(WDT_TIMEOUT) ")");
static void __iomem *mmio_base;
static struct timer_list timer;
static unsigned long next_heartbeat;
MODULE_PARM_DESC(timeout, "Watchdog timeout in seconds.");
#define EP93XX_WATCHDOG 0x00
#define EP93XX_WDSTATUS 0x04
/* reset the wdt every ~200ms - the heartbeat of the device is 0.250 seconds*/
#define WDT_INTERVAL (HZ/5)
static void ep93xx_wdt_timer_ping(unsigned long data)
{
if (time_before(jiffies, next_heartbeat))
writel(0x5555, mmio_base + EP93XX_WATCHDOG);
/* Re-set the timer interval */
mod_timer(&timer, jiffies + WDT_INTERVAL);
}
struct ep93xx_wdt_priv {
void __iomem *mmio;
struct watchdog_device wdd;
};
static int ep93xx_wdt_start(struct watchdog_device *wdd)
{
next_heartbeat = jiffies + (timeout * HZ);
struct ep93xx_wdt_priv *priv = watchdog_get_drvdata(wdd);
writel(0xaaaa, mmio_base + EP93XX_WATCHDOG);
mod_timer(&timer, jiffies + WDT_INTERVAL);
writel(0xaaaa, priv->mmio + EP93XX_WATCHDOG);
return 0;
}
static int ep93xx_wdt_stop(struct watchdog_device *wdd)
{
del_timer_sync(&timer);
writel(0xaa55, mmio_base + EP93XX_WATCHDOG);
struct ep93xx_wdt_priv *priv = watchdog_get_drvdata(wdd);
writel(0xaa55, priv->mmio + EP93XX_WATCHDOG);
return 0;
}
static int ep93xx_wdt_keepalive(struct watchdog_device *wdd)
static int ep93xx_wdt_ping(struct watchdog_device *wdd)
{
/* user land ping */
next_heartbeat = jiffies + (timeout * HZ);
struct ep93xx_wdt_priv *priv = watchdog_get_drvdata(wdd);
writel(0x5555, priv->mmio + EP93XX_WATCHDOG);
return 0;
}
static const struct watchdog_info ep93xx_wdt_ident = {
.options = WDIOF_CARDRESET |
WDIOF_SETTIMEOUT |
WDIOF_MAGICCLOSE |
WDIOF_KEEPALIVEPING,
.identity = "EP93xx Watchdog",
};
static struct watchdog_ops ep93xx_wdt_ops = {
static const struct watchdog_ops ep93xx_wdt_ops = {
.owner = THIS_MODULE,
.start = ep93xx_wdt_start,
.stop = ep93xx_wdt_stop,
.ping = ep93xx_wdt_keepalive,
};
static struct watchdog_device ep93xx_wdt_wdd = {
.info = &ep93xx_wdt_ident,
.ops = &ep93xx_wdt_ops,
.ping = ep93xx_wdt_ping,
};
static int ep93xx_wdt_probe(struct platform_device *pdev)
{
struct ep93xx_wdt_priv *priv;
struct watchdog_device *wdd;
struct resource *res;
unsigned long val;
int err;
int ret;
priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
if (!priv)
return -ENOMEM;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
mmio_base = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(mmio_base))
return PTR_ERR(mmio_base);
priv->mmio = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(priv->mmio))
return PTR_ERR(priv->mmio);
if (timeout < 1 || timeout > 3600) {
timeout = WDT_TIMEOUT;
dev_warn(&pdev->dev,
"timeout value must be 1<=x<=3600, using %d\n",
timeout);
}
val = readl(priv->mmio + EP93XX_WATCHDOG);
val = readl(mmio_base + EP93XX_WATCHDOG);
ep93xx_wdt_wdd.bootstatus = (val & 0x01) ? WDIOF_CARDRESET : 0;
ep93xx_wdt_wdd.timeout = timeout;
ep93xx_wdt_wdd.parent = &pdev->dev;
wdd = &priv->wdd;
wdd->bootstatus = (val & 0x01) ? WDIOF_CARDRESET : 0;
wdd->info = &ep93xx_wdt_ident;
wdd->ops = &ep93xx_wdt_ops;
wdd->min_timeout = 1;
wdd->max_hw_heartbeat_ms = 200;
wdd->parent = &pdev->dev;
watchdog_set_nowayout(&ep93xx_wdt_wdd, nowayout);
watchdog_set_nowayout(wdd, nowayout);
setup_timer(&timer, ep93xx_wdt_timer_ping, 1);
wdd->timeout = WDT_TIMEOUT;
watchdog_init_timeout(wdd, timeout, &pdev->dev);
err = watchdog_register_device(&ep93xx_wdt_wdd);
if (err)
return err;
watchdog_set_drvdata(wdd, priv);
dev_info(&pdev->dev,
"EP93XX watchdog, driver version " WDT_VERSION "%s\n",
(val & 0x08) ? " (nCS1 disable detected)" : "");
ret = devm_watchdog_register_device(&pdev->dev, wdd);
if (ret)
return ret;
return 0;
}
dev_info(&pdev->dev, "EP93XX watchdog driver %s\n",
(val & 0x08) ? " (nCS1 disable detected)" : "");
static int ep93xx_wdt_remove(struct platform_device *pdev)
{
watchdog_unregister_device(&ep93xx_wdt_wdd);
return 0;
}
......@@ -160,7 +136,6 @@ static struct platform_driver ep93xx_wdt_driver = {
.name = "ep93xx-wdt",
},
.probe = ep93xx_wdt_probe,
.remove = ep93xx_wdt_remove,
};
module_platform_driver(ep93xx_wdt_driver);
......@@ -170,4 +145,3 @@ MODULE_AUTHOR("Alessandro Zummo <a.zummo@towertech.it>");
MODULE_AUTHOR("H Hartley Sweeten <hsweeten@visionengravers.com>");
MODULE_DESCRIPTION("EP93xx Watchdog");
MODULE_LICENSE("GPL");
MODULE_VERSION(WDT_VERSION);
/*
* Watchdog driver for Cortina Systems Gemini SoC
*
* Copyright (C) 2017 Linus Walleij <linus.walleij@linaro.org>
*
* Inspired by the out-of-tree drivers from OpenWRT:
* Copyright (C) 2009 Paulius Zaleckas <paulius.zaleckas@teltonika.lt>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/bitops.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/of_device.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
#include <linux/watchdog.h>
#define GEMINI_WDCOUNTER 0x0
#define GEMINI_WDLOAD 0x4
#define GEMINI_WDRESTART 0x8
#define GEMINI_WDCR 0xC
#define WDRESTART_MAGIC 0x5AB9
#define WDCR_CLOCK_5MHZ BIT(4)
#define WDCR_SYS_RST BIT(1)
#define WDCR_ENABLE BIT(0)
#define WDT_CLOCK 5000000 /* 5 MHz */
struct gemini_wdt {
struct watchdog_device wdd;
struct device *dev;
void __iomem *base;
};
static inline
struct gemini_wdt *to_gemini_wdt(struct watchdog_device *wdd)
{
return container_of(wdd, struct gemini_wdt, wdd);
}
static int gemini_wdt_start(struct watchdog_device *wdd)
{
struct gemini_wdt *gwdt = to_gemini_wdt(wdd);
writel(wdd->timeout * WDT_CLOCK, gwdt->base + GEMINI_WDLOAD);
writel(WDRESTART_MAGIC, gwdt->base + GEMINI_WDRESTART);
/* set clock before enabling */
writel(WDCR_CLOCK_5MHZ | WDCR_SYS_RST,
gwdt->base + GEMINI_WDCR);
writel(WDCR_CLOCK_5MHZ | WDCR_SYS_RST | WDCR_ENABLE,
gwdt->base + GEMINI_WDCR);
return 0;
}
static int gemini_wdt_stop(struct watchdog_device *wdd)
{
struct gemini_wdt *gwdt = to_gemini_wdt(wdd);
writel(0, gwdt->base + GEMINI_WDCR);
return 0;
}
static int gemini_wdt_ping(struct watchdog_device *wdd)
{
struct gemini_wdt *gwdt = to_gemini_wdt(wdd);
writel(WDRESTART_MAGIC, gwdt->base + GEMINI_WDRESTART);
return 0;
}
static int gemini_wdt_set_timeout(struct watchdog_device *wdd,
unsigned int timeout)
{
wdd->timeout = timeout;
if (watchdog_active(wdd))
gemini_wdt_start(wdd);
return 0;
}
static irqreturn_t gemini_wdt_interrupt(int irq, void *data)
{
struct gemini_wdt *gwdt = data;
watchdog_notify_pretimeout(&gwdt->wdd);
return IRQ_HANDLED;
}
static const struct watchdog_ops gemini_wdt_ops = {
.start = gemini_wdt_start,
.stop = gemini_wdt_stop,
.ping = gemini_wdt_ping,
.set_timeout = gemini_wdt_set_timeout,
.owner = THIS_MODULE,
};
static const struct watchdog_info gemini_wdt_info = {
.options = WDIOF_KEEPALIVEPING
| WDIOF_MAGICCLOSE
| WDIOF_SETTIMEOUT,
.identity = KBUILD_MODNAME,
};
static int gemini_wdt_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct resource *res;
struct gemini_wdt *gwdt;
unsigned int reg;
int irq;
int ret;
gwdt = devm_kzalloc(dev, sizeof(*gwdt), GFP_KERNEL);
if (!gwdt)
return -ENOMEM;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
gwdt->base = devm_ioremap_resource(dev, res);
if (IS_ERR(gwdt->base))
return PTR_ERR(gwdt->base);
irq = platform_get_irq(pdev, 0);
if (!irq)
return -EINVAL;
gwdt->dev = dev;
gwdt->wdd.info = &gemini_wdt_info;
gwdt->wdd.ops = &gemini_wdt_ops;
gwdt->wdd.min_timeout = 1;
gwdt->wdd.max_timeout = 0xFFFFFFFF / WDT_CLOCK;
gwdt->wdd.parent = dev;
/*
* If 'timeout-sec' unspecified in devicetree, assume a 13 second
* default.
*/
gwdt->wdd.timeout = 13U;
watchdog_init_timeout(&gwdt->wdd, 0, dev);
reg = readw(gwdt->base + GEMINI_WDCR);
if (reg & WDCR_ENABLE) {
/* Watchdog was enabled by the bootloader, disable it. */
reg &= ~WDCR_ENABLE;
writel(reg, gwdt->base + GEMINI_WDCR);
}
ret = devm_request_irq(dev, irq, gemini_wdt_interrupt, 0,
"watchdog bark", gwdt);
if (ret)
return ret;
ret = devm_watchdog_register_device(dev, &gwdt->wdd);
if (ret) {
dev_err(&pdev->dev, "failed to register watchdog\n");
return ret;
}
/* Set up platform driver data */
platform_set_drvdata(pdev, gwdt);
dev_info(dev, "Gemini watchdog driver enabled\n");
return 0;
}
static int __maybe_unused gemini_wdt_suspend(struct device *dev)
{
struct gemini_wdt *gwdt = dev_get_drvdata(dev);
unsigned int reg;
reg = readw(gwdt->base + GEMINI_WDCR);
reg &= ~WDCR_ENABLE;
writel(reg, gwdt->base + GEMINI_WDCR);
return 0;
}
static int __maybe_unused gemini_wdt_resume(struct device *dev)
{
struct gemini_wdt *gwdt = dev_get_drvdata(dev);
unsigned int reg;
if (watchdog_active(&gwdt->wdd)) {
reg = readw(gwdt->base + GEMINI_WDCR);
reg |= WDCR_ENABLE;
writel(reg, gwdt->base + GEMINI_WDCR);
}
return 0;
}
static const struct dev_pm_ops gemini_wdt_dev_pm_ops = {
SET_SYSTEM_SLEEP_PM_OPS(gemini_wdt_suspend,
gemini_wdt_resume)
};
#ifdef CONFIG_OF
static const struct of_device_id gemini_wdt_match[] = {
{ .compatible = "cortina,gemini-watchdog" },
{},
};
MODULE_DEVICE_TABLE(of, gemini_wdt_match);
#endif
static struct platform_driver gemini_wdt_driver = {
.probe = gemini_wdt_probe,
.driver = {
.name = "gemini-wdt",
.of_match_table = of_match_ptr(gemini_wdt_match),
.pm = &gemini_wdt_dev_pm_ops,
},
};
module_platform_driver(gemini_wdt_driver);
MODULE_AUTHOR("Linus Walleij");
MODULE_DESCRIPTION("Watchdog driver for Gemini");
MODULE_LICENSE("GPL");
......@@ -72,22 +72,24 @@
/* Address definitions for the TCO */
/* TCO base address */
#define TCOBASE (iTCO_wdt_private.tco_res->start)
#define TCOBASE(p) ((p)->tco_res->start)
/* SMI Control and Enable Register */
#define SMI_EN (iTCO_wdt_private.smi_res->start)
#define TCO_RLD (TCOBASE + 0x00) /* TCO Timer Reload and Curr. Value */
#define TCOv1_TMR (TCOBASE + 0x01) /* TCOv1 Timer Initial Value */
#define TCO_DAT_IN (TCOBASE + 0x02) /* TCO Data In Register */
#define TCO_DAT_OUT (TCOBASE + 0x03) /* TCO Data Out Register */
#define TCO1_STS (TCOBASE + 0x04) /* TCO1 Status Register */
#define TCO2_STS (TCOBASE + 0x06) /* TCO2 Status Register */
#define TCO1_CNT (TCOBASE + 0x08) /* TCO1 Control Register */
#define TCO2_CNT (TCOBASE + 0x0a) /* TCO2 Control Register */
#define TCOv2_TMR (TCOBASE + 0x12) /* TCOv2 Timer Initial Value */
#define SMI_EN(p) ((p)->smi_res->start)
#define TCO_RLD(p) (TCOBASE(p) + 0x00) /* TCO Timer Reload/Curr. Value */
#define TCOv1_TMR(p) (TCOBASE(p) + 0x01) /* TCOv1 Timer Initial Value*/
#define TCO_DAT_IN(p) (TCOBASE(p) + 0x02) /* TCO Data In Register */
#define TCO_DAT_OUT(p) (TCOBASE(p) + 0x03) /* TCO Data Out Register */
#define TCO1_STS(p) (TCOBASE(p) + 0x04) /* TCO1 Status Register */
#define TCO2_STS(p) (TCOBASE(p) + 0x06) /* TCO2 Status Register */
#define TCO1_CNT(p) (TCOBASE(p) + 0x08) /* TCO1 Control Register */
#define TCO2_CNT(p) (TCOBASE(p) + 0x0a) /* TCO2 Control Register */
#define TCOv2_TMR(p) (TCOBASE(p) + 0x12) /* TCOv2 Timer Initial Value*/
/* internal variables */
static struct { /* this is private data for the iTCO_wdt device */
struct iTCO_wdt_private {
struct watchdog_device wddev;
/* TCO version/generation */
unsigned int iTCO_version;
struct resource *tco_res;
......@@ -100,12 +102,11 @@ static struct { /* this is private data for the iTCO_wdt device */
unsigned long __iomem *gcs_pmc;
/* the lock for io operations */
spinlock_t io_lock;
struct platform_device *dev;
/* the PCI-device */
struct pci_dev *pdev;
struct pci_dev *pci_dev;
/* whether or not the watchdog has been suspended */
bool suspended;
} iTCO_wdt_private;
};
/* module parameters */
#define WATCHDOG_TIMEOUT 30 /* 30 sec default heartbeat */
......@@ -135,21 +136,23 @@ MODULE_PARM_DESC(turn_SMI_watchdog_clear_off,
* every 0.6 seconds. v3's internal timer is stored as seconds (some
* datasheets incorrectly state 0.6 seconds).
*/
static inline unsigned int seconds_to_ticks(int secs)
static inline unsigned int seconds_to_ticks(struct iTCO_wdt_private *p,
int secs)
{
return iTCO_wdt_private.iTCO_version == 3 ? secs : (secs * 10) / 6;
return p->iTCO_version == 3 ? secs : (secs * 10) / 6;
}
static inline unsigned int ticks_to_seconds(int ticks)
static inline unsigned int ticks_to_seconds(struct iTCO_wdt_private *p,
int ticks)
{
return iTCO_wdt_private.iTCO_version == 3 ? ticks : (ticks * 6) / 10;
return p->iTCO_version == 3 ? ticks : (ticks * 6) / 10;
}
static inline u32 no_reboot_bit(void)
static inline u32 no_reboot_bit(struct iTCO_wdt_private *p)
{
u32 enable_bit;
switch (iTCO_wdt_private.iTCO_version) {
switch (p->iTCO_version) {
case 5:
case 3:
enable_bit = 0x00000010;
......@@ -167,40 +170,40 @@ static inline u32 no_reboot_bit(void)
return enable_bit;
}
static void iTCO_wdt_set_NO_REBOOT_bit(void)
static void iTCO_wdt_set_NO_REBOOT_bit(struct iTCO_wdt_private *p)
{
u32 val32;
/* Set the NO_REBOOT bit: this disables reboots */
if (iTCO_wdt_private.iTCO_version >= 2) {
val32 = readl(iTCO_wdt_private.gcs_pmc);
val32 |= no_reboot_bit();
writel(val32, iTCO_wdt_private.gcs_pmc);
} else if (iTCO_wdt_private.iTCO_version == 1) {
pci_read_config_dword(iTCO_wdt_private.pdev, 0xd4, &val32);
val32 |= no_reboot_bit();
pci_write_config_dword(iTCO_wdt_private.pdev, 0xd4, val32);
if (p->iTCO_version >= 2) {
val32 = readl(p->gcs_pmc);
val32 |= no_reboot_bit(p);
writel(val32, p->gcs_pmc);
} else if (p->iTCO_version == 1) {
pci_read_config_dword(p->pci_dev, 0xd4, &val32);
val32 |= no_reboot_bit(p);
pci_write_config_dword(p->pci_dev, 0xd4, val32);
}
}
static int iTCO_wdt_unset_NO_REBOOT_bit(void)
static int iTCO_wdt_unset_NO_REBOOT_bit(struct iTCO_wdt_private *p)
{
u32 enable_bit = no_reboot_bit();
u32 enable_bit = no_reboot_bit(p);
u32 val32 = 0;
/* Unset the NO_REBOOT bit: this enables reboots */
if (iTCO_wdt_private.iTCO_version >= 2) {
val32 = readl(iTCO_wdt_private.gcs_pmc);
if (p->iTCO_version >= 2) {
val32 = readl(p->gcs_pmc);
val32 &= ~enable_bit;
writel(val32, iTCO_wdt_private.gcs_pmc);
writel(val32, p->gcs_pmc);
val32 = readl(iTCO_wdt_private.gcs_pmc);
} else if (iTCO_wdt_private.iTCO_version == 1) {
pci_read_config_dword(iTCO_wdt_private.pdev, 0xd4, &val32);
val32 = readl(p->gcs_pmc);
} else if (p->iTCO_version == 1) {
pci_read_config_dword(p->pci_dev, 0xd4, &val32);
val32 &= ~enable_bit;
pci_write_config_dword(iTCO_wdt_private.pdev, 0xd4, val32);
pci_write_config_dword(p->pci_dev, 0xd4, val32);
pci_read_config_dword(iTCO_wdt_private.pdev, 0xd4, &val32);
pci_read_config_dword(p->pci_dev, 0xd4, &val32);
}
if (val32 & enable_bit)
......@@ -211,32 +214,33 @@ static int iTCO_wdt_unset_NO_REBOOT_bit(void)
static int iTCO_wdt_start(struct watchdog_device *wd_dev)
{
struct iTCO_wdt_private *p = watchdog_get_drvdata(wd_dev);
unsigned int val;
spin_lock(&iTCO_wdt_private.io_lock);
spin_lock(&p->io_lock);
iTCO_vendor_pre_start(iTCO_wdt_private.smi_res, wd_dev->timeout);
iTCO_vendor_pre_start(p->smi_res, wd_dev->timeout);
/* disable chipset's NO_REBOOT bit */
if (iTCO_wdt_unset_NO_REBOOT_bit()) {
spin_unlock(&iTCO_wdt_private.io_lock);
if (iTCO_wdt_unset_NO_REBOOT_bit(p)) {
spin_unlock(&p->io_lock);
pr_err("failed to reset NO_REBOOT flag, reboot disabled by hardware/BIOS\n");
return -EIO;
}
/* Force the timer to its reload value by writing to the TCO_RLD
register */
if (iTCO_wdt_private.iTCO_version >= 2)
outw(0x01, TCO_RLD);
else if (iTCO_wdt_private.iTCO_version == 1)
outb(0x01, TCO_RLD);
if (p->iTCO_version >= 2)
outw(0x01, TCO_RLD(p));
else if (p->iTCO_version == 1)
outb(0x01, TCO_RLD(p));
/* Bit 11: TCO Timer Halt -> 0 = The TCO timer is enabled to count */
val = inw(TCO1_CNT);
val = inw(TCO1_CNT(p));
val &= 0xf7ff;
outw(val, TCO1_CNT);
val = inw(TCO1_CNT);
spin_unlock(&iTCO_wdt_private.io_lock);
outw(val, TCO1_CNT(p));
val = inw(TCO1_CNT(p));
spin_unlock(&p->io_lock);
if (val & 0x0800)
return -1;
......@@ -245,22 +249,23 @@ static int iTCO_wdt_start(struct watchdog_device *wd_dev)
static int iTCO_wdt_stop(struct watchdog_device *wd_dev)
{
struct iTCO_wdt_private *p = watchdog_get_drvdata(wd_dev);
unsigned int val;
spin_lock(&iTCO_wdt_private.io_lock);
spin_lock(&p->io_lock);
iTCO_vendor_pre_stop(iTCO_wdt_private.smi_res);
iTCO_vendor_pre_stop(p->smi_res);
/* Bit 11: TCO Timer Halt -> 1 = The TCO timer is disabled */
val = inw(TCO1_CNT);
val = inw(TCO1_CNT(p));
val |= 0x0800;
outw(val, TCO1_CNT);
val = inw(TCO1_CNT);
outw(val, TCO1_CNT(p));
val = inw(TCO1_CNT(p));
/* Set the NO_REBOOT bit to prevent later reboots, just for sure */
iTCO_wdt_set_NO_REBOOT_bit();
iTCO_wdt_set_NO_REBOOT_bit(p);
spin_unlock(&iTCO_wdt_private.io_lock);
spin_unlock(&p->io_lock);
if ((val & 0x0800) == 0)
return -1;
......@@ -269,67 +274,70 @@ static int iTCO_wdt_stop(struct watchdog_device *wd_dev)
static int iTCO_wdt_ping(struct watchdog_device *wd_dev)
{
spin_lock(&iTCO_wdt_private.io_lock);
struct iTCO_wdt_private *p = watchdog_get_drvdata(wd_dev);
iTCO_vendor_pre_keepalive(iTCO_wdt_private.smi_res, wd_dev->timeout);
spin_lock(&p->io_lock);
iTCO_vendor_pre_keepalive(p->smi_res, wd_dev->timeout);
/* Reload the timer by writing to the TCO Timer Counter register */
if (iTCO_wdt_private.iTCO_version >= 2) {
outw(0x01, TCO_RLD);
} else if (iTCO_wdt_private.iTCO_version == 1) {
if (p->iTCO_version >= 2) {
outw(0x01, TCO_RLD(p));
} else if (p->iTCO_version == 1) {
/* Reset the timeout status bit so that the timer
* needs to count down twice again before rebooting */
outw(0x0008, TCO1_STS); /* write 1 to clear bit */
outw(0x0008, TCO1_STS(p)); /* write 1 to clear bit */
outb(0x01, TCO_RLD);
outb(0x01, TCO_RLD(p));
}
spin_unlock(&iTCO_wdt_private.io_lock);
spin_unlock(&p->io_lock);
return 0;
}
static int iTCO_wdt_set_timeout(struct watchdog_device *wd_dev, unsigned int t)
{
struct iTCO_wdt_private *p = watchdog_get_drvdata(wd_dev);
unsigned int val16;
unsigned char val8;
unsigned int tmrval;
tmrval = seconds_to_ticks(t);
tmrval = seconds_to_ticks(p, t);
/* For TCO v1 the timer counts down twice before rebooting */
if (iTCO_wdt_private.iTCO_version == 1)
if (p->iTCO_version == 1)
tmrval /= 2;
/* from the specs: */
/* "Values of 0h-3h are ignored and should not be attempted" */
if (tmrval < 0x04)
return -EINVAL;
if (((iTCO_wdt_private.iTCO_version >= 2) && (tmrval > 0x3ff)) ||
((iTCO_wdt_private.iTCO_version == 1) && (tmrval > 0x03f)))
if ((p->iTCO_version >= 2 && tmrval > 0x3ff) ||
(p->iTCO_version == 1 && tmrval > 0x03f))
return -EINVAL;
iTCO_vendor_pre_set_heartbeat(tmrval);
/* Write new heartbeat to watchdog */
if (iTCO_wdt_private.iTCO_version >= 2) {
spin_lock(&iTCO_wdt_private.io_lock);
val16 = inw(TCOv2_TMR);
if (p->iTCO_version >= 2) {
spin_lock(&p->io_lock);
val16 = inw(TCOv2_TMR(p));
val16 &= 0xfc00;
val16 |= tmrval;
outw(val16, TCOv2_TMR);
val16 = inw(TCOv2_TMR);
spin_unlock(&iTCO_wdt_private.io_lock);
outw(val16, TCOv2_TMR(p));
val16 = inw(TCOv2_TMR(p));
spin_unlock(&p->io_lock);
if ((val16 & 0x3ff) != tmrval)
return -EINVAL;
} else if (iTCO_wdt_private.iTCO_version == 1) {
spin_lock(&iTCO_wdt_private.io_lock);
val8 = inb(TCOv1_TMR);
} else if (p->iTCO_version == 1) {
spin_lock(&p->io_lock);
val8 = inb(TCOv1_TMR(p));
val8 &= 0xc0;
val8 |= (tmrval & 0xff);
outb(val8, TCOv1_TMR);
val8 = inb(TCOv1_TMR);
spin_unlock(&iTCO_wdt_private.io_lock);
outb(val8, TCOv1_TMR(p));
val8 = inb(TCOv1_TMR(p));
spin_unlock(&p->io_lock);
if ((val8 & 0x3f) != tmrval)
return -EINVAL;
......@@ -341,27 +349,28 @@ static int iTCO_wdt_set_timeout(struct watchdog_device *wd_dev, unsigned int t)
static unsigned int iTCO_wdt_get_timeleft(struct watchdog_device *wd_dev)
{
struct iTCO_wdt_private *p = watchdog_get_drvdata(wd_dev);
unsigned int val16;
unsigned char val8;
unsigned int time_left = 0;
/* read the TCO Timer */
if (iTCO_wdt_private.iTCO_version >= 2) {
spin_lock(&iTCO_wdt_private.io_lock);
val16 = inw(TCO_RLD);
if (p->iTCO_version >= 2) {
spin_lock(&p->io_lock);
val16 = inw(TCO_RLD(p));
val16 &= 0x3ff;
spin_unlock(&iTCO_wdt_private.io_lock);
spin_unlock(&p->io_lock);
time_left = ticks_to_seconds(val16);
} else if (iTCO_wdt_private.iTCO_version == 1) {
spin_lock(&iTCO_wdt_private.io_lock);
val8 = inb(TCO_RLD);
time_left = ticks_to_seconds(p, val16);
} else if (p->iTCO_version == 1) {
spin_lock(&p->io_lock);
val8 = inb(TCO_RLD(p));
val8 &= 0x3f;
if (!(inw(TCO1_STS) & 0x0008))
val8 += (inb(TCOv1_TMR) & 0x3f);
spin_unlock(&iTCO_wdt_private.io_lock);
if (!(inw(TCO1_STS(p)) & 0x0008))
val8 += (inb(TCOv1_TMR(p)) & 0x3f);
spin_unlock(&p->io_lock);
time_left = ticks_to_seconds(val8);
time_left = ticks_to_seconds(p, val8);
}
return time_left;
}
......@@ -387,209 +396,152 @@ static const struct watchdog_ops iTCO_wdt_ops = {
.get_timeleft = iTCO_wdt_get_timeleft,
};
static struct watchdog_device iTCO_wdt_watchdog_dev = {
.info = &ident,
.ops = &iTCO_wdt_ops,
};
/*
* Init & exit routines
*/
static void iTCO_wdt_cleanup(void)
{
/* Stop the timer before we leave */
if (!nowayout)
iTCO_wdt_stop(&iTCO_wdt_watchdog_dev);
/* Deregister */
watchdog_unregister_device(&iTCO_wdt_watchdog_dev);
/* release resources */
release_region(iTCO_wdt_private.tco_res->start,
resource_size(iTCO_wdt_private.tco_res));
release_region(iTCO_wdt_private.smi_res->start,
resource_size(iTCO_wdt_private.smi_res));
if (iTCO_wdt_private.iTCO_version >= 2) {
iounmap(iTCO_wdt_private.gcs_pmc);
release_mem_region(iTCO_wdt_private.gcs_pmc_res->start,
resource_size(iTCO_wdt_private.gcs_pmc_res));
}
iTCO_wdt_private.tco_res = NULL;
iTCO_wdt_private.smi_res = NULL;
iTCO_wdt_private.gcs_pmc_res = NULL;
iTCO_wdt_private.gcs_pmc = NULL;
}
static int iTCO_wdt_probe(struct platform_device *dev)
static int iTCO_wdt_probe(struct platform_device *pdev)
{
int ret = -ENODEV;
struct device *dev = &pdev->dev;
struct itco_wdt_platform_data *pdata = dev_get_platdata(dev);
struct iTCO_wdt_private *p;
unsigned long val32;
struct itco_wdt_platform_data *pdata = dev_get_platdata(&dev->dev);
int ret;
if (!pdata)
goto out;
return -ENODEV;
p = devm_kzalloc(dev, sizeof(*p), GFP_KERNEL);
if (!p)
return -ENOMEM;
spin_lock_init(&iTCO_wdt_private.io_lock);
spin_lock_init(&p->io_lock);
iTCO_wdt_private.tco_res =
platform_get_resource(dev, IORESOURCE_IO, ICH_RES_IO_TCO);
if (!iTCO_wdt_private.tco_res)
goto out;
p->tco_res = platform_get_resource(pdev, IORESOURCE_IO, ICH_RES_IO_TCO);
if (!p->tco_res)
return -ENODEV;
iTCO_wdt_private.smi_res =
platform_get_resource(dev, IORESOURCE_IO, ICH_RES_IO_SMI);
if (!iTCO_wdt_private.smi_res)
goto out;
p->smi_res = platform_get_resource(pdev, IORESOURCE_IO, ICH_RES_IO_SMI);
if (!p->smi_res)
return -ENODEV;
iTCO_wdt_private.iTCO_version = pdata->version;
iTCO_wdt_private.dev = dev;
iTCO_wdt_private.pdev = to_pci_dev(dev->dev.parent);
p->iTCO_version = pdata->version;
p->pci_dev = to_pci_dev(dev->parent);
/*
* Get the Memory-Mapped GCS or PMC register, we need it for the
* NO_REBOOT flag (TCO v2 and v3).
*/
if (iTCO_wdt_private.iTCO_version >= 2) {
iTCO_wdt_private.gcs_pmc_res = platform_get_resource(dev,
IORESOURCE_MEM,
ICH_RES_MEM_GCS_PMC);
if (!iTCO_wdt_private.gcs_pmc_res)
goto out;
if (!request_mem_region(iTCO_wdt_private.gcs_pmc_res->start,
resource_size(iTCO_wdt_private.gcs_pmc_res), dev->name)) {
ret = -EBUSY;
goto out;
}
iTCO_wdt_private.gcs_pmc = ioremap(iTCO_wdt_private.gcs_pmc_res->start,
resource_size(iTCO_wdt_private.gcs_pmc_res));
if (!iTCO_wdt_private.gcs_pmc) {
ret = -EIO;
goto unreg_gcs_pmc;
}
if (p->iTCO_version >= 2) {
p->gcs_pmc_res = platform_get_resource(pdev,
IORESOURCE_MEM,
ICH_RES_MEM_GCS_PMC);
p->gcs_pmc = devm_ioremap_resource(dev, p->gcs_pmc_res);
if (IS_ERR(p->gcs_pmc))
return PTR_ERR(p->gcs_pmc);
}
/* Check chipset's NO_REBOOT bit */
if (iTCO_wdt_unset_NO_REBOOT_bit() && iTCO_vendor_check_noreboot_on()) {
if (iTCO_wdt_unset_NO_REBOOT_bit(p) &&
iTCO_vendor_check_noreboot_on()) {
pr_info("unable to reset NO_REBOOT flag, device disabled by hardware/BIOS\n");
ret = -ENODEV; /* Cannot reset NO_REBOOT bit */
goto unmap_gcs_pmc;
return -ENODEV; /* Cannot reset NO_REBOOT bit */
}
/* Set the NO_REBOOT bit to prevent later reboots, just for sure */
iTCO_wdt_set_NO_REBOOT_bit();
iTCO_wdt_set_NO_REBOOT_bit(p);
/* The TCO logic uses the TCO_EN bit in the SMI_EN register */
if (!request_region(iTCO_wdt_private.smi_res->start,
resource_size(iTCO_wdt_private.smi_res), dev->name)) {
if (!devm_request_region(dev, p->smi_res->start,
resource_size(p->smi_res),
pdev->name)) {
pr_err("I/O address 0x%04llx already in use, device disabled\n",
(u64)SMI_EN);
ret = -EBUSY;
goto unmap_gcs_pmc;
(u64)SMI_EN(p));
return -EBUSY;
}
if (turn_SMI_watchdog_clear_off >= iTCO_wdt_private.iTCO_version) {
if (turn_SMI_watchdog_clear_off >= p->iTCO_version) {
/*
* Bit 13: TCO_EN -> 0
* Disables TCO logic generating an SMI#
*/
val32 = inl(SMI_EN);
val32 = inl(SMI_EN(p));
val32 &= 0xffffdfff; /* Turn off SMI clearing watchdog */
outl(val32, SMI_EN);
outl(val32, SMI_EN(p));
}
if (!request_region(iTCO_wdt_private.tco_res->start,
resource_size(iTCO_wdt_private.tco_res), dev->name)) {
if (!devm_request_region(dev, p->tco_res->start,
resource_size(p->tco_res),
pdev->name)) {
pr_err("I/O address 0x%04llx already in use, device disabled\n",
(u64)TCOBASE);
ret = -EBUSY;
goto unreg_smi;
(u64)TCOBASE(p));
return -EBUSY;
}
pr_info("Found a %s TCO device (Version=%d, TCOBASE=0x%04llx)\n",
pdata->name, pdata->version, (u64)TCOBASE);
pdata->name, pdata->version, (u64)TCOBASE(p));
/* Clear out the (probably old) status */
switch (iTCO_wdt_private.iTCO_version) {
switch (p->iTCO_version) {
case 5:
case 4:
outw(0x0008, TCO1_STS); /* Clear the Time Out Status bit */
outw(0x0002, TCO2_STS); /* Clear SECOND_TO_STS bit */
outw(0x0008, TCO1_STS(p)); /* Clear the Time Out Status bit */
outw(0x0002, TCO2_STS(p)); /* Clear SECOND_TO_STS bit */
break;
case 3:
outl(0x20008, TCO1_STS);
outl(0x20008, TCO1_STS(p));
break;
case 2:
case 1:
default:
outw(0x0008, TCO1_STS); /* Clear the Time Out Status bit */
outw(0x0002, TCO2_STS); /* Clear SECOND_TO_STS bit */
outw(0x0004, TCO2_STS); /* Clear BOOT_STS bit */
outw(0x0008, TCO1_STS(p)); /* Clear the Time Out Status bit */
outw(0x0002, TCO2_STS(p)); /* Clear SECOND_TO_STS bit */
outw(0x0004, TCO2_STS(p)); /* Clear BOOT_STS bit */
break;
}
iTCO_wdt_watchdog_dev.bootstatus = 0;
iTCO_wdt_watchdog_dev.timeout = WATCHDOG_TIMEOUT;
watchdog_set_nowayout(&iTCO_wdt_watchdog_dev, nowayout);
iTCO_wdt_watchdog_dev.parent = &dev->dev;
p->wddev.info = &ident,
p->wddev.ops = &iTCO_wdt_ops,
p->wddev.bootstatus = 0;
p->wddev.timeout = WATCHDOG_TIMEOUT;
watchdog_set_nowayout(&p->wddev, nowayout);
p->wddev.parent = dev;
watchdog_set_drvdata(&p->wddev, p);
platform_set_drvdata(pdev, p);
/* Make sure the watchdog is not running */
iTCO_wdt_stop(&iTCO_wdt_watchdog_dev);
iTCO_wdt_stop(&p->wddev);
/* Check that the heartbeat value is within it's range;
if not reset to the default */
if (iTCO_wdt_set_timeout(&iTCO_wdt_watchdog_dev, heartbeat)) {
iTCO_wdt_set_timeout(&iTCO_wdt_watchdog_dev, WATCHDOG_TIMEOUT);
if (iTCO_wdt_set_timeout(&p->wddev, heartbeat)) {
iTCO_wdt_set_timeout(&p->wddev, WATCHDOG_TIMEOUT);
pr_info("timeout value out of range, using %d\n",
WATCHDOG_TIMEOUT);
}
ret = watchdog_register_device(&iTCO_wdt_watchdog_dev);
watchdog_stop_on_reboot(&p->wddev);
ret = devm_watchdog_register_device(dev, &p->wddev);
if (ret != 0) {
pr_err("cannot register watchdog device (err=%d)\n", ret);
goto unreg_tco;
return ret;
}
pr_info("initialized. heartbeat=%d sec (nowayout=%d)\n",
heartbeat, nowayout);
return 0;
unreg_tco:
release_region(iTCO_wdt_private.tco_res->start,
resource_size(iTCO_wdt_private.tco_res));
unreg_smi:
release_region(iTCO_wdt_private.smi_res->start,
resource_size(iTCO_wdt_private.smi_res));
unmap_gcs_pmc:
if (iTCO_wdt_private.iTCO_version >= 2)
iounmap(iTCO_wdt_private.gcs_pmc);
unreg_gcs_pmc:
if (iTCO_wdt_private.iTCO_version >= 2)
release_mem_region(iTCO_wdt_private.gcs_pmc_res->start,
resource_size(iTCO_wdt_private.gcs_pmc_res));
out:
iTCO_wdt_private.tco_res = NULL;
iTCO_wdt_private.smi_res = NULL;
iTCO_wdt_private.gcs_pmc_res = NULL;
iTCO_wdt_private.gcs_pmc = NULL;
return ret;
}
static int iTCO_wdt_remove(struct platform_device *dev)
static int iTCO_wdt_remove(struct platform_device *pdev)
{
if (iTCO_wdt_private.tco_res || iTCO_wdt_private.smi_res)
iTCO_wdt_cleanup();
struct iTCO_wdt_private *p = platform_get_drvdata(pdev);
return 0;
}
/* Stop the timer before we leave */
if (!nowayout)
iTCO_wdt_stop(&p->wddev);
static void iTCO_wdt_shutdown(struct platform_device *dev)
{
iTCO_wdt_stop(NULL);
return 0;
}
#ifdef CONFIG_PM_SLEEP
......@@ -610,21 +562,24 @@ static inline bool need_suspend(void) { return true; }
static int iTCO_wdt_suspend_noirq(struct device *dev)
{
struct iTCO_wdt_private *p = dev_get_drvdata(dev);
int ret = 0;
iTCO_wdt_private.suspended = false;
if (watchdog_active(&iTCO_wdt_watchdog_dev) && need_suspend()) {
ret = iTCO_wdt_stop(&iTCO_wdt_watchdog_dev);
p->suspended = false;
if (watchdog_active(&p->wddev) && need_suspend()) {
ret = iTCO_wdt_stop(&p->wddev);
if (!ret)
iTCO_wdt_private.suspended = true;
p->suspended = true;
}
return ret;
}
static int iTCO_wdt_resume_noirq(struct device *dev)
{
if (iTCO_wdt_private.suspended)
iTCO_wdt_start(&iTCO_wdt_watchdog_dev);
struct iTCO_wdt_private *p = dev_get_drvdata(dev);
if (p->suspended)
iTCO_wdt_start(&p->wddev);
return 0;
}
......@@ -642,7 +597,6 @@ static const struct dev_pm_ops iTCO_wdt_pm = {
static struct platform_driver iTCO_wdt_driver = {
.probe = iTCO_wdt_probe,
.remove = iTCO_wdt_remove,
.shutdown = iTCO_wdt_shutdown,
.driver = {
.name = DRV_NAME,
.pm = ITCO_WDT_PM_OPS,
......@@ -651,15 +605,9 @@ static struct platform_driver iTCO_wdt_driver = {
static int __init iTCO_wdt_init_module(void)
{
int err;
pr_info("Intel TCO WatchDog Timer Driver v%s\n", DRV_VERSION);
err = platform_driver_register(&iTCO_wdt_driver);
if (err)
return err;
return 0;
return platform_driver_register(&iTCO_wdt_driver);
}
static void __exit iTCO_wdt_cleanup_module(void)
......
......@@ -161,7 +161,7 @@ static int pdc_wdt_restart(struct watchdog_device *wdt_dev,
return 0;
}
static struct watchdog_info pdc_wdt_info = {
static const struct watchdog_info pdc_wdt_info = {
.identity = "IMG PDC Watchdog",
.options = WDIOF_SETTIMEOUT |
WDIOF_KEEPALIVEPING |
......
......@@ -137,7 +137,6 @@ static int mid_wdt_probe(struct platform_device *pdev)
wdt_dev->parent = &pdev->dev;
watchdog_set_drvdata(wdt_dev, &pdev->dev);
platform_set_drvdata(pdev, wdt_dev);
ret = devm_request_irq(&pdev->dev, pdata->irq, mid_wdt_irq,
IRQF_SHARED | IRQF_NO_SUSPEND, "watchdog",
......@@ -151,7 +150,7 @@ static int mid_wdt_probe(struct platform_device *pdev)
/* Make sure the watchdog is not running */
wdt_stop(wdt_dev);
ret = watchdog_register_device(wdt_dev);
ret = devm_watchdog_register_device(&pdev->dev, wdt_dev);
if (ret) {
dev_err(&pdev->dev, "error registering watchdog device\n");
return ret;
......@@ -162,16 +161,8 @@ static int mid_wdt_probe(struct platform_device *pdev)
return 0;
}
static int mid_wdt_remove(struct platform_device *pdev)
{
struct watchdog_device *wd = platform_get_drvdata(pdev);
watchdog_unregister_device(wd);
return 0;
}
static struct platform_driver mid_wdt_driver = {
.probe = mid_wdt_probe,
.remove = mid_wdt_remove,
.driver = {
.name = "intel_mid_wdt",
},
......
......@@ -422,7 +422,7 @@ static int kempld_wdt_probe_stages(struct watchdog_device *wdd)
return 0;
}
static struct watchdog_info kempld_wdt_info = {
static const struct watchdog_info kempld_wdt_info = {
.identity = "KEMPLD Watchdog",
.options = WDIOF_SETTIMEOUT |
WDIOF_KEEPALIVEPING |
......
......@@ -3,7 +3,7 @@
* under the terms of the GNU General Public License version 2 as published
* by the Free Software Foundation.
*
* Copyright (C) 2010 John Crispin <blogic@openwrt.org>
* Copyright (C) 2010 John Crispin <john@phrozen.org>
* Based on EP93xx wdt driver
*/
......@@ -240,6 +240,6 @@ module_platform_driver(ltq_wdt_driver);
module_param(nowayout, bool, 0);
MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started");
MODULE_AUTHOR("John Crispin <blogic@openwrt.org>");
MODULE_AUTHOR("John Crispin <john@phrozen.org>");
MODULE_DESCRIPTION("Lantiq SoC Watchdog");
MODULE_LICENSE("GPL");
......@@ -181,7 +181,7 @@ static int lpc18xx_wdt_restart(struct watchdog_device *wdt_dev,
return 0;
}
static struct watchdog_info lpc18xx_wdt_info = {
static const struct watchdog_info lpc18xx_wdt_info = {
.identity = "NXP LPC18xx Watchdog",
.options = WDIOF_SETTIMEOUT |
WDIOF_KEEPALIVEPING |
......
......@@ -212,33 +212,14 @@ static int a21_wdt_probe(struct platform_device *pdev)
drv->wdt = a21_wdt;
dev_set_drvdata(&pdev->dev, drv);
ret = watchdog_register_device(&a21_wdt);
ret = devm_watchdog_register_device(&pdev->dev, &a21_wdt);
if (ret) {
dev_err(&pdev->dev, "Cannot register watchdog device\n");
goto err_register_wd;
return ret;
}
dev_info(&pdev->dev, "MEN A21 watchdog timer driver enabled\n");
return 0;
err_register_wd:
mutex_destroy(&drv->lock);
return ret;
}
static int a21_wdt_remove(struct platform_device *pdev)
{
struct a21_wdt_drv *drv = dev_get_drvdata(&pdev->dev);
dev_warn(&pdev->dev,
"Unregistering A21 watchdog driver, board may reboot\n");
watchdog_unregister_device(&drv->wdt);
mutex_destroy(&drv->lock);
return 0;
}
......@@ -257,7 +238,6 @@ MODULE_DEVICE_TABLE(of, a21_wdt_ids);
static struct platform_driver a21_wdt_driver = {
.probe = a21_wdt_probe,
.remove = a21_wdt_remove,
.shutdown = a21_wdt_shutdown,
.driver = {
.name = "a21-watchdog",
......
......@@ -201,38 +201,19 @@ static int meson_wdt_probe(struct platform_device *pdev)
meson_wdt_stop(&meson_wdt->wdt_dev);
err = watchdog_register_device(&meson_wdt->wdt_dev);
watchdog_stop_on_reboot(&meson_wdt->wdt_dev);
err = devm_watchdog_register_device(&pdev->dev, &meson_wdt->wdt_dev);
if (err)
return err;
platform_set_drvdata(pdev, meson_wdt);
dev_info(&pdev->dev, "Watchdog enabled (timeout=%d sec, nowayout=%d)",
meson_wdt->wdt_dev.timeout, nowayout);
return 0;
}
static int meson_wdt_remove(struct platform_device *pdev)
{
struct meson_wdt_dev *meson_wdt = platform_get_drvdata(pdev);
watchdog_unregister_device(&meson_wdt->wdt_dev);
return 0;
}
static void meson_wdt_shutdown(struct platform_device *pdev)
{
struct meson_wdt_dev *meson_wdt = platform_get_drvdata(pdev);
meson_wdt_stop(&meson_wdt->wdt_dev);
}
static struct platform_driver meson_wdt_driver = {
.probe = meson_wdt_probe,
.remove = meson_wdt_remove,
.shutdown = meson_wdt_shutdown,
.driver = {
.name = DRV_NAME,
.of_match_table = meson_wdt_dt_ids,
......
/*
* Ralink MT7621/MT7628 built-in hardware watchdog timer
*
* Copyright (C) 2014 John Crispin <blogic@openwrt.org>
* Copyright (C) 2014 John Crispin <john@phrozen.org>
*
* This driver was based on: drivers/watchdog/rt2880_wdt.c
*
......@@ -110,7 +110,7 @@ static struct watchdog_info mt7621_wdt_info = {
.options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE,
};
static struct watchdog_ops mt7621_wdt_ops = {
static const struct watchdog_ops mt7621_wdt_ops = {
.owner = THIS_MODULE,
.start = mt7621_wdt_start,
.stop = mt7621_wdt_stop,
......@@ -181,5 +181,5 @@ static struct platform_driver mt7621_wdt_driver = {
module_platform_driver(mt7621_wdt_driver);
MODULE_DESCRIPTION("MediaTek MT762x hardware watchdog driver");
MODULE_AUTHOR("John Crispin <blogic@openwrt.org");
MODULE_AUTHOR("John Crispin <john@phrozen.org");
MODULE_LICENSE("GPL v2");
/*
* Copyright (C) 2016 National Instruments Corp.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include <linux/acpi.h>
#include <linux/bitops.h>
#include <linux/device.h>
#include <linux/io.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/watchdog.h>
#define LOCK 0xA5
#define UNLOCK 0x5A
#define WDT_CTRL_RESET_EN BIT(7)
#define WDT_RELOAD_PORT_EN BIT(7)
#define WDT_CTRL 1
#define WDT_RELOAD_CTRL 2
#define WDT_PRESET_PRESCALE 4
#define WDT_REG_LOCK 5
#define WDT_COUNT 6
#define WDT_RELOAD_PORT 7
#define WDT_MIN_TIMEOUT 1
#define WDT_MAX_TIMEOUT 464
#define WDT_DEFAULT_TIMEOUT 80
#define WDT_MAX_COUNTER 15
static unsigned int timeout;
module_param(timeout, uint, 0);
MODULE_PARM_DESC(timeout,
"Watchdog timeout in seconds. (default="
__MODULE_STRING(WDT_DEFAULT_TIMEOUT) ")");
static bool nowayout = WATCHDOG_NOWAYOUT;
module_param(nowayout, bool, 0);
MODULE_PARM_DESC(nowayout,
"Watchdog cannot be stopped once started. (default="
__MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
struct nic7018_wdt {
u16 io_base;
u32 period;
struct watchdog_device wdd;
};
struct nic7018_config {
u32 period;
u8 divider;
};
static const struct nic7018_config nic7018_configs[] = {
{ 2, 4 },
{ 32, 5 },
};
static inline u32 nic7018_timeout(u32 period, u8 counter)
{
return period * counter - period / 2;
}
static const struct nic7018_config *nic7018_get_config(u32 timeout,
u8 *counter)
{
const struct nic7018_config *config;
u8 count;
if (timeout < 30 && timeout != 16) {
config = &nic7018_configs[0];
count = timeout / 2 + 1;
} else {
config = &nic7018_configs[1];
count = DIV_ROUND_UP(timeout + 16, 32);
if (count > WDT_MAX_COUNTER)
count = WDT_MAX_COUNTER;
}
*counter = count;
return config;
}
static int nic7018_set_timeout(struct watchdog_device *wdd,
unsigned int timeout)
{
struct nic7018_wdt *wdt = watchdog_get_drvdata(wdd);
const struct nic7018_config *config;
u8 counter;
config = nic7018_get_config(timeout, &counter);
outb(counter << 4 | config->divider,
wdt->io_base + WDT_PRESET_PRESCALE);
wdd->timeout = nic7018_timeout(config->period, counter);
wdt->period = config->period;
return 0;
}
static int nic7018_start(struct watchdog_device *wdd)
{
struct nic7018_wdt *wdt = watchdog_get_drvdata(wdd);
u8 control;
nic7018_set_timeout(wdd, wdd->timeout);
control = inb(wdt->io_base + WDT_RELOAD_CTRL);
outb(control | WDT_RELOAD_PORT_EN, wdt->io_base + WDT_RELOAD_CTRL);
outb(1, wdt->io_base + WDT_RELOAD_PORT);
control = inb(wdt->io_base + WDT_CTRL);
outb(control | WDT_CTRL_RESET_EN, wdt->io_base + WDT_CTRL);
return 0;
}
static int nic7018_stop(struct watchdog_device *wdd)
{
struct nic7018_wdt *wdt = watchdog_get_drvdata(wdd);
outb(0, wdt->io_base + WDT_CTRL);
outb(0, wdt->io_base + WDT_RELOAD_CTRL);
outb(0xF0, wdt->io_base + WDT_PRESET_PRESCALE);
return 0;
}
static int nic7018_ping(struct watchdog_device *wdd)
{
struct nic7018_wdt *wdt = watchdog_get_drvdata(wdd);
outb(1, wdt->io_base + WDT_RELOAD_PORT);
return 0;
}
static unsigned int nic7018_get_timeleft(struct watchdog_device *wdd)
{
struct nic7018_wdt *wdt = watchdog_get_drvdata(wdd);
u8 count;
count = inb(wdt->io_base + WDT_COUNT) & 0xF;
if (!count)
return 0;
return nic7018_timeout(wdt->period, count);
}
static const struct watchdog_info nic7018_wdd_info = {
.options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE,
.identity = "NIC7018 Watchdog",
};
static const struct watchdog_ops nic7018_wdd_ops = {
.owner = THIS_MODULE,
.start = nic7018_start,
.stop = nic7018_stop,
.ping = nic7018_ping,
.set_timeout = nic7018_set_timeout,
.get_timeleft = nic7018_get_timeleft,
};
static int nic7018_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct watchdog_device *wdd;
struct nic7018_wdt *wdt;
struct resource *io_rc;
int ret;
wdt = devm_kzalloc(dev, sizeof(*wdt), GFP_KERNEL);
if (!wdt)
return -ENOMEM;
platform_set_drvdata(pdev, wdt);
io_rc = platform_get_resource(pdev, IORESOURCE_IO, 0);
if (!io_rc) {
dev_err(dev, "missing IO resources\n");
return -EINVAL;
}
if (!devm_request_region(dev, io_rc->start, resource_size(io_rc),
KBUILD_MODNAME)) {
dev_err(dev, "failed to get IO region\n");
return -EBUSY;
}
wdt->io_base = io_rc->start;
wdd = &wdt->wdd;
wdd->info = &nic7018_wdd_info;
wdd->ops = &nic7018_wdd_ops;
wdd->min_timeout = WDT_MIN_TIMEOUT;
wdd->max_timeout = WDT_MAX_TIMEOUT;
wdd->timeout = WDT_DEFAULT_TIMEOUT;
wdd->parent = dev;
watchdog_set_drvdata(wdd, wdt);
watchdog_set_nowayout(wdd, nowayout);
ret = watchdog_init_timeout(wdd, timeout, dev);
if (ret)
dev_warn(dev, "unable to set timeout value, using default\n");
/* Unlock WDT register */
outb(UNLOCK, wdt->io_base + WDT_REG_LOCK);
ret = watchdog_register_device(wdd);
if (ret) {
outb(LOCK, wdt->io_base + WDT_REG_LOCK);
dev_err(dev, "failed to register watchdog\n");
return ret;
}
dev_dbg(dev, "io_base=0x%04X, timeout=%d, nowayout=%d\n",
wdt->io_base, timeout, nowayout);
return 0;
}
static int nic7018_remove(struct platform_device *pdev)
{
struct nic7018_wdt *wdt = platform_get_drvdata(pdev);
watchdog_unregister_device(&wdt->wdd);
/* Lock WDT register */
outb(LOCK, wdt->io_base + WDT_REG_LOCK);
return 0;
}
static const struct acpi_device_id nic7018_device_ids[] = {
{"NIC7018", 0},
{"", 0},
};
MODULE_DEVICE_TABLE(acpi, nic7018_device_ids);
static struct platform_driver watchdog_driver = {
.probe = nic7018_probe,
.remove = nic7018_remove,
.driver = {
.name = KBUILD_MODNAME,
.acpi_match_table = ACPI_PTR(nic7018_device_ids),
},
};
module_platform_driver(watchdog_driver);
MODULE_DESCRIPTION("National Instruments NIC7018 Watchdog driver");
MODULE_AUTHOR("Hui Chun Ong <hui.chun.ong@ni.com>");
MODULE_LICENSE("GPL");
......@@ -395,7 +395,7 @@ static void __iomem *orion_wdt_ioremap_rstout(struct platform_device *pdev,
rstout = internal_regs + ORION_RSTOUT_MASK_OFFSET;
WARN(1, FW_BUG "falling back to harcoded RSTOUT reg %pa\n", &rstout);
WARN(1, FW_BUG "falling back to hardcoded RSTOUT reg %pa\n", &rstout);
return devm_ioremap(&pdev->dev, rstout, 0x4);
}
......
......@@ -54,7 +54,7 @@ static struct {
struct timer_list timer; /* The timer that pings the watchdog */
} pikawdt_private;
static struct watchdog_info ident = {
static struct watchdog_info ident __ro_after_init = {
.identity = DRV_NAME,
.options = WDIOF_CARDRESET |
WDIOF_SETTIMEOUT |
......
......@@ -130,7 +130,7 @@ static int rn5t618_wdt_ping(struct watchdog_device *wdt_dev)
RN5T618_PWRIRQ_IR_WDOG, 0);
}
static struct watchdog_info rn5t618_wdt_info = {
static const struct watchdog_info rn5t618_wdt_info = {
.options = WDIOF_SETTIMEOUT | WDIOF_MAGICCLOSE |
WDIOF_KEEPALIVEPING,
.identity = DRIVER_NAME,
......
......@@ -2,7 +2,7 @@
* Ralink RT288x/RT3xxx/MT76xx built-in hardware watchdog timer
*
* Copyright (C) 2011 Gabor Juhos <juhosg@openwrt.org>
* Copyright (C) 2013 John Crispin <blogic@openwrt.org>
* Copyright (C) 2013 John Crispin <john@phrozen.org>
*
* This driver was based on: drivers/watchdog/softdog.c
*
......@@ -124,7 +124,7 @@ static struct watchdog_info rt288x_wdt_info = {
.options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE,
};
static struct watchdog_ops rt288x_wdt_ops = {
static const struct watchdog_ops rt288x_wdt_ops = {
.owner = THIS_MODULE,
.start = rt288x_wdt_start,
.stop = rt288x_wdt_stop,
......
......@@ -23,8 +23,6 @@
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/types.h>
......@@ -46,6 +44,7 @@
#define S3C2410_WTCON 0x00
#define S3C2410_WTDAT 0x04
#define S3C2410_WTCNT 0x08
#define S3C2410_WTCLRINT 0x0c
#define S3C2410_WTCNT_MAXCNT 0xffff
......@@ -64,14 +63,15 @@
#define S3C2410_WTCON_PRESCALE_MASK (0xff << 8)
#define S3C2410_WTCON_PRESCALE_MAX 0xff
#define CONFIG_S3C2410_WATCHDOG_ATBOOT (0)
#define CONFIG_S3C2410_WATCHDOG_DEFAULT_TIME (15)
#define S3C2410_WATCHDOG_ATBOOT (0)
#define S3C2410_WATCHDOG_DEFAULT_TIME (15)
#define EXYNOS5_RST_STAT_REG_OFFSET 0x0404
#define EXYNOS5_WDT_DISABLE_REG_OFFSET 0x0408
#define EXYNOS5_WDT_MASK_RESET_REG_OFFSET 0x040c
#define QUIRK_HAS_PMU_CONFIG (1 << 0)
#define QUIRK_HAS_RST_STAT (1 << 1)
#define QUIRK_HAS_WTCLRINT_REG (1 << 2)
/* These quirks require that we have a PMU register map */
#define QUIRKS_HAVE_PMUREG (QUIRK_HAS_PMU_CONFIG | \
......@@ -79,26 +79,23 @@
static bool nowayout = WATCHDOG_NOWAYOUT;
static int tmr_margin;
static int tmr_atboot = CONFIG_S3C2410_WATCHDOG_ATBOOT;
static int tmr_atboot = S3C2410_WATCHDOG_ATBOOT;
static int soft_noboot;
static int debug;
module_param(tmr_margin, int, 0);
module_param(tmr_atboot, int, 0);
module_param(nowayout, bool, 0);
module_param(soft_noboot, int, 0);
module_param(debug, int, 0);
MODULE_PARM_DESC(tmr_margin, "Watchdog tmr_margin in seconds. (default="
__MODULE_STRING(CONFIG_S3C2410_WATCHDOG_DEFAULT_TIME) ")");
__MODULE_STRING(S3C2410_WATCHDOG_DEFAULT_TIME) ")");
MODULE_PARM_DESC(tmr_atboot,
"Watchdog is started at boot time if set to 1, default="
__MODULE_STRING(CONFIG_S3C2410_WATCHDOG_ATBOOT));
__MODULE_STRING(S3C2410_WATCHDOG_ATBOOT));
MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default="
__MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
MODULE_PARM_DESC(soft_noboot, "Watchdog action, set to 1 to ignore reboots, "
"0 to reboot (default 0)");
MODULE_PARM_DESC(debug, "Watchdog debug, set to >1 for debug (default 0)");
/**
* struct s3c2410_wdt_variant - Per-variant config data
......@@ -143,13 +140,18 @@ static const struct s3c2410_wdt_variant drv_data_s3c2410 = {
};
#ifdef CONFIG_OF
static const struct s3c2410_wdt_variant drv_data_s3c6410 = {
.quirks = QUIRK_HAS_WTCLRINT_REG,
};
static const struct s3c2410_wdt_variant drv_data_exynos5250 = {
.disable_reg = EXYNOS5_WDT_DISABLE_REG_OFFSET,
.mask_reset_reg = EXYNOS5_WDT_MASK_RESET_REG_OFFSET,
.mask_bit = 20,
.rst_stat_reg = EXYNOS5_RST_STAT_REG_OFFSET,
.rst_stat_bit = 20,
.quirks = QUIRK_HAS_PMU_CONFIG | QUIRK_HAS_RST_STAT,
.quirks = QUIRK_HAS_PMU_CONFIG | QUIRK_HAS_RST_STAT \
| QUIRK_HAS_WTCLRINT_REG,
};
static const struct s3c2410_wdt_variant drv_data_exynos5420 = {
......@@ -158,7 +160,8 @@ static const struct s3c2410_wdt_variant drv_data_exynos5420 = {
.mask_bit = 0,
.rst_stat_reg = EXYNOS5_RST_STAT_REG_OFFSET,
.rst_stat_bit = 9,
.quirks = QUIRK_HAS_PMU_CONFIG | QUIRK_HAS_RST_STAT,
.quirks = QUIRK_HAS_PMU_CONFIG | QUIRK_HAS_RST_STAT \
| QUIRK_HAS_WTCLRINT_REG,
};
static const struct s3c2410_wdt_variant drv_data_exynos7 = {
......@@ -167,12 +170,15 @@ static const struct s3c2410_wdt_variant drv_data_exynos7 = {
.mask_bit = 23,
.rst_stat_reg = EXYNOS5_RST_STAT_REG_OFFSET,
.rst_stat_bit = 23, /* A57 WDTRESET */
.quirks = QUIRK_HAS_PMU_CONFIG | QUIRK_HAS_RST_STAT,
.quirks = QUIRK_HAS_PMU_CONFIG | QUIRK_HAS_RST_STAT \
| QUIRK_HAS_WTCLRINT_REG,
};
static const struct of_device_id s3c2410_wdt_match[] = {
{ .compatible = "samsung,s3c2410-wdt",
.data = &drv_data_s3c2410 },
{ .compatible = "samsung,s3c6410-wdt",
.data = &drv_data_s3c6410 },
{ .compatible = "samsung,exynos5250-wdt",
.data = &drv_data_exynos5250 },
{ .compatible = "samsung,exynos5420-wdt",
......@@ -193,14 +199,6 @@ static const struct platform_device_id s3c2410_wdt_ids[] = {
};
MODULE_DEVICE_TABLE(platform, s3c2410_wdt_ids);
/* watchdog control routines */
#define DBG(fmt, ...) \
do { \
if (debug) \
pr_info(fmt, ##__VA_ARGS__); \
} while (0)
/* functions */
static inline unsigned int s3c2410wdt_max_timeout(struct clk *clock)
......@@ -296,8 +294,8 @@ static int s3c2410wdt_start(struct watchdog_device *wdd)
wtcon |= S3C2410_WTCON_RSTEN;
}
DBG("%s: count=0x%08x, wtcon=%08lx\n",
__func__, wdt->count, wtcon);
dev_dbg(wdt->dev, "Starting watchdog: count=0x%08x, wtcon=%08lx\n",
wdt->count, wtcon);
writel(wdt->count, wdt->reg_base + S3C2410_WTDAT);
writel(wdt->count, wdt->reg_base + S3C2410_WTCNT);
......@@ -326,8 +324,8 @@ static int s3c2410wdt_set_heartbeat(struct watchdog_device *wdd, unsigned timeou
freq = DIV_ROUND_UP(freq, 128);
count = timeout * freq;
DBG("%s: count=%d, timeout=%d, freq=%lu\n",
__func__, count, timeout, freq);
dev_dbg(wdt->dev, "Heartbeat: count=%d, timeout=%d, freq=%lu\n",
count, timeout, freq);
/* if the count is bigger than the watchdog register,
then work out what we need to do (and if) we can
......@@ -343,8 +341,8 @@ static int s3c2410wdt_set_heartbeat(struct watchdog_device *wdd, unsigned timeou
}
}
DBG("%s: timeout=%d, divisor=%d, count=%d (%08x)\n",
__func__, timeout, divisor, count, DIV_ROUND_UP(count, divisor));
dev_dbg(wdt->dev, "Heartbeat: timeout=%d, divisor=%d, count=%d (%08x)\n",
timeout, divisor, count, DIV_ROUND_UP(count, divisor));
count = DIV_ROUND_UP(count, divisor);
wdt->count = count;
......@@ -394,7 +392,7 @@ static const struct watchdog_info s3c2410_wdt_ident = {
.identity = "S3C2410 Watchdog",
};
static struct watchdog_ops s3c2410wdt_ops = {
static const struct watchdog_ops s3c2410wdt_ops = {
.owner = THIS_MODULE,
.start = s3c2410wdt_start,
.stop = s3c2410wdt_stop,
......@@ -406,7 +404,7 @@ static struct watchdog_ops s3c2410wdt_ops = {
static struct watchdog_device s3c2410_wdd = {
.info = &s3c2410_wdt_ident,
.ops = &s3c2410wdt_ops,
.timeout = CONFIG_S3C2410_WATCHDOG_DEFAULT_TIME,
.timeout = S3C2410_WATCHDOG_DEFAULT_TIME,
};
/* interrupt handler code */
......@@ -418,6 +416,10 @@ static irqreturn_t s3c2410wdt_irq(int irqno, void *param)
dev_info(wdt->dev, "watchdog timer expired (irq)\n");
s3c2410wdt_keepalive(&wdt->wdt_device);
if (wdt->drv_data->quirks & QUIRK_HAS_WTCLRINT_REG)
writel(0x1, wdt->reg_base + S3C2410_WTCLRINT);
return IRQ_HANDLED;
}
......@@ -505,9 +507,8 @@ static inline unsigned int s3c2410wdt_get_bootstatus(struct s3c2410_wdt *wdt)
return 0;
}
/* s3c2410_get_wdt_driver_data */
static inline struct s3c2410_wdt_variant *
get_wdt_drv_data(struct platform_device *pdev)
s3c2410_get_wdt_drv_data(struct platform_device *pdev)
{
if (pdev->dev.of_node) {
const struct of_device_id *match;
......@@ -529,8 +530,6 @@ static int s3c2410wdt_probe(struct platform_device *pdev)
int started = 0;
int ret;
DBG("%s: probe=%p\n", __func__, pdev);
dev = &pdev->dev;
wdt = devm_kzalloc(dev, sizeof(*wdt), GFP_KERNEL);
......@@ -541,7 +540,7 @@ static int s3c2410wdt_probe(struct platform_device *pdev)
spin_lock_init(&wdt->lock);
wdt->wdt_device = s3c2410_wdd;
wdt->drv_data = get_wdt_drv_data(pdev);
wdt->drv_data = s3c2410_get_wdt_drv_data(pdev);
if (wdt->drv_data->quirks & QUIRKS_HAVE_PMUREG) {
wdt->pmureg = syscon_regmap_lookup_by_phandle(dev->of_node,
"samsung,syscon-phandle");
......@@ -566,8 +565,6 @@ static int s3c2410wdt_probe(struct platform_device *pdev)
goto err;
}
DBG("probe: mapped reg_base=%p\n", wdt->reg_base);
wdt->clock = devm_clk_get(dev, "watchdog");
if (IS_ERR(wdt->clock)) {
dev_err(dev, "failed to find watchdog clock source\n");
......@@ -600,12 +597,12 @@ static int s3c2410wdt_probe(struct platform_device *pdev)
wdt->wdt_device.timeout);
if (ret) {
started = s3c2410wdt_set_heartbeat(&wdt->wdt_device,
CONFIG_S3C2410_WATCHDOG_DEFAULT_TIME);
S3C2410_WATCHDOG_DEFAULT_TIME);
if (started == 0)
dev_info(dev,
"tmr_margin value out of range, default %d used\n",
CONFIG_S3C2410_WATCHDOG_DEFAULT_TIME);
S3C2410_WATCHDOG_DEFAULT_TIME);
else
dev_info(dev, "default timer value is out of range, "
"cannot start\n");
......
......@@ -188,12 +188,14 @@ static int __init sa1100dog_init(void)
pre_margin = oscr_freq * margin;
ret = misc_register(&sa1100dog_miscdev);
if (ret == 0)
if (ret == 0) {
pr_info("SA1100/PXA2xx Watchdog Timer: timer margin %d sec\n",
margin);
return ret;
err:
return 0;
}
clk_disable_unprepare(clk);
err:
clk_put(clk);
return ret;
}
......
......@@ -28,7 +28,7 @@
struct sama5d4_wdt {
struct watchdog_device wdd;
void __iomem *reg_base;
u32 config;
u32 mr;
};
static int wdt_timeout = WDT_DEFAULT_TIMEOUT;
......@@ -53,11 +53,9 @@ MODULE_PARM_DESC(nowayout,
static int sama5d4_wdt_start(struct watchdog_device *wdd)
{
struct sama5d4_wdt *wdt = watchdog_get_drvdata(wdd);
u32 reg;
reg = wdt_read(wdt, AT91_WDT_MR);
reg &= ~AT91_WDT_WDDIS;
wdt_write(wdt, AT91_WDT_MR, reg);
wdt->mr &= ~AT91_WDT_WDDIS;
wdt_write(wdt, AT91_WDT_MR, wdt->mr);
return 0;
}
......@@ -65,11 +63,9 @@ static int sama5d4_wdt_start(struct watchdog_device *wdd)
static int sama5d4_wdt_stop(struct watchdog_device *wdd)
{
struct sama5d4_wdt *wdt = watchdog_get_drvdata(wdd);
u32 reg;
reg = wdt_read(wdt, AT91_WDT_MR);
reg |= AT91_WDT_WDDIS;
wdt_write(wdt, AT91_WDT_MR, reg);
wdt->mr |= AT91_WDT_WDDIS;
wdt_write(wdt, AT91_WDT_MR, wdt->mr);
return 0;
}
......@@ -88,14 +84,12 @@ static int sama5d4_wdt_set_timeout(struct watchdog_device *wdd,
{
struct sama5d4_wdt *wdt = watchdog_get_drvdata(wdd);
u32 value = WDT_SEC2TICKS(timeout);
u32 reg;
reg = wdt_read(wdt, AT91_WDT_MR);
reg &= ~AT91_WDT_WDV;
reg &= ~AT91_WDT_WDD;
reg |= AT91_WDT_SET_WDV(value);
reg |= AT91_WDT_SET_WDD(value);
wdt_write(wdt, AT91_WDT_MR, reg);
wdt->mr &= ~AT91_WDT_WDV;
wdt->mr &= ~AT91_WDT_WDD;
wdt->mr |= AT91_WDT_SET_WDV(value);
wdt->mr |= AT91_WDT_SET_WDD(value);
wdt_write(wdt, AT91_WDT_MR, wdt->mr);
wdd->timeout = timeout;
......@@ -107,7 +101,7 @@ static const struct watchdog_info sama5d4_wdt_info = {
.identity = "Atmel SAMA5D4 Watchdog",
};
static struct watchdog_ops sama5d4_wdt_ops = {
static const struct watchdog_ops sama5d4_wdt_ops = {
.owner = THIS_MODULE,
.start = sama5d4_wdt_start,
.stop = sama5d4_wdt_stop,
......@@ -132,19 +126,19 @@ static int of_sama5d4_wdt_init(struct device_node *np, struct sama5d4_wdt *wdt)
{
const char *tmp;
wdt->config = AT91_WDT_WDDIS;
wdt->mr = AT91_WDT_WDDIS;
if (!of_property_read_string(np, "atmel,watchdog-type", &tmp) &&
!strcmp(tmp, "software"))
wdt->config |= AT91_WDT_WDFIEN;
wdt->mr |= AT91_WDT_WDFIEN;
else
wdt->config |= AT91_WDT_WDRSTEN;
wdt->mr |= AT91_WDT_WDRSTEN;
if (of_property_read_bool(np, "atmel,idle-halt"))
wdt->config |= AT91_WDT_WDIDLEHLT;
wdt->mr |= AT91_WDT_WDIDLEHLT;
if (of_property_read_bool(np, "atmel,dbg-halt"))
wdt->config |= AT91_WDT_WDDBGHLT;
wdt->mr |= AT91_WDT_WDDBGHLT;
return 0;
}
......@@ -163,11 +157,10 @@ static int sama5d4_wdt_init(struct sama5d4_wdt *wdt)
reg &= ~AT91_WDT_WDDIS;
wdt_write(wdt, AT91_WDT_MR, reg);
reg = wdt->config;
reg |= AT91_WDT_SET_WDD(value);
reg |= AT91_WDT_SET_WDV(value);
wdt->mr |= AT91_WDT_SET_WDD(value);
wdt->mr |= AT91_WDT_SET_WDV(value);
wdt_write(wdt, AT91_WDT_MR, reg);
wdt_write(wdt, AT91_WDT_MR, wdt->mr);
return 0;
}
......@@ -211,7 +204,7 @@ static int sama5d4_wdt_probe(struct platform_device *pdev)
return ret;
}
if ((wdt->config & AT91_WDT_WDFIEN) && irq) {
if ((wdt->mr & AT91_WDT_WDFIEN) && irq) {
ret = devm_request_irq(&pdev->dev, irq, sama5d4_wdt_irq_handler,
IRQF_SHARED | IRQF_IRQPOLL |
IRQF_NO_SUSPEND, pdev->name, pdev);
......@@ -265,11 +258,28 @@ static const struct of_device_id sama5d4_wdt_of_match[] = {
};
MODULE_DEVICE_TABLE(of, sama5d4_wdt_of_match);
#ifdef CONFIG_PM_SLEEP
static int sama5d4_wdt_resume(struct device *dev)
{
struct sama5d4_wdt *wdt = dev_get_drvdata(dev);
wdt_write(wdt, AT91_WDT_MR, wdt->mr & ~AT91_WDT_WDDIS);
if (wdt->mr & AT91_WDT_WDDIS)
wdt_write(wdt, AT91_WDT_MR, wdt->mr);
return 0;
}
#endif
static SIMPLE_DEV_PM_OPS(sama5d4_wdt_pm_ops, NULL,
sama5d4_wdt_resume);
static struct platform_driver sama5d4_wdt_driver = {
.probe = sama5d4_wdt_probe,
.remove = sama5d4_wdt_remove,
.driver = {
.name = "sama5d4_wdt",
.pm = &sama5d4_wdt_pm_ops,
.of_match_table = sama5d4_wdt_of_match,
}
};
......
......@@ -207,7 +207,7 @@ static irqreturn_t sbsa_gwdt_interrupt(int irq, void *dev_id)
return IRQ_HANDLED;
}
static struct watchdog_info sbsa_gwdt_info = {
static const struct watchdog_info sbsa_gwdt_info = {
.identity = WATCHDOG_NAME,
.options = WDIOF_SETTIMEOUT |
WDIOF_KEEPALIVEPING |
......@@ -215,7 +215,7 @@ static struct watchdog_info sbsa_gwdt_info = {
WDIOF_CARDRESET,
};
static struct watchdog_ops sbsa_gwdt_ops = {
static const struct watchdog_ops sbsa_gwdt_ops = {
.owner = THIS_MODULE,
.start = sbsa_gwdt_start,
.stop = sbsa_gwdt_stop,
......
......@@ -127,7 +127,7 @@ static const struct watchdog_info sirfsoc_wdt_ident = {
.identity = "SiRFSOC Watchdog",
};
static struct watchdog_ops sirfsoc_wdt_ops = {
static const struct watchdog_ops sirfsoc_wdt_ops = {
.owner = THIS_MODULE,
.start = sirfsoc_wdt_enable,
.stop = sirfsoc_wdt_disable,
......
......@@ -87,11 +87,13 @@ static int softdog_ping(struct watchdog_device *w)
if (!mod_timer(&softdog_ticktock, jiffies + (w->timeout * HZ)))
__module_get(THIS_MODULE);
if (w->pretimeout)
mod_timer(&softdog_preticktock, jiffies +
(w->timeout - w->pretimeout) * HZ);
else
del_timer(&softdog_preticktock);
if (IS_ENABLED(CONFIG_SOFT_WATCHDOG_PRETIMEOUT)) {
if (w->pretimeout)
mod_timer(&softdog_preticktock, jiffies +
(w->timeout - w->pretimeout) * HZ);
else
del_timer(&softdog_preticktock);
}
return 0;
}
......@@ -101,15 +103,15 @@ static int softdog_stop(struct watchdog_device *w)
if (del_timer(&softdog_ticktock))
module_put(THIS_MODULE);
del_timer(&softdog_preticktock);
if (IS_ENABLED(CONFIG_SOFT_WATCHDOG_PRETIMEOUT))
del_timer(&softdog_preticktock);
return 0;
}
static struct watchdog_info softdog_info = {
.identity = "Software Watchdog",
.options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE |
WDIOF_PRETIMEOUT,
.options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE,
};
static const struct watchdog_ops softdog_ops = {
......@@ -134,6 +136,9 @@ static int __init softdog_init(void)
watchdog_set_nowayout(&softdog_dev, nowayout);
watchdog_stop_on_reboot(&softdog_dev);
if (IS_ENABLED(CONFIG_SOFT_WATCHDOG_PRETIMEOUT))
softdog_info.options |= WDIOF_PRETIMEOUT;
ret = watchdog_register_device(&softdog_dev);
if (ret)
return ret;
......
......@@ -77,7 +77,7 @@ static const struct watchdog_info sun4v_wdt_ident = {
.firmware_version = 0,
};
static struct watchdog_ops sun4v_wdt_ops = {
static const struct watchdog_ops sun4v_wdt_ops = {
.owner = THIS_MODULE,
.start = sun4v_wdt_ping,
.stop = sun4v_wdt_stop,
......
......@@ -242,8 +242,6 @@ static int sunxi_wdt_probe(struct platform_device *pdev)
if (!sunxi_wdt)
return -EINVAL;
platform_set_drvdata(pdev, sunxi_wdt);
device = of_match_device(sunxi_wdt_dt_ids, &pdev->dev);
if (!device)
return -ENODEV;
......@@ -270,7 +268,8 @@ static int sunxi_wdt_probe(struct platform_device *pdev)
sunxi_wdt_stop(&sunxi_wdt->wdt_dev);
err = watchdog_register_device(&sunxi_wdt->wdt_dev);
watchdog_stop_on_reboot(&sunxi_wdt->wdt_dev);
err = devm_watchdog_register_device(&pdev->dev, &sunxi_wdt->wdt_dev);
if (unlikely(err))
return err;
......@@ -280,27 +279,8 @@ static int sunxi_wdt_probe(struct platform_device *pdev)
return 0;
}
static int sunxi_wdt_remove(struct platform_device *pdev)
{
struct sunxi_wdt_dev *sunxi_wdt = platform_get_drvdata(pdev);
watchdog_unregister_device(&sunxi_wdt->wdt_dev);
watchdog_set_drvdata(&sunxi_wdt->wdt_dev, NULL);
return 0;
}
static void sunxi_wdt_shutdown(struct platform_device *pdev)
{
struct sunxi_wdt_dev *sunxi_wdt = platform_get_drvdata(pdev);
sunxi_wdt_stop(&sunxi_wdt->wdt_dev);
}
static struct platform_driver sunxi_wdt_driver = {
.probe = sunxi_wdt_probe,
.remove = sunxi_wdt_remove,
.shutdown = sunxi_wdt_shutdown,
.driver = {
.name = DRV_NAME,
.of_match_table = sunxi_wdt_dt_ids,
......
......@@ -15,9 +15,7 @@
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/notifier.h>
#include <linux/platform_device.h>
#include <linux/reboot.h>
#include <linux/watchdog.h>
#define DEFAULT_TIMEOUT 30
......@@ -47,7 +45,6 @@ struct tangox_wdt_device {
void __iomem *base;
unsigned long clk_rate;
struct clk *clk;
struct notifier_block restart;
};
static int tangox_wdt_set_timeout(struct watchdog_device *wdt,
......@@ -96,24 +93,24 @@ static const struct watchdog_info tangox_wdt_info = {
.identity = "tangox watchdog",
};
static int tangox_wdt_restart(struct watchdog_device *wdt,
unsigned long action, void *data)
{
struct tangox_wdt_device *dev = watchdog_get_drvdata(wdt);
writel(1, dev->base + WD_COUNTER);
return 0;
}
static const struct watchdog_ops tangox_wdt_ops = {
.start = tangox_wdt_start,
.stop = tangox_wdt_stop,
.set_timeout = tangox_wdt_set_timeout,
.get_timeleft = tangox_wdt_get_timeleft,
.restart = tangox_wdt_restart,
};
static int tangox_wdt_restart(struct notifier_block *nb, unsigned long action,
void *data)
{
struct tangox_wdt_device *dev =
container_of(nb, struct tangox_wdt_device, restart);
writel(1, dev->base + WD_COUNTER);
return NOTIFY_DONE;
}
static int tangox_wdt_probe(struct platform_device *pdev)
{
struct tangox_wdt_device *dev;
......@@ -174,18 +171,14 @@ static int tangox_wdt_probe(struct platform_device *pdev)
tangox_wdt_start(&dev->wdt);
}
watchdog_set_restart_priority(&dev->wdt, 128);
err = watchdog_register_device(&dev->wdt);
if (err)
goto err;
platform_set_drvdata(pdev, dev);
dev->restart.notifier_call = tangox_wdt_restart;
dev->restart.priority = 128;
err = register_restart_handler(&dev->restart);
if (err)
dev_warn(&pdev->dev, "failed to register restart handler\n");
dev_info(&pdev->dev, "SMP86xx/SMP87xx watchdog registered\n");
return 0;
......@@ -202,7 +195,6 @@ static int tangox_wdt_remove(struct platform_device *pdev)
tangox_wdt_stop(&dev->wdt);
clk_disable_unprepare(dev->clk);
unregister_restart_handler(&dev->restart);
watchdog_unregister_device(&dev->wdt);
return 0;
......
......@@ -226,7 +226,7 @@ static int tegra_wdt_probe(struct platform_device *pdev)
watchdog_set_nowayout(wdd, nowayout);
ret = watchdog_register_device(wdd);
ret = devm_watchdog_register_device(&pdev->dev, wdd);
if (ret) {
dev_err(&pdev->dev,
"failed to register watchdog device\n");
......@@ -248,8 +248,6 @@ static int tegra_wdt_remove(struct platform_device *pdev)
tegra_wdt_stop(&wdt->wdd);
watchdog_unregister_device(&wdt->wdd);
dev_info(&pdev->dev, "removed wdt\n");
return 0;
......
......@@ -13,428 +13,159 @@
* warranty of any kind, whether express or implied.
*/
#include <linux/fs.h>
#include <linux/io.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/miscdevice.h>
#include <linux/mutex.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/watchdog.h>
#include <linux/uaccess.h>
#include <linux/io.h>
#define TS72XX_WDT_FEED_VAL 0x05
#define TS72XX_WDT_DEFAULT_TIMEOUT 8
#define TS72XX_WDT_DEFAULT_TIMEOUT 30
static int timeout = TS72XX_WDT_DEFAULT_TIMEOUT;
static int timeout;
module_param(timeout, int, 0);
MODULE_PARM_DESC(timeout, "Watchdog timeout in seconds. "
"(1 <= timeout <= 8, default="
__MODULE_STRING(TS72XX_WDT_DEFAULT_TIMEOUT)
")");
MODULE_PARM_DESC(timeout, "Watchdog timeout in seconds.");
static bool nowayout = WATCHDOG_NOWAYOUT;
module_param(nowayout, bool, 0);
MODULE_PARM_DESC(nowayout, "Disable watchdog shutdown on close");
/**
* struct ts72xx_wdt - watchdog control structure
* @lock: lock that protects this structure
* @regval: watchdog timeout value suitable for control register
* @flags: flags controlling watchdog device state
* @control_reg: watchdog control register
* @feed_reg: watchdog feed register
* @pdev: back pointer to platform dev
*/
struct ts72xx_wdt {
struct mutex lock;
int regval;
#define TS72XX_WDT_BUSY_FLAG 1
#define TS72XX_WDT_EXPECT_CLOSE_FLAG 2
int flags;
/* priv->control_reg */
#define TS72XX_WDT_CTRL_DISABLE 0x00
#define TS72XX_WDT_CTRL_250MS 0x01
#define TS72XX_WDT_CTRL_500MS 0x02
#define TS72XX_WDT_CTRL_1SEC 0x03
#define TS72XX_WDT_CTRL_RESERVED 0x04
#define TS72XX_WDT_CTRL_2SEC 0x05
#define TS72XX_WDT_CTRL_4SEC 0x06
#define TS72XX_WDT_CTRL_8SEC 0x07
/* priv->feed_reg */
#define TS72XX_WDT_FEED_VAL 0x05
struct ts72xx_wdt_priv {
void __iomem *control_reg;
void __iomem *feed_reg;
struct platform_device *pdev;
struct watchdog_device wdd;
unsigned char regval;
};
static struct platform_device *ts72xx_wdt_pdev;
/*
* TS-72xx Watchdog supports following timeouts (value written
* to control register):
* value description
* -------------------------
* 0x00 watchdog disabled
* 0x01 250ms
* 0x02 500ms
* 0x03 1s
* 0x04 reserved
* 0x05 2s
* 0x06 4s
* 0x07 8s
*
* Timeouts below 1s are not very usable so we don't
* allow them at all.
*
* We provide two functions that convert between these:
* timeout_to_regval() and regval_to_timeout().
*/
static const struct {
int timeout;
int regval;
} ts72xx_wdt_map[] = {
{ 1, 3 },
{ 2, 5 },
{ 4, 6 },
{ 8, 7 },
};
/**
* timeout_to_regval() - converts given timeout to control register value
* @new_timeout: timeout in seconds to be converted
*
* Function converts given @new_timeout into valid value that can
* be programmed into watchdog control register. When conversion is
* not possible, function returns %-EINVAL.
*/
static int timeout_to_regval(int new_timeout)
{
int i;
/* first limit it to 1 - 8 seconds */
new_timeout = clamp_val(new_timeout, 1, 8);
for (i = 0; i < ARRAY_SIZE(ts72xx_wdt_map); i++) {
if (ts72xx_wdt_map[i].timeout >= new_timeout)
return ts72xx_wdt_map[i].regval;
}
return -EINVAL;
}
/**
* regval_to_timeout() - converts control register value to timeout
* @regval: control register value to be converted
*
* Function converts given @regval to timeout in seconds (1, 2, 4 or 8).
* If @regval cannot be converted, function returns %-EINVAL.
*/
static int regval_to_timeout(int regval)
static int ts72xx_wdt_start(struct watchdog_device *wdd)
{
int i;
struct ts72xx_wdt_priv *priv = watchdog_get_drvdata(wdd);
for (i = 0; i < ARRAY_SIZE(ts72xx_wdt_map); i++) {
if (ts72xx_wdt_map[i].regval == regval)
return ts72xx_wdt_map[i].timeout;
}
writeb(TS72XX_WDT_FEED_VAL, priv->feed_reg);
writeb(priv->regval, priv->control_reg);
return -EINVAL;
return 0;
}
/**
* ts72xx_wdt_kick() - kick the watchdog
* @wdt: watchdog to be kicked
*
* Called with @wdt->lock held.
*/
static inline void ts72xx_wdt_kick(struct ts72xx_wdt *wdt)
static int ts72xx_wdt_stop(struct watchdog_device *wdd)
{
__raw_writeb(TS72XX_WDT_FEED_VAL, wdt->feed_reg);
}
struct ts72xx_wdt_priv *priv = watchdog_get_drvdata(wdd);
/**
* ts72xx_wdt_start() - starts the watchdog timer
* @wdt: watchdog to be started
*
* This function programs timeout to watchdog timer
* and starts it.
*
* Called with @wdt->lock held.
*/
static void ts72xx_wdt_start(struct ts72xx_wdt *wdt)
{
/*
* To program the wdt, it first must be "fed" and
* only after that (within 30 usecs) the configuration
* can be changed.
*/
ts72xx_wdt_kick(wdt);
__raw_writeb((u8)wdt->regval, wdt->control_reg);
}
writeb(TS72XX_WDT_FEED_VAL, priv->feed_reg);
writeb(TS72XX_WDT_CTRL_DISABLE, priv->control_reg);
/**
* ts72xx_wdt_stop() - stops the watchdog timer
* @wdt: watchdog to be stopped
*
* Called with @wdt->lock held.
*/
static void ts72xx_wdt_stop(struct ts72xx_wdt *wdt)
{
ts72xx_wdt_kick(wdt);
__raw_writeb(0, wdt->control_reg);
return 0;
}
static int ts72xx_wdt_open(struct inode *inode, struct file *file)
static int ts72xx_wdt_ping(struct watchdog_device *wdd)
{
struct ts72xx_wdt *wdt = platform_get_drvdata(ts72xx_wdt_pdev);
int regval;
/*
* Try to convert default timeout to valid register
* value first.
*/
regval = timeout_to_regval(timeout);
if (regval < 0) {
dev_err(&wdt->pdev->dev,
"failed to convert timeout (%d) to register value\n",
timeout);
return regval;
}
if (mutex_lock_interruptible(&wdt->lock))
return -ERESTARTSYS;
struct ts72xx_wdt_priv *priv = watchdog_get_drvdata(wdd);
if ((wdt->flags & TS72XX_WDT_BUSY_FLAG) != 0) {
mutex_unlock(&wdt->lock);
return -EBUSY;
}
wdt->flags = TS72XX_WDT_BUSY_FLAG;
wdt->regval = regval;
file->private_data = wdt;
ts72xx_wdt_start(wdt);
writeb(TS72XX_WDT_FEED_VAL, priv->feed_reg);
mutex_unlock(&wdt->lock);
return nonseekable_open(inode, file);
return 0;
}
static int ts72xx_wdt_release(struct inode *inode, struct file *file)
static int ts72xx_wdt_settimeout(struct watchdog_device *wdd, unsigned int to)
{
struct ts72xx_wdt *wdt = file->private_data;
if (mutex_lock_interruptible(&wdt->lock))
return -ERESTARTSYS;
if ((wdt->flags & TS72XX_WDT_EXPECT_CLOSE_FLAG) != 0) {
ts72xx_wdt_stop(wdt);
struct ts72xx_wdt_priv *priv = watchdog_get_drvdata(wdd);
if (to == 1) {
priv->regval = TS72XX_WDT_CTRL_1SEC;
} else if (to == 2) {
priv->regval = TS72XX_WDT_CTRL_2SEC;
} else if (to <= 4) {
priv->regval = TS72XX_WDT_CTRL_4SEC;
to = 4;
} else {
dev_warn(&wdt->pdev->dev,
"TS-72XX WDT device closed unexpectly. "
"Watchdog timer will not stop!\n");
/*
* Kick it one more time, to give userland some time
* to recover (for example, respawning the kicker
* daemon).
*/
ts72xx_wdt_kick(wdt);
priv->regval = TS72XX_WDT_CTRL_8SEC;
if (to <= 8)
to = 8;
}
wdt->flags = 0;
wdd->timeout = to;
mutex_unlock(&wdt->lock);
return 0;
}
static ssize_t ts72xx_wdt_write(struct file *file,
const char __user *data,
size_t len,
loff_t *ppos)
{
struct ts72xx_wdt *wdt = file->private_data;
if (!len)
return 0;
if (mutex_lock_interruptible(&wdt->lock))
return -ERESTARTSYS;
ts72xx_wdt_kick(wdt);
/*
* Support for magic character closing. User process
* writes 'V' into the device, just before it is closed.
* This means that we know that the wdt timer can be
* stopped after user closes the device.
*/
if (!nowayout) {
int i;
for (i = 0; i < len; i++) {
char c;
/* In case it was set long ago */
wdt->flags &= ~TS72XX_WDT_EXPECT_CLOSE_FLAG;
if (get_user(c, data + i)) {
mutex_unlock(&wdt->lock);
return -EFAULT;
}
if (c == 'V') {
wdt->flags |= TS72XX_WDT_EXPECT_CLOSE_FLAG;
break;
}
}
if (watchdog_active(wdd)) {
ts72xx_wdt_stop(wdd);
ts72xx_wdt_start(wdd);
}
mutex_unlock(&wdt->lock);
return len;
return 0;
}
static const struct watchdog_info winfo = {
.options = WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT |
static const struct watchdog_info ts72xx_wdt_ident = {
.options = WDIOF_KEEPALIVEPING |
WDIOF_SETTIMEOUT |
WDIOF_MAGICCLOSE,
.firmware_version = 1,
.identity = "TS-72XX WDT",
};
static long ts72xx_wdt_ioctl(struct file *file, unsigned int cmd,
unsigned long arg)
{
struct ts72xx_wdt *wdt = file->private_data;
void __user *argp = (void __user *)arg;
int __user *p = (int __user *)argp;
int error = 0;
if (mutex_lock_interruptible(&wdt->lock))
return -ERESTARTSYS;
switch (cmd) {
case WDIOC_GETSUPPORT:
if (copy_to_user(argp, &winfo, sizeof(winfo)))
error = -EFAULT;
break;
case WDIOC_GETSTATUS:
case WDIOC_GETBOOTSTATUS:
error = put_user(0, p);
break;
case WDIOC_KEEPALIVE:
ts72xx_wdt_kick(wdt);
break;
case WDIOC_SETOPTIONS: {
int options;
error = get_user(options, p);
if (error)
break;
error = -EINVAL;
if ((options & WDIOS_DISABLECARD) != 0) {
ts72xx_wdt_stop(wdt);
error = 0;
}
if ((options & WDIOS_ENABLECARD) != 0) {
ts72xx_wdt_start(wdt);
error = 0;
}
break;
}
case WDIOC_SETTIMEOUT: {
int new_timeout;
int regval;
error = get_user(new_timeout, p);
if (error)
break;
regval = timeout_to_regval(new_timeout);
if (regval < 0) {
error = regval;
break;
}
ts72xx_wdt_stop(wdt);
wdt->regval = regval;
ts72xx_wdt_start(wdt);
/*FALLTHROUGH*/
}
case WDIOC_GETTIMEOUT:
error = put_user(regval_to_timeout(wdt->regval), p);
break;
default:
error = -ENOTTY;
break;
}
mutex_unlock(&wdt->lock);
return error;
}
static const struct file_operations ts72xx_wdt_fops = {
static struct watchdog_ops ts72xx_wdt_ops = {
.owner = THIS_MODULE,
.llseek = no_llseek,
.open = ts72xx_wdt_open,
.release = ts72xx_wdt_release,
.write = ts72xx_wdt_write,
.unlocked_ioctl = ts72xx_wdt_ioctl,
};
static struct miscdevice ts72xx_wdt_miscdev = {
.minor = WATCHDOG_MINOR,
.name = "watchdog",
.fops = &ts72xx_wdt_fops,
.start = ts72xx_wdt_start,
.stop = ts72xx_wdt_stop,
.ping = ts72xx_wdt_ping,
.set_timeout = ts72xx_wdt_settimeout,
};
static int ts72xx_wdt_probe(struct platform_device *pdev)
{
struct ts72xx_wdt *wdt;
struct resource *r1, *r2;
int error = 0;
struct ts72xx_wdt_priv *priv;
struct watchdog_device *wdd;
struct resource *res;
int ret;
wdt = devm_kzalloc(&pdev->dev, sizeof(struct ts72xx_wdt), GFP_KERNEL);
if (!wdt)
priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
if (!priv)
return -ENOMEM;
r1 = platform_get_resource(pdev, IORESOURCE_MEM, 0);
wdt->control_reg = devm_ioremap_resource(&pdev->dev, r1);
if (IS_ERR(wdt->control_reg))
return PTR_ERR(wdt->control_reg);
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
priv->control_reg = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(priv->control_reg))
return PTR_ERR(priv->control_reg);
r2 = platform_get_resource(pdev, IORESOURCE_MEM, 1);
wdt->feed_reg = devm_ioremap_resource(&pdev->dev, r2);
if (IS_ERR(wdt->feed_reg))
return PTR_ERR(wdt->feed_reg);
res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
priv->feed_reg = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(priv->feed_reg))
return PTR_ERR(priv->feed_reg);
platform_set_drvdata(pdev, wdt);
ts72xx_wdt_pdev = pdev;
wdt->pdev = pdev;
mutex_init(&wdt->lock);
wdd = &priv->wdd;
wdd->info = &ts72xx_wdt_ident;
wdd->ops = &ts72xx_wdt_ops;
wdd->min_timeout = 1;
wdd->max_hw_heartbeat_ms = 8000;
wdd->parent = &pdev->dev;
/* make sure that the watchdog is disabled */
ts72xx_wdt_stop(wdt);
watchdog_set_nowayout(wdd, nowayout);
error = misc_register(&ts72xx_wdt_miscdev);
if (error) {
dev_err(&pdev->dev, "failed to register miscdev\n");
return error;
}
wdd->timeout = TS72XX_WDT_DEFAULT_TIMEOUT;
watchdog_init_timeout(wdd, timeout, &pdev->dev);
dev_info(&pdev->dev, "TS-72xx Watchdog driver\n");
watchdog_set_drvdata(wdd, priv);
return 0;
}
ret = devm_watchdog_register_device(&pdev->dev, wdd);
if (ret)
return ret;
dev_info(&pdev->dev, "TS-72xx Watchdog driver\n");
static int ts72xx_wdt_remove(struct platform_device *pdev)
{
misc_deregister(&ts72xx_wdt_miscdev);
return 0;
}
static struct platform_driver ts72xx_wdt_driver = {
.probe = ts72xx_wdt_probe,
.remove = ts72xx_wdt_remove,
.driver = {
.name = "ts72xx-wdt",
},
......
......@@ -297,7 +297,7 @@ static unsigned int wdt_get_time(struct watchdog_device *wdog)
* Kernel Interfaces
*/
static struct watchdog_info wdt_info = {
static const struct watchdog_info wdt_info = {
.options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE,
.identity = "W83627HF Watchdog",
};
......
......@@ -987,6 +987,11 @@ static void watchdog_cdev_unregister(struct watchdog_device *wdd)
wdd->wd_data = NULL;
mutex_unlock(&wd_data->lock);
if (watchdog_active(wdd) &&
test_bit(WDOG_STOP_ON_UNREGISTER, &wdd->status)) {
watchdog_stop(wdd);
}
cancel_delayed_work_sync(&wd_data->work);
kref_put(&wd_data->kref, watchdog_core_data_release);
......
......@@ -194,7 +194,7 @@ static int wm831x_wdt_probe(struct platform_device *pdev)
if (ret < 0) {
dev_err(wm831x->dev, "Failed to read watchdog status: %d\n",
ret);
goto err;
return ret;
}
reg = ret;
......@@ -203,10 +203,8 @@ static int wm831x_wdt_probe(struct platform_device *pdev)
driver_data = devm_kzalloc(&pdev->dev, sizeof(*driver_data),
GFP_KERNEL);
if (!driver_data) {
ret = -ENOMEM;
goto err;
}
if (!driver_data)
return -ENOMEM;
mutex_init(&driver_data->lock);
driver_data->wm831x = wm831x;
......@@ -253,7 +251,7 @@ static int wm831x_wdt_probe(struct platform_device *pdev)
dev_err(wm831x->dev,
"Failed to request update GPIO: %d\n",
ret);
goto err;
return ret;
}
driver_data->update_gpio = pdata->update_gpio;
......@@ -269,37 +267,22 @@ static int wm831x_wdt_probe(struct platform_device *pdev)
} else {
dev_err(wm831x->dev,
"Failed to unlock security key: %d\n", ret);
goto err;
return ret;
}
}
ret = watchdog_register_device(&driver_data->wdt);
ret = devm_watchdog_register_device(&pdev->dev, &driver_data->wdt);
if (ret != 0) {
dev_err(wm831x->dev, "watchdog_register_device() failed: %d\n",
ret);
goto err;
return ret;
}
platform_set_drvdata(pdev, driver_data);
return 0;
err:
return ret;
}
static int wm831x_wdt_remove(struct platform_device *pdev)
{
struct wm831x_wdt_drvdata *driver_data = platform_get_drvdata(pdev);
watchdog_unregister_device(&driver_data->wdt);
return 0;
}
static struct platform_driver wm831x_wdt_driver = {
.probe = wm831x_wdt_probe,
.remove = wm831x_wdt_remove,
.driver = {
.name = "wm831x-watchdog",
},
......
/*
* watchdog driver for ZTE's zx2967 family
*
* Copyright (C) 2017 ZTE Ltd.
*
* Author: Baoyou Xie <baoyou.xie@linaro.org>
*
* License terms: GNU General Public License (GPL) version 2
*/
#include <linux/clk.h>
#include <linux/io.h>
#include <linux/mfd/syscon.h>
#include <linux/module.h>
#include <linux/of_address.h>
#include <linux/platform_device.h>
#include <linux/regmap.h>
#include <linux/reset.h>
#include <linux/watchdog.h>
#define ZX2967_WDT_CFG_REG 0x4
#define ZX2967_WDT_LOAD_REG 0x8
#define ZX2967_WDT_REFRESH_REG 0x18
#define ZX2967_WDT_START_REG 0x1c
#define ZX2967_WDT_REFRESH_MASK GENMASK(5, 0)
#define ZX2967_WDT_CFG_DIV(n) ((((n) & 0xff) - 1) << 8)
#define ZX2967_WDT_START_EN 0x1
/*
* Hardware magic number.
* When watchdog reg is written, the lowest 16 bits are valid, but
* the highest 16 bits should be always this number.
*/
#define ZX2967_WDT_WRITEKEY (0x1234 << 16)
#define ZX2967_WDT_VAL_MASK GENMASK(15, 0)
#define ZX2967_WDT_DIV_DEFAULT 16
#define ZX2967_WDT_DEFAULT_TIMEOUT 32
#define ZX2967_WDT_MIN_TIMEOUT 1
#define ZX2967_WDT_MAX_TIMEOUT 524
#define ZX2967_WDT_MAX_COUNT 0xffff
#define ZX2967_WDT_CLK_FREQ 0x8000
#define ZX2967_WDT_FLAG_REBOOT_MON BIT(0)
struct zx2967_wdt {
struct watchdog_device wdt_device;
void __iomem *reg_base;
struct clk *clock;
};
static inline u32 zx2967_wdt_readl(struct zx2967_wdt *wdt, u16 reg)
{
return readl_relaxed(wdt->reg_base + reg);
}
static inline void zx2967_wdt_writel(struct zx2967_wdt *wdt, u16 reg, u32 val)
{
writel_relaxed(val | ZX2967_WDT_WRITEKEY, wdt->reg_base + reg);
}
static void zx2967_wdt_refresh(struct zx2967_wdt *wdt)
{
u32 val;
val = zx2967_wdt_readl(wdt, ZX2967_WDT_REFRESH_REG);
/*
* Bit 4-5, 1 and 2: refresh config info
* Bit 2-3, 1 and 2: refresh counter
* Bit 0-1, 1 and 2: refresh int-value
* we shift each group value between 1 and 2 to refresh all data.
*/
val ^= ZX2967_WDT_REFRESH_MASK;
zx2967_wdt_writel(wdt, ZX2967_WDT_REFRESH_REG,
val & ZX2967_WDT_VAL_MASK);
}
static int
zx2967_wdt_set_timeout(struct watchdog_device *wdd, unsigned int timeout)
{
struct zx2967_wdt *wdt = watchdog_get_drvdata(wdd);
unsigned int divisor = ZX2967_WDT_DIV_DEFAULT;
u32 count;
count = timeout * ZX2967_WDT_CLK_FREQ;
if (count > divisor * ZX2967_WDT_MAX_COUNT)
divisor = DIV_ROUND_UP(count, ZX2967_WDT_MAX_COUNT);
count = DIV_ROUND_UP(count, divisor);
zx2967_wdt_writel(wdt, ZX2967_WDT_CFG_REG,
ZX2967_WDT_CFG_DIV(divisor) & ZX2967_WDT_VAL_MASK);
zx2967_wdt_writel(wdt, ZX2967_WDT_LOAD_REG,
count & ZX2967_WDT_VAL_MASK);
zx2967_wdt_refresh(wdt);
wdd->timeout = (count * divisor) / ZX2967_WDT_CLK_FREQ;
return 0;
}
static void __zx2967_wdt_start(struct zx2967_wdt *wdt)
{
u32 val;
val = zx2967_wdt_readl(wdt, ZX2967_WDT_START_REG);
val |= ZX2967_WDT_START_EN;
zx2967_wdt_writel(wdt, ZX2967_WDT_START_REG,
val & ZX2967_WDT_VAL_MASK);
}
static void __zx2967_wdt_stop(struct zx2967_wdt *wdt)
{
u32 val;
val = zx2967_wdt_readl(wdt, ZX2967_WDT_START_REG);
val &= ~ZX2967_WDT_START_EN;
zx2967_wdt_writel(wdt, ZX2967_WDT_START_REG,
val & ZX2967_WDT_VAL_MASK);
}
static int zx2967_wdt_start(struct watchdog_device *wdd)
{
struct zx2967_wdt *wdt = watchdog_get_drvdata(wdd);
zx2967_wdt_set_timeout(wdd, wdd->timeout);
__zx2967_wdt_start(wdt);
return 0;
}
static int zx2967_wdt_stop(struct watchdog_device *wdd)
{
struct zx2967_wdt *wdt = watchdog_get_drvdata(wdd);
__zx2967_wdt_stop(wdt);
return 0;
}
static int zx2967_wdt_keepalive(struct watchdog_device *wdd)
{
struct zx2967_wdt *wdt = watchdog_get_drvdata(wdd);
zx2967_wdt_refresh(wdt);
return 0;
}
#define ZX2967_WDT_OPTIONS \
(WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE)
static const struct watchdog_info zx2967_wdt_ident = {
.options = ZX2967_WDT_OPTIONS,
.identity = "zx2967 watchdog",
};
static struct watchdog_ops zx2967_wdt_ops = {
.owner = THIS_MODULE,
.start = zx2967_wdt_start,
.stop = zx2967_wdt_stop,
.ping = zx2967_wdt_keepalive,
.set_timeout = zx2967_wdt_set_timeout,
};
static void zx2967_wdt_reset_sysctrl(struct device *dev)
{
int ret;
void __iomem *regmap;
unsigned int offset, mask, config;
struct of_phandle_args out_args;
ret = of_parse_phandle_with_fixed_args(dev->of_node,
"zte,wdt-reset-sysctrl", 3, 0, &out_args);
if (ret)
return;
offset = out_args.args[0];
config = out_args.args[1];
mask = out_args.args[2];
regmap = syscon_node_to_regmap(out_args.np);
if (IS_ERR(regmap)) {
of_node_put(out_args.np);
return;
}
regmap_update_bits(regmap, offset, mask, config);
of_node_put(out_args.np);
}
static int zx2967_wdt_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct zx2967_wdt *wdt;
struct resource *base;
int ret;
struct reset_control *rstc;
wdt = devm_kzalloc(dev, sizeof(*wdt), GFP_KERNEL);
if (!wdt)
return -ENOMEM;
platform_set_drvdata(pdev, wdt);
wdt->wdt_device.info = &zx2967_wdt_ident;
wdt->wdt_device.ops = &zx2967_wdt_ops;
wdt->wdt_device.timeout = ZX2967_WDT_DEFAULT_TIMEOUT;
wdt->wdt_device.max_timeout = ZX2967_WDT_MAX_TIMEOUT;
wdt->wdt_device.min_timeout = ZX2967_WDT_MIN_TIMEOUT;
wdt->wdt_device.parent = &pdev->dev;
base = platform_get_resource(pdev, IORESOURCE_MEM, 0);
wdt->reg_base = devm_ioremap_resource(dev, base);
if (IS_ERR(wdt->reg_base)) {
dev_err(dev, "ioremap failed\n");
return PTR_ERR(wdt->reg_base);
}
zx2967_wdt_reset_sysctrl(dev);
wdt->clock = devm_clk_get(dev, NULL);
if (IS_ERR(wdt->clock)) {
dev_err(dev, "failed to find watchdog clock source\n");
return PTR_ERR(wdt->clock);
}
ret = clk_prepare_enable(wdt->clock);
if (ret < 0) {
dev_err(dev, "failed to enable clock\n");
return ret;
}
clk_set_rate(wdt->clock, ZX2967_WDT_CLK_FREQ);
rstc = devm_reset_control_get(dev, NULL);
if (IS_ERR(rstc)) {
dev_err(dev, "failed to get rstc");
ret = PTR_ERR(rstc);
goto err;
}
reset_control_assert(rstc);
reset_control_deassert(rstc);
watchdog_set_drvdata(&wdt->wdt_device, wdt);
watchdog_init_timeout(&wdt->wdt_device,
ZX2967_WDT_DEFAULT_TIMEOUT, dev);
watchdog_set_nowayout(&wdt->wdt_device, WATCHDOG_NOWAYOUT);
ret = watchdog_register_device(&wdt->wdt_device);
if (ret)
goto err;
dev_info(dev, "watchdog enabled (timeout=%d sec, nowayout=%d)",
wdt->wdt_device.timeout, WATCHDOG_NOWAYOUT);
return 0;
err:
clk_disable_unprepare(wdt->clock);
return ret;
}
static int zx2967_wdt_remove(struct platform_device *pdev)
{
struct zx2967_wdt *wdt = platform_get_drvdata(pdev);
watchdog_unregister_device(&wdt->wdt_device);
clk_disable_unprepare(wdt->clock);
return 0;
}
static const struct of_device_id zx2967_wdt_match[] = {
{ .compatible = "zte,zx296718-wdt", },
{}
};
MODULE_DEVICE_TABLE(of, zx2967_wdt_match);
static struct platform_driver zx2967_wdt_driver = {
.probe = zx2967_wdt_probe,
.remove = zx2967_wdt_remove,
.driver = {
.name = "zx2967-wdt",
.of_match_table = of_match_ptr(zx2967_wdt_match),
},
};
module_platform_driver(zx2967_wdt_driver);
MODULE_AUTHOR("Baoyou Xie <baoyou.xie@linaro.org>");
MODULE_DESCRIPTION("ZTE zx2967 Watchdog Device Driver");
MODULE_LICENSE("GPL v2");
......@@ -117,6 +117,7 @@ struct watchdog_device {
#define WDOG_NO_WAY_OUT 1 /* Is 'nowayout' feature set ? */
#define WDOG_STOP_ON_REBOOT 2 /* Should be stopped on reboot */
#define WDOG_HW_RUNNING 3 /* True if HW watchdog running */
#define WDOG_STOP_ON_UNREGISTER 4 /* Should be stopped on unregister */
struct list_head deferred;
};
......@@ -151,6 +152,12 @@ static inline void watchdog_stop_on_reboot(struct watchdog_device *wdd)
set_bit(WDOG_STOP_ON_REBOOT, &wdd->status);
}
/* Use the following function to stop the watchdog when unregistering it */
static inline void watchdog_stop_on_unregister(struct watchdog_device *wdd)
{
set_bit(WDOG_STOP_ON_UNREGISTER, &wdd->status);
}
/* Use the following function to check if a timeout value is invalid */
static inline bool watchdog_timeout_invalid(struct watchdog_device *wdd, unsigned int t)
{
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment