aboutsummaryrefslogtreecommitdiff
path: root/drivers/watchdog
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/watchdog')
-rw-r--r--drivers/watchdog/Kconfig365
-rw-r--r--drivers/watchdog/Makefile25
-rw-r--r--drivers/watchdog/acquirewdt.c29
-rw-r--r--drivers/watchdog/advantechwdt.c26
-rw-r--r--drivers/watchdog/alim1535_wdt.c3
-rw-r--r--drivers/watchdog/alim7101_wdt.c3
-rw-r--r--drivers/watchdog/ar7_wdt.c48
-rw-r--r--drivers/watchdog/at32ap700x_wdt.c34
-rw-r--r--drivers/watchdog/at91rm9200_wdt.c16
-rw-r--r--drivers/watchdog/at91sam9_wdt.c436
-rw-r--r--drivers/watchdog/ath79_wdt.c96
-rw-r--r--drivers/watchdog/bcm2835_wdt.c185
-rw-r--r--drivers/watchdog/bcm47xx_wdt.c340
-rw-r--r--drivers/watchdog/bcm63xx_wdt.c22
-rw-r--r--drivers/watchdog/bcm_kona_wdt.c368
-rw-r--r--drivers/watchdog/bfin_wdt.c7
-rw-r--r--drivers/watchdog/booke_wdt.c219
-rw-r--r--drivers/watchdog/coh901327_wdt.c31
-rw-r--r--drivers/watchdog/cpu5wdt.c11
-rw-r--r--drivers/watchdog/cpwd.c15
-rw-r--r--drivers/watchdog/da9052_wdt.c245
-rw-r--r--drivers/watchdog/da9055_wdt.c209
-rw-r--r--drivers/watchdog/davinci_wdt.c271
-rw-r--r--drivers/watchdog/diag288_wdt.c316
-rw-r--r--drivers/watchdog/dw_wdt.c61
-rw-r--r--drivers/watchdog/ep93xx_wdt.c27
-rw-r--r--drivers/watchdog/eurotechwdt.c1
-rw-r--r--drivers/watchdog/f71808e_wdt.c4
-rw-r--r--drivers/watchdog/gef_wdt.c7
-rw-r--r--drivers/watchdog/geodewdt.c22
-rw-r--r--drivers/watchdog/gpio_wdt.c254
-rw-r--r--drivers/watchdog/hpwdt.c138
-rw-r--r--drivers/watchdog/i6300esb.c28
-rw-r--r--drivers/watchdog/iTCO_vendor.h6
-rw-r--r--drivers/watchdog/iTCO_vendor_support.c43
-rw-r--r--drivers/watchdog/iTCO_wdt.c822
-rw-r--r--drivers/watchdog/ib700wdt.c26
-rw-r--r--drivers/watchdog/ibmasr.c3
-rw-r--r--drivers/watchdog/ie6xx_wdt.c346
-rw-r--r--drivers/watchdog/imx2_wdt.c348
-rw-r--r--drivers/watchdog/indydog.c14
-rw-r--r--drivers/watchdog/intel-mid_wdt.c184
-rw-r--r--drivers/watchdog/intel_scu_watchdog.c9
-rw-r--r--drivers/watchdog/iop_wdt.c1
-rw-r--r--drivers/watchdog/it8712f_wdt.c1
-rw-r--r--drivers/watchdog/it87_wdt.c49
-rw-r--r--drivers/watchdog/ixp2000_wdt.c215
-rw-r--r--drivers/watchdog/ixp4xx_wdt.c2
-rw-r--r--drivers/watchdog/jz4740_wdt.c17
-rw-r--r--drivers/watchdog/kempld_wdt.c579
-rw-r--r--drivers/watchdog/ks8695_wdt.c21
-rw-r--r--drivers/watchdog/lantiq_wdt.c67
-rw-r--r--drivers/watchdog/m54xx_wdt.c22
-rw-r--r--drivers/watchdog/machzwd.c1
-rw-r--r--drivers/watchdog/max63xx_wdt.c16
-rw-r--r--drivers/watchdog/mena21_wdt.c270
-rw-r--r--drivers/watchdog/mixcomwd.c3
-rw-r--r--drivers/watchdog/moxart_wdt.c180
-rw-r--r--drivers/watchdog/mpc8xxx_wdt.c121
-rw-r--r--drivers/watchdog/mpcore_wdt.c457
-rw-r--r--drivers/watchdog/mtx-1_wdt.c11
-rw-r--r--drivers/watchdog/mv64x60_wdt.c13
-rw-r--r--drivers/watchdog/nuc900_wdt.c59
-rw-r--r--drivers/watchdog/nv_tco.c13
-rw-r--r--drivers/watchdog/octeon-wdt-main.c11
-rw-r--r--drivers/watchdog/of_xilinx_wdt.c428
-rw-r--r--drivers/watchdog/omap_wdt.c374
-rw-r--r--drivers/watchdog/orion_wdt.c662
-rw-r--r--drivers/watchdog/pc87413_wdt.c9
-rw-r--r--drivers/watchdog/pcwd.c12
-rw-r--r--drivers/watchdog/pcwd_pci.c30
-rw-r--r--drivers/watchdog/pcwd_usb.c60
-rw-r--r--drivers/watchdog/pika_wdt.c3
-rw-r--r--drivers/watchdog/pnx4008_wdt.c39
-rw-r--r--drivers/watchdog/pnx833x_wdt.c1
-rw-r--r--drivers/watchdog/rc32434_wdt.c20
-rw-r--r--drivers/watchdog/rdc321x_wdt.c10
-rw-r--r--drivers/watchdog/retu_wdt.c177
-rw-r--r--drivers/watchdog/riowd.c19
-rw-r--r--drivers/watchdog/rt2880_wdt.c206
-rw-r--r--drivers/watchdog/s3c2410_wdt.c527
-rw-r--r--drivers/watchdog/sa1100_wdt.c15
-rw-r--r--drivers/watchdog/sb_wdog.c3
-rw-r--r--drivers/watchdog/sbc60xxwdt.c1
-rw-r--r--drivers/watchdog/sbc7240_wdt.c2
-rw-r--r--drivers/watchdog/sbc8360.c1
-rw-r--r--drivers/watchdog/sbc_epx_c3.c1
-rw-r--r--drivers/watchdog/sbc_fitpc2_wdt.c2
-rw-r--r--drivers/watchdog/sc1200wdt.c4
-rw-r--r--drivers/watchdog/sc520_wdt.c4
-rw-r--r--drivers/watchdog/sch311x_wdt.c60
-rw-r--r--drivers/watchdog/scx200_wdt.c1
-rw-r--r--drivers/watchdog/shwdt.c319
-rw-r--r--drivers/watchdog/sirfsoc_wdt.c226
-rw-r--r--drivers/watchdog/smsc37b787_wdt.c2
-rw-r--r--drivers/watchdog/softdog.c5
-rw-r--r--drivers/watchdog/sp5100_tco.c211
-rw-r--r--drivers/watchdog/sp5100_tco.h46
-rw-r--r--drivers/watchdog/sp805_wdt.c285
-rw-r--r--drivers/watchdog/stmp3xxx_rtc_wdt.c108
-rw-r--r--drivers/watchdog/stmp3xxx_wdt.c288
-rw-r--r--drivers/watchdog/sunxi_wdt.c237
-rw-r--r--drivers/watchdog/tegra_wdt.c302
-rw-r--r--drivers/watchdog/ts72xx_wdt.c126
-rw-r--r--drivers/watchdog/twl4030_wdt.c207
-rw-r--r--drivers/watchdog/txx9wdt.c21
-rw-r--r--drivers/watchdog/ux500_wdt.c169
-rw-r--r--drivers/watchdog/via_wdt.c30
-rw-r--r--drivers/watchdog/w83627hf_wdt.c540
-rw-r--r--drivers/watchdog/w83697hf_wdt.c461
-rw-r--r--drivers/watchdog/w83697ug_wdt.c398
-rw-r--r--drivers/watchdog/w83877f_wdt.c1
-rw-r--r--drivers/watchdog/w83977f_wdt.c1
-rw-r--r--drivers/watchdog/wafer5823wdt.c1
-rw-r--r--drivers/watchdog/watchdog_core.c141
-rw-r--r--drivers/watchdog/watchdog_core.h (renamed from drivers/watchdog/watchdog_dev.h)8
-rw-r--r--drivers/watchdog/watchdog_dev.c381
-rw-r--r--drivers/watchdog/wdrtas.c31
-rw-r--r--drivers/watchdog/wdt.c2
-rw-r--r--drivers/watchdog/wdt285.c4
-rw-r--r--drivers/watchdog/wdt977.c1
-rw-r--r--drivers/watchdog/wdt_pci.c45
-rw-r--r--drivers/watchdog/wm831x_wdt.c43
-rw-r--r--drivers/watchdog/wm8350_wdt.c6
-rw-r--r--drivers/watchdog/xen_wdt.c7
125 files changed, 8767 insertions, 6349 deletions
diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig
index 37096246c93..76dd54122f7 100644
--- a/drivers/watchdog/Kconfig
+++ b/drivers/watchdog/Kconfig
@@ -64,6 +64,37 @@ config SOFT_WATCHDOG
To compile this driver as a module, choose M here: the
module will be called softdog.
+config DA9052_WATCHDOG
+ tristate "Dialog DA9052 Watchdog"
+ depends on PMIC_DA9052
+ select WATCHDOG_CORE
+ help
+ Support for the watchdog in the DA9052 PMIC. Watchdog trigger
+ cause system reset.
+
+ Say Y here to include support for the DA9052 watchdog.
+ Alternatively say M to compile the driver as a module,
+ which will be called da9052_wdt.
+
+config DA9055_WATCHDOG
+ tristate "Dialog Semiconductor DA9055 Watchdog"
+ depends on MFD_DA9055
+ select WATCHDOG_CORE
+ help
+ If you say yes here you get support for watchdog on the Dialog
+ Semiconductor DA9055 PMIC.
+
+ This driver can also be built as a module. If so, the module
+ will be called da9055_wdt.
+
+config GPIO_WATCHDOG
+ tristate "Watchdog device controlled through GPIO-line"
+ depends on OF_GPIO
+ select WATCHDOG_CORE
+ help
+ If you say yes here you get support for watchdog device
+ controlled through GPIO-line.
+
config WM831X_WATCHDOG
tristate "WM831x watchdog"
depends on MFD_WM831X
@@ -80,13 +111,23 @@ config WM8350_WATCHDOG
Support for the watchdog in the WM8350 AudioPlus PMIC. When
the watchdog triggers the system will be reset.
+config XILINX_WATCHDOG
+ tristate "Xilinx Watchdog timer"
+ select WATCHDOG_CORE
+ help
+ Watchdog driver for the xps_timebase_wdt ip core.
+
+ To compile this driver as a module, choose M here: the
+ module will be called of_xilinx_wdt.
+
# ALPHA Architecture
# ARM Architecture
config ARM_SP805_WATCHDOG
tristate "ARM SP805 Watchdog"
- depends on ARM_AMBA
+ depends on (ARM || ARM64) && ARM_AMBA
+ select WATCHDOG_CORE
help
ARM Primecell SP805 Watchdog timer. This will reboot your system when
the timeout is reached.
@@ -101,6 +142,7 @@ config AT91RM9200_WATCHDOG
config AT91SAM9X_WATCHDOG
tristate "AT91SAM9X / AT91CAP9 watchdog"
depends on ARCH_AT91 && !ARCH_AT91RM9200
+ select WATCHDOG_CORE
help
Watchdog timer embedded into AT91SAM9X and AT91CAP9 chips. This will
reboot your system when the timeout is reached.
@@ -129,17 +171,6 @@ config 977_WATCHDOG
Not sure? It's safe to say N.
-config IXP2000_WATCHDOG
- tristate "IXP2000 Watchdog"
- depends on ARCH_IXP2000
- help
- Say Y here if to include support for the watchdog timer
- in the Intel IXP2000(2400, 2800, 2850) network processors.
- This driver can be built as a module by choosing M. The module
- will be called ixp2000_wdt.
-
- Say N if you are unsure.
-
config IXP4XX_WATCHDOG
tristate "IXP4xx Watchdog"
depends on ARCH_IXP4XX
@@ -174,6 +205,7 @@ config S3C2410_WATCHDOG
tristate "S3C2410 Watchdog"
depends on HAVE_S3C2410_WATCHDOG
select WATCHDOG_CORE
+ select MFD_SYSCON if ARCH_EXYNOS5
help
Watchdog timer block in the Samsung SoCs. This will reboot
the system when the timer expires with the watchdog enabled.
@@ -200,22 +232,13 @@ config SA1100_WATCHDOG
config DW_WATCHDOG
tristate "Synopsys DesignWare watchdog"
- depends on ARM && HAVE_CLK
+ depends on HAS_IOMEM
help
Say Y here if to include support for the Synopsys DesignWare
- watchdog timer found in many ARM chips.
+ watchdog timer found in many chips.
To compile this driver as a module, choose M here: the
module will be called dw_wdt.
-config MPCORE_WATCHDOG
- tristate "MPcore watchdog"
- depends on HAVE_ARM_TWD
- help
- Watchdog timer embedded into the MPcore system.
-
- To compile this driver as a module, choose M here: the
- module will be called mpcore_wdt.
-
config EP93XX_WATCHDOG
tristate "EP93xx Watchdog"
depends on ARCH_EP93XX
@@ -230,17 +253,18 @@ config EP93XX_WATCHDOG
config OMAP_WATCHDOG
tristate "OMAP Watchdog"
depends on ARCH_OMAP16XX || ARCH_OMAP2PLUS
+ select WATCHDOG_CORE
help
Support for TI OMAP1610/OMAP1710/OMAP2420/OMAP3430/OMAP4430 watchdog. Say 'Y'
here to enable the OMAP1610/OMAP1710/OMAP2420/OMAP3430/OMAP4430 watchdog timer.
config PNX4008_WATCHDOG
- tristate "PNX4008 and LPC32XX Watchdog"
- depends on ARCH_PNX4008 || ARCH_LPC32XX
+ tristate "LPC32XX Watchdog"
+ depends on ARCH_LPC32XX
select WATCHDOG_CORE
help
Say Y here if to include support for the watchdog timer
- in the PNX4008 or LPC32XX processor.
+ in the LPC32XX processor.
This driver can be built as a module by choosing M. The module
will be called pnx4008_wdt.
@@ -248,7 +272,7 @@ config PNX4008_WATCHDOG
config IOP_WATCHDOG
tristate "IOP Watchdog"
- depends on PLAT_IOP
+ depends on ARCH_IOP13XX
select WATCHDOG_NOWAYOUT if (ARCH_IOP32X || ARCH_IOP33X)
help
Say Y here if to include support for the watchdog timer
@@ -264,10 +288,11 @@ config IOP_WATCHDOG
config DAVINCI_WATCHDOG
tristate "DaVinci watchdog"
- depends on ARCH_DAVINCI
+ depends on ARCH_DAVINCI || ARCH_KEYSTONE
+ select WATCHDOG_CORE
help
Say Y here if to include support for the watchdog timer
- in the DaVinci DM644x/DM646x processors.
+ in the DaVinci DM644x/DM646x or Keystone processors.
To compile this driver as a module, choose M here: the
module will be called davinci_wdt.
@@ -276,13 +301,24 @@ config DAVINCI_WATCHDOG
config ORION_WATCHDOG
tristate "Orion watchdog"
- depends on ARCH_ORION5X || ARCH_KIRKWOOD
+ depends on ARCH_ORION5X || ARCH_KIRKWOOD || ARCH_DOVE || MACH_DOVE || ARCH_MVEBU
+ select WATCHDOG_CORE
help
Say Y here if to include support for the watchdog timer
in the Marvell Orion5x and Kirkwood ARM SoCs.
To compile this driver as a module, choose M here: the
module will be called orion_wdt.
+config SUNXI_WATCHDOG
+ tristate "Allwinner SoCs watchdog support"
+ depends on ARCH_SUNXI
+ select WATCHDOG_CORE
+ help
+ Say Y here to include support for the watchdog timer
+ in Allwinner SoCs.
+ To compile this driver as a module, choose M here: the
+ module will be called sunxi_wdt.
+
config COH901327_WATCHDOG
bool "ST-Ericsson COH 901 327 watchdog"
depends on ARCH_U300
@@ -297,18 +333,20 @@ config COH901327_WATCHDOG
config TWL4030_WATCHDOG
tristate "TWL4030 Watchdog"
depends on TWL4030_CORE
+ select WATCHDOG_CORE
help
Support for TI TWL4030 watchdog. Say 'Y' here to enable the
watchdog timer support for TWL4030 chips.
-config STMP3XXX_WATCHDOG
- tristate "Freescale STMP3XXX watchdog"
- depends on ARCH_STMP3XXX
+config STMP3XXX_RTC_WATCHDOG
+ tristate "Freescale STMP3XXX & i.MX23/28 watchdog"
+ depends on RTC_DRV_STMP
+ select WATCHDOG_CORE
help
- Say Y here if to include support for the watchdog timer
- for the Sigmatel STMP37XX/378X SoC.
+ Say Y here to include support for the watchdog timer inside
+ the RTC for the STMP37XX/378X or i.MX23/28 SoC.
To compile this driver as a module, choose M here: the
- module will be called stmp3xxx_wdt.
+ module will be called stmp3xxx_rtc_wdt.
config NUC900_WATCHDOG
tristate "Nuvoton NUC900 watchdog"
@@ -339,7 +377,9 @@ config MAX63XX_WATCHDOG
config IMX2_WDT
tristate "IMX2+ Watchdog"
- depends on IMX_HAVE_PLATFORM_IMX2_WDT
+ depends on ARCH_MXC
+ select REGMAP_MMIO
+ select WATCHDOG_CORE
help
This is the driver for the hardware watchdog
on the Freescale IMX2 and later processors.
@@ -349,6 +389,60 @@ config IMX2_WDT
To compile this driver as a module, choose M here: the
module will be called imx2_wdt.
+config UX500_WATCHDOG
+ tristate "ST-Ericsson Ux500 watchdog"
+ depends on MFD_DB8500_PRCMU
+ select WATCHDOG_CORE
+ default y
+ help
+ Say Y here to include Watchdog timer support for the watchdog
+ existing in the prcmu of ST-Ericsson Ux500 series platforms.
+
+ To compile this driver as a module, choose M here: the
+ module will be called ux500_wdt.
+
+config RETU_WATCHDOG
+ tristate "Retu watchdog"
+ depends on MFD_RETU
+ select WATCHDOG_CORE
+ help
+ Retu watchdog driver for Nokia Internet Tablets (770, N800,
+ N810). At least on N800 the watchdog cannot be disabled, so
+ this driver is essential and you should enable it.
+
+ To compile this driver as a module, choose M here: the
+ module will be called retu_wdt.
+
+config MOXART_WDT
+ tristate "MOXART watchdog"
+ depends on ARCH_MOXART
+ help
+ Say Y here to include Watchdog timer support for the watchdog
+ existing on the MOXA ART SoC series platforms.
+
+ To compile this driver as a module, choose M here: the
+ module will be called moxart_wdt.
+
+config SIRFSOC_WATCHDOG
+ tristate "SiRFSOC watchdog"
+ depends on ARCH_SIRF
+ select WATCHDOG_CORE
+ default y
+ help
+ Support for CSR SiRFprimaII and SiRFatlasVI watchdog. When
+ the watchdog triggers the system will be reset.
+
+config TEGRA_WATCHDOG
+ tristate "Tegra watchdog"
+ depends on ARCH_TEGRA || COMPILE_TEST
+ select WATCHDOG_CORE
+ help
+ Say Y here to include support for the watchdog timer
+ embedded in NVIDIA Tegra SoCs.
+
+ To compile this driver as a module, choose M here: the
+ module will be called tegra_wdt.
+
# AVR32 Architecture
config AT32AP700X_WDT
@@ -375,8 +469,6 @@ config BFIN_WDT
# FRV Architecture
-# H8300 Architecture
-
# X86 (i386 + ia64 + x86_64) Architecture
config ACQUIRE_WDT
@@ -428,7 +520,7 @@ config ALIM7101_WDT
config F71808E_WDT
tristate "Fintek F71808E, F71862FG, F71869, F71882FG and F71889FG Watchdog"
- depends on X86 && EXPERIMENTAL
+ depends on X86
help
This is the driver for the hardware watchdog on the Fintek
F71808E, F71862FG, F71869, F71882FG and F71889FG Super I/O controllers.
@@ -463,7 +555,7 @@ config GEODE_WDT
config SC520_WDT
tristate "AMD Elan SC520 processor Watchdog"
- depends on X86
+ depends on MELAN
help
This is the driver for the hardware watchdog built in to the
AMD "Elan" SC520 microcomputer commonly used in embedded systems.
@@ -543,7 +635,7 @@ config WAFER_WDT
config I6300ESB_WDT
tristate "Intel 6300ESB Timer/Watchdog"
- depends on X86 && PCI
+ depends on PCI
---help---
Hardware driver for the watchdog timer built into the Intel
6300ESB controller hub.
@@ -551,18 +643,46 @@ config I6300ESB_WDT
To compile this driver as a module, choose M here: the
module will be called i6300esb.
+config IE6XX_WDT
+ tristate "Intel Atom E6xx Watchdog"
+ depends on X86 && PCI
+ select WATCHDOG_CORE
+ select MFD_CORE
+ select LPC_SCH
+ ---help---
+ Hardware driver for the watchdog timer built into the Intel
+ Atom E6XX (TunnelCreek) processor.
+
+ To compile this driver as a module, choose M here: the
+ module will be called ie6xx_wdt.
+
config INTEL_SCU_WATCHDOG
bool "Intel SCU Watchdog for Mobile Platforms"
- depends on X86_MRST
+ depends on X86_INTEL_MID
---help---
Hardware driver for the watchdog time built into the Intel SCU
for Intel Mobile Platforms.
To compile this driver as a module, choose M here.
+config INTEL_MID_WATCHDOG
+ tristate "Intel MID Watchdog Timer"
+ depends on X86_INTEL_MID
+ select WATCHDOG_CORE
+ ---help---
+ Watchdog timer driver built into the Intel SCU for Intel MID
+ Platforms.
+
+ This driver currently supports only the watchdog evolution
+ implementation in SCU, available for Merrifield generation.
+
+ To compile this driver as a module, choose M here.
+
config ITCO_WDT
tristate "Intel TCO Timer/Watchdog"
depends on (X86 || IA64) && PCI
+ select WATCHDOG_CORE
+ select LPC_ICH
---help---
Hardware driver for the intel TCO timer based watchdog devices.
These drivers are included in the Intel 82801 I/O Controller
@@ -604,10 +724,15 @@ config IT8712F_WDT
config IT87_WDT
tristate "IT87 Watchdog Timer"
- depends on X86 && EXPERIMENTAL
+ depends on X86
---help---
This is the driver for the hardware watchdog on the ITE IT8702,
- IT8712, IT8716, IT8718, IT8720, IT8721, IT8726 Super I/O chips.
+ IT8712, IT8716, IT8718, IT8720, IT8721, IT8726 and IT8728
+ Super I/O chips.
+
+ If the driver does not work, then make sure that the game port in
+ the BIOS is enabled.
+
This watchdog simply watches your kernel to make sure it doesn't
freeze, and if it does, it reboots your computer after a certain
amount of time.
@@ -625,6 +750,17 @@ config HP_WATCHDOG
To compile this driver as a module, choose M here: the module will be
called hpwdt.
+config KEMPLD_WDT
+ tristate "Kontron COM Watchdog Timer"
+ depends on MFD_KEMPLD
+ select WATCHDOG_CORE
+ help
+ Support for the PLD watchdog on some Kontron ETX and COMexpress
+ (ETXexpress) modules
+
+ This driver can also be built as a module. If so, the module will be
+ called kempld_wdt.
+
config HPWDT_NMI_DECODING
bool "NMI decoding support for the HP ProLiant iLO2+ Hardware Watchdog Timer"
depends on HP_WATCHDOG
@@ -714,7 +850,7 @@ config 60XX_WDT
config SBC8360_WDT
tristate "SBC8360 Watchdog Timer"
- depends on X86
+ depends on X86_32
---help---
This is the driver for the hardware watchdog on the SBC8360 Single
@@ -780,7 +916,7 @@ config SMSC37B787_WDT
config VIA_WDT
tristate "VIA Watchdog Timer"
- depends on X86
+ depends on X86 && PCI
select WATCHDOG_CORE
---help---
This is the driver for the hardware watchdog timer on VIA
@@ -792,48 +928,28 @@ config VIA_WDT
Most people will say N.
config W83627HF_WDT
- tristate "W83627HF/W83627DHG Watchdog Timer"
+ tristate "Watchdog timer for W83627HF/W83627DHG and compatibles"
depends on X86
+ select WATCHDOG_CORE
---help---
- This is the driver for the hardware watchdog on the W83627HF chipset
- as used in Advantech PC-9578 and Tyan S2721-533 motherboards
- (and likely others). The driver also supports the W83627DHG chip.
- This watchdog simply watches your kernel to make sure it doesn't
- freeze, and if it does, it reboots your computer after a certain
- amount of time.
-
- To compile this driver as a module, choose M here: the
- module will be called w83627hf_wdt.
-
- Most people will say N.
+ This is the driver for the hardware watchdog on the following
+ Super I/O chips.
+ W83627DHG/DHG-P/EHF/EHG/F/G/HF/S/SF/THF/UHG/UG
+ W83637HF
+ W83667HG/HG-B
+ W83687THF
+ W83697HF
+ W83697UG
+ NCT6775
+ NCT6776
+ NCT6779
-config W83697HF_WDT
- tristate "W83697HF/W83697HG Watchdog Timer"
- depends on X86
- ---help---
- This is the driver for the hardware watchdog on the W83697HF/HG
- chipset as used in Dedibox/VIA motherboards (and likely others).
This watchdog simply watches your kernel to make sure it doesn't
freeze, and if it does, it reboots your computer after a certain
amount of time.
To compile this driver as a module, choose M here: the
- module will be called w83697hf_wdt.
-
- Most people will say N.
-
-config W83697UG_WDT
- tristate "W83697UG/W83697UF Watchdog Timer"
- depends on X86
- ---help---
- This is the driver for the hardware watchdog on the W83697UG/UF
- chipset as used in MSI Fuzzy CX700 VIA motherboards (and likely others).
- This watchdog simply watches your kernel to make sure it doesn't
- freeze, and if it does, it reboots your computer after a certain
- amount of time.
-
- To compile this driver as a module, choose M here: the
- module will be called w83697ug_wdt.
+ module will be called w83627hf_wdt.
Most people will say N.
@@ -912,18 +1028,6 @@ config M54xx_WATCHDOG
# MicroBlaze Architecture
-config XILINX_WATCHDOG
- tristate "Xilinx Watchdog timer"
- depends on MICROBLAZE
- ---help---
- Watchdog driver for the xps_timebase_wdt ip core.
-
- IMPORTANT: The xps_timebase_wdt parent must have the property
- "clock-frequency" at device tree.
-
- To compile this driver as a module, choose M here: the
- module will be called of_xilinx_wdt.
-
# MIPS Architecture
config ATH79_WDT
@@ -936,8 +1040,9 @@ config ATH79_WDT
config BCM47XX_WDT
tristate "Broadcom BCM47xx Watchdog Timer"
depends on BCM47XX
+ select WATCHDOG_CORE
help
- Hardware driver for the Broadcom BCM47xx Watchog Timer.
+ Hardware driver for the Broadcom BCM47xx Watchdog Timer.
config RC32434_WDT
tristate "IDT RC32434 SoC Watchdog Timer"
@@ -1009,7 +1114,7 @@ config TXX9_WDT
config OCTEON_WDT
tristate "Cavium OCTEON SOC family Watchdog Timer"
- depends on CPU_CAVIUM_OCTEON
+ depends on CAVIUM_OCTEON_SOC
default y
select EXPORT_UASM if OCTEON_WDT = m
help
@@ -1035,12 +1140,52 @@ config BCM63XX_WDT
To compile this driver as a loadable module, choose M here.
The module will be called bcm63xx_wdt.
+config BCM2835_WDT
+ tristate "Broadcom BCM2835 hardware watchdog"
+ depends on ARCH_BCM2835
+ select WATCHDOG_CORE
+ help
+ Watchdog driver for the built in watchdog hardware in Broadcom
+ BCM2835 SoC.
+
+ To compile this driver as a loadable module, choose M here.
+ The module will be called bcm2835_wdt.
+
+config BCM_KONA_WDT
+ tristate "BCM Kona Watchdog"
+ depends on ARCH_BCM_MOBILE
+ select WATCHDOG_CORE
+ help
+ Support for the watchdog timer on the following Broadcom BCM281xx
+ family, which includes BCM11130, BCM11140, BCM11351, BCM28145 and
+ BCM28155 variants.
+
+ Say 'Y' or 'M' here to enable the driver. The module will be called
+ bcm_kona_wdt.
+
+config BCM_KONA_WDT_DEBUG
+ bool "DEBUGFS support for BCM Kona Watchdog"
+ depends on BCM_KONA_WDT
+ 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
+ timer hardware registres.
+
+ If in doubt, say 'N'.
+
config LANTIQ_WDT
tristate "Lantiq SoC watchdog"
depends on LANTIQ
help
Hardware driver for the Lantiq SoC Watchdog Timer.
+config RALINK_WDT
+ tristate "Ralink SoC watchdog"
+ select WATCHDOG_CORE
+ depends on RALINK
+ help
+ Hardware driver for the Ralink SoC Watchdog Timer.
+
# PARISC Architecture
# POWERPC Architecture
@@ -1060,6 +1205,7 @@ config MPC5200_WDT
config 8xxx_WDT
tristate "MPC8xxx Platform Watchdog Timer"
depends on PPC_8xx || PPC_83xx || PPC_86xx
+ select WATCHDOG_CORE
help
This driver is for a SoC level watchdog that exists on some
Freescale PowerPC processors. So far this driver supports:
@@ -1084,6 +1230,7 @@ config PIKA_WDT
config BOOKE_WDT
tristate "PowerPC Book-E Watchdog Timer"
depends on BOOKE || 4xx
+ select WATCHDOG_CORE
---help---
Watchdog driver for PowerPC Book-E chips, such as the Freescale
MPC85xx SOCs and the IBM PowerPC 440.
@@ -1094,10 +1241,10 @@ config BOOKE_WDT
config BOOKE_WDT_DEFAULT_TIMEOUT
int "PowerPC Book-E Watchdog Timer Default Timeout"
depends on BOOKE_WDT
- default 38 if FSL_BOOKE
- range 0 63 if FSL_BOOKE
- default 3 if !FSL_BOOKE
- range 0 3 if !FSL_BOOKE
+ default 38 if PPC_FSL_BOOK3E
+ range 0 63 if PPC_FSL_BOOK3E
+ default 3 if !PPC_FSL_BOOK3E
+ range 0 3 if !PPC_FSL_BOOK3E
help
Select the default watchdog timer period to be used by the PowerPC
Book-E watchdog driver. A watchdog "event" occurs when the bit
@@ -1108,6 +1255,18 @@ config BOOKE_WDT_DEFAULT_TIMEOUT
The value can be overridden by the wdt_period command-line parameter.
+config MEN_A21_WDT
+ tristate "MEN A21 VME CPU Carrier Board Watchdog Timer"
+ select WATCHDOG_CORE
+ depends on GPIOLIB
+ help
+ Watchdog driver for MEN A21 VMEbus CPU Carrier Boards.
+
+ The driver can also be built as a module. If so, the module will be
+ called mena21_wdt.
+
+ If unsure select N here.
+
# PPC64 Architecture
config WATCHDOG_RTAS
@@ -1121,14 +1280,17 @@ config WATCHDOG_RTAS
# S390 Architecture
-config ZVM_WATCHDOG
- tristate "z/VM Watchdog Timer"
+config DIAG288_WATCHDOG
+ tristate "System z diag288 Watchdog"
depends on S390
+ select WATCHDOG_CORE
help
IBM s/390 and zSeries machines running under z/VM 5.1 or later
provide a virtual watchdog timer to their guest that cause a
user define Control Program command to be executed after a
timeout.
+ LPAR provides a very similar interface. This driver handles
+ both.
To compile this driver as a module, choose M here. The module
will be called vmwatchdog.
@@ -1138,6 +1300,7 @@ config ZVM_WATCHDOG
config SH_WDT
tristate "SuperH Watchdog"
depends on SUPERH && (CPU_SH3 || CPU_SH4)
+ select WATCHDOG_CORE
help
This driver adds watchdog support for the integrated watchdog in the
SuperH processors. If you have one of these processors and wish
diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile
index e8f479a1640..468c3204c3b 100644
--- a/drivers/watchdog/Makefile
+++ b/drivers/watchdog/Makefile
@@ -36,23 +36,29 @@ 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_IXP2000_WATCHDOG) += ixp2000_wdt.o
obj-$(CONFIG_IXP4XX_WATCHDOG) += ixp4xx_wdt.o
obj-$(CONFIG_KS8695_WATCHDOG) += ks8695_wdt.o
obj-$(CONFIG_S3C2410_WATCHDOG) += s3c2410_wdt.o
obj-$(CONFIG_SA1100_WATCHDOG) += sa1100_wdt.o
obj-$(CONFIG_DW_WATCHDOG) += dw_wdt.o
-obj-$(CONFIG_MPCORE_WATCHDOG) += mpcore_wdt.o
obj-$(CONFIG_EP93XX_WATCHDOG) += ep93xx_wdt.o
obj-$(CONFIG_PNX4008_WATCHDOG) += pnx4008_wdt.o
obj-$(CONFIG_IOP_WATCHDOG) += iop_wdt.o
obj-$(CONFIG_DAVINCI_WATCHDOG) += davinci_wdt.o
obj-$(CONFIG_ORION_WATCHDOG) += orion_wdt.o
+obj-$(CONFIG_SUNXI_WATCHDOG) += sunxi_wdt.o
obj-$(CONFIG_COH901327_WATCHDOG) += coh901327_wdt.o
-obj-$(CONFIG_STMP3XXX_WATCHDOG) += stmp3xxx_wdt.o
+obj-$(CONFIG_STMP3XXX_RTC_WATCHDOG) += stmp3xxx_rtc_wdt.o
obj-$(CONFIG_NUC900_WATCHDOG) += nuc900_wdt.o
obj-$(CONFIG_TS72XX_WATCHDOG) += ts72xx_wdt.o
obj-$(CONFIG_IMX2_WDT) += imx2_wdt.o
+obj-$(CONFIG_UX500_WATCHDOG) += ux500_wdt.o
+obj-$(CONFIG_RETU_WATCHDOG) += retu_wdt.o
+obj-$(CONFIG_BCM2835_WDT) += bcm2835_wdt.o
+obj-$(CONFIG_MOXART_WDT) += moxart_wdt.o
+obj-$(CONFIG_SIRFSOC_WATCHDOG) += sirfsoc_wdt.o
+obj-$(CONFIG_BCM_KONA_WDT) += bcm_kona_wdt.o
+obj-$(CONFIG_TEGRA_WATCHDOG) += tegra_wdt.o
# AVR32 Architecture
obj-$(CONFIG_AT32AP700X_WDT) += at32ap700x_wdt.o
@@ -64,8 +70,6 @@ obj-$(CONFIG_BFIN_WDT) += bfin_wdt.o
# FRV Architecture
-# H8300 Architecture
-
# X86 (i386 + ia64 + x86_64) Architecture
obj-$(CONFIG_ACQUIRE_WDT) += acquirewdt.o
obj-$(CONFIG_ADVANTECH_WDT) += advantechwdt.o
@@ -81,6 +85,7 @@ obj-$(CONFIG_IB700_WDT) += ib700wdt.o
obj-$(CONFIG_IBMASR) += ibmasr.o
obj-$(CONFIG_WAFER_WDT) += wafer5823wdt.o
obj-$(CONFIG_I6300ESB_WDT) += i6300esb.o
+obj-$(CONFIG_IE6XX_WDT) += ie6xx_wdt.o
obj-$(CONFIG_ITCO_WDT) += iTCO_wdt.o
ifeq ($(CONFIG_ITCO_VENDOR_SUPPORT),y)
obj-$(CONFIG_ITCO_WDT) += iTCO_vendor_support.o
@@ -88,6 +93,7 @@ endif
obj-$(CONFIG_IT8712F_WDT) += it8712f_wdt.o
obj-$(CONFIG_IT87_WDT) += it87_wdt.o
obj-$(CONFIG_HP_WATCHDOG) += hpwdt.o
+obj-$(CONFIG_KEMPLD_WDT) += kempld_wdt.o
obj-$(CONFIG_SC1200_WDT) += sc1200wdt.o
obj-$(CONFIG_SCx200_WDT) += scx200_wdt.o
obj-$(CONFIG_PC87413_WDT) += pc87413_wdt.o
@@ -101,13 +107,12 @@ obj-$(CONFIG_SMSC_SCH311X_WDT) += sch311x_wdt.o
obj-$(CONFIG_SMSC37B787_WDT) += smsc37b787_wdt.o
obj-$(CONFIG_VIA_WDT) += via_wdt.o
obj-$(CONFIG_W83627HF_WDT) += w83627hf_wdt.o
-obj-$(CONFIG_W83697HF_WDT) += w83697hf_wdt.o
-obj-$(CONFIG_W83697UG_WDT) += w83697ug_wdt.o
obj-$(CONFIG_W83877F_WDT) += w83877f_wdt.o
obj-$(CONFIG_W83977F_WDT) += w83977f_wdt.o
obj-$(CONFIG_MACHZ_WDT) += machzwd.o
obj-$(CONFIG_SBC_EPX_C3_WATCHDOG) += sbc_epx_c3.o
obj-$(CONFIG_INTEL_SCU_WATCHDOG) += intel_scu_watchdog.o
+obj-$(CONFIG_INTEL_MID_WATCHDOG) += intel-mid_wdt.o
# M32R Architecture
@@ -132,6 +137,7 @@ obj-$(CONFIG_TXX9_WDT) += txx9wdt.o
obj-$(CONFIG_OCTEON_WDT) += octeon-wdt.o
octeon-wdt-y := octeon-wdt-main.o octeon-wdt-nmi.o
obj-$(CONFIG_LANTIQ_WDT) += lantiq_wdt.o
+obj-$(CONFIG_RALINK_WDT) += rt2880_wdt.o
# PARISC Architecture
@@ -141,11 +147,13 @@ obj-$(CONFIG_8xxx_WDT) += mpc8xxx_wdt.o
obj-$(CONFIG_MV64X60_WDT) += mv64x60_wdt.o
obj-$(CONFIG_PIKA_WDT) += pika_wdt.o
obj-$(CONFIG_BOOKE_WDT) += booke_wdt.o
+obj-$(CONFIG_MEN_A21_WDT) += mena21_wdt.o
# PPC64 Architecture
obj-$(CONFIG_WATCHDOG_RTAS) += wdrtas.o
# S390 Architecture
+obj-$(CONFIG_DIAG288_WATCHDOG) += diag288_wdt.o
# SUPERH (sh + sh64) Architecture
obj-$(CONFIG_SH_WDT) += shwdt.o
@@ -163,6 +171,9 @@ obj-$(CONFIG_WATCHDOG_CP1XXX) += cpwd.o
obj-$(CONFIG_XEN_WDT) += xen_wdt.o
# Architecture Independent
+obj-$(CONFIG_DA9052_WATCHDOG) += da9052_wdt.o
+obj-$(CONFIG_DA9055_WATCHDOG) += da9055_wdt.o
+obj-$(CONFIG_GPIO_WATCHDOG) += gpio_wdt.o
obj-$(CONFIG_WM831X_WATCHDOG) += wm831x_wdt.o
obj-$(CONFIG_WM8350_WATCHDOG) += wm8350_wdt.o
obj-$(CONFIG_MAX63XX_WATCHDOG) += max63xx_wdt.o
diff --git a/drivers/watchdog/acquirewdt.c b/drivers/watchdog/acquirewdt.c
index 4397881c83f..5614416f103 100644
--- a/drivers/watchdog/acquirewdt.c
+++ b/drivers/watchdog/acquirewdt.c
@@ -60,8 +60,7 @@
#include <linux/types.h> /* For standard types (like size_t) */
#include <linux/errno.h> /* For the -ENODEV/... values */
#include <linux/kernel.h> /* For printk/panic/... */
-#include <linux/miscdevice.h> /* For MODULE_ALIAS_MISCDEV
- (WATCHDOG_MINOR) */
+#include <linux/miscdevice.h> /* For struct miscdevice */
#include <linux/watchdog.h> /* For the watchdog specific items */
#include <linux/fs.h> /* For file operations */
#include <linux/ioport.h> /* For io-port access */
@@ -240,7 +239,7 @@ static struct miscdevice acq_miscdev = {
* Init & exit routines
*/
-static int __devinit acq_probe(struct platform_device *dev)
+static int __init acq_probe(struct platform_device *dev)
{
int ret;
@@ -275,7 +274,7 @@ out:
return ret;
}
-static int __devexit acq_remove(struct platform_device *dev)
+static int acq_remove(struct platform_device *dev)
{
misc_deregister(&acq_miscdev);
release_region(wdt_start, 1);
@@ -292,8 +291,7 @@ static void acq_shutdown(struct platform_device *dev)
}
static struct platform_driver acquirewdt_driver = {
- .probe = acq_probe,
- .remove = __devexit_p(acq_remove),
+ .remove = acq_remove,
.shutdown = acq_shutdown,
.driver = {
.owner = THIS_MODULE,
@@ -307,20 +305,18 @@ static int __init acq_init(void)
pr_info("WDT driver for Acquire single board computer initialising\n");
- err = platform_driver_register(&acquirewdt_driver);
- if (err)
- return err;
-
acq_platform_device = platform_device_register_simple(DRV_NAME,
-1, NULL, 0);
- if (IS_ERR(acq_platform_device)) {
- err = PTR_ERR(acq_platform_device);
- goto unreg_platform_driver;
- }
+ if (IS_ERR(acq_platform_device))
+ return PTR_ERR(acq_platform_device);
+
+ err = platform_driver_probe(&acquirewdt_driver, acq_probe);
+ if (err)
+ goto unreg_platform_device;
return 0;
-unreg_platform_driver:
- platform_driver_unregister(&acquirewdt_driver);
+unreg_platform_device:
+ platform_device_unregister(acq_platform_device);
return err;
}
@@ -337,4 +333,3 @@ module_exit(acq_exit);
MODULE_AUTHOR("David Woodhouse");
MODULE_DESCRIPTION("Acquire Inc. Single Board Computer Watchdog Timer driver");
MODULE_LICENSE("GPL");
-MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
diff --git a/drivers/watchdog/advantechwdt.c b/drivers/watchdog/advantechwdt.c
index 64ae9e9fed9..7796db7fa6e 100644
--- a/drivers/watchdog/advantechwdt.c
+++ b/drivers/watchdog/advantechwdt.c
@@ -238,7 +238,7 @@ static struct miscdevice advwdt_miscdev = {
* Init & exit routines
*/
-static int __devinit advwdt_probe(struct platform_device *dev)
+static int __init advwdt_probe(struct platform_device *dev)
{
int ret;
@@ -282,7 +282,7 @@ unreg_stop:
goto out;
}
-static int __devexit advwdt_remove(struct platform_device *dev)
+static int advwdt_remove(struct platform_device *dev)
{
misc_deregister(&advwdt_miscdev);
release_region(wdt_start, 1);
@@ -299,8 +299,7 @@ static void advwdt_shutdown(struct platform_device *dev)
}
static struct platform_driver advwdt_driver = {
- .probe = advwdt_probe,
- .remove = __devexit_p(advwdt_remove),
+ .remove = advwdt_remove,
.shutdown = advwdt_shutdown,
.driver = {
.owner = THIS_MODULE,
@@ -314,21 +313,19 @@ static int __init advwdt_init(void)
pr_info("WDT driver for Advantech single board computer initialising\n");
- err = platform_driver_register(&advwdt_driver);
- if (err)
- return err;
-
advwdt_platform_device = platform_device_register_simple(DRV_NAME,
-1, NULL, 0);
- if (IS_ERR(advwdt_platform_device)) {
- err = PTR_ERR(advwdt_platform_device);
- goto unreg_platform_driver;
- }
+ if (IS_ERR(advwdt_platform_device))
+ return PTR_ERR(advwdt_platform_device);
+
+ err = platform_driver_probe(&advwdt_driver, advwdt_probe);
+ if (err)
+ goto unreg_platform_device;
return 0;
-unreg_platform_driver:
- platform_driver_unregister(&advwdt_driver);
+unreg_platform_device:
+ platform_device_unregister(advwdt_platform_device);
return err;
}
@@ -345,4 +342,3 @@ module_exit(advwdt_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Marek Michalkiewicz <marekm@linux.org.pl>");
MODULE_DESCRIPTION("Advantech Single Board Computer WDT driver");
-MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
diff --git a/drivers/watchdog/alim1535_wdt.c b/drivers/watchdog/alim1535_wdt.c
index 41b84936a52..3a17fbd39f8 100644
--- a/drivers/watchdog/alim1535_wdt.c
+++ b/drivers/watchdog/alim1535_wdt.c
@@ -301,7 +301,7 @@ static int ali_notify_sys(struct notifier_block *this,
* want to register another driver on the same PCI id.
*/
-static DEFINE_PCI_DEVICE_TABLE(ali_pci_tbl) __used = {
+static const struct pci_device_id ali_pci_tbl[] __used = {
{ PCI_VENDOR_ID_AL, 0x1533, PCI_ANY_ID, PCI_ANY_ID,},
{ PCI_VENDOR_ID_AL, 0x1535, PCI_ANY_ID, PCI_ANY_ID,},
{ 0, },
@@ -452,4 +452,3 @@ module_exit(watchdog_exit);
MODULE_AUTHOR("Alan Cox");
MODULE_DESCRIPTION("ALi M1535 PMU Watchdog Timer driver");
MODULE_LICENSE("GPL");
-MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
diff --git a/drivers/watchdog/alim7101_wdt.c b/drivers/watchdog/alim7101_wdt.c
index 5eee55012e3..996b2f7d330 100644
--- a/drivers/watchdog/alim7101_wdt.c
+++ b/drivers/watchdog/alim7101_wdt.c
@@ -414,7 +414,7 @@ err_out:
module_init(alim7101_wdt_init);
module_exit(alim7101_wdt_unload);
-static DEFINE_PCI_DEVICE_TABLE(alim7101_pci_tbl) __used = {
+static const struct pci_device_id alim7101_pci_tbl[] __used = {
{ PCI_DEVICE(PCI_VENDOR_ID_AL, PCI_DEVICE_ID_AL_M1533) },
{ PCI_DEVICE(PCI_VENDOR_ID_AL, PCI_DEVICE_ID_AL_M7101) },
{ }
@@ -425,4 +425,3 @@ MODULE_DEVICE_TABLE(pci, alim7101_pci_tbl);
MODULE_AUTHOR("Steve Hill");
MODULE_DESCRIPTION("ALi M7101 PMU Computer Watchdog Timer driver");
MODULE_LICENSE("GPL");
-MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
diff --git a/drivers/watchdog/ar7_wdt.c b/drivers/watchdog/ar7_wdt.c
index 639ae9a23fb..ae6c287a49c 100644
--- a/drivers/watchdog/ar7_wdt.c
+++ b/drivers/watchdog/ar7_wdt.c
@@ -28,7 +28,6 @@
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/errno.h>
-#include <linux/init.h>
#include <linux/miscdevice.h>
#include <linux/platform_device.h>
#include <linux/watchdog.h>
@@ -46,7 +45,6 @@
MODULE_AUTHOR("Nicolas Thill <nico@openwrt.org>");
MODULE_DESCRIPTION(LONGNAME);
MODULE_LICENSE("GPL");
-MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
static int margin = 60;
module_param(margin, int, 0);
@@ -274,37 +272,20 @@ static struct miscdevice ar7_wdt_miscdev = {
.fops = &ar7_wdt_fops,
};
-static int __devinit ar7_wdt_probe(struct platform_device *pdev)
+static int ar7_wdt_probe(struct platform_device *pdev)
{
int rc;
ar7_regs_wdt =
platform_get_resource_byname(pdev, IORESOURCE_MEM, "regs");
- if (!ar7_regs_wdt) {
- pr_err("could not get registers resource\n");
- rc = -ENODEV;
- goto out;
- }
-
- if (!request_mem_region(ar7_regs_wdt->start,
- resource_size(ar7_regs_wdt), LONGNAME)) {
- pr_warn("watchdog I/O region busy\n");
- rc = -EBUSY;
- goto out;
- }
-
- ar7_wdt = ioremap(ar7_regs_wdt->start, resource_size(ar7_regs_wdt));
- if (!ar7_wdt) {
- pr_err("could not ioremap registers\n");
- rc = -ENXIO;
- goto out_mem_region;
- }
+ ar7_wdt = devm_ioremap_resource(&pdev->dev, ar7_regs_wdt);
+ if (IS_ERR(ar7_wdt))
+ return PTR_ERR(ar7_wdt);
vbus_clk = clk_get(NULL, "vbus");
if (IS_ERR(vbus_clk)) {
pr_err("could not get vbus clock\n");
- rc = PTR_ERR(vbus_clk);
- goto out_mem_region;
+ return PTR_ERR(vbus_clk);
}
ar7_wdt_disable_wdt();
@@ -314,24 +295,21 @@ static int __devinit ar7_wdt_probe(struct platform_device *pdev)
rc = misc_register(&ar7_wdt_miscdev);
if (rc) {
pr_err("unable to register misc device\n");
- goto out_alloc;
+ goto out;
}
- goto out;
+ return 0;
-out_alloc:
- iounmap(ar7_wdt);
-out_mem_region:
- release_mem_region(ar7_regs_wdt->start, resource_size(ar7_regs_wdt));
out:
+ clk_put(vbus_clk);
+ vbus_clk = NULL;
return rc;
}
-static int __devexit ar7_wdt_remove(struct platform_device *pdev)
+static int ar7_wdt_remove(struct platform_device *pdev)
{
misc_deregister(&ar7_wdt_miscdev);
- iounmap(ar7_wdt);
- release_mem_region(ar7_regs_wdt->start, resource_size(ar7_regs_wdt));
-
+ clk_put(vbus_clk);
+ vbus_clk = NULL;
return 0;
}
@@ -343,7 +321,7 @@ static void ar7_wdt_shutdown(struct platform_device *pdev)
static struct platform_driver ar7_wdt_driver = {
.probe = ar7_wdt_probe,
- .remove = __devexit_p(ar7_wdt_remove),
+ .remove = ar7_wdt_remove,
.shutdown = ar7_wdt_shutdown,
.driver = {
.owner = THIS_MODULE,
diff --git a/drivers/watchdog/at32ap700x_wdt.c b/drivers/watchdog/at32ap700x_wdt.c
index 2896430ce42..25b5c67d3af 100644
--- a/drivers/watchdog/at32ap700x_wdt.c
+++ b/drivers/watchdog/at32ap700x_wdt.c
@@ -321,13 +321,12 @@ static int __init at32_wdt_probe(struct platform_device *pdev)
return -ENXIO;
}
- wdt = kzalloc(sizeof(struct wdt_at32ap700x), GFP_KERNEL);
- if (!wdt) {
- dev_dbg(&pdev->dev, "no memory for wdt structure\n");
+ wdt = devm_kzalloc(&pdev->dev, sizeof(struct wdt_at32ap700x),
+ GFP_KERNEL);
+ if (!wdt)
return -ENOMEM;
- }
- wdt->regs = ioremap(regs->start, resource_size(regs));
+ wdt->regs = devm_ioremap(&pdev->dev, regs->start, resource_size(regs));
if (!wdt->regs) {
ret = -ENOMEM;
dev_dbg(&pdev->dev, "could not map I/O memory\n");
@@ -342,7 +341,7 @@ static int __init at32_wdt_probe(struct platform_device *pdev)
dev_info(&pdev->dev, "CPU must be reset with external "
"reset or POR due to silicon errata.\n");
ret = -EIO;
- goto err_iounmap;
+ goto err_free;
} else {
wdt->users = 0;
}
@@ -364,7 +363,7 @@ static int __init at32_wdt_probe(struct platform_device *pdev)
ret = misc_register(&wdt->miscdev);
if (ret) {
dev_dbg(&pdev->dev, "failed to register wdt miscdev\n");
- goto err_register;
+ goto err_free;
}
dev_info(&pdev->dev,
@@ -373,12 +372,7 @@ static int __init at32_wdt_probe(struct platform_device *pdev)
return 0;
-err_register:
- platform_set_drvdata(pdev, NULL);
-err_iounmap:
- iounmap(wdt->regs);
err_free:
- kfree(wdt);
wdt = NULL;
return ret;
}
@@ -391,10 +385,7 @@ static int __exit at32_wdt_remove(struct platform_device *pdev)
at32_wdt_stop();
misc_deregister(&wdt->miscdev);
- iounmap(wdt->regs);
- kfree(wdt);
wdt = NULL;
- platform_set_drvdata(pdev, NULL);
}
return 0;
}
@@ -436,19 +427,8 @@ static struct platform_driver at32_wdt_driver = {
.shutdown = at32_wdt_shutdown,
};
-static int __init at32_wdt_init(void)
-{
- return platform_driver_probe(&at32_wdt_driver, at32_wdt_probe);
-}
-module_init(at32_wdt_init);
-
-static void __exit at32_wdt_exit(void)
-{
- platform_driver_unregister(&at32_wdt_driver);
-}
-module_exit(at32_wdt_exit);
+module_platform_driver_probe(at32_wdt_driver, at32_wdt_probe);
MODULE_AUTHOR("Hans-Christian Egtvedt <egtvedt@samfundet.no>");
MODULE_DESCRIPTION("Watchdog driver for Atmel AT32AP700X");
MODULE_LICENSE("GPL");
-MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
diff --git a/drivers/watchdog/at91rm9200_wdt.c b/drivers/watchdog/at91rm9200_wdt.c
index 7ef99a169e3..dee6cc21d27 100644
--- a/drivers/watchdog/at91rm9200_wdt.c
+++ b/drivers/watchdog/at91rm9200_wdt.c
@@ -24,6 +24,8 @@
#include <linux/types.h>
#include <linux/watchdog.h>
#include <linux/uaccess.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
#include <mach/at91_st.h>
#define WDT_DEFAULT_TIME 5 /* seconds */
@@ -199,7 +201,7 @@ static struct miscdevice at91wdt_miscdev = {
.fops = &at91wdt_fops,
};
-static int __devinit at91wdt_probe(struct platform_device *pdev)
+static int at91wdt_probe(struct platform_device *pdev)
{
int res;
@@ -216,7 +218,7 @@ static int __devinit at91wdt_probe(struct platform_device *pdev)
return 0;
}
-static int __devexit at91wdt_remove(struct platform_device *pdev)
+static int at91wdt_remove(struct platform_device *pdev)
{
int res;
@@ -252,15 +254,22 @@ static int at91wdt_resume(struct platform_device *pdev)
#define at91wdt_resume NULL
#endif
+static const struct of_device_id at91_wdt_dt_ids[] = {
+ { .compatible = "atmel,at91rm9200-wdt" },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, at91_wdt_dt_ids);
+
static struct platform_driver at91wdt_driver = {
.probe = at91wdt_probe,
- .remove = __devexit_p(at91wdt_remove),
+ .remove = at91wdt_remove,
.shutdown = at91wdt_shutdown,
.suspend = at91wdt_suspend,
.resume = at91wdt_resume,
.driver = {
.name = "at91_wdt",
.owner = THIS_MODULE,
+ .of_match_table = at91_wdt_dt_ids,
},
};
@@ -288,5 +297,4 @@ module_exit(at91_wdt_exit);
MODULE_AUTHOR("Andrew Victor");
MODULE_DESCRIPTION("Watchdog driver for Atmel AT91RM9200");
MODULE_LICENSE("GPL");
-MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
MODULE_ALIAS("platform:at91_wdt");
diff --git a/drivers/watchdog/at91sam9_wdt.c b/drivers/watchdog/at91sam9_wdt.c
index 05e1be85fde..489729b2629 100644
--- a/drivers/watchdog/at91sam9_wdt.c
+++ b/drivers/watchdog/at91sam9_wdt.c
@@ -18,36 +18,48 @@
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/errno.h>
-#include <linux/fs.h>
#include <linux/init.h>
+#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/kernel.h>
-#include <linux/miscdevice.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/platform_device.h>
+#include <linux/reboot.h>
#include <linux/types.h>
#include <linux/watchdog.h>
#include <linux/jiffies.h>
#include <linux/timer.h>
#include <linux/bitops.h>
#include <linux/uaccess.h>
+#include <linux/of.h>
+#include <linux/of_irq.h>
#include "at91sam9_wdt.h"
#define DRV_NAME "AT91SAM9 Watchdog"
-#define wdt_read(field) \
- __raw_readl(at91wdt_private.base + field)
-#define wdt_write(field, val) \
- __raw_writel((val), at91wdt_private.base + field)
+#define wdt_read(wdt, field) \
+ __raw_readl((wdt)->base + (field))
+#define wdt_write(wtd, field, val) \
+ __raw_writel((val), (wdt)->base + (field))
/* AT91SAM9 watchdog runs a 12bit counter @ 256Hz,
* use this to convert a watchdog
* value from/to milliseconds.
*/
-#define ms_to_ticks(t) (((t << 8) / 1000) - 1)
-#define ticks_to_ms(t) (((t + 1) * 1000) >> 8)
+#define ticks_to_hz_rounddown(t) ((((t) + 1) * HZ) >> 8)
+#define ticks_to_hz_roundup(t) (((((t) + 1) * HZ) + 255) >> 8)
+#define ticks_to_secs(t) (((t) + 1) >> 8)
+#define secs_to_ticks(s) ((s) ? (((s) << 8) - 1) : 0)
+
+#define WDT_MR_RESET 0x3FFF2FFF
+
+/* Watchdog max counter value in ticks */
+#define WDT_COUNTER_MAX_TICKS 0xFFF
+
+/* Watchdog max delta/value in secs */
+#define WDT_COUNTER_MAX_SECS ticks_to_secs(WDT_COUNTER_MAX_TICKS)
/* Hardware timeout in seconds */
#define WDT_HW_TIMEOUT 2
@@ -57,7 +69,7 @@
/* User land timeout */
#define WDT_HEARTBEAT 15
-static int heartbeat = WDT_HEARTBEAT;
+static int heartbeat;
module_param(heartbeat, int, 0);
MODULE_PARM_DESC(heartbeat, "Watchdog heartbeats in seconds. "
"(default = " __MODULE_STRING(WDT_HEARTBEAT) ")");
@@ -67,25 +79,40 @@ module_param(nowayout, bool, 0);
MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started "
"(default=" __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
-static void at91_ping(unsigned long data);
-
-static struct {
+#define to_wdt(wdd) container_of(wdd, struct at91wdt, wdd)
+struct at91wdt {
+ struct watchdog_device wdd;
void __iomem *base;
unsigned long next_heartbeat; /* the next_heartbeat for the timer */
- unsigned long open;
- char expect_close;
struct timer_list timer; /* The timer that pings the watchdog */
-} at91wdt_private;
+ u32 mr;
+ u32 mr_mask;
+ unsigned long heartbeat; /* WDT heartbeat in jiffies */
+ bool nowayout;
+ unsigned int irq;
+};
/* ......................................................................... */
+static irqreturn_t wdt_interrupt(int irq, void *dev_id)
+{
+ struct at91wdt *wdt = (struct at91wdt *)dev_id;
+
+ if (wdt_read(wdt, AT91_WDT_SR)) {
+ pr_crit("at91sam9 WDT software reset\n");
+ emergency_restart();
+ pr_crit("Reboot didn't ?????\n");
+ }
+
+ return IRQ_HANDLED;
+}
/*
* Reload the watchdog timer. (ie, pat the watchdog)
*/
-static inline void at91_wdt_reset(void)
+static inline void at91_wdt_reset(struct at91wdt *wdt)
{
- wdt_write(AT91_WDT_CR, AT91_WDT_KEY | AT91_WDT_WDRSTT);
+ wdt_write(wdt, AT91_WDT_CR, AT91_WDT_KEY | AT91_WDT_WDRSTT);
}
/*
@@ -93,237 +120,286 @@ static inline void at91_wdt_reset(void)
*/
static void at91_ping(unsigned long data)
{
- if (time_before(jiffies, at91wdt_private.next_heartbeat) ||
- (!nowayout && !at91wdt_private.open)) {
- at91_wdt_reset();
- mod_timer(&at91wdt_private.timer, jiffies + WDT_TIMEOUT);
- } else
+ struct at91wdt *wdt = (struct at91wdt *)data;
+ if (time_before(jiffies, wdt->next_heartbeat) ||
+ !watchdog_active(&wdt->wdd)) {
+ at91_wdt_reset(wdt);
+ mod_timer(&wdt->timer, jiffies + wdt->heartbeat);
+ } else {
pr_crit("I will reset your machine !\n");
+ }
}
-/*
- * Watchdog device is opened, and watchdog starts running.
- */
-static int at91_wdt_open(struct inode *inode, struct file *file)
+static int at91_wdt_start(struct watchdog_device *wdd)
{
- if (test_and_set_bit(0, &at91wdt_private.open))
- return -EBUSY;
+ struct at91wdt *wdt = to_wdt(wdd);
+ /* calculate when the next userspace timeout will be */
+ wdt->next_heartbeat = jiffies + wdd->timeout * HZ;
+ return 0;
+}
- at91wdt_private.next_heartbeat = jiffies + heartbeat * HZ;
- mod_timer(&at91wdt_private.timer, jiffies + WDT_TIMEOUT);
+static int at91_wdt_stop(struct watchdog_device *wdd)
+{
+ /* The watchdog timer hardware can not be stopped... */
+ return 0;
+}
- return nonseekable_open(inode, file);
+static int at91_wdt_set_timeout(struct watchdog_device *wdd, unsigned int new_timeout)
+{
+ wdd->timeout = new_timeout;
+ return at91_wdt_start(wdd);
}
-/*
- * Close the watchdog device.
- */
-static int at91_wdt_close(struct inode *inode, struct file *file)
+static int at91_wdt_init(struct platform_device *pdev, struct at91wdt *wdt)
{
- clear_bit(0, &at91wdt_private.open);
+ u32 tmp;
+ u32 delta;
+ u32 value;
+ int err;
+ u32 mask = wdt->mr_mask;
+ unsigned long min_heartbeat = 1;
+ unsigned long max_heartbeat;
+ struct device *dev = &pdev->dev;
+
+ tmp = wdt_read(wdt, AT91_WDT_MR);
+ if ((tmp & mask) != (wdt->mr & mask)) {
+ if (tmp == WDT_MR_RESET) {
+ wdt_write(wdt, AT91_WDT_MR, wdt->mr);
+ tmp = wdt_read(wdt, AT91_WDT_MR);
+ }
+ }
- /* stop internal ping */
- if (!at91wdt_private.expect_close)
- del_timer(&at91wdt_private.timer);
+ if (tmp & AT91_WDT_WDDIS) {
+ if (wdt->mr & AT91_WDT_WDDIS)
+ return 0;
+ dev_err(dev, "watchdog is disabled\n");
+ return -EINVAL;
+ }
- at91wdt_private.expect_close = 0;
- return 0;
-}
+ value = tmp & AT91_WDT_WDV;
+ delta = (tmp & AT91_WDT_WDD) >> 16;
-/*
- * Set the watchdog time interval in 1/256Hz (write-once)
- * Counter is 12 bit.
- */
-static int at91_wdt_settimeout(unsigned int timeout)
-{
- unsigned int reg;
- unsigned int mr;
-
- /* Check if disabled */
- mr = wdt_read(AT91_WDT_MR);
- if (mr & AT91_WDT_WDDIS) {
- pr_err("sorry, watchdog is disabled\n");
- return -EIO;
+ if (delta < value)
+ min_heartbeat = ticks_to_hz_roundup(value - delta);
+
+ max_heartbeat = ticks_to_hz_rounddown(value);
+ if (!max_heartbeat) {
+ dev_err(dev,
+ "heartbeat is too small for the system to handle it correctly\n");
+ return -EINVAL;
}
/*
- * All counting occurs at SLOW_CLOCK / 128 = 256 Hz
- *
- * Since WDV is a 12-bit counter, the maximum period is
- * 4096 / 256 = 16 seconds.
+ * Try to reset the watchdog counter 4 or 2 times more often than
+ * actually requested, to avoid spurious watchdog reset.
+ * If this is not possible because of the min_heartbeat value, reset
+ * it at the min_heartbeat period.
*/
- reg = AT91_WDT_WDRSTEN /* causes watchdog reset */
- /* | AT91_WDT_WDRPROC causes processor reset only */
- | AT91_WDT_WDDBGHLT /* disabled in debug mode */
- | AT91_WDT_WDD /* restart at any time */
- | (timeout & AT91_WDT_WDV); /* timer value */
- wdt_write(AT91_WDT_MR, reg);
+ if ((max_heartbeat / 4) >= min_heartbeat)
+ wdt->heartbeat = max_heartbeat / 4;
+ else if ((max_heartbeat / 2) >= min_heartbeat)
+ wdt->heartbeat = max_heartbeat / 2;
+ else
+ wdt->heartbeat = min_heartbeat;
+
+ if (max_heartbeat < min_heartbeat + 4)
+ dev_warn(dev,
+ "min heartbeat and max heartbeat might be too close for the system to handle it correctly\n");
+
+ if ((tmp & AT91_WDT_WDFIEN) && wdt->irq) {
+ err = request_irq(wdt->irq, wdt_interrupt,
+ IRQF_SHARED | IRQF_IRQPOLL,
+ pdev->name, wdt);
+ if (err)
+ return err;
+ }
+
+ if ((tmp & wdt->mr_mask) != (wdt->mr & wdt->mr_mask))
+ dev_warn(dev,
+ "watchdog already configured differently (mr = %x expecting %x)\n",
+ tmp & wdt->mr_mask, wdt->mr & wdt->mr_mask);
+
+ setup_timer(&wdt->timer, at91_ping, (unsigned long)wdt);
+
+ /*
+ * Use min_heartbeat the first time to avoid spurious watchdog reset:
+ * we don't know for how long the watchdog counter is running, and
+ * - resetting it right now might trigger a watchdog fault reset
+ * - waiting for heartbeat time might lead to a watchdog timeout
+ * reset
+ */
+ mod_timer(&wdt->timer, jiffies + min_heartbeat);
+
+ /* Try to set timeout from device tree first */
+ if (watchdog_init_timeout(&wdt->wdd, 0, dev))
+ watchdog_init_timeout(&wdt->wdd, heartbeat, dev);
+ watchdog_set_nowayout(&wdt->wdd, wdt->nowayout);
+ err = watchdog_register_device(&wdt->wdd);
+ if (err)
+ goto out_stop_timer;
+
+ wdt->next_heartbeat = jiffies + wdt->wdd.timeout * HZ;
return 0;
+
+out_stop_timer:
+ del_timer(&wdt->timer);
+ return err;
}
+/* ......................................................................... */
+
static const struct watchdog_info at91_wdt_info = {
.identity = DRV_NAME,
.options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING |
WDIOF_MAGICCLOSE,
};
-/*
- * Handle commands from user-space.
- */
-static long at91_wdt_ioctl(struct file *file,
- unsigned int cmd, unsigned long arg)
+static const struct watchdog_ops at91_wdt_ops = {
+ .owner = THIS_MODULE,
+ .start = at91_wdt_start,
+ .stop = at91_wdt_stop,
+ .set_timeout = at91_wdt_set_timeout,
+};
+
+#if defined(CONFIG_OF)
+static int of_at91wdt_init(struct device_node *np, struct at91wdt *wdt)
{
- void __user *argp = (void __user *)arg;
- int __user *p = argp;
- int new_value;
+ u32 min = 0;
+ u32 max = WDT_COUNTER_MAX_SECS;
+ const char *tmp;
+
+ /* Get the interrupts property */
+ wdt->irq = irq_of_parse_and_map(np, 0);
+ if (!wdt->irq)
+ dev_warn(wdt->wdd.parent, "failed to get IRQ from DT\n");
+
+ if (!of_property_read_u32_index(np, "atmel,max-heartbeat-sec", 0,
+ &max)) {
+ if (!max || max > WDT_COUNTER_MAX_SECS)
+ max = WDT_COUNTER_MAX_SECS;
+
+ if (!of_property_read_u32_index(np, "atmel,min-heartbeat-sec",
+ 0, &min)) {
+ if (min >= max)
+ min = max - 1;
+ }
+ }
- switch (cmd) {
- case WDIOC_GETSUPPORT:
- return copy_to_user(argp, &at91_wdt_info,
- sizeof(at91_wdt_info)) ? -EFAULT : 0;
+ min = secs_to_ticks(min);
+ max = secs_to_ticks(max);
+
+ wdt->mr_mask = 0x3FFFFFFF;
+ wdt->mr = 0;
+ if (!of_property_read_string(np, "atmel,watchdog-type", &tmp) &&
+ !strcmp(tmp, "software")) {
+ wdt->mr |= AT91_WDT_WDFIEN;
+ wdt->mr_mask &= ~AT91_WDT_WDRPROC;
+ } else {
+ wdt->mr |= AT91_WDT_WDRSTEN;
+ }
- case WDIOC_GETSTATUS:
- case WDIOC_GETBOOTSTATUS:
- return put_user(0, p);
+ if (!of_property_read_string(np, "atmel,reset-type", &tmp) &&
+ !strcmp(tmp, "proc"))
+ wdt->mr |= AT91_WDT_WDRPROC;
- case WDIOC_KEEPALIVE:
- at91wdt_private.next_heartbeat = jiffies + heartbeat * HZ;
- return 0;
+ if (of_property_read_bool(np, "atmel,disable")) {
+ wdt->mr |= AT91_WDT_WDDIS;
+ wdt->mr_mask &= AT91_WDT_WDDIS;
+ }
- case WDIOC_SETTIMEOUT:
- if (get_user(new_value, p))
- return -EFAULT;
+ if (of_property_read_bool(np, "atmel,idle-halt"))
+ wdt->mr |= AT91_WDT_WDIDLEHLT;
- heartbeat = new_value;
- at91wdt_private.next_heartbeat = jiffies + heartbeat * HZ;
+ if (of_property_read_bool(np, "atmel,dbg-halt"))
+ wdt->mr |= AT91_WDT_WDDBGHLT;
- return put_user(new_value, p); /* return current value */
+ wdt->mr |= max | ((max - min) << 16);
- case WDIOC_GETTIMEOUT:
- return put_user(heartbeat, p);
- }
- return -ENOTTY;
+ return 0;
}
-
-/*
- * Pat the watchdog whenever device is written to.
- */
-static ssize_t at91_wdt_write(struct file *file, const char *data, size_t len,
- loff_t *ppos)
+#else
+static inline int of_at91wdt_init(struct device_node *np, struct at91wdt *wdt)
{
- if (!len)
- return 0;
-
- /* Scan for magic character */
- if (!nowayout) {
- size_t i;
-
- at91wdt_private.expect_close = 0;
-
- for (i = 0; i < len; i++) {
- char c;
- if (get_user(c, data + i))
- return -EFAULT;
- if (c == 'V') {
- at91wdt_private.expect_close = 42;
- break;
- }
- }
- }
-
- at91wdt_private.next_heartbeat = jiffies + heartbeat * HZ;
-
- return len;
+ return 0;
}
-
-/* ......................................................................... */
-
-static const struct file_operations at91wdt_fops = {
- .owner = THIS_MODULE,
- .llseek = no_llseek,
- .unlocked_ioctl = at91_wdt_ioctl,
- .open = at91_wdt_open,
- .release = at91_wdt_close,
- .write = at91_wdt_write,
-};
-
-static struct miscdevice at91wdt_miscdev = {
- .minor = WATCHDOG_MINOR,
- .name = "watchdog",
- .fops = &at91wdt_fops,
-};
+#endif
static int __init at91wdt_probe(struct platform_device *pdev)
{
struct resource *r;
- int res;
+ int err;
+ struct at91wdt *wdt;
- if (at91wdt_miscdev.parent)
- return -EBUSY;
- at91wdt_miscdev.parent = &pdev->dev;
+ wdt = devm_kzalloc(&pdev->dev, sizeof(*wdt), GFP_KERNEL);
+ if (!wdt)
+ return -ENOMEM;
+
+ wdt->mr = (WDT_HW_TIMEOUT * 256) | AT91_WDT_WDRSTEN | AT91_WDT_WDD |
+ AT91_WDT_WDDBGHLT | AT91_WDT_WDIDLEHLT;
+ wdt->mr_mask = 0x3FFFFFFF;
+ wdt->nowayout = nowayout;
+ wdt->wdd.parent = &pdev->dev;
+ wdt->wdd.info = &at91_wdt_info;
+ wdt->wdd.ops = &at91_wdt_ops;
+ wdt->wdd.timeout = WDT_HEARTBEAT;
+ wdt->wdd.min_timeout = 1;
+ wdt->wdd.max_timeout = 0xFFFF;
r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (!r)
- return -ENODEV;
- at91wdt_private.base = ioremap(r->start, resource_size(r));
- if (!at91wdt_private.base) {
- dev_err(&pdev->dev, "failed to map registers, aborting.\n");
- return -ENOMEM;
+ wdt->base = devm_ioremap_resource(&pdev->dev, r);
+ if (IS_ERR(wdt->base))
+ return PTR_ERR(wdt->base);
+
+ if (pdev->dev.of_node) {
+ err = of_at91wdt_init(pdev->dev.of_node, wdt);
+ if (err)
+ return err;
}
- /* Set watchdog */
- res = at91_wdt_settimeout(ms_to_ticks(WDT_HW_TIMEOUT * 1000));
- if (res)
- return res;
+ err = at91_wdt_init(pdev, wdt);
+ if (err)
+ return err;
- res = misc_register(&at91wdt_miscdev);
- if (res)
- return res;
-
- at91wdt_private.next_heartbeat = jiffies + heartbeat * HZ;
- setup_timer(&at91wdt_private.timer, at91_ping, 0);
- mod_timer(&at91wdt_private.timer, jiffies + WDT_TIMEOUT);
+ platform_set_drvdata(pdev, wdt);
pr_info("enabled (heartbeat=%d sec, nowayout=%d)\n",
- heartbeat, nowayout);
+ wdt->wdd.timeout, wdt->nowayout);
return 0;
}
static int __exit at91wdt_remove(struct platform_device *pdev)
{
- int res;
+ struct at91wdt *wdt = platform_get_drvdata(pdev);
+ watchdog_unregister_device(&wdt->wdd);
- res = misc_deregister(&at91wdt_miscdev);
- if (!res)
- at91wdt_miscdev.parent = NULL;
+ pr_warn("I quit now, hardware will probably reboot!\n");
+ del_timer(&wdt->timer);
- return res;
+ return 0;
}
+#if defined(CONFIG_OF)
+static const struct of_device_id at91_wdt_dt_ids[] = {
+ { .compatible = "atmel,at91sam9260-wdt" },
+ { /* sentinel */ }
+};
+
+MODULE_DEVICE_TABLE(of, at91_wdt_dt_ids);
+#endif
+
static struct platform_driver at91wdt_driver = {
.remove = __exit_p(at91wdt_remove),
.driver = {
.name = "at91_wdt",
.owner = THIS_MODULE,
+ .of_match_table = of_match_ptr(at91_wdt_dt_ids),
},
};
-static int __init at91sam_wdt_init(void)
-{
- return platform_driver_probe(&at91wdt_driver, at91wdt_probe);
-}
-
-static void __exit at91sam_wdt_exit(void)
-{
- platform_driver_unregister(&at91wdt_driver);
-}
-
-module_init(at91sam_wdt_init);
-module_exit(at91sam_wdt_exit);
+module_platform_driver_probe(at91wdt_driver, at91wdt_probe);
MODULE_AUTHOR("Renaud CERRATO <r.cerrato@til-technologies.fr>");
MODULE_DESCRIPTION("Watchdog driver for Atmel AT91SAM9x processors");
MODULE_LICENSE("GPL");
-MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
diff --git a/drivers/watchdog/ath79_wdt.c b/drivers/watchdog/ath79_wdt.c
index 1f9371f49c4..41ac4660fb8 100644
--- a/drivers/watchdog/ath79_wdt.c
+++ b/drivers/watchdog/ath79_wdt.c
@@ -20,9 +20,10 @@
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/bitops.h>
+#include <linux/delay.h>
#include <linux/errno.h>
#include <linux/fs.h>
-#include <linux/init.h>
+#include <linux/io.h>
#include <linux/kernel.h>
#include <linux/miscdevice.h>
#include <linux/module.h>
@@ -32,14 +33,16 @@
#include <linux/watchdog.h>
#include <linux/clk.h>
#include <linux/err.h>
-
-#include <asm/mach-ath79/ath79.h>
-#include <asm/mach-ath79/ar71xx_regs.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
#define DRIVER_NAME "ath79-wdt"
#define WDT_TIMEOUT 15 /* seconds */
+#define WDOG_REG_CTRL 0x00
+#define WDOG_REG_TIMER 0x04
+
#define WDOG_CTRL_LAST_RESET BIT(31)
#define WDOG_CTRL_ACTION_MASK 3
#define WDOG_CTRL_ACTION_NONE 0 /* no action */
@@ -66,27 +69,47 @@ static struct clk *wdt_clk;
static unsigned long wdt_freq;
static int boot_status;
static int max_timeout;
+static void __iomem *wdt_base;
+
+static inline void ath79_wdt_wr(unsigned reg, u32 val)
+{
+ iowrite32(val, wdt_base + reg);
+}
+
+static inline u32 ath79_wdt_rr(unsigned reg)
+{
+ return ioread32(wdt_base + reg);
+}
static inline void ath79_wdt_keepalive(void)
{
- ath79_reset_wr(AR71XX_RESET_REG_WDOG, wdt_freq * timeout);
+ ath79_wdt_wr(WDOG_REG_TIMER, wdt_freq * timeout);
/* flush write */
- ath79_reset_rr(AR71XX_RESET_REG_WDOG);
+ ath79_wdt_rr(WDOG_REG_TIMER);
}
static inline void ath79_wdt_enable(void)
{
ath79_wdt_keepalive();
- ath79_reset_wr(AR71XX_RESET_REG_WDOG_CTRL, WDOG_CTRL_ACTION_FCR);
+
+ /*
+ * Updating the TIMER register requires a few microseconds
+ * on the AR934x SoCs at least. Use a small delay to ensure
+ * that the TIMER register is updated within the hardware
+ * before enabling the watchdog.
+ */
+ udelay(2);
+
+ ath79_wdt_wr(WDOG_REG_CTRL, WDOG_CTRL_ACTION_FCR);
/* flush write */
- ath79_reset_rr(AR71XX_RESET_REG_WDOG_CTRL);
+ ath79_wdt_rr(WDOG_REG_CTRL);
}
static inline void ath79_wdt_disable(void)
{
- ath79_reset_wr(AR71XX_RESET_REG_WDOG_CTRL, WDOG_CTRL_ACTION_NONE);
+ ath79_wdt_wr(WDOG_REG_CTRL, WDOG_CTRL_ACTION_NONE);
/* flush write */
- ath79_reset_rr(AR71XX_RESET_REG_WDOG_CTRL);
+ ath79_wdt_rr(WDOG_REG_CTRL);
}
static int ath79_wdt_set_timeout(int val)
@@ -224,18 +247,27 @@ static struct miscdevice ath79_wdt_miscdev = {
.fops = &ath79_wdt_fops,
};
-static int __devinit ath79_wdt_probe(struct platform_device *pdev)
+static int ath79_wdt_probe(struct platform_device *pdev)
{
+ struct resource *res;
u32 ctrl;
int err;
- wdt_clk = clk_get(&pdev->dev, "wdt");
+ if (wdt_base)
+ return -EBUSY;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ wdt_base = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(wdt_base))
+ return PTR_ERR(wdt_base);
+
+ wdt_clk = devm_clk_get(&pdev->dev, "wdt");
if (IS_ERR(wdt_clk))
return PTR_ERR(wdt_clk);
- err = clk_enable(wdt_clk);
+ err = clk_prepare_enable(wdt_clk);
if (err)
- goto err_clk_put;
+ return err;
wdt_freq = clk_get_rate(wdt_clk);
if (!wdt_freq) {
@@ -251,7 +283,7 @@ static int __devinit ath79_wdt_probe(struct platform_device *pdev)
max_timeout, timeout);
}
- ctrl = ath79_reset_rr(AR71XX_RESET_REG_WDOG_CTRL);
+ ctrl = ath79_wdt_rr(WDOG_REG_CTRL);
boot_status = (ctrl & WDOG_CTRL_LAST_RESET) ? WDIOF_CARDRESET : 0;
err = misc_register(&ath79_wdt_miscdev);
@@ -264,17 +296,14 @@ static int __devinit ath79_wdt_probe(struct platform_device *pdev)
return 0;
err_clk_disable:
- clk_disable(wdt_clk);
-err_clk_put:
- clk_put(wdt_clk);
+ clk_disable_unprepare(wdt_clk);
return err;
}
-static int __devexit ath79_wdt_remove(struct platform_device *pdev)
+static int ath79_wdt_remove(struct platform_device *pdev)
{
misc_deregister(&ath79_wdt_miscdev);
- clk_disable(wdt_clk);
- clk_put(wdt_clk);
+ clk_disable_unprepare(wdt_clk);
return 0;
}
@@ -283,30 +312,29 @@ static void ath97_wdt_shutdown(struct platform_device *pdev)
ath79_wdt_disable();
}
+#ifdef CONFIG_OF
+static const struct of_device_id ath79_wdt_match[] = {
+ { .compatible = "qca,ar7130-wdt" },
+ {},
+};
+MODULE_DEVICE_TABLE(of, ath79_wdt_match);
+#endif
+
static struct platform_driver ath79_wdt_driver = {
- .remove = __devexit_p(ath79_wdt_remove),
+ .probe = ath79_wdt_probe,
+ .remove = ath79_wdt_remove,
.shutdown = ath97_wdt_shutdown,
.driver = {
.name = DRIVER_NAME,
.owner = THIS_MODULE,
+ .of_match_table = of_match_ptr(ath79_wdt_match),
},
};
-static int __init ath79_wdt_init(void)
-{
- return platform_driver_probe(&ath79_wdt_driver, ath79_wdt_probe);
-}
-module_init(ath79_wdt_init);
-
-static void __exit ath79_wdt_exit(void)
-{
- platform_driver_unregister(&ath79_wdt_driver);
-}
-module_exit(ath79_wdt_exit);
+module_platform_driver(ath79_wdt_driver);
MODULE_DESCRIPTION("Atheros AR71XX/AR724X/AR913X hardware watchdog driver");
MODULE_AUTHOR("Gabor Juhos <juhosg@openwrt.org");
MODULE_AUTHOR("Imre Kaloz <kaloz@openwrt.org");
MODULE_LICENSE("GPL v2");
MODULE_ALIAS("platform:" DRIVER_NAME);
-MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
diff --git a/drivers/watchdog/bcm2835_wdt.c b/drivers/watchdog/bcm2835_wdt.c
new file mode 100644
index 00000000000..8df450c090a
--- /dev/null
+++ b/drivers/watchdog/bcm2835_wdt.c
@@ -0,0 +1,185 @@
+/*
+ * Watchdog driver for Broadcom BCM2835
+ *
+ * "bcm2708_wdog" driver written by Luke Diamand that was obtained from
+ * branch "rpi-3.6.y" of git://github.com/raspberrypi/linux.git was used
+ * as a hardware reference for the Broadcom BCM2835 watchdog timer.
+ *
+ * Copyright (C) 2013 Lubomir Rintel <lkundrak@v3.sk>
+ *
+ * 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.
+ */
+
+#include <linux/types.h>
+#include <linux/module.h>
+#include <linux/io.h>
+#include <linux/watchdog.h>
+#include <linux/platform_device.h>
+#include <linux/of_address.h>
+
+#define PM_RSTC 0x1c
+#define PM_WDOG 0x24
+
+#define PM_PASSWORD 0x5a000000
+
+#define PM_WDOG_TIME_SET 0x000fffff
+#define PM_RSTC_WRCFG_CLR 0xffffffcf
+#define PM_RSTC_WRCFG_SET 0x00000030
+#define PM_RSTC_WRCFG_FULL_RESET 0x00000020
+#define PM_RSTC_RESET 0x00000102
+
+#define SECS_TO_WDOG_TICKS(x) ((x) << 16)
+#define WDOG_TICKS_TO_SECS(x) ((x) >> 16)
+
+struct bcm2835_wdt {
+ void __iomem *base;
+ spinlock_t lock;
+};
+
+static unsigned int heartbeat;
+static bool nowayout = WATCHDOG_NOWAYOUT;
+
+static int bcm2835_wdt_start(struct watchdog_device *wdog)
+{
+ struct bcm2835_wdt *wdt = watchdog_get_drvdata(wdog);
+ uint32_t cur;
+ unsigned long flags;
+
+ spin_lock_irqsave(&wdt->lock, flags);
+
+ writel_relaxed(PM_PASSWORD | (SECS_TO_WDOG_TICKS(wdog->timeout) &
+ PM_WDOG_TIME_SET), wdt->base + PM_WDOG);
+ cur = readl_relaxed(wdt->base + PM_RSTC);
+ writel_relaxed(PM_PASSWORD | (cur & PM_RSTC_WRCFG_CLR) |
+ PM_RSTC_WRCFG_FULL_RESET, wdt->base + PM_RSTC);
+
+ spin_unlock_irqrestore(&wdt->lock, flags);
+
+ return 0;
+}
+
+static int bcm2835_wdt_stop(struct watchdog_device *wdog)
+{
+ struct bcm2835_wdt *wdt = watchdog_get_drvdata(wdog);
+
+ writel_relaxed(PM_PASSWORD | PM_RSTC_RESET, wdt->base + PM_RSTC);
+ dev_info(wdog->dev, "Watchdog timer stopped");
+ return 0;
+}
+
+static int bcm2835_wdt_set_timeout(struct watchdog_device *wdog, unsigned int t)
+{
+ wdog->timeout = t;
+ return 0;
+}
+
+static unsigned int bcm2835_wdt_get_timeleft(struct watchdog_device *wdog)
+{
+ struct bcm2835_wdt *wdt = watchdog_get_drvdata(wdog);
+
+ uint32_t ret = readl_relaxed(wdt->base + PM_WDOG);
+ return WDOG_TICKS_TO_SECS(ret & PM_WDOG_TIME_SET);
+}
+
+static struct watchdog_ops bcm2835_wdt_ops = {
+ .owner = THIS_MODULE,
+ .start = bcm2835_wdt_start,
+ .stop = bcm2835_wdt_stop,
+ .set_timeout = bcm2835_wdt_set_timeout,
+ .get_timeleft = bcm2835_wdt_get_timeleft,
+};
+
+static struct watchdog_info bcm2835_wdt_info = {
+ .options = WDIOF_SETTIMEOUT | WDIOF_MAGICCLOSE |
+ WDIOF_KEEPALIVEPING,
+ .identity = "Broadcom BCM2835 Watchdog timer",
+};
+
+static struct watchdog_device bcm2835_wdt_wdd = {
+ .info = &bcm2835_wdt_info,
+ .ops = &bcm2835_wdt_ops,
+ .min_timeout = 1,
+ .max_timeout = WDOG_TICKS_TO_SECS(PM_WDOG_TIME_SET),
+ .timeout = WDOG_TICKS_TO_SECS(PM_WDOG_TIME_SET),
+};
+
+static int bcm2835_wdt_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct device_node *np = dev->of_node;
+ struct bcm2835_wdt *wdt;
+ int err;
+
+ wdt = devm_kzalloc(dev, sizeof(struct bcm2835_wdt), GFP_KERNEL);
+ if (!wdt)
+ return -ENOMEM;
+ platform_set_drvdata(pdev, wdt);
+
+ spin_lock_init(&wdt->lock);
+
+ wdt->base = of_iomap(np, 0);
+ if (!wdt->base) {
+ dev_err(dev, "Failed to remap watchdog regs");
+ return -ENODEV;
+ }
+
+ watchdog_set_drvdata(&bcm2835_wdt_wdd, wdt);
+ watchdog_init_timeout(&bcm2835_wdt_wdd, heartbeat, dev);
+ watchdog_set_nowayout(&bcm2835_wdt_wdd, nowayout);
+ err = watchdog_register_device(&bcm2835_wdt_wdd);
+ if (err) {
+ dev_err(dev, "Failed to register watchdog device");
+ iounmap(wdt->base);
+ return err;
+ }
+
+ dev_info(dev, "Broadcom BCM2835 watchdog timer");
+ return 0;
+}
+
+static int bcm2835_wdt_remove(struct platform_device *pdev)
+{
+ struct bcm2835_wdt *wdt = platform_get_drvdata(pdev);
+
+ 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", },
+ {},
+};
+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",
+ .owner = THIS_MODULE,
+ .of_match_table = bcm2835_wdt_of_match,
+ },
+};
+module_platform_driver(bcm2835_wdt_driver);
+
+module_param(heartbeat, uint, 0);
+MODULE_PARM_DESC(heartbeat, "Initial watchdog heartbeat in seconds");
+
+module_param(nowayout, bool, 0);
+MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default="
+ __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
+
+MODULE_AUTHOR("Lubomir Rintel <lkundrak@v3.sk>");
+MODULE_DESCRIPTION("Driver for Broadcom BCM2835 watchdog timer");
+MODULE_LICENSE("GPL");
diff --git a/drivers/watchdog/bcm47xx_wdt.c b/drivers/watchdog/bcm47xx_wdt.c
index bc0e91e78e8..b61fcc53597 100644
--- a/drivers/watchdog/bcm47xx_wdt.c
+++ b/drivers/watchdog/bcm47xx_wdt.c
@@ -3,6 +3,7 @@
*
* Copyright (C) 2008 Aleksandar Radovanovic <biblbroks@sezampro.rs>
* Copyright (C) 2009 Matthieu CASTET <castet.matthieu@free.fr>
+ * Copyright (C) 2012-2013 Hauke Mehrtens <hauke@hauke-m.de>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@@ -12,165 +13,142 @@
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+#include <linux/bcm47xx_wdt.h>
#include <linux/bitops.h>
#include <linux/errno.h>
-#include <linux/fs.h>
-#include <linux/init.h>
#include <linux/kernel.h>
-#include <linux/miscdevice.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
+#include <linux/platform_device.h>
#include <linux/reboot.h>
#include <linux/types.h>
-#include <linux/uaccess.h>
#include <linux/watchdog.h>
#include <linux/timer.h>
#include <linux/jiffies.h>
-#include <linux/ssb/ssb_embedded.h>
-#include <asm/mach-bcm47xx/bcm47xx.h>
#define DRV_NAME "bcm47xx_wdt"
#define WDT_DEFAULT_TIME 30 /* seconds */
-#define WDT_MAX_TIME 255 /* seconds */
+#define WDT_SOFTTIMER_MAX 255 /* seconds */
+#define WDT_SOFTTIMER_THRESHOLD 60 /* seconds */
-static int wdt_time = WDT_DEFAULT_TIME;
+static int timeout = WDT_DEFAULT_TIME;
static bool nowayout = WATCHDOG_NOWAYOUT;
-module_param(wdt_time, int, 0);
-MODULE_PARM_DESC(wdt_time, "Watchdog time in seconds. (default="
+module_param(timeout, int, 0);
+MODULE_PARM_DESC(timeout, "Watchdog time in seconds. (default="
__MODULE_STRING(WDT_DEFAULT_TIME) ")");
-#ifdef CONFIG_WATCHDOG_NOWAYOUT
module_param(nowayout, bool, 0);
MODULE_PARM_DESC(nowayout,
"Watchdog cannot be stopped once started (default="
__MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
-#endif
-static unsigned long bcm47xx_wdt_busy;
-static char expect_release;
-static struct timer_list wdt_timer;
-static atomic_t ticks;
-
-static inline void bcm47xx_wdt_hw_start(void)
+static inline struct bcm47xx_wdt *bcm47xx_wdt_get(struct watchdog_device *wdd)
{
- /* this is 2,5s on 100Mhz clock and 2s on 133 Mhz */
- switch (bcm47xx_bus_type) {
-#ifdef CONFIG_BCM47XX_SSB
- case BCM47XX_BUS_TYPE_SSB:
- ssb_watchdog_timer_set(&bcm47xx_bus.ssb, 0xfffffff);
- break;
-#endif
-#ifdef CONFIG_BCM47XX_BCMA
- case BCM47XX_BUS_TYPE_BCMA:
- bcma_chipco_watchdog_timer_set(&bcm47xx_bus.bcma.bus.drv_cc,
- 0xfffffff);
- break;
-#endif
- }
+ return container_of(wdd, struct bcm47xx_wdt, wdd);
}
-static inline int bcm47xx_wdt_hw_stop(void)
+static int bcm47xx_wdt_hard_keepalive(struct watchdog_device *wdd)
{
- switch (bcm47xx_bus_type) {
-#ifdef CONFIG_BCM47XX_SSB
- case BCM47XX_BUS_TYPE_SSB:
- return ssb_watchdog_timer_set(&bcm47xx_bus.ssb, 0);
-#endif
-#ifdef CONFIG_BCM47XX_BCMA
- case BCM47XX_BUS_TYPE_BCMA:
- bcma_chipco_watchdog_timer_set(&bcm47xx_bus.bcma.bus.drv_cc, 0);
- return 0;
-#endif
- }
- return -EINVAL;
-}
+ struct bcm47xx_wdt *wdt = bcm47xx_wdt_get(wdd);
-static void bcm47xx_timer_tick(unsigned long unused)
-{
- if (!atomic_dec_and_test(&ticks)) {
- bcm47xx_wdt_hw_start();
- mod_timer(&wdt_timer, jiffies + HZ);
- } else {
- pr_crit("Watchdog will fire soon!!!\n");
- }
+ wdt->timer_set_ms(wdt, wdd->timeout * 1000);
+
+ return 0;
}
-static inline void bcm47xx_wdt_pet(void)
+static int bcm47xx_wdt_hard_start(struct watchdog_device *wdd)
{
- atomic_set(&ticks, wdt_time);
+ return 0;
}
-static void bcm47xx_wdt_start(void)
+static int bcm47xx_wdt_hard_stop(struct watchdog_device *wdd)
{
- bcm47xx_wdt_pet();
- bcm47xx_timer_tick(0);
+ struct bcm47xx_wdt *wdt = bcm47xx_wdt_get(wdd);
+
+ wdt->timer_set(wdt, 0);
+
+ return 0;
}
-static void bcm47xx_wdt_pause(void)
+static int bcm47xx_wdt_hard_set_timeout(struct watchdog_device *wdd,
+ unsigned int new_time)
{
- del_timer_sync(&wdt_timer);
- bcm47xx_wdt_hw_stop();
+ struct bcm47xx_wdt *wdt = bcm47xx_wdt_get(wdd);
+ u32 max_timer = wdt->max_timer_ms;
+
+ if (new_time < 1 || new_time > max_timer / 1000) {
+ pr_warn("timeout value must be 1<=x<=%d, using %d\n",
+ max_timer / 1000, new_time);
+ return -EINVAL;
+ }
+
+ wdd->timeout = new_time;
+ return 0;
}
-static void bcm47xx_wdt_stop(void)
+static struct watchdog_ops bcm47xx_wdt_hard_ops = {
+ .owner = THIS_MODULE,
+ .start = bcm47xx_wdt_hard_start,
+ .stop = bcm47xx_wdt_hard_stop,
+ .ping = bcm47xx_wdt_hard_keepalive,
+ .set_timeout = bcm47xx_wdt_hard_set_timeout,
+};
+
+static void bcm47xx_wdt_soft_timer_tick(unsigned long data)
{
- bcm47xx_wdt_pause();
+ struct bcm47xx_wdt *wdt = (struct bcm47xx_wdt *)data;
+ u32 next_tick = min(wdt->wdd.timeout * 1000, wdt->max_timer_ms);
+
+ if (!atomic_dec_and_test(&wdt->soft_ticks)) {
+ wdt->timer_set_ms(wdt, next_tick);
+ mod_timer(&wdt->soft_timer, jiffies + HZ);
+ } else {
+ pr_crit("Watchdog will fire soon!!!\n");
+ }
}
-static int bcm47xx_wdt_settimeout(int new_time)
+static int bcm47xx_wdt_soft_keepalive(struct watchdog_device *wdd)
{
- if ((new_time <= 0) || (new_time > WDT_MAX_TIME))
- return -EINVAL;
+ struct bcm47xx_wdt *wdt = bcm47xx_wdt_get(wdd);
+
+ atomic_set(&wdt->soft_ticks, wdd->timeout);
- wdt_time = new_time;
return 0;
}
-static int bcm47xx_wdt_open(struct inode *inode, struct file *file)
+static int bcm47xx_wdt_soft_start(struct watchdog_device *wdd)
{
- if (test_and_set_bit(0, &bcm47xx_wdt_busy))
- return -EBUSY;
+ struct bcm47xx_wdt *wdt = bcm47xx_wdt_get(wdd);
+
+ bcm47xx_wdt_soft_keepalive(wdd);
+ bcm47xx_wdt_soft_timer_tick((unsigned long)wdt);
- bcm47xx_wdt_start();
- return nonseekable_open(inode, file);
+ return 0;
}
-static int bcm47xx_wdt_release(struct inode *inode, struct file *file)
+static int bcm47xx_wdt_soft_stop(struct watchdog_device *wdd)
{
- if (expect_release == 42) {
- bcm47xx_wdt_stop();
- } else {
- pr_crit("Unexpected close, not stopping watchdog!\n");
- bcm47xx_wdt_start();
- }
+ struct bcm47xx_wdt *wdt = bcm47xx_wdt_get(wdd);
+
+ del_timer_sync(&wdt->soft_timer);
+ wdt->timer_set(wdt, 0);
- clear_bit(0, &bcm47xx_wdt_busy);
- expect_release = 0;
return 0;
}
-static ssize_t bcm47xx_wdt_write(struct file *file, const char __user *data,
- size_t len, loff_t *ppos)
+static int bcm47xx_wdt_soft_set_timeout(struct watchdog_device *wdd,
+ unsigned int new_time)
{
- if (len) {
- if (!nowayout) {
- size_t i;
-
- expect_release = 0;
-
- for (i = 0; i != len; i++) {
- char c;
- if (get_user(c, data + i))
- return -EFAULT;
- if (c == 'V')
- expect_release = 42;
- }
- }
- bcm47xx_wdt_pet();
+ if (new_time < 1 || new_time > WDT_SOFTTIMER_MAX) {
+ pr_warn("timeout value must be 1<=x<=%d, using %d\n",
+ WDT_SOFTTIMER_MAX, new_time);
+ return -EINVAL;
}
- return len;
+
+ wdd->timeout = new_time;
+ return 0;
}
static const struct watchdog_info bcm47xx_wdt_info = {
@@ -180,130 +158,100 @@ static const struct watchdog_info bcm47xx_wdt_info = {
WDIOF_MAGICCLOSE,
};
-static long bcm47xx_wdt_ioctl(struct file *file,
- unsigned int cmd, unsigned long arg)
-{
- void __user *argp = (void __user *)arg;
- int __user *p = argp;
- int new_value, retval = -EINVAL;
-
- switch (cmd) {
- case WDIOC_GETSUPPORT:
- return copy_to_user(argp, &bcm47xx_wdt_info,
- sizeof(bcm47xx_wdt_info)) ? -EFAULT : 0;
-
- case WDIOC_GETSTATUS:
- case WDIOC_GETBOOTSTATUS:
- return put_user(0, p);
-
- case WDIOC_SETOPTIONS:
- if (get_user(new_value, p))
- return -EFAULT;
-
- if (new_value & WDIOS_DISABLECARD) {
- bcm47xx_wdt_stop();
- retval = 0;
- }
-
- if (new_value & WDIOS_ENABLECARD) {
- bcm47xx_wdt_start();
- retval = 0;
- }
-
- return retval;
-
- case WDIOC_KEEPALIVE:
- bcm47xx_wdt_pet();
- return 0;
-
- case WDIOC_SETTIMEOUT:
- if (get_user(new_value, p))
- return -EFAULT;
-
- if (bcm47xx_wdt_settimeout(new_value))
- return -EINVAL;
-
- bcm47xx_wdt_pet();
-
- case WDIOC_GETTIMEOUT:
- return put_user(wdt_time, p);
-
- default:
- return -ENOTTY;
- }
-}
-
static int bcm47xx_wdt_notify_sys(struct notifier_block *this,
- unsigned long code, void *unused)
+ unsigned long code, void *unused)
{
+ struct bcm47xx_wdt *wdt;
+
+ wdt = container_of(this, struct bcm47xx_wdt, notifier);
if (code == SYS_DOWN || code == SYS_HALT)
- bcm47xx_wdt_stop();
+ wdt->wdd.ops->stop(&wdt->wdd);
return NOTIFY_DONE;
}
-static const struct file_operations bcm47xx_wdt_fops = {
+static struct watchdog_ops bcm47xx_wdt_soft_ops = {
.owner = THIS_MODULE,
- .llseek = no_llseek,
- .unlocked_ioctl = bcm47xx_wdt_ioctl,
- .open = bcm47xx_wdt_open,
- .release = bcm47xx_wdt_release,
- .write = bcm47xx_wdt_write,
-};
-
-static struct miscdevice bcm47xx_wdt_miscdev = {
- .minor = WATCHDOG_MINOR,
- .name = "watchdog",
- .fops = &bcm47xx_wdt_fops,
-};
-
-static struct notifier_block bcm47xx_wdt_notifier = {
- .notifier_call = bcm47xx_wdt_notify_sys,
+ .start = bcm47xx_wdt_soft_start,
+ .stop = bcm47xx_wdt_soft_stop,
+ .ping = bcm47xx_wdt_soft_keepalive,
+ .set_timeout = bcm47xx_wdt_soft_set_timeout,
};
-static int __init bcm47xx_wdt_init(void)
+static int bcm47xx_wdt_probe(struct platform_device *pdev)
{
int ret;
+ bool soft;
+ struct bcm47xx_wdt *wdt = dev_get_platdata(&pdev->dev);
- if (bcm47xx_wdt_hw_stop() < 0)
- return -ENODEV;
+ if (!wdt)
+ return -ENXIO;
- setup_timer(&wdt_timer, bcm47xx_timer_tick, 0L);
+ soft = wdt->max_timer_ms < WDT_SOFTTIMER_THRESHOLD * 1000;
- if (bcm47xx_wdt_settimeout(wdt_time)) {
- bcm47xx_wdt_settimeout(WDT_DEFAULT_TIME);
- pr_info("wdt_time value must be 0 < wdt_time < %d, using %d\n",
- (WDT_MAX_TIME + 1), wdt_time);
+ if (soft) {
+ wdt->wdd.ops = &bcm47xx_wdt_soft_ops;
+ setup_timer(&wdt->soft_timer, bcm47xx_wdt_soft_timer_tick,
+ (long unsigned int)wdt);
+ } else {
+ wdt->wdd.ops = &bcm47xx_wdt_hard_ops;
}
- ret = register_reboot_notifier(&bcm47xx_wdt_notifier);
+ wdt->wdd.info = &bcm47xx_wdt_info;
+ wdt->wdd.timeout = WDT_DEFAULT_TIME;
+ ret = wdt->wdd.ops->set_timeout(&wdt->wdd, timeout);
if (ret)
- return ret;
+ goto err_timer;
+ watchdog_set_nowayout(&wdt->wdd, nowayout);
- ret = misc_register(&bcm47xx_wdt_miscdev);
- if (ret) {
- unregister_reboot_notifier(&bcm47xx_wdt_notifier);
- return ret;
- }
+ wdt->notifier.notifier_call = &bcm47xx_wdt_notify_sys;
+
+ ret = register_reboot_notifier(&wdt->notifier);
+ if (ret)
+ goto err_timer;
- pr_info("BCM47xx Watchdog Timer enabled (%d seconds%s)\n",
- wdt_time, nowayout ? ", nowayout" : "");
+ ret = watchdog_register_device(&wdt->wdd);
+ if (ret)
+ goto err_notifier;
+
+ dev_info(&pdev->dev, "BCM47xx Watchdog Timer enabled (%d seconds%s%s)\n",
+ timeout, nowayout ? ", nowayout" : "",
+ soft ? ", Software Timer" : "");
return 0;
+
+err_notifier:
+ unregister_reboot_notifier(&wdt->notifier);
+err_timer:
+ if (soft)
+ del_timer_sync(&wdt->soft_timer);
+
+ return ret;
}
-static void __exit bcm47xx_wdt_exit(void)
+static int bcm47xx_wdt_remove(struct platform_device *pdev)
{
- if (!nowayout)
- bcm47xx_wdt_stop();
+ struct bcm47xx_wdt *wdt = dev_get_platdata(&pdev->dev);
- misc_deregister(&bcm47xx_wdt_miscdev);
+ if (!wdt)
+ return -ENXIO;
- unregister_reboot_notifier(&bcm47xx_wdt_notifier);
+ watchdog_unregister_device(&wdt->wdd);
+ unregister_reboot_notifier(&wdt->notifier);
+
+ return 0;
}
-module_init(bcm47xx_wdt_init);
-module_exit(bcm47xx_wdt_exit);
+static struct platform_driver bcm47xx_wdt_driver = {
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "bcm47xx-wdt",
+ },
+ .probe = bcm47xx_wdt_probe,
+ .remove = bcm47xx_wdt_remove,
+};
+
+module_platform_driver(bcm47xx_wdt_driver);
MODULE_AUTHOR("Aleksandar Radovanovic");
+MODULE_AUTHOR("Hauke Mehrtens <hauke@hauke-m.de>");
MODULE_DESCRIPTION("Watchdog driver for Broadcom BCM47xx");
MODULE_LICENSE("GPL");
-MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
diff --git a/drivers/watchdog/bcm63xx_wdt.c b/drivers/watchdog/bcm63xx_wdt.c
index 8379dc32fd9..5a8e879a430 100644
--- a/drivers/watchdog/bcm63xx_wdt.c
+++ b/drivers/watchdog/bcm63xx_wdt.c
@@ -15,7 +15,7 @@
#include <linux/bitops.h>
#include <linux/errno.h>
#include <linux/fs.h>
-#include <linux/init.h>
+#include <linux/io.h>
#include <linux/kernel.h>
#include <linux/miscdevice.h>
#include <linux/module.h>
@@ -44,7 +44,6 @@
static struct {
void __iomem *regs;
struct timer_list timer;
- int default_ticks;
unsigned long inuse;
atomic_t ticks;
} bcm63xx_wdt_device;
@@ -236,7 +235,7 @@ static struct miscdevice bcm63xx_wdt_miscdev = {
};
-static int __devinit bcm63xx_wdt_probe(struct platform_device *pdev)
+static int bcm63xx_wdt_probe(struct platform_device *pdev)
{
int ret;
struct resource *r;
@@ -249,7 +248,8 @@ static int __devinit bcm63xx_wdt_probe(struct platform_device *pdev)
return -ENODEV;
}
- bcm63xx_wdt_device.regs = ioremap_nocache(r->start, resource_size(r));
+ bcm63xx_wdt_device.regs = devm_ioremap_nocache(&pdev->dev, r->start,
+ resource_size(r));
if (!bcm63xx_wdt_device.regs) {
dev_err(&pdev->dev, "failed to remap I/O resources\n");
return -ENXIO;
@@ -258,7 +258,7 @@ static int __devinit bcm63xx_wdt_probe(struct platform_device *pdev)
ret = bcm63xx_timer_register(TIMER_WDT_ID, bcm63xx_wdt_isr, NULL);
if (ret < 0) {
dev_err(&pdev->dev, "failed to register wdt timer isr\n");
- goto unmap;
+ return ret;
}
if (bcm63xx_wdt_settimeout(wdt_time)) {
@@ -281,19 +281,16 @@ static int __devinit bcm63xx_wdt_probe(struct platform_device *pdev)
unregister_timer:
bcm63xx_timer_unregister(TIMER_WDT_ID);
-unmap:
- iounmap(bcm63xx_wdt_device.regs);
return ret;
}
-static int __devexit bcm63xx_wdt_remove(struct platform_device *pdev)
+static int bcm63xx_wdt_remove(struct platform_device *pdev)
{
if (!nowayout)
bcm63xx_wdt_pause();
misc_deregister(&bcm63xx_wdt_miscdev);
bcm63xx_timer_unregister(TIMER_WDT_ID);
- iounmap(bcm63xx_wdt_device.regs);
return 0;
}
@@ -302,9 +299,9 @@ static void bcm63xx_wdt_shutdown(struct platform_device *pdev)
bcm63xx_wdt_pause();
}
-static struct platform_driver bcm63xx_wdt = {
+static struct platform_driver bcm63xx_wdt_driver = {
.probe = bcm63xx_wdt_probe,
- .remove = __devexit_p(bcm63xx_wdt_remove),
+ .remove = bcm63xx_wdt_remove,
.shutdown = bcm63xx_wdt_shutdown,
.driver = {
.owner = THIS_MODULE,
@@ -312,11 +309,10 @@ static struct platform_driver bcm63xx_wdt = {
}
};
-module_platform_driver(bcm63xx_wdt);
+module_platform_driver(bcm63xx_wdt_driver);
MODULE_AUTHOR("Miguel Gaio <miguel.gaio@efixo.com>");
MODULE_AUTHOR("Florian Fainelli <florian@openwrt.org>");
MODULE_DESCRIPTION("Driver for the Broadcom BCM63xx SoC watchdog");
MODULE_LICENSE("GPL");
-MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
MODULE_ALIAS("platform:bcm63xx-wdt");
diff --git a/drivers/watchdog/bcm_kona_wdt.c b/drivers/watchdog/bcm_kona_wdt.c
new file mode 100644
index 00000000000..9c248099f4a
--- /dev/null
+++ b/drivers/watchdog/bcm_kona_wdt.c
@@ -0,0 +1,368 @@
+/*
+ * Copyright (C) 2013 Broadcom Corporation
+ *
+ * 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 version 2.
+ *
+ * This program is distributed "as is" WITHOUT ANY WARRANTY of any
+ * kind, whether express or implied; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/debugfs.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/of_address.h>
+#include <linux/platform_device.h>
+#include <linux/watchdog.h>
+
+#define SECWDOG_CTRL_REG 0x00000000
+#define SECWDOG_COUNT_REG 0x00000004
+
+#define SECWDOG_RESERVED_MASK 0x1dffffff
+#define SECWDOG_WD_LOAD_FLAG 0x10000000
+#define SECWDOG_EN_MASK 0x08000000
+#define SECWDOG_SRSTEN_MASK 0x04000000
+#define SECWDOG_RES_MASK 0x00f00000
+#define SECWDOG_COUNT_MASK 0x000fffff
+
+#define SECWDOG_MAX_COUNT SECWDOG_COUNT_MASK
+#define SECWDOG_CLKS_SHIFT 20
+#define SECWDOG_MAX_RES 15
+#define SECWDOG_DEFAULT_RESOLUTION 4
+#define SECWDOG_MAX_TRY 1000
+
+#define SECS_TO_TICKS(x, w) ((x) << (w)->resolution)
+#define TICKS_TO_SECS(x, w) ((x) >> (w)->resolution)
+
+#define BCM_KONA_WDT_NAME "bcm_kona_wdt"
+
+struct bcm_kona_wdt {
+ void __iomem *base;
+ /*
+ * One watchdog tick is 1/(2^resolution) seconds. Resolution can take
+ * the values 0-15, meaning one tick can be 1s to 30.52us. Our default
+ * resolution of 4 means one tick is 62.5ms.
+ *
+ * The watchdog counter is 20 bits. Depending on resolution, the maximum
+ * counter value of 0xfffff expires after about 12 days (resolution 0)
+ * down to only 32s (resolution 15). The default resolution of 4 gives
+ * us a maximum of about 18 hours and 12 minutes before the watchdog
+ * times out.
+ */
+ int resolution;
+ spinlock_t lock;
+#ifdef CONFIG_BCM_KONA_WDT_DEBUG
+ unsigned long busy_count;
+ struct dentry *debugfs;
+#endif
+};
+
+static int secure_register_read(struct bcm_kona_wdt *wdt, uint32_t offset)
+{
+ uint32_t val;
+ unsigned count = 0;
+
+ /*
+ * If the WD_LOAD_FLAG is set, the watchdog counter field is being
+ * updated in hardware. Once the WD timer is updated in hardware, it
+ * gets cleared.
+ */
+ do {
+ if (unlikely(count > 1))
+ udelay(5);
+ val = readl_relaxed(wdt->base + offset);
+ count++;
+ } while ((val & SECWDOG_WD_LOAD_FLAG) && count < SECWDOG_MAX_TRY);
+
+#ifdef CONFIG_BCM_KONA_WDT_DEBUG
+ /* Remember the maximum number iterations due to WD_LOAD_FLAG */
+ if (count > wdt->busy_count)
+ wdt->busy_count = count;
+#endif
+
+ /* This is the only place we return a negative value. */
+ if (val & SECWDOG_WD_LOAD_FLAG)
+ return -ETIMEDOUT;
+
+ /* We always mask out reserved bits. */
+ val &= SECWDOG_RESERVED_MASK;
+
+ return val;
+}
+
+#ifdef CONFIG_BCM_KONA_WDT_DEBUG
+
+static int bcm_kona_wdt_dbg_show(struct seq_file *s, void *data)
+{
+ int ctl_val, cur_val, ret;
+ unsigned long flags;
+ struct bcm_kona_wdt *wdt = s->private;
+
+ if (!wdt)
+ return seq_puts(s, "No device pointer\n");
+
+ spin_lock_irqsave(&wdt->lock, flags);
+ ctl_val = secure_register_read(wdt, SECWDOG_CTRL_REG);
+ cur_val = secure_register_read(wdt, SECWDOG_COUNT_REG);
+ spin_unlock_irqrestore(&wdt->lock, flags);
+
+ if (ctl_val < 0 || cur_val < 0) {
+ ret = seq_puts(s, "Error accessing hardware\n");
+ } else {
+ int ctl, cur, ctl_sec, cur_sec, res;
+
+ ctl = ctl_val & SECWDOG_COUNT_MASK;
+ res = (ctl_val & SECWDOG_RES_MASK) >> SECWDOG_CLKS_SHIFT;
+ cur = cur_val & SECWDOG_COUNT_MASK;
+ ctl_sec = TICKS_TO_SECS(ctl, wdt);
+ cur_sec = TICKS_TO_SECS(cur, wdt);
+ ret = seq_printf(s, "Resolution: %d / %d\n"
+ "Control: %d s / %d (%#x) ticks\n"
+ "Current: %d s / %d (%#x) ticks\n"
+ "Busy count: %lu\n", res,
+ wdt->resolution, ctl_sec, ctl, ctl, cur_sec,
+ cur, cur, wdt->busy_count);
+ }
+
+ return ret;
+}
+
+static int bcm_kona_dbg_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, bcm_kona_wdt_dbg_show, inode->i_private);
+}
+
+static const struct file_operations bcm_kona_dbg_operations = {
+ .open = bcm_kona_dbg_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+static void bcm_kona_wdt_debug_init(struct platform_device *pdev)
+{
+ struct dentry *dir;
+ struct bcm_kona_wdt *wdt = platform_get_drvdata(pdev);
+
+ if (!wdt)
+ return;
+
+ wdt->debugfs = NULL;
+
+ dir = debugfs_create_dir(BCM_KONA_WDT_NAME, NULL);
+ if (IS_ERR_OR_NULL(dir))
+ return;
+
+ if (debugfs_create_file("info", S_IFREG | S_IRUGO, dir, wdt,
+ &bcm_kona_dbg_operations))
+ wdt->debugfs = dir;
+ else
+ debugfs_remove_recursive(dir);
+}
+
+static void bcm_kona_wdt_debug_exit(struct platform_device *pdev)
+{
+ struct bcm_kona_wdt *wdt = platform_get_drvdata(pdev);
+
+ if (wdt && wdt->debugfs) {
+ debugfs_remove_recursive(wdt->debugfs);
+ wdt->debugfs = NULL;
+ }
+}
+
+#else
+
+static void bcm_kona_wdt_debug_init(struct platform_device *pdev) {}
+static void bcm_kona_wdt_debug_exit(struct platform_device *pdev) {}
+
+#endif /* CONFIG_BCM_KONA_WDT_DEBUG */
+
+static int bcm_kona_wdt_ctrl_reg_modify(struct bcm_kona_wdt *wdt,
+ unsigned mask, unsigned newval)
+{
+ int val;
+ unsigned long flags;
+ int ret = 0;
+
+ spin_lock_irqsave(&wdt->lock, flags);
+
+ val = secure_register_read(wdt, SECWDOG_CTRL_REG);
+ if (val < 0) {
+ ret = val;
+ } else {
+ val &= ~mask;
+ val |= newval;
+ writel_relaxed(val, wdt->base + SECWDOG_CTRL_REG);
+ }
+
+ spin_unlock_irqrestore(&wdt->lock, flags);
+
+ return ret;
+}
+
+static int bcm_kona_wdt_set_resolution_reg(struct bcm_kona_wdt *wdt)
+{
+ if (wdt->resolution > SECWDOG_MAX_RES)
+ return -EINVAL;
+
+ return bcm_kona_wdt_ctrl_reg_modify(wdt, SECWDOG_RES_MASK,
+ wdt->resolution << SECWDOG_CLKS_SHIFT);
+}
+
+static int bcm_kona_wdt_set_timeout_reg(struct watchdog_device *wdog,
+ unsigned watchdog_flags)
+{
+ struct bcm_kona_wdt *wdt = watchdog_get_drvdata(wdog);
+
+ return bcm_kona_wdt_ctrl_reg_modify(wdt, SECWDOG_COUNT_MASK,
+ SECS_TO_TICKS(wdog->timeout, wdt) |
+ watchdog_flags);
+}
+
+static int bcm_kona_wdt_set_timeout(struct watchdog_device *wdog,
+ unsigned int t)
+{
+ wdog->timeout = t;
+ return 0;
+}
+
+static unsigned int bcm_kona_wdt_get_timeleft(struct watchdog_device *wdog)
+{
+ struct bcm_kona_wdt *wdt = watchdog_get_drvdata(wdog);
+ int val;
+ unsigned long flags;
+
+ spin_lock_irqsave(&wdt->lock, flags);
+ val = secure_register_read(wdt, SECWDOG_COUNT_REG);
+ spin_unlock_irqrestore(&wdt->lock, flags);
+
+ if (val < 0)
+ return val;
+
+ return TICKS_TO_SECS(val & SECWDOG_COUNT_MASK, wdt);
+}
+
+static int bcm_kona_wdt_start(struct watchdog_device *wdog)
+{
+ return bcm_kona_wdt_set_timeout_reg(wdog,
+ SECWDOG_EN_MASK | SECWDOG_SRSTEN_MASK);
+}
+
+static int bcm_kona_wdt_stop(struct watchdog_device *wdog)
+{
+ struct bcm_kona_wdt *wdt = watchdog_get_drvdata(wdog);
+
+ return bcm_kona_wdt_ctrl_reg_modify(wdt, SECWDOG_EN_MASK |
+ SECWDOG_SRSTEN_MASK, 0);
+}
+
+static struct watchdog_ops bcm_kona_wdt_ops = {
+ .owner = THIS_MODULE,
+ .start = bcm_kona_wdt_start,
+ .stop = bcm_kona_wdt_stop,
+ .set_timeout = bcm_kona_wdt_set_timeout,
+ .get_timeleft = bcm_kona_wdt_get_timeleft,
+};
+
+static struct watchdog_info bcm_kona_wdt_info = {
+ .options = WDIOF_SETTIMEOUT | WDIOF_MAGICCLOSE |
+ WDIOF_KEEPALIVEPING,
+ .identity = "Broadcom Kona Watchdog Timer",
+};
+
+static struct watchdog_device bcm_kona_wdt_wdd = {
+ .info = &bcm_kona_wdt_info,
+ .ops = &bcm_kona_wdt_ops,
+ .min_timeout = 1,
+ .max_timeout = SECWDOG_MAX_COUNT >> SECWDOG_DEFAULT_RESOLUTION,
+ .timeout = SECWDOG_MAX_COUNT >> SECWDOG_DEFAULT_RESOLUTION,
+};
+
+static void bcm_kona_wdt_shutdown(struct platform_device *pdev)
+{
+ bcm_kona_wdt_stop(&bcm_kona_wdt_wdd);
+}
+
+static int bcm_kona_wdt_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct bcm_kona_wdt *wdt;
+ struct resource *res;
+ int ret;
+
+ wdt = devm_kzalloc(dev, sizeof(*wdt), GFP_KERNEL);
+ if (!wdt)
+ return -ENOMEM;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ wdt->base = devm_ioremap_resource(dev, res);
+ if (IS_ERR(wdt->base))
+ return -ENODEV;
+
+ wdt->resolution = SECWDOG_DEFAULT_RESOLUTION;
+ ret = bcm_kona_wdt_set_resolution_reg(wdt);
+ if (ret) {
+ dev_err(dev, "Failed to set resolution (error: %d)", ret);
+ return ret;
+ }
+
+ spin_lock_init(&wdt->lock);
+ platform_set_drvdata(pdev, wdt);
+ watchdog_set_drvdata(&bcm_kona_wdt_wdd, wdt);
+
+ ret = bcm_kona_wdt_set_timeout_reg(&bcm_kona_wdt_wdd, 0);
+ if (ret) {
+ dev_err(dev, "Failed set watchdog timeout");
+ return ret;
+ }
+
+ ret = watchdog_register_device(&bcm_kona_wdt_wdd);
+ if (ret) {
+ dev_err(dev, "Failed to register watchdog device");
+ return ret;
+ }
+
+ bcm_kona_wdt_debug_init(pdev);
+ dev_dbg(dev, "Broadcom Kona Watchdog Timer");
+
+ return 0;
+}
+
+static int bcm_kona_wdt_remove(struct platform_device *pdev)
+{
+ bcm_kona_wdt_debug_exit(pdev);
+ bcm_kona_wdt_shutdown(pdev);
+ watchdog_unregister_device(&bcm_kona_wdt_wdd);
+ dev_dbg(&pdev->dev, "Watchdog driver disabled");
+
+ return 0;
+}
+
+static const struct of_device_id bcm_kona_wdt_of_match[] = {
+ { .compatible = "brcm,kona-wdt", },
+ {},
+};
+MODULE_DEVICE_TABLE(of, bcm_kona_wdt_of_match);
+
+static struct platform_driver bcm_kona_wdt_driver = {
+ .driver = {
+ .name = BCM_KONA_WDT_NAME,
+ .owner = THIS_MODULE,
+ .of_match_table = bcm_kona_wdt_of_match,
+ },
+ .probe = bcm_kona_wdt_probe,
+ .remove = bcm_kona_wdt_remove,
+ .shutdown = bcm_kona_wdt_shutdown,
+};
+
+module_platform_driver(bcm_kona_wdt_driver);
+
+MODULE_ALIAS("platform:" BCM_KONA_WDT_NAME);
+MODULE_AUTHOR("Markus Mayer <mmayer@broadcom.com>");
+MODULE_DESCRIPTION("Broadcom Kona Watchdog Driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/watchdog/bfin_wdt.c b/drivers/watchdog/bfin_wdt.c
index 38bc383e067..a3b6a5b30f9 100644
--- a/drivers/watchdog/bfin_wdt.c
+++ b/drivers/watchdog/bfin_wdt.c
@@ -356,7 +356,7 @@ static const struct watchdog_info bfin_wdt_info = {
* Registers the misc device. Actual device
* initialization is handled by bfin_wdt_open().
*/
-static int __devinit bfin_wdt_probe(struct platform_device *pdev)
+static int bfin_wdt_probe(struct platform_device *pdev)
{
int ret;
@@ -379,7 +379,7 @@ static int __devinit bfin_wdt_probe(struct platform_device *pdev)
* Unregisters the misc device. Actual device
* deinitialization is handled by bfin_wdt_close().
*/
-static int __devexit bfin_wdt_remove(struct platform_device *pdev)
+static int bfin_wdt_remove(struct platform_device *pdev)
{
misc_deregister(&bfin_wdt_miscdev);
return 0;
@@ -401,7 +401,7 @@ static struct platform_device *bfin_wdt_device;
static struct platform_driver bfin_wdt_driver = {
.probe = bfin_wdt_probe,
- .remove = __devexit_p(bfin_wdt_remove),
+ .remove = bfin_wdt_remove,
.shutdown = bfin_wdt_shutdown,
.suspend = bfin_wdt_suspend,
.resume = bfin_wdt_resume,
@@ -465,7 +465,6 @@ module_exit(bfin_wdt_exit);
MODULE_AUTHOR("Michele d'Amico, Mike Frysinger <vapier@gentoo.org>");
MODULE_DESCRIPTION("Blackfin Watchdog Device Driver");
MODULE_LICENSE("GPL");
-MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
module_param(timeout, uint, 0);
MODULE_PARM_DESC(timeout,
diff --git a/drivers/watchdog/booke_wdt.c b/drivers/watchdog/booke_wdt.c
index ce0ab4415ef..08a785398ea 100644
--- a/drivers/watchdog/booke_wdt.c
+++ b/drivers/watchdog/booke_wdt.c
@@ -15,12 +15,8 @@
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/module.h>
-#include <linux/fs.h>
#include <linux/smp.h>
-#include <linux/miscdevice.h>
-#include <linux/notifier.h>
#include <linux/watchdog.h>
-#include <linux/uaccess.h>
#include <asm/reg_booke.h>
#include <asm/time.h>
@@ -37,7 +33,7 @@
u32 booke_wdt_enabled;
u32 booke_wdt_period = CONFIG_BOOKE_WDT_DEFAULT_TIMEOUT;
-#ifdef CONFIG_FSL_BOOKE
+#ifdef CONFIG_PPC_FSL_BOOK3E
#define WDTP(x) ((((x)&0x3)<<30)|(((x)&0x3c)<<15))
#define WDTP_MASK (WDTP(0x3f))
#else
@@ -45,7 +41,29 @@ u32 booke_wdt_period = CONFIG_BOOKE_WDT_DEFAULT_TIMEOUT;
#define WDTP_MASK (TCR_WP_MASK)
#endif
-static DEFINE_SPINLOCK(booke_wdt_lock);
+/* Checks wdt=x and wdt_period=xx command-line option */
+notrace int __init early_parse_wdt(char *p)
+{
+ if (p && strncmp(p, "0", 1) != 0)
+ booke_wdt_enabled = 1;
+
+ return 0;
+}
+early_param("wdt", early_parse_wdt);
+
+int __init early_parse_wdt_period(char *p)
+{
+ unsigned long ret;
+ if (p) {
+ if (!kstrtol(p, 0, &ret))
+ booke_wdt_period = ret;
+ }
+
+ return 0;
+}
+early_param("wdt_period", early_parse_wdt_period);
+
+#ifdef CONFIG_PPC_FSL_BOOK3E
/* For the specified period, determine the number of seconds
* corresponding to the reset time. There will be a watchdog
@@ -86,20 +104,39 @@ static unsigned int sec_to_period(unsigned int secs)
return 0;
}
+#define MAX_WDT_TIMEOUT period_to_sec(1)
+
+#else /* CONFIG_PPC_FSL_BOOK3E */
+
+static unsigned long long period_to_sec(unsigned int period)
+{
+ return period;
+}
+
+static unsigned int sec_to_period(unsigned int secs)
+{
+ return secs;
+}
+
+#define MAX_WDT_TIMEOUT 3 /* from Kconfig */
+
+#endif /* !CONFIG_PPC_FSL_BOOK3E */
+
static void __booke_wdt_set(void *data)
{
u32 val;
+ struct watchdog_device *wdog = data;
val = mfspr(SPRN_TCR);
val &= ~WDTP_MASK;
- val |= WDTP(booke_wdt_period);
+ val |= WDTP(sec_to_period(wdog->timeout));
mtspr(SPRN_TCR, val);
}
-static void booke_wdt_set(void)
+static void booke_wdt_set(void *data)
{
- on_each_cpu(__booke_wdt_set, NULL, 0);
+ on_each_cpu(__booke_wdt_set, data, 0);
}
static void __booke_wdt_ping(void *data)
@@ -107,20 +144,23 @@ static void __booke_wdt_ping(void *data)
mtspr(SPRN_TSR, TSR_ENW|TSR_WIS);
}
-static void booke_wdt_ping(void)
+static int booke_wdt_ping(struct watchdog_device *wdog)
{
on_each_cpu(__booke_wdt_ping, NULL, 0);
+
+ return 0;
}
static void __booke_wdt_enable(void *data)
{
u32 val;
+ struct watchdog_device *wdog = data;
/* clear status before enabling watchdog */
__booke_wdt_ping(NULL);
val = mfspr(SPRN_TCR);
val &= ~WDTP_MASK;
- val |= (TCR_WIE|TCR_WRC(WRC_CHIP)|WDTP(booke_wdt_period));
+ val |= (TCR_WIE|TCR_WRC(WRC_CHIP)|WDTP(sec_to_period(wdog->timeout)));
mtspr(SPRN_TCR, val);
}
@@ -146,153 +186,72 @@ static void __booke_wdt_disable(void *data)
}
-static ssize_t booke_wdt_write(struct file *file, const char __user *buf,
- size_t count, loff_t *ppos)
-{
- booke_wdt_ping();
- return count;
-}
-
-static struct watchdog_info ident = {
- .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING,
- .identity = "PowerPC Book-E Watchdog",
-};
-
-static long booke_wdt_ioctl(struct file *file,
- unsigned int cmd, unsigned long arg)
+static int booke_wdt_start(struct watchdog_device *wdog)
{
- u32 tmp = 0;
- u32 __user *p = (u32 __user *)arg;
-
- switch (cmd) {
- case WDIOC_GETSUPPORT:
- if (copy_to_user((void *)arg, &ident, sizeof(ident)))
- return -EFAULT;
- case WDIOC_GETSTATUS:
- return put_user(0, p);
- case WDIOC_GETBOOTSTATUS:
- /* XXX: something is clearing TSR */
- tmp = mfspr(SPRN_TSR) & TSR_WRS(3);
- /* returns CARDRESET if last reset was caused by the WDT */
- return (tmp ? WDIOF_CARDRESET : 0);
- case WDIOC_SETOPTIONS:
- if (get_user(tmp, p))
- return -EINVAL;
- if (tmp == WDIOS_ENABLECARD) {
- booke_wdt_ping();
- break;
- } else
- return -EINVAL;
- return 0;
- case WDIOC_KEEPALIVE:
- booke_wdt_ping();
- return 0;
- case WDIOC_SETTIMEOUT:
- if (get_user(tmp, p))
- return -EFAULT;
-#ifdef CONFIG_FSL_BOOKE
- /* period of 1 gives the largest possible timeout */
- if (tmp > period_to_sec(1))
- return -EINVAL;
- booke_wdt_period = sec_to_period(tmp);
-#else
- booke_wdt_period = tmp;
-#endif
- booke_wdt_set();
- /* Fall */
- case WDIOC_GETTIMEOUT:
-#ifdef CONFIG_FSL_BOOKE
- return put_user(period_to_sec(booke_wdt_period), p);
-#else
- return put_user(booke_wdt_period, p);
-#endif
- default:
- return -ENOTTY;
- }
+ on_each_cpu(__booke_wdt_enable, wdog, 0);
+ pr_debug("watchdog enabled (timeout = %u sec)\n", wdog->timeout);
return 0;
}
-/* wdt_is_active stores wether or not the /dev/watchdog device is opened */
-static unsigned long wdt_is_active;
-
-static int booke_wdt_open(struct inode *inode, struct file *file)
+static int booke_wdt_stop(struct watchdog_device *wdog)
{
- /* /dev/watchdog can only be opened once */
- if (test_and_set_bit(0, &wdt_is_active))
- return -EBUSY;
-
- spin_lock(&booke_wdt_lock);
- if (booke_wdt_enabled == 0) {
- booke_wdt_enabled = 1;
- on_each_cpu(__booke_wdt_enable, NULL, 0);
- pr_debug("watchdog enabled (timeout = %llu sec)\n",
- period_to_sec(booke_wdt_period));
- }
- spin_unlock(&booke_wdt_lock);
+ on_each_cpu(__booke_wdt_disable, NULL, 0);
+ pr_debug("watchdog disabled\n");
- return nonseekable_open(inode, file);
+ return 0;
}
-static int booke_wdt_release(struct inode *inode, struct file *file)
+static int booke_wdt_set_timeout(struct watchdog_device *wdt_dev,
+ unsigned int timeout)
{
-#ifndef CONFIG_WATCHDOG_NOWAYOUT
- /* Normally, the watchdog is disabled when /dev/watchdog is closed, but
- * if CONFIG_WATCHDOG_NOWAYOUT is defined, then it means that the
- * watchdog should remain enabled. So we disable it only if
- * CONFIG_WATCHDOG_NOWAYOUT is not defined.
- */
- on_each_cpu(__booke_wdt_disable, NULL, 0);
- booke_wdt_enabled = 0;
- pr_debug("watchdog disabled\n");
-#endif
-
- clear_bit(0, &wdt_is_active);
+ if (timeout > MAX_WDT_TIMEOUT)
+ return -EINVAL;
+ wdt_dev->timeout = timeout;
+ booke_wdt_set(wdt_dev);
return 0;
}
-static const struct file_operations booke_wdt_fops = {
+static struct watchdog_info booke_wdt_info = {
+ .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING,
+ .identity = "PowerPC Book-E Watchdog",
+};
+
+static struct watchdog_ops booke_wdt_ops = {
.owner = THIS_MODULE,
- .llseek = no_llseek,
- .write = booke_wdt_write,
- .unlocked_ioctl = booke_wdt_ioctl,
- .open = booke_wdt_open,
- .release = booke_wdt_release,
+ .start = booke_wdt_start,
+ .stop = booke_wdt_stop,
+ .ping = booke_wdt_ping,
+ .set_timeout = booke_wdt_set_timeout,
};
-static struct miscdevice booke_wdt_miscdev = {
- .minor = WATCHDOG_MINOR,
- .name = "watchdog",
- .fops = &booke_wdt_fops,
+static struct watchdog_device booke_wdt_dev = {
+ .info = &booke_wdt_info,
+ .ops = &booke_wdt_ops,
+ .min_timeout = 1,
+ .max_timeout = 0xFFFF
};
static void __exit booke_wdt_exit(void)
{
- misc_deregister(&booke_wdt_miscdev);
+ watchdog_unregister_device(&booke_wdt_dev);
}
static int __init booke_wdt_init(void)
{
int ret = 0;
+ bool nowayout = WATCHDOG_NOWAYOUT;
pr_info("powerpc book-e watchdog driver loaded\n");
- ident.firmware_version = cur_cpu_spec->pvr_value;
-
- ret = misc_register(&booke_wdt_miscdev);
- if (ret) {
- pr_err("cannot register device (minor=%u, ret=%i)\n",
- WATCHDOG_MINOR, ret);
- return ret;
- }
-
- spin_lock(&booke_wdt_lock);
- if (booke_wdt_enabled == 1) {
- pr_info("watchdog enabled (timeout = %llu sec)\n",
- period_to_sec(booke_wdt_period));
- on_each_cpu(__booke_wdt_enable, NULL, 0);
- }
- spin_unlock(&booke_wdt_lock);
+ booke_wdt_info.firmware_version = cur_cpu_spec->pvr_value;
+ booke_wdt_set_timeout(&booke_wdt_dev,
+ period_to_sec(booke_wdt_period));
+ watchdog_set_nowayout(&booke_wdt_dev, nowayout);
+ if (booke_wdt_enabled)
+ booke_wdt_start(&booke_wdt_dev);
+
+ ret = watchdog_register_device(&booke_wdt_dev);
return ret;
}
diff --git a/drivers/watchdog/coh901327_wdt.c b/drivers/watchdog/coh901327_wdt.c
index 6876430a9f5..4bd070f524e 100644
--- a/drivers/watchdog/coh901327_wdt.c
+++ b/drivers/watchdog/coh901327_wdt.c
@@ -263,6 +263,7 @@ static int __exit coh901327_remove(struct platform_device *pdev)
watchdog_unregister_device(&coh901327_wdt);
coh901327_disable();
free_irq(irq, pdev);
+ clk_unprepare(clk);
clk_put(clk);
iounmap(virtbase);
release_mem_region(phybase, physize);
@@ -300,9 +301,9 @@ static int __init coh901327_probe(struct platform_device *pdev)
dev_err(&pdev->dev, "could not get clock\n");
goto out_no_clk;
}
- ret = clk_enable(clk);
+ ret = clk_prepare_enable(clk);
if (ret) {
- dev_err(&pdev->dev, "could not enable clock\n");
+ dev_err(&pdev->dev, "could not prepare and enable clock\n");
goto out_no_clk_enable;
}
@@ -353,9 +354,9 @@ static int __init coh901327_probe(struct platform_device *pdev)
clk_disable(clk);
- if (margin < 1 || margin > 327)
- margin = 60;
- coh901327_wdt.timeout = margin;
+ ret = watchdog_init_timeout(&coh901327_wdt, margin, &pdev->dev);
+ if (ret < 0)
+ coh901327_wdt.timeout = 60;
ret = watchdog_register_device(&coh901327_wdt);
if (ret == 0)
@@ -369,7 +370,7 @@ static int __init coh901327_probe(struct platform_device *pdev)
out_no_wdog:
free_irq(irq, pdev);
out_no_irq:
- clk_disable(clk);
+ clk_disable_unprepare(clk);
out_no_clk_enable:
clk_put(clk);
out_no_clk:
@@ -440,27 +441,23 @@ void coh901327_watchdog_reset(void)
/* Return and await doom */
}
+static const struct of_device_id coh901327_dt_match[] = {
+ { .compatible = "stericsson,coh901327" },
+ {},
+};
+
static struct platform_driver coh901327_driver = {
.driver = {
.owner = THIS_MODULE,
.name = "coh901327_wdog",
+ .of_match_table = coh901327_dt_match,
},
.remove = __exit_p(coh901327_remove),
.suspend = coh901327_suspend,
.resume = coh901327_resume,
};
-static int __init coh901327_init(void)
-{
- return platform_driver_probe(&coh901327_driver, coh901327_probe);
-}
-module_init(coh901327_init);
-
-static void __exit coh901327_exit(void)
-{
- platform_driver_unregister(&coh901327_driver);
-}
-module_exit(coh901327_exit);
+module_platform_driver_probe(coh901327_driver, coh901327_probe);
MODULE_AUTHOR("Linus Walleij <linus.walleij@stericsson.com>");
MODULE_DESCRIPTION("COH 901 327 Watchdog");
diff --git a/drivers/watchdog/cpu5wdt.c b/drivers/watchdog/cpu5wdt.c
index 7e888393de1..6d03e8e30f8 100644
--- a/drivers/watchdog/cpu5wdt.c
+++ b/drivers/watchdog/cpu5wdt.c
@@ -27,7 +27,6 @@
#include <linux/errno.h>
#include <linux/miscdevice.h>
#include <linux/fs.h>
-#include <linux/init.h>
#include <linux/ioport.h>
#include <linux/timer.h>
#include <linux/completion.h>
@@ -215,7 +214,7 @@ static struct miscdevice cpu5wdt_misc = {
/* init/exit function */
-static int __devinit cpu5wdt_init(void)
+static int cpu5wdt_init(void)
{
unsigned int val;
int err;
@@ -256,16 +255,17 @@ no_port:
return err;
}
-static int __devinit cpu5wdt_init_module(void)
+static int cpu5wdt_init_module(void)
{
return cpu5wdt_init();
}
-static void __devexit cpu5wdt_exit(void)
+static void cpu5wdt_exit(void)
{
if (cpu5wdt_device.queue) {
cpu5wdt_device.queue = 0;
wait_for_completion(&cpu5wdt_device.stop);
+ del_timer(&cpu5wdt_device.timer);
}
misc_deregister(&cpu5wdt_misc);
@@ -274,7 +274,7 @@ static void __devexit cpu5wdt_exit(void)
}
-static void __devexit cpu5wdt_exit_module(void)
+static void cpu5wdt_exit_module(void)
{
cpu5wdt_exit();
}
@@ -288,7 +288,6 @@ MODULE_AUTHOR("Heiko Ronsdorf <hero@ihg.uni-duisburg.de>");
MODULE_DESCRIPTION("sma cpu5 watchdog driver");
MODULE_SUPPORTED_DEVICE("sma cpu5 watchdog");
MODULE_LICENSE("GPL");
-MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
module_param(port, int, 0);
MODULE_PARM_DESC(port, "base address of watchdog card, default is 0x91");
diff --git a/drivers/watchdog/cpwd.c b/drivers/watchdog/cpwd.c
index 95b1b954de1..e55ed702209 100644
--- a/drivers/watchdog/cpwd.c
+++ b/drivers/watchdog/cpwd.c
@@ -21,7 +21,6 @@
#include <linux/fs.h>
#include <linux/errno.h>
#include <linux/major.h>
-#include <linux/init.h>
#include <linux/miscdevice.h>
#include <linux/interrupt.h>
#include <linux/ioport.h>
@@ -411,7 +410,7 @@ static long cpwd_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
.identity = DRIVER_NAME,
};
void __user *argp = (void __user *)arg;
- struct inode *inode = file->f_path.dentry->d_inode;
+ struct inode *inode = file_inode(file);
int index = iminor(inode) - WD0_MINOR;
struct cpwd *p = cpwd_device;
int setopt = 0;
@@ -499,7 +498,7 @@ static long cpwd_compat_ioctl(struct file *file, unsigned int cmd,
static ssize_t cpwd_write(struct file *file, const char __user *buf,
size_t count, loff_t *ppos)
{
- struct inode *inode = file->f_path.dentry->d_inode;
+ struct inode *inode = file_inode(file);
struct cpwd *p = cpwd_device;
int index = iminor(inode);
@@ -528,7 +527,7 @@ static const struct file_operations cpwd_fops = {
.llseek = no_llseek,
};
-static int __devinit cpwd_probe(struct platform_device *op)
+static int cpwd_probe(struct platform_device *op)
{
struct device_node *options;
const char *str_prop;
@@ -621,7 +620,7 @@ static int __devinit cpwd_probe(struct platform_device *op)
WD_BADMODEL);
}
- dev_set_drvdata(&op->dev, p);
+ platform_set_drvdata(op, p);
cpwd_device = p;
err = 0;
@@ -640,9 +639,9 @@ out_free:
goto out;
}
-static int __devexit cpwd_remove(struct platform_device *op)
+static int cpwd_remove(struct platform_device *op)
{
- struct cpwd *p = dev_get_drvdata(&op->dev);
+ struct cpwd *p = platform_get_drvdata(op);
int i;
for (i = 0; i < WD_NUMDEVS; i++) {
@@ -684,7 +683,7 @@ static struct platform_driver cpwd_driver = {
.of_match_table = cpwd_match,
},
.probe = cpwd_probe,
- .remove = __devexit_p(cpwd_remove),
+ .remove = cpwd_remove,
};
module_platform_driver(cpwd_driver);
diff --git a/drivers/watchdog/da9052_wdt.c b/drivers/watchdog/da9052_wdt.c
new file mode 100644
index 00000000000..2e9589652e1
--- /dev/null
+++ b/drivers/watchdog/da9052_wdt.c
@@ -0,0 +1,245 @@
+/*
+ * System monitoring driver for DA9052 PMICs.
+ *
+ * Copyright(c) 2012 Dialog Semiconductor Ltd.
+ *
+ * Author: Anthony Olech <Anthony.Olech@diasemi.com>
+ *
+ * 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.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/uaccess.h>
+#include <linux/platform_device.h>
+#include <linux/time.h>
+#include <linux/watchdog.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/jiffies.h>
+
+#include <linux/mfd/da9052/reg.h>
+#include <linux/mfd/da9052/da9052.h>
+
+#define DA9052_DEF_TIMEOUT 4
+#define DA9052_TWDMIN 256
+
+struct da9052_wdt_data {
+ struct watchdog_device wdt;
+ struct da9052 *da9052;
+ struct kref kref;
+ unsigned long jpast;
+};
+
+static const struct {
+ u8 reg_val;
+ int time; /* Seconds */
+} da9052_wdt_maps[] = {
+ { 1, 2 },
+ { 2, 4 },
+ { 3, 8 },
+ { 4, 16 },
+ { 5, 32 },
+ { 5, 33 }, /* Actual time 32.768s so included both 32s and 33s */
+ { 6, 65 },
+ { 6, 66 }, /* Actual time 65.536s so include both, 65s and 66s */
+ { 7, 131 },
+};
+
+
+static void da9052_wdt_release_resources(struct kref *r)
+{
+}
+
+static int da9052_wdt_set_timeout(struct watchdog_device *wdt_dev,
+ unsigned int timeout)
+{
+ struct da9052_wdt_data *driver_data = watchdog_get_drvdata(wdt_dev);
+ struct da9052 *da9052 = driver_data->da9052;
+ int ret, i;
+
+ /*
+ * Disable the Watchdog timer before setting
+ * new time out.
+ */
+ ret = da9052_reg_update(da9052, DA9052_CONTROL_D_REG,
+ DA9052_CONTROLD_TWDSCALE, 0);
+ if (ret < 0) {
+ dev_err(da9052->dev, "Failed to disable watchdog bit, %d\n",
+ ret);
+ return ret;
+ }
+ if (timeout) {
+ /*
+ * To change the timeout, da9052 needs to
+ * be disabled for at least 150 us.
+ */
+ udelay(150);
+
+ /* Set the desired timeout */
+ for (i = 0; i < ARRAY_SIZE(da9052_wdt_maps); i++)
+ if (da9052_wdt_maps[i].time == timeout)
+ break;
+
+ if (i == ARRAY_SIZE(da9052_wdt_maps))
+ ret = -EINVAL;
+ else
+ ret = da9052_reg_update(da9052, DA9052_CONTROL_D_REG,
+ DA9052_CONTROLD_TWDSCALE,
+ da9052_wdt_maps[i].reg_val);
+ if (ret < 0) {
+ dev_err(da9052->dev,
+ "Failed to update timescale bit, %d\n", ret);
+ return ret;
+ }
+
+ wdt_dev->timeout = timeout;
+ driver_data->jpast = jiffies;
+ }
+
+ return 0;
+}
+
+static void da9052_wdt_ref(struct watchdog_device *wdt_dev)
+{
+ struct da9052_wdt_data *driver_data = watchdog_get_drvdata(wdt_dev);
+
+ kref_get(&driver_data->kref);
+}
+
+static void da9052_wdt_unref(struct watchdog_device *wdt_dev)
+{
+ struct da9052_wdt_data *driver_data = watchdog_get_drvdata(wdt_dev);
+
+ kref_put(&driver_data->kref, da9052_wdt_release_resources);
+}
+
+static int da9052_wdt_start(struct watchdog_device *wdt_dev)
+{
+ return da9052_wdt_set_timeout(wdt_dev, wdt_dev->timeout);
+}
+
+static int da9052_wdt_stop(struct watchdog_device *wdt_dev)
+{
+ return da9052_wdt_set_timeout(wdt_dev, 0);
+}
+
+static int da9052_wdt_ping(struct watchdog_device *wdt_dev)
+{
+ struct da9052_wdt_data *driver_data = watchdog_get_drvdata(wdt_dev);
+ struct da9052 *da9052 = driver_data->da9052;
+ unsigned long msec, jnow = jiffies;
+ int ret;
+
+ /*
+ * We have a minimum time for watchdog window called TWDMIN. A write
+ * to the watchdog before this elapsed time should cause an error.
+ */
+ msec = (jnow - driver_data->jpast) * 1000/HZ;
+ if (msec < DA9052_TWDMIN)
+ mdelay(msec);
+
+ /* Reset the watchdog timer */
+ ret = da9052_reg_update(da9052, DA9052_CONTROL_D_REG,
+ DA9052_CONTROLD_WATCHDOG, 1 << 7);
+ if (ret < 0)
+ goto err_strobe;
+
+ /*
+ * 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;
+}
+
+static struct watchdog_info da9052_wdt_info = {
+ .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING,
+ .identity = "DA9052 Watchdog",
+};
+
+static const struct watchdog_ops da9052_wdt_ops = {
+ .owner = THIS_MODULE,
+ .start = da9052_wdt_start,
+ .stop = da9052_wdt_stop,
+ .ping = da9052_wdt_ping,
+ .set_timeout = da9052_wdt_set_timeout,
+ .ref = da9052_wdt_ref,
+ .unref = da9052_wdt_unref,
+};
+
+
+static int da9052_wdt_probe(struct platform_device *pdev)
+{
+ struct da9052 *da9052 = dev_get_drvdata(pdev->dev.parent);
+ struct da9052_wdt_data *driver_data;
+ struct watchdog_device *da9052_wdt;
+ int ret;
+
+ driver_data = devm_kzalloc(&pdev->dev, sizeof(*driver_data),
+ GFP_KERNEL);
+ if (!driver_data) {
+ ret = -ENOMEM;
+ goto err;
+ }
+ driver_data->da9052 = da9052;
+
+ da9052_wdt = &driver_data->wdt;
+
+ da9052_wdt->timeout = DA9052_DEF_TIMEOUT;
+ da9052_wdt->info = &da9052_wdt_info;
+ da9052_wdt->ops = &da9052_wdt_ops;
+ watchdog_set_drvdata(da9052_wdt, driver_data);
+
+ kref_init(&driver_data->kref);
+
+ ret = da9052_reg_update(da9052, DA9052_CONTROL_D_REG,
+ DA9052_CONTROLD_TWDSCALE, 0);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "Failed to disable watchdog bits, %d\n",
+ ret);
+ goto err;
+ }
+
+ ret = watchdog_register_device(&driver_data->wdt);
+ if (ret != 0) {
+ dev_err(da9052->dev, "watchdog_register_device() failed: %d\n",
+ ret);
+ goto err;
+ }
+
+ 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);
+ kref_put(&driver_data->kref, da9052_wdt_release_resources);
+
+ return 0;
+}
+
+static struct platform_driver da9052_wdt_driver = {
+ .probe = da9052_wdt_probe,
+ .remove = da9052_wdt_remove,
+ .driver = {
+ .name = "da9052-watchdog",
+ },
+};
+
+module_platform_driver(da9052_wdt_driver);
+
+MODULE_AUTHOR("Anthony Olech <Anthony.Olech@diasemi.com>");
+MODULE_DESCRIPTION("DA9052 SM Device Driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:da9052-watchdog");
diff --git a/drivers/watchdog/da9055_wdt.c b/drivers/watchdog/da9055_wdt.c
new file mode 100644
index 00000000000..495089d8dbf
--- /dev/null
+++ b/drivers/watchdog/da9055_wdt.c
@@ -0,0 +1,209 @@
+/*
+ * System monitoring driver for DA9055 PMICs.
+ *
+ * Copyright(c) 2012 Dialog Semiconductor Ltd.
+ *
+ * Author: David Dajun Chen <dchen@diasemi.com>
+ *
+ * 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.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/platform_device.h>
+#include <linux/watchdog.h>
+#include <linux/delay.h>
+
+#include <linux/mfd/da9055/core.h>
+#include <linux/mfd/da9055/reg.h>
+
+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) ")");
+
+#define DA9055_DEF_TIMEOUT 4
+#define DA9055_TWDMIN 256
+
+struct da9055_wdt_data {
+ struct watchdog_device wdt;
+ struct da9055 *da9055;
+ struct kref kref;
+};
+
+static const struct {
+ u8 reg_val;
+ int user_time; /* In seconds */
+} da9055_wdt_maps[] = {
+ { 0, 0 },
+ { 1, 2 },
+ { 2, 4 },
+ { 3, 8 },
+ { 4, 16 },
+ { 5, 32 },
+ { 5, 33 }, /* Actual time 32.768s so included both 32s and 33s */
+ { 6, 65 },
+ { 6, 66 }, /* Actual time 65.536s so include both, 65s and 66s */
+ { 7, 131 },
+};
+
+static int da9055_wdt_set_timeout(struct watchdog_device *wdt_dev,
+ unsigned int timeout)
+{
+ struct da9055_wdt_data *driver_data = watchdog_get_drvdata(wdt_dev);
+ struct da9055 *da9055 = driver_data->da9055;
+ int ret, i;
+
+ for (i = 0; i < ARRAY_SIZE(da9055_wdt_maps); i++)
+ if (da9055_wdt_maps[i].user_time == timeout)
+ break;
+
+ if (i == ARRAY_SIZE(da9055_wdt_maps))
+ ret = -EINVAL;
+ else
+ ret = da9055_reg_update(da9055, DA9055_REG_CONTROL_B,
+ DA9055_TWDSCALE_MASK,
+ da9055_wdt_maps[i].reg_val <<
+ DA9055_TWDSCALE_SHIFT);
+ if (ret < 0) {
+ dev_err(da9055->dev,
+ "Failed to update timescale bit, %d\n", ret);
+ return ret;
+ }
+
+ wdt_dev->timeout = timeout;
+
+ return 0;
+}
+
+static int da9055_wdt_ping(struct watchdog_device *wdt_dev)
+{
+ struct da9055_wdt_data *driver_data = watchdog_get_drvdata(wdt_dev);
+ struct da9055 *da9055 = driver_data->da9055;
+
+ /*
+ * We have a minimum time for watchdog window called TWDMIN. A write
+ * to the watchdog before this elapsed time will cause an error.
+ */
+ mdelay(DA9055_TWDMIN);
+
+ /* Reset the watchdog timer */
+ return da9055_reg_update(da9055, DA9055_REG_CONTROL_E,
+ DA9055_WATCHDOG_MASK, 1);
+}
+
+static void da9055_wdt_release_resources(struct kref *r)
+{
+}
+
+static void da9055_wdt_ref(struct watchdog_device *wdt_dev)
+{
+ struct da9055_wdt_data *driver_data = watchdog_get_drvdata(wdt_dev);
+
+ kref_get(&driver_data->kref);
+}
+
+static void da9055_wdt_unref(struct watchdog_device *wdt_dev)
+{
+ struct da9055_wdt_data *driver_data = watchdog_get_drvdata(wdt_dev);
+
+ kref_put(&driver_data->kref, da9055_wdt_release_resources);
+}
+
+static int da9055_wdt_start(struct watchdog_device *wdt_dev)
+{
+ return da9055_wdt_set_timeout(wdt_dev, wdt_dev->timeout);
+}
+
+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 = {
+ .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING,
+ .identity = "DA9055 Watchdog",
+};
+
+static const struct watchdog_ops da9055_wdt_ops = {
+ .owner = THIS_MODULE,
+ .start = da9055_wdt_start,
+ .stop = da9055_wdt_stop,
+ .ping = da9055_wdt_ping,
+ .set_timeout = da9055_wdt_set_timeout,
+ .ref = da9055_wdt_ref,
+ .unref = da9055_wdt_unref,
+};
+
+static int da9055_wdt_probe(struct platform_device *pdev)
+{
+ struct da9055 *da9055 = dev_get_drvdata(pdev->dev.parent);
+ struct da9055_wdt_data *driver_data;
+ struct watchdog_device *da9055_wdt;
+ int ret;
+
+ driver_data = devm_kzalloc(&pdev->dev, sizeof(*driver_data),
+ GFP_KERNEL);
+ if (!driver_data)
+ return -ENOMEM;
+
+ driver_data->da9055 = da9055;
+
+ da9055_wdt = &driver_data->wdt;
+
+ da9055_wdt->timeout = DA9055_DEF_TIMEOUT;
+ da9055_wdt->info = &da9055_wdt_info;
+ da9055_wdt->ops = &da9055_wdt_ops;
+ watchdog_set_nowayout(da9055_wdt, nowayout);
+ watchdog_set_drvdata(da9055_wdt, driver_data);
+
+ kref_init(&driver_data->kref);
+
+ ret = da9055_wdt_stop(da9055_wdt);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "Failed to stop watchdog, %d\n", ret);
+ goto err;
+ }
+
+ platform_set_drvdata(pdev, driver_data);
+
+ ret = watchdog_register_device(&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);
+ kref_put(&driver_data->kref, da9055_wdt_release_resources);
+
+ return 0;
+}
+
+static struct platform_driver da9055_wdt_driver = {
+ .probe = da9055_wdt_probe,
+ .remove = da9055_wdt_remove,
+ .driver = {
+ .name = "da9055-watchdog",
+ },
+};
+
+module_platform_driver(da9055_wdt_driver);
+
+MODULE_AUTHOR("David Dajun Chen <dchen@diasemi.com>");
+MODULE_DESCRIPTION("DA9055 watchdog");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:da9055-watchdog");
diff --git a/drivers/watchdog/davinci_wdt.c b/drivers/watchdog/davinci_wdt.c
index c8c5c8032bc..d09ad2254b5 100644
--- a/drivers/watchdog/davinci_wdt.c
+++ b/drivers/watchdog/davinci_wdt.c
@@ -3,7 +3,7 @@
*
* Watchdog driver for DaVinci DM644x/DM646x processors
*
- * Copyright (C) 2006 Texas Instruments.
+ * Copyright (C) 2006-2013 Texas Instruments.
*
* 2007 (c) MontaVista Software, Inc. This file is licensed under
* the terms of the GNU General Public License version 2. This program
@@ -15,18 +15,12 @@
#include <linux/moduleparam.h>
#include <linux/types.h>
#include <linux/kernel.h>
-#include <linux/fs.h>
-#include <linux/miscdevice.h>
#include <linux/watchdog.h>
-#include <linux/init.h>
-#include <linux/bitops.h>
#include <linux/platform_device.h>
-#include <linux/spinlock.h>
-#include <linux/uaccess.h>
#include <linux/io.h>
#include <linux/device.h>
#include <linux/clk.h>
-#include <linux/slab.h>
+#include <linux/err.h>
#define MODULE_NAME "DAVINCI-WDT: "
@@ -60,215 +54,175 @@
#define WDKEY_SEQ0 (0xa5c6 << 16)
#define WDKEY_SEQ1 (0xda7e << 16)
-static int heartbeat = DEFAULT_HEARTBEAT;
+static int heartbeat;
-static DEFINE_SPINLOCK(io_lock);
-static unsigned long wdt_status;
-#define WDT_IN_USE 0
-#define WDT_OK_TO_CLOSE 1
-#define WDT_REGION_INITED 2
-#define WDT_DEVICE_INITED 3
-
-static struct resource *wdt_mem;
-static void __iomem *wdt_base;
-struct clk *wdt_clk;
-
-static void wdt_service(void)
-{
- spin_lock(&io_lock);
-
- /* put watchdog in service state */
- iowrite32(WDKEY_SEQ0, wdt_base + WDTCR);
- /* put watchdog in active state */
- iowrite32(WDKEY_SEQ1, wdt_base + WDTCR);
-
- spin_unlock(&io_lock);
-}
+/*
+ * struct to hold data for each WDT device
+ * @base - base io address of WD device
+ * @clk - source clock of WDT
+ * @wdd - hold watchdog device as is in WDT core
+ */
+struct davinci_wdt_device {
+ void __iomem *base;
+ struct clk *clk;
+ struct watchdog_device wdd;
+};
-static void wdt_enable(void)
+static int davinci_wdt_start(struct watchdog_device *wdd)
{
u32 tgcr;
u32 timer_margin;
unsigned long wdt_freq;
+ struct davinci_wdt_device *davinci_wdt = watchdog_get_drvdata(wdd);
- wdt_freq = clk_get_rate(wdt_clk);
-
- spin_lock(&io_lock);
+ wdt_freq = clk_get_rate(davinci_wdt->clk);
/* disable, internal clock source */
- iowrite32(0, wdt_base + TCR);
+ iowrite32(0, davinci_wdt->base + TCR);
/* reset timer, set mode to 64-bit watchdog, and unreset */
- iowrite32(0, wdt_base + TGCR);
+ iowrite32(0, davinci_wdt->base + TGCR);
tgcr = TIMMODE_64BIT_WDOG | TIM12RS_UNRESET | TIM34RS_UNRESET;
- iowrite32(tgcr, wdt_base + TGCR);
+ iowrite32(tgcr, davinci_wdt->base + TGCR);
/* clear counter regs */
- iowrite32(0, wdt_base + TIM12);
- iowrite32(0, wdt_base + TIM34);
+ iowrite32(0, davinci_wdt->base + TIM12);
+ iowrite32(0, davinci_wdt->base + TIM34);
/* set timeout period */
- timer_margin = (((u64)heartbeat * wdt_freq) & 0xffffffff);
- iowrite32(timer_margin, wdt_base + PRD12);
- timer_margin = (((u64)heartbeat * wdt_freq) >> 32);
- iowrite32(timer_margin, wdt_base + PRD34);
+ timer_margin = (((u64)wdd->timeout * wdt_freq) & 0xffffffff);
+ iowrite32(timer_margin, davinci_wdt->base + PRD12);
+ timer_margin = (((u64)wdd->timeout * wdt_freq) >> 32);
+ iowrite32(timer_margin, davinci_wdt->base + PRD34);
/* enable run continuously */
- iowrite32(ENAMODE12_PERIODIC, wdt_base + TCR);
+ iowrite32(ENAMODE12_PERIODIC, davinci_wdt->base + TCR);
/* Once the WDT is in pre-active state write to
* TIM12, TIM34, PRD12, PRD34, TCR, TGCR, WDTCR are
* write protected (except for the WDKEY field)
*/
/* put watchdog in pre-active state */
- iowrite32(WDKEY_SEQ0 | WDEN, wdt_base + WDTCR);
+ iowrite32(WDKEY_SEQ0 | WDEN, davinci_wdt->base + WDTCR);
/* put watchdog in active state */
- iowrite32(WDKEY_SEQ1 | WDEN, wdt_base + WDTCR);
-
- spin_unlock(&io_lock);
+ iowrite32(WDKEY_SEQ1 | WDEN, davinci_wdt->base + WDTCR);
+ return 0;
}
-static int davinci_wdt_open(struct inode *inode, struct file *file)
+static int davinci_wdt_ping(struct watchdog_device *wdd)
{
- if (test_and_set_bit(WDT_IN_USE, &wdt_status))
- return -EBUSY;
-
- wdt_enable();
+ struct davinci_wdt_device *davinci_wdt = watchdog_get_drvdata(wdd);
- return nonseekable_open(inode, file);
+ /* put watchdog in service state */
+ iowrite32(WDKEY_SEQ0, davinci_wdt->base + WDTCR);
+ /* put watchdog in active state */
+ iowrite32(WDKEY_SEQ1, davinci_wdt->base + WDTCR);
+ return 0;
}
-static ssize_t
-davinci_wdt_write(struct file *file, const char *data, size_t len,
- loff_t *ppos)
+static unsigned int davinci_wdt_get_timeleft(struct watchdog_device *wdd)
{
- if (len)
- wdt_service();
+ u64 timer_counter;
+ unsigned long freq;
+ u32 val;
+ struct davinci_wdt_device *davinci_wdt = watchdog_get_drvdata(wdd);
- return len;
-}
+ /* if timeout has occured then return 0 */
+ val = ioread32(davinci_wdt->base + WDTCR);
+ if (val & WDFLAG)
+ return 0;
-static const struct watchdog_info ident = {
- .options = WDIOF_KEEPALIVEPING,
- .identity = "DaVinci Watchdog",
-};
+ freq = clk_get_rate(davinci_wdt->clk);
-static long davinci_wdt_ioctl(struct file *file,
- unsigned int cmd, unsigned long arg)
-{
- int ret = -ENOTTY;
-
- switch (cmd) {
- case WDIOC_GETSUPPORT:
- ret = copy_to_user((struct watchdog_info *)arg, &ident,
- sizeof(ident)) ? -EFAULT : 0;
- break;
-
- case WDIOC_GETSTATUS:
- case WDIOC_GETBOOTSTATUS:
- ret = put_user(0, (int *)arg);
- break;
-
- case WDIOC_KEEPALIVE:
- wdt_service();
- ret = 0;
- break;
-
- case WDIOC_GETTIMEOUT:
- ret = put_user(heartbeat, (int *)arg);
- break;
- }
- return ret;
-}
+ if (!freq)
+ return 0;
-static int davinci_wdt_release(struct inode *inode, struct file *file)
-{
- wdt_service();
- clear_bit(WDT_IN_USE, &wdt_status);
+ timer_counter = ioread32(davinci_wdt->base + TIM12);
+ timer_counter |= ((u64)ioread32(davinci_wdt->base + TIM34) << 32);
- return 0;
+ do_div(timer_counter, freq);
+
+ return wdd->timeout - timer_counter;
}
-static const struct file_operations davinci_wdt_fops = {
- .owner = THIS_MODULE,
- .llseek = no_llseek,
- .write = davinci_wdt_write,
- .unlocked_ioctl = davinci_wdt_ioctl,
- .open = davinci_wdt_open,
- .release = davinci_wdt_release,
+static const struct watchdog_info davinci_wdt_info = {
+ .options = WDIOF_KEEPALIVEPING,
+ .identity = "DaVinci/Keystone Watchdog",
};
-static struct miscdevice davinci_wdt_miscdev = {
- .minor = WATCHDOG_MINOR,
- .name = "watchdog",
- .fops = &davinci_wdt_fops,
+static const struct watchdog_ops davinci_wdt_ops = {
+ .owner = THIS_MODULE,
+ .start = davinci_wdt_start,
+ .stop = davinci_wdt_ping,
+ .ping = davinci_wdt_ping,
+ .get_timeleft = davinci_wdt_get_timeleft,
};
-static int __devinit davinci_wdt_probe(struct platform_device *pdev)
+static int davinci_wdt_probe(struct platform_device *pdev)
{
- int ret = 0, size;
+ int ret = 0;
struct device *dev = &pdev->dev;
+ struct resource *wdt_mem;
+ struct watchdog_device *wdd;
+ struct davinci_wdt_device *davinci_wdt;
- wdt_clk = clk_get(dev, NULL);
- if (WARN_ON(IS_ERR(wdt_clk)))
- return PTR_ERR(wdt_clk);
+ davinci_wdt = devm_kzalloc(dev, sizeof(*davinci_wdt), GFP_KERNEL);
+ if (!davinci_wdt)
+ return -ENOMEM;
- clk_enable(wdt_clk);
+ davinci_wdt->clk = devm_clk_get(dev, NULL);
+ if (WARN_ON(IS_ERR(davinci_wdt->clk)))
+ return PTR_ERR(davinci_wdt->clk);
- if (heartbeat < 1 || heartbeat > MAX_HEARTBEAT)
- heartbeat = DEFAULT_HEARTBEAT;
+ clk_prepare_enable(davinci_wdt->clk);
- dev_info(dev, "heartbeat %d sec\n", heartbeat);
+ platform_set_drvdata(pdev, davinci_wdt);
+
+ wdd = &davinci_wdt->wdd;
+ wdd->info = &davinci_wdt_info;
+ wdd->ops = &davinci_wdt_ops;
+ wdd->min_timeout = 1;
+ wdd->max_timeout = MAX_HEARTBEAT;
+ wdd->timeout = DEFAULT_HEARTBEAT;
+
+ watchdog_init_timeout(wdd, heartbeat, dev);
+
+ dev_info(dev, "heartbeat %d sec\n", wdd->timeout);
+
+ watchdog_set_drvdata(wdd, davinci_wdt);
+ watchdog_set_nowayout(wdd, 1);
wdt_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (wdt_mem == NULL) {
- dev_err(dev, "failed to get memory region resource\n");
- return -ENOENT;
- }
-
- size = resource_size(wdt_mem);
- if (!request_mem_region(wdt_mem->start, size, pdev->name)) {
- dev_err(dev, "failed to get memory region\n");
- return -ENOENT;
- }
-
- wdt_base = ioremap(wdt_mem->start, size);
- if (!wdt_base) {
- dev_err(dev, "failed to map memory region\n");
- release_mem_region(wdt_mem->start, size);
- wdt_mem = NULL;
- return -ENOMEM;
- }
-
- ret = misc_register(&davinci_wdt_miscdev);
- if (ret < 0) {
- dev_err(dev, "cannot register misc device\n");
- release_mem_region(wdt_mem->start, size);
- wdt_mem = NULL;
- } else {
- set_bit(WDT_DEVICE_INITED, &wdt_status);
- }
-
- iounmap(wdt_base);
+ davinci_wdt->base = devm_ioremap_resource(dev, wdt_mem);
+ if (IS_ERR(davinci_wdt->base))
+ return PTR_ERR(davinci_wdt->base);
+
+ ret = watchdog_register_device(wdd);
+ if (ret < 0)
+ dev_err(dev, "cannot register watchdog device\n");
+
return ret;
}
-static int __devexit davinci_wdt_remove(struct platform_device *pdev)
+static int davinci_wdt_remove(struct platform_device *pdev)
{
- misc_deregister(&davinci_wdt_miscdev);
- if (wdt_mem) {
- release_mem_region(wdt_mem->start, resource_size(wdt_mem));
- wdt_mem = NULL;
- }
+ struct davinci_wdt_device *davinci_wdt = platform_get_drvdata(pdev);
- clk_disable(wdt_clk);
- clk_put(wdt_clk);
+ watchdog_unregister_device(&davinci_wdt->wdd);
+ clk_disable_unprepare(davinci_wdt->clk);
return 0;
}
+static const struct of_device_id davinci_wdt_of_match[] = {
+ { .compatible = "ti,davinci-wdt", },
+ {},
+};
+MODULE_DEVICE_TABLE(of, davinci_wdt_of_match);
+
static struct platform_driver platform_wdt_driver = {
.driver = {
- .name = "watchdog",
+ .name = "davinci-wdt",
.owner = THIS_MODULE,
+ .of_match_table = davinci_wdt_of_match,
},
.probe = davinci_wdt_probe,
- .remove = __devexit_p(davinci_wdt_remove),
+ .remove = davinci_wdt_remove,
};
module_platform_driver(platform_wdt_driver);
@@ -283,5 +237,4 @@ MODULE_PARM_DESC(heartbeat,
__MODULE_STRING(DEFAULT_HEARTBEAT));
MODULE_LICENSE("GPL");
-MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
-MODULE_ALIAS("platform:watchdog");
+MODULE_ALIAS("platform:davinci-wdt");
diff --git a/drivers/watchdog/diag288_wdt.c b/drivers/watchdog/diag288_wdt.c
new file mode 100644
index 00000000000..429494b6c82
--- /dev/null
+++ b/drivers/watchdog/diag288_wdt.c
@@ -0,0 +1,316 @@
+/*
+ * Watchdog driver for z/VM and LPAR using the diag 288 interface.
+ *
+ * Under z/VM, expiration of the watchdog will send a "system restart" command
+ * to CP.
+ *
+ * The command can be altered using the module parameter "cmd". This is
+ * not recommended because it's only supported on z/VM but not whith LPAR.
+ *
+ * On LPAR, the watchdog will always trigger a system restart. the module
+ * paramter cmd is meaningless here.
+ *
+ *
+ * Copyright IBM Corp. 2004, 2013
+ * Author(s): Arnd Bergmann (arndb@de.ibm.com)
+ * Philipp Hachtmann (phacht@de.ibm.com)
+ *
+ */
+
+#define KMSG_COMPONENT "diag288_wdt"
+#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
+
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/slab.h>
+#include <linux/miscdevice.h>
+#include <linux/watchdog.h>
+#include <linux/suspend.h>
+#include <asm/ebcdic.h>
+#include <linux/io.h>
+#include <linux/uaccess.h>
+
+#define MAX_CMDLEN 240
+#define DEFAULT_CMD "SYSTEM RESTART"
+
+#define MIN_INTERVAL 15 /* Minimal time supported by diag88 */
+#define MAX_INTERVAL 3600 /* One hour should be enough - pure estimation */
+
+#define WDT_DEFAULT_TIMEOUT 30
+
+/* Function codes - init, change, cancel */
+#define WDT_FUNC_INIT 0
+#define WDT_FUNC_CHANGE 1
+#define WDT_FUNC_CANCEL 2
+#define WDT_FUNC_CONCEAL 0x80000000
+
+/* Action codes for LPAR watchdog */
+#define LPARWDT_RESTART 0
+
+static char wdt_cmd[MAX_CMDLEN] = DEFAULT_CMD;
+static bool conceal_on;
+static bool nowayout_info = WATCHDOG_NOWAYOUT;
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Arnd Bergmann <arndb@de.ibm.com>");
+MODULE_AUTHOR("Philipp Hachtmann <phacht@de.ibm.com>");
+
+MODULE_DESCRIPTION("System z diag288 Watchdog Timer");
+
+module_param_string(cmd, wdt_cmd, MAX_CMDLEN, 0644);
+MODULE_PARM_DESC(cmd, "CP command that is run when the watchdog triggers (z/VM only)");
+
+module_param_named(conceal, conceal_on, bool, 0644);
+MODULE_PARM_DESC(conceal, "Enable the CONCEAL CP option while the watchdog is active (z/VM only)");
+
+module_param_named(nowayout, nowayout_info, bool, 0444);
+MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default = CONFIG_WATCHDOG_NOWAYOUT)");
+
+MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
+MODULE_ALIAS("vmwatchdog");
+
+static int __diag288(unsigned int func, unsigned int timeout,
+ unsigned long action, unsigned int len)
+{
+ register unsigned long __func asm("2") = func;
+ register unsigned long __timeout asm("3") = timeout;
+ register unsigned long __action asm("4") = action;
+ register unsigned long __len asm("5") = len;
+ int err;
+
+ err = -EINVAL;
+ asm volatile(
+ " diag %1, %3, 0x288\n"
+ "0: la %0, 0\n"
+ "1:\n"
+ EX_TABLE(0b, 1b)
+ : "+d" (err) : "d"(__func), "d"(__timeout),
+ "d"(__action), "d"(__len) : "1", "cc");
+ return err;
+}
+
+static int __diag288_vm(unsigned int func, unsigned int timeout,
+ char *cmd, size_t len)
+{
+ return __diag288(func, timeout, virt_to_phys(cmd), len);
+}
+
+static int __diag288_lpar(unsigned int func, unsigned int timeout,
+ unsigned long action)
+{
+ return __diag288(func, timeout, action, 0);
+}
+
+static int wdt_start(struct watchdog_device *dev)
+{
+ char *ebc_cmd;
+ size_t len;
+ int ret;
+ unsigned int func;
+
+ ret = -ENODEV;
+
+ if (MACHINE_IS_VM) {
+ ebc_cmd = kmalloc(MAX_CMDLEN, GFP_KERNEL);
+ if (!ebc_cmd)
+ return -ENOMEM;
+ len = strlcpy(ebc_cmd, wdt_cmd, MAX_CMDLEN);
+ ASCEBC(ebc_cmd, MAX_CMDLEN);
+ EBC_TOUPPER(ebc_cmd, MAX_CMDLEN);
+
+ func = conceal_on ? (WDT_FUNC_INIT | WDT_FUNC_CONCEAL)
+ : WDT_FUNC_INIT;
+ ret = __diag288_vm(func, dev->timeout, ebc_cmd, len);
+ WARN_ON(ret != 0);
+ kfree(ebc_cmd);
+ }
+
+ if (MACHINE_IS_LPAR) {
+ ret = __diag288_lpar(WDT_FUNC_INIT,
+ dev->timeout, LPARWDT_RESTART);
+ }
+
+ if (ret) {
+ pr_err("The watchdog cannot be activated\n");
+ return ret;
+ }
+ pr_info("The watchdog was activated\n");
+ return 0;
+}
+
+static int wdt_stop(struct watchdog_device *dev)
+{
+ int ret;
+
+ ret = __diag288(WDT_FUNC_CANCEL, 0, 0, 0);
+ pr_info("The watchdog was deactivated\n");
+ return ret;
+}
+
+static int wdt_ping(struct watchdog_device *dev)
+{
+ char *ebc_cmd;
+ size_t len;
+ int ret;
+ unsigned int func;
+
+ ret = -ENODEV;
+
+ if (MACHINE_IS_VM) {
+ ebc_cmd = kmalloc(MAX_CMDLEN, GFP_KERNEL);
+ if (!ebc_cmd)
+ return -ENOMEM;
+ len = strlcpy(ebc_cmd, wdt_cmd, MAX_CMDLEN);
+ ASCEBC(ebc_cmd, MAX_CMDLEN);
+ EBC_TOUPPER(ebc_cmd, MAX_CMDLEN);
+
+ /*
+ * It seems to be ok to z/VM to use the init function to
+ * retrigger the watchdog. On LPAR WDT_FUNC_CHANGE must
+ * be used when the watchdog is running.
+ */
+ func = conceal_on ? (WDT_FUNC_INIT | WDT_FUNC_CONCEAL)
+ : WDT_FUNC_INIT;
+
+ ret = __diag288_vm(func, dev->timeout, ebc_cmd, len);
+ WARN_ON(ret != 0);
+ kfree(ebc_cmd);
+ }
+
+ if (MACHINE_IS_LPAR)
+ ret = __diag288_lpar(WDT_FUNC_CHANGE, dev->timeout, 0);
+
+ if (ret)
+ pr_err("The watchdog timer cannot be started or reset\n");
+ return ret;
+}
+
+static int wdt_set_timeout(struct watchdog_device * dev, unsigned int new_to)
+{
+ dev->timeout = new_to;
+ return wdt_ping(dev);
+}
+
+static struct watchdog_ops wdt_ops = {
+ .owner = THIS_MODULE,
+ .start = wdt_start,
+ .stop = wdt_stop,
+ .ping = wdt_ping,
+ .set_timeout = wdt_set_timeout,
+};
+
+static struct watchdog_info wdt_info = {
+ .options = WDIOF_SETTIMEOUT | WDIOF_MAGICCLOSE,
+ .firmware_version = 0,
+ .identity = "z Watchdog",
+};
+
+static struct watchdog_device wdt_dev = {
+ .parent = NULL,
+ .info = &wdt_info,
+ .ops = &wdt_ops,
+ .bootstatus = 0,
+ .timeout = WDT_DEFAULT_TIMEOUT,
+ .min_timeout = MIN_INTERVAL,
+ .max_timeout = MAX_INTERVAL,
+};
+
+/*
+ * It makes no sense to go into suspend while the watchdog is running.
+ * Depending on the memory size, the watchdog might trigger, while we
+ * are still saving the memory.
+ * We reuse the open flag to ensure that suspend and watchdog open are
+ * exclusive operations
+ */
+static int wdt_suspend(void)
+{
+ if (test_and_set_bit(WDOG_DEV_OPEN, &wdt_dev.status)) {
+ pr_err("Linux cannot be suspended while the watchdog is in use\n");
+ return notifier_from_errno(-EBUSY);
+ }
+ if (test_bit(WDOG_ACTIVE, &wdt_dev.status)) {
+ clear_bit(WDOG_DEV_OPEN, &wdt_dev.status);
+ pr_err("Linux cannot be suspended while the watchdog is in use\n");
+ return notifier_from_errno(-EBUSY);
+ }
+ return NOTIFY_DONE;
+}
+
+static int wdt_resume(void)
+{
+ clear_bit(WDOG_DEV_OPEN, &wdt_dev.status);
+ return NOTIFY_DONE;
+}
+
+static int wdt_power_event(struct notifier_block *this, unsigned long event,
+ void *ptr)
+{
+ switch (event) {
+ case PM_POST_HIBERNATION:
+ case PM_POST_SUSPEND:
+ return wdt_resume();
+ case PM_HIBERNATION_PREPARE:
+ case PM_SUSPEND_PREPARE:
+ return wdt_suspend();
+ default:
+ return NOTIFY_DONE;
+ }
+}
+
+static struct notifier_block wdt_power_notifier = {
+ .notifier_call = wdt_power_event,
+};
+
+static int __init diag288_init(void)
+{
+ int ret;
+ char ebc_begin[] = {
+ 194, 197, 199, 201, 213
+ };
+
+ watchdog_set_nowayout(&wdt_dev, nowayout_info);
+
+ if (MACHINE_IS_VM) {
+ pr_info("The watchdog device driver detected a z/VM environment\n");
+ if (__diag288_vm(WDT_FUNC_INIT, 15,
+ ebc_begin, sizeof(ebc_begin)) != 0) {
+ pr_err("The watchdog cannot be initialized\n");
+ return -EINVAL;
+ }
+ } else if (MACHINE_IS_LPAR) {
+ pr_info("The watchdog device driver detected an LPAR environment\n");
+ if (__diag288_lpar(WDT_FUNC_INIT, 30, LPARWDT_RESTART)) {
+ pr_err("The watchdog cannot be initialized\n");
+ return -EINVAL;
+ }
+ } else {
+ pr_err("Linux runs in an environment that does not support the diag288 watchdog\n");
+ return -ENODEV;
+ }
+
+ if (__diag288_lpar(WDT_FUNC_CANCEL, 0, 0)) {
+ pr_err("The watchdog cannot be deactivated\n");
+ return -EINVAL;
+ }
+
+ ret = register_pm_notifier(&wdt_power_notifier);
+ if (ret)
+ return ret;
+
+ ret = watchdog_register_device(&wdt_dev);
+ if (ret)
+ unregister_pm_notifier(&wdt_power_notifier);
+
+ return ret;
+}
+
+static void __exit diag288_exit(void)
+{
+ watchdog_unregister_device(&wdt_dev);
+ unregister_pm_notifier(&wdt_power_notifier);
+}
+
+module_init(diag288_init);
+module_exit(diag288_exit);
diff --git a/drivers/watchdog/dw_wdt.c b/drivers/watchdog/dw_wdt.c
index 06de1211a44..ee4f86ba83e 100644
--- a/drivers/watchdog/dw_wdt.c
+++ b/drivers/watchdog/dw_wdt.c
@@ -8,7 +8,7 @@
* 2 of the License, or (at your option) any later version.
*
* This file implements a driver for the Synopsys DesignWare watchdog device
- * in the many ARM subsystems. The watchdog has 16 different timeout periods
+ * in the many subsystems. The watchdog has 16 different timeout periods
* and these are a function of the input clock frequency.
*
* The DesignWare watchdog cannot be stopped once it has been started so we
@@ -29,6 +29,7 @@
#include <linux/miscdevice.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
+#include <linux/of.h>
#include <linux/pm.h>
#include <linux/platform_device.h>
#include <linux/spinlock.h>
@@ -154,8 +155,8 @@ static int dw_wdt_open(struct inode *inode, struct file *filp)
return nonseekable_open(inode, filp);
}
-ssize_t dw_wdt_write(struct file *filp, const char __user *buf, size_t len,
- loff_t *offset)
+static ssize_t dw_wdt_write(struct file *filp, const char __user *buf,
+ size_t len, loff_t *offset)
{
if (!len)
return 0;
@@ -203,12 +204,12 @@ static long dw_wdt_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
switch (cmd) {
case WDIOC_GETSUPPORT:
- return copy_to_user((struct watchdog_info *)arg, &dw_wdt_ident,
+ return copy_to_user((void __user *)arg, &dw_wdt_ident,
sizeof(dw_wdt_ident)) ? -EFAULT : 0;
case WDIOC_GETSTATUS:
case WDIOC_GETBOOTSTATUS:
- return put_user(0, (int *)arg);
+ return put_user(0, (int __user *)arg);
case WDIOC_KEEPALIVE:
dw_wdt_set_next_heartbeat();
@@ -252,17 +253,17 @@ static int dw_wdt_release(struct inode *inode, struct file *filp)
return 0;
}
-#ifdef CONFIG_PM
+#ifdef CONFIG_PM_SLEEP
static int dw_wdt_suspend(struct device *dev)
{
- clk_disable(dw_wdt.clk);
+ clk_disable_unprepare(dw_wdt.clk);
return 0;
}
static int dw_wdt_resume(struct device *dev)
{
- int err = clk_enable(dw_wdt.clk);
+ int err = clk_prepare_enable(dw_wdt.clk);
if (err)
return err;
@@ -271,12 +272,9 @@ static int dw_wdt_resume(struct device *dev)
return 0;
}
+#endif /* CONFIG_PM_SLEEP */
-static const struct dev_pm_ops dw_wdt_pm_ops = {
- .suspend = dw_wdt_suspend,
- .resume = dw_wdt_resume,
-};
-#endif /* CONFIG_PM */
+static SIMPLE_DEV_PM_OPS(dw_wdt_pm_ops, dw_wdt_suspend, dw_wdt_resume);
static const struct file_operations wdt_fops = {
.owner = THIS_MODULE,
@@ -293,7 +291,7 @@ static struct miscdevice dw_wdt_miscdev = {
.minor = WATCHDOG_MINOR,
};
-static int __devinit dw_wdt_drv_probe(struct platform_device *pdev)
+static int dw_wdt_drv_probe(struct platform_device *pdev)
{
int ret;
struct resource *mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
@@ -301,17 +299,17 @@ static int __devinit dw_wdt_drv_probe(struct platform_device *pdev)
if (!mem)
return -EINVAL;
- dw_wdt.regs = devm_request_and_ioremap(&pdev->dev, mem);
- if (!dw_wdt.regs)
- return -ENOMEM;
+ dw_wdt.regs = devm_ioremap_resource(&pdev->dev, mem);
+ if (IS_ERR(dw_wdt.regs))
+ return PTR_ERR(dw_wdt.regs);
- dw_wdt.clk = clk_get(&pdev->dev, NULL);
+ dw_wdt.clk = devm_clk_get(&pdev->dev, NULL);
if (IS_ERR(dw_wdt.clk))
return PTR_ERR(dw_wdt.clk);
- ret = clk_enable(dw_wdt.clk);
+ ret = clk_prepare_enable(dw_wdt.clk);
if (ret)
- goto out_put_clk;
+ return ret;
spin_lock_init(&dw_wdt.lock);
@@ -326,32 +324,36 @@ static int __devinit dw_wdt_drv_probe(struct platform_device *pdev)
return 0;
out_disable_clk:
- clk_disable(dw_wdt.clk);
-out_put_clk:
- clk_put(dw_wdt.clk);
+ clk_disable_unprepare(dw_wdt.clk);
return ret;
}
-static int __devexit dw_wdt_drv_remove(struct platform_device *pdev)
+static int dw_wdt_drv_remove(struct platform_device *pdev)
{
misc_deregister(&dw_wdt_miscdev);
- clk_disable(dw_wdt.clk);
- clk_put(dw_wdt.clk);
+ clk_disable_unprepare(dw_wdt.clk);
return 0;
}
+#ifdef CONFIG_OF
+static const struct of_device_id dw_wdt_of_match[] = {
+ { .compatible = "snps,dw-wdt", },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, dw_wdt_of_match);
+#endif
+
static struct platform_driver dw_wdt_driver = {
.probe = dw_wdt_drv_probe,
- .remove = __devexit_p(dw_wdt_drv_remove),
+ .remove = dw_wdt_drv_remove,
.driver = {
.name = "dw_wdt",
.owner = THIS_MODULE,
-#ifdef CONFIG_PM
+ .of_match_table = of_match_ptr(dw_wdt_of_match),
.pm = &dw_wdt_pm_ops,
-#endif /* CONFIG_PM */
},
};
@@ -360,4 +362,3 @@ module_platform_driver(dw_wdt_driver);
MODULE_AUTHOR("Jamie Iles");
MODULE_DESCRIPTION("Synopsys DesignWare Watchdog Driver");
MODULE_LICENSE("GPL");
-MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
diff --git a/drivers/watchdog/ep93xx_wdt.c b/drivers/watchdog/ep93xx_wdt.c
index 77050037597..5f54e1e5819 100644
--- a/drivers/watchdog/ep93xx_wdt.c
+++ b/drivers/watchdog/ep93xx_wdt.c
@@ -28,7 +28,6 @@
#include <linux/platform_device.h>
#include <linux/module.h>
-#include <linux/miscdevice.h>
#include <linux/watchdog.h>
#include <linux/timer.h>
#include <linux/io.h>
@@ -112,23 +111,16 @@ static struct watchdog_device ep93xx_wdt_wdd = {
.ops = &ep93xx_wdt_ops,
};
-static int __devinit ep93xx_wdt_probe(struct platform_device *pdev)
+static int ep93xx_wdt_probe(struct platform_device *pdev)
{
struct resource *res;
unsigned long val;
int err;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (!res)
- return -ENXIO;
-
- if (!devm_request_mem_region(&pdev->dev, res->start,
- resource_size(res), pdev->name))
- return -EBUSY;
-
- mmio_base = devm_ioremap(&pdev->dev, res->start, resource_size(res));
- if (!mmio_base)
- return -ENXIO;
+ mmio_base = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(mmio_base))
+ return PTR_ERR(mmio_base);
if (timeout < 1 || timeout > 3600) {
timeout = WDT_TIMEOUT;
@@ -156,7 +148,7 @@ static int __devinit ep93xx_wdt_probe(struct platform_device *pdev)
return 0;
}
-static int __devexit ep93xx_wdt_remove(struct platform_device *pdev)
+static int ep93xx_wdt_remove(struct platform_device *pdev)
{
watchdog_unregister_device(&ep93xx_wdt_wdd);
return 0;
@@ -168,15 +160,14 @@ static struct platform_driver ep93xx_wdt_driver = {
.name = "ep93xx-wdt",
},
.probe = ep93xx_wdt_probe,
- .remove = __devexit_p(ep93xx_wdt_remove),
+ .remove = ep93xx_wdt_remove,
};
module_platform_driver(ep93xx_wdt_driver);
-MODULE_AUTHOR("Ray Lehtiniemi <rayl@mail.com>,"
- "Alessandro Zummo <a.zummo@towertech.it>,"
- "H Hartley Sweeten <hsweeten@visionengravers.com>");
+MODULE_AUTHOR("Ray Lehtiniemi <rayl@mail.com>");
+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);
-MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
diff --git a/drivers/watchdog/eurotechwdt.c b/drivers/watchdog/eurotechwdt.c
index cd31b8a2a72..23ee53240c4 100644
--- a/drivers/watchdog/eurotechwdt.c
+++ b/drivers/watchdog/eurotechwdt.c
@@ -477,4 +477,3 @@ module_exit(eurwdt_exit);
MODULE_AUTHOR("Rodolfo Giometti");
MODULE_DESCRIPTION("Driver for Eurotech CPU-1220/1410 on board watchdog");
MODULE_LICENSE("GPL");
-MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
diff --git a/drivers/watchdog/f71808e_wdt.c b/drivers/watchdog/f71808e_wdt.c
index c65b0a5a020..016bd935519 100644
--- a/drivers/watchdog/f71808e_wdt.c
+++ b/drivers/watchdog/f71808e_wdt.c
@@ -56,6 +56,7 @@
#define SIO_F71858_ID 0x0507 /* Chipset ID */
#define SIO_F71862_ID 0x0601 /* Chipset ID */
#define SIO_F71869_ID 0x0814 /* Chipset ID */
+#define SIO_F71869A_ID 0x1007 /* Chipset ID */
#define SIO_F71882_ID 0x0541 /* Chipset ID */
#define SIO_F71889_ID 0x0723 /* Chipset ID */
@@ -195,7 +196,7 @@ static inline int superio_enter(int base)
return -EBUSY;
}
- /* according to the datasheet the key must be send twice! */
+ /* according to the datasheet the key must be sent twice! */
outb(SIO_UNLOCK_KEY, base);
outb(SIO_UNLOCK_KEY, base);
@@ -756,6 +757,7 @@ static int __init f71808e_find(int sioaddr)
err = f71862fg_pin_configure(0); /* validate module parameter */
break;
case SIO_F71869_ID:
+ case SIO_F71869A_ID:
watchdog.type = f71869;
break;
case SIO_F71882_ID:
diff --git a/drivers/watchdog/gef_wdt.c b/drivers/watchdog/gef_wdt.c
index 17f4cae770c..25beb30878d 100644
--- a/drivers/watchdog/gef_wdt.c
+++ b/drivers/watchdog/gef_wdt.c
@@ -34,6 +34,7 @@
#include <linux/watchdog.h>
#include <linux/fs.h>
#include <linux/of.h>
+#include <linux/of_address.h>
#include <linux/of_platform.h>
#include <linux/io.h>
#include <linux/uaccess.h>
@@ -262,7 +263,7 @@ static struct miscdevice gef_wdt_miscdev = {
};
-static int __devinit gef_wdt_probe(struct platform_device *dev)
+static int gef_wdt_probe(struct platform_device *dev)
{
int timeout = 10;
u32 freq;
@@ -285,7 +286,7 @@ static int __devinit gef_wdt_probe(struct platform_device *dev)
return misc_register(&gef_wdt_miscdev);
}
-static int __devexit gef_wdt_remove(struct platform_device *dev)
+static int gef_wdt_remove(struct platform_device *dev)
{
misc_deregister(&gef_wdt_miscdev);
@@ -310,6 +311,7 @@ static struct platform_driver gef_wdt_driver = {
.of_match_table = gef_wdt_ids,
},
.probe = gef_wdt_probe,
+ .remove = gef_wdt_remove,
};
static int __init gef_wdt_init(void)
@@ -329,5 +331,4 @@ module_exit(gef_wdt_exit);
MODULE_AUTHOR("Martyn Welch <martyn.welch@ge.com>");
MODULE_DESCRIPTION("GE watchdog driver");
MODULE_LICENSE("GPL");
-MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
MODULE_ALIAS("platform:gef_wdt");
diff --git a/drivers/watchdog/geodewdt.c b/drivers/watchdog/geodewdt.c
index dc563b680ab..4c43e3fa8bd 100644
--- a/drivers/watchdog/geodewdt.c
+++ b/drivers/watchdog/geodewdt.c
@@ -215,7 +215,7 @@ static struct miscdevice geodewdt_miscdev = {
.fops = &geodewdt_fops,
};
-static int __devinit geodewdt_probe(struct platform_device *dev)
+static int __init geodewdt_probe(struct platform_device *dev)
{
int ret;
@@ -243,7 +243,7 @@ static int __devinit geodewdt_probe(struct platform_device *dev)
return ret;
}
-static int __devexit geodewdt_remove(struct platform_device *dev)
+static int geodewdt_remove(struct platform_device *dev)
{
misc_deregister(&geodewdt_miscdev);
return 0;
@@ -255,8 +255,7 @@ static void geodewdt_shutdown(struct platform_device *dev)
}
static struct platform_driver geodewdt_driver = {
- .probe = geodewdt_probe,
- .remove = __devexit_p(geodewdt_remove),
+ .remove = geodewdt_remove,
.shutdown = geodewdt_shutdown,
.driver = {
.owner = THIS_MODULE,
@@ -268,20 +267,18 @@ static int __init geodewdt_init(void)
{
int ret;
- ret = platform_driver_register(&geodewdt_driver);
- if (ret)
- return ret;
-
geodewdt_platform_device = platform_device_register_simple(DRV_NAME,
-1, NULL, 0);
- if (IS_ERR(geodewdt_platform_device)) {
- ret = PTR_ERR(geodewdt_platform_device);
+ if (IS_ERR(geodewdt_platform_device))
+ return PTR_ERR(geodewdt_platform_device);
+
+ ret = platform_driver_probe(&geodewdt_driver, geodewdt_probe);
+ if (ret)
goto err;
- }
return 0;
err:
- platform_driver_unregister(&geodewdt_driver);
+ platform_device_unregister(geodewdt_platform_device);
return ret;
}
@@ -297,4 +294,3 @@ module_exit(geodewdt_exit);
MODULE_AUTHOR("Advanced Micro Devices, Inc");
MODULE_DESCRIPTION("Geode GX/LX Watchdog Driver");
MODULE_LICENSE("GPL");
-MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
diff --git a/drivers/watchdog/gpio_wdt.c b/drivers/watchdog/gpio_wdt.c
new file mode 100644
index 00000000000..220a9e07cfd
--- /dev/null
+++ b/drivers/watchdog/gpio_wdt.c
@@ -0,0 +1,254 @@
+/*
+ * Driver for watchdog device controlled through GPIO-line
+ *
+ * Author: 2013, Alexander Shiyan <shc_work@mail.ru>
+ *
+ * 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.
+ */
+
+#include <linux/err.h>
+#include <linux/delay.h>
+#include <linux/module.h>
+#include <linux/notifier.h>
+#include <linux/of_gpio.h>
+#include <linux/platform_device.h>
+#include <linux/reboot.h>
+#include <linux/watchdog.h>
+
+#define SOFT_TIMEOUT_MIN 1
+#define SOFT_TIMEOUT_DEF 60
+#define SOFT_TIMEOUT_MAX 0xffff
+
+enum {
+ HW_ALGO_TOGGLE,
+ HW_ALGO_LEVEL,
+};
+
+struct gpio_wdt_priv {
+ int gpio;
+ bool active_low;
+ bool state;
+ unsigned int hw_algo;
+ unsigned int hw_margin;
+ unsigned long last_jiffies;
+ struct notifier_block notifier;
+ struct timer_list timer;
+ struct watchdog_device wdd;
+};
+
+static void gpio_wdt_disable(struct gpio_wdt_priv *priv)
+{
+ gpio_set_value_cansleep(priv->gpio, !priv->active_low);
+
+ /* Put GPIO back to tristate */
+ if (priv->hw_algo == HW_ALGO_TOGGLE)
+ gpio_direction_input(priv->gpio);
+}
+
+static int gpio_wdt_start(struct watchdog_device *wdd)
+{
+ struct gpio_wdt_priv *priv = watchdog_get_drvdata(wdd);
+
+ priv->state = priv->active_low;
+ gpio_direction_output(priv->gpio, priv->state);
+ priv->last_jiffies = jiffies;
+ mod_timer(&priv->timer, priv->last_jiffies + priv->hw_margin);
+
+ return 0;
+}
+
+static int gpio_wdt_stop(struct watchdog_device *wdd)
+{
+ struct gpio_wdt_priv *priv = watchdog_get_drvdata(wdd);
+
+ mod_timer(&priv->timer, 0);
+ gpio_wdt_disable(priv);
+
+ return 0;
+}
+
+static int gpio_wdt_ping(struct watchdog_device *wdd)
+{
+ struct gpio_wdt_priv *priv = watchdog_get_drvdata(wdd);
+
+ priv->last_jiffies = jiffies;
+
+ return 0;
+}
+
+static int gpio_wdt_set_timeout(struct watchdog_device *wdd, unsigned int t)
+{
+ wdd->timeout = t;
+
+ return gpio_wdt_ping(wdd);
+}
+
+static void gpio_wdt_hwping(unsigned long data)
+{
+ struct watchdog_device *wdd = (struct watchdog_device *)data;
+ struct gpio_wdt_priv *priv = watchdog_get_drvdata(wdd);
+
+ if (time_after(jiffies, priv->last_jiffies +
+ msecs_to_jiffies(wdd->timeout * 1000))) {
+ dev_crit(wdd->dev, "Timer expired. System will reboot soon!\n");
+ return;
+ }
+
+ /* Restart timer */
+ mod_timer(&priv->timer, jiffies + priv->hw_margin);
+
+ switch (priv->hw_algo) {
+ case HW_ALGO_TOGGLE:
+ /* Toggle output pin */
+ priv->state = !priv->state;
+ gpio_set_value_cansleep(priv->gpio, priv->state);
+ break;
+ case HW_ALGO_LEVEL:
+ /* Pulse */
+ gpio_set_value_cansleep(priv->gpio, !priv->active_low);
+ udelay(1);
+ gpio_set_value_cansleep(priv->gpio, priv->active_low);
+ break;
+ }
+}
+
+static int gpio_wdt_notify_sys(struct notifier_block *nb, unsigned long code,
+ void *unused)
+{
+ struct gpio_wdt_priv *priv = container_of(nb, struct gpio_wdt_priv,
+ notifier);
+
+ mod_timer(&priv->timer, 0);
+
+ switch (code) {
+ case SYS_HALT:
+ case SYS_POWER_OFF:
+ gpio_wdt_disable(priv);
+ break;
+ default:
+ break;
+ }
+
+ return NOTIFY_DONE;
+}
+
+static const struct watchdog_info gpio_wdt_ident = {
+ .options = WDIOF_MAGICCLOSE | WDIOF_KEEPALIVEPING |
+ WDIOF_SETTIMEOUT,
+ .identity = "GPIO Watchdog",
+};
+
+static const struct watchdog_ops gpio_wdt_ops = {
+ .owner = THIS_MODULE,
+ .start = gpio_wdt_start,
+ .stop = gpio_wdt_stop,
+ .ping = gpio_wdt_ping,
+ .set_timeout = gpio_wdt_set_timeout,
+};
+
+static int gpio_wdt_probe(struct platform_device *pdev)
+{
+ struct gpio_wdt_priv *priv;
+ enum of_gpio_flags flags;
+ unsigned int hw_margin;
+ unsigned long f = 0;
+ const char *algo;
+ int ret;
+
+ priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ priv->gpio = of_get_gpio_flags(pdev->dev.of_node, 0, &flags);
+ if (!gpio_is_valid(priv->gpio))
+ return priv->gpio;
+
+ priv->active_low = flags & OF_GPIO_ACTIVE_LOW;
+
+ ret = of_property_read_string(pdev->dev.of_node, "hw_algo", &algo);
+ if (ret)
+ return ret;
+ if (!strncmp(algo, "toggle", 6)) {
+ priv->hw_algo = HW_ALGO_TOGGLE;
+ f = GPIOF_IN;
+ } else if (!strncmp(algo, "level", 5)) {
+ priv->hw_algo = HW_ALGO_LEVEL;
+ f = priv->active_low ? GPIOF_OUT_INIT_HIGH : GPIOF_OUT_INIT_LOW;
+ } else {
+ return -EINVAL;
+ }
+
+ ret = devm_gpio_request_one(&pdev->dev, priv->gpio, f,
+ dev_name(&pdev->dev));
+ if (ret)
+ return ret;
+
+ ret = of_property_read_u32(pdev->dev.of_node,
+ "hw_margin_ms", &hw_margin);
+ if (ret)
+ return ret;
+ /* Disallow values lower than 2 and higher than 65535 ms */
+ if (hw_margin < 2 || hw_margin > 65535)
+ return -EINVAL;
+
+ /* Use safe value (1/2 of real timeout) */
+ priv->hw_margin = msecs_to_jiffies(hw_margin / 2);
+
+ watchdog_set_drvdata(&priv->wdd, priv);
+
+ priv->wdd.info = &gpio_wdt_ident;
+ priv->wdd.ops = &gpio_wdt_ops;
+ priv->wdd.min_timeout = SOFT_TIMEOUT_MIN;
+ priv->wdd.max_timeout = SOFT_TIMEOUT_MAX;
+
+ if (watchdog_init_timeout(&priv->wdd, 0, &pdev->dev) < 0)
+ priv->wdd.timeout = SOFT_TIMEOUT_DEF;
+
+ setup_timer(&priv->timer, gpio_wdt_hwping, (unsigned long)&priv->wdd);
+
+ ret = watchdog_register_device(&priv->wdd);
+ if (ret)
+ return ret;
+
+ priv->notifier.notifier_call = gpio_wdt_notify_sys;
+ ret = register_reboot_notifier(&priv->notifier);
+ if (ret)
+ watchdog_unregister_device(&priv->wdd);
+
+ return ret;
+}
+
+static int gpio_wdt_remove(struct platform_device *pdev)
+{
+ struct gpio_wdt_priv *priv = platform_get_drvdata(pdev);
+
+ del_timer_sync(&priv->timer);
+ unregister_reboot_notifier(&priv->notifier);
+ watchdog_unregister_device(&priv->wdd);
+
+ return 0;
+}
+
+static const struct of_device_id gpio_wdt_dt_ids[] = {
+ { .compatible = "linux,wdt-gpio", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, gpio_wdt_dt_ids);
+
+static struct platform_driver gpio_wdt_driver = {
+ .driver = {
+ .name = "gpio-wdt",
+ .owner = THIS_MODULE,
+ .of_match_table = gpio_wdt_dt_ids,
+ },
+ .probe = gpio_wdt_probe,
+ .remove = gpio_wdt_remove,
+};
+module_platform_driver(gpio_wdt_driver);
+
+MODULE_AUTHOR("Alexander Shiyan <shc_work@mail.ru>");
+MODULE_DESCRIPTION("GPIO Watchdog");
+MODULE_LICENSE("GPL");
diff --git a/drivers/watchdog/hpwdt.c b/drivers/watchdog/hpwdt.c
index cbc7ceef278..75d2243b94f 100644
--- a/drivers/watchdog/hpwdt.c
+++ b/drivers/watchdog/hpwdt.c
@@ -17,7 +17,6 @@
#include <linux/device.h>
#include <linux/fs.h>
-#include <linux/init.h>
#include <linux/io.h>
#include <linux/bitops.h>
#include <linux/kernel.h>
@@ -39,7 +38,7 @@
#endif /* CONFIG_HPWDT_NMI_DECODING */
#include <asm/nmi.h>
-#define HPWDT_VERSION "1.3.0"
+#define HPWDT_VERSION "1.3.3"
#define SECS_TO_TICKS(secs) ((secs) * 1000 / 128)
#define TICKS_TO_SECS(ticks) ((ticks) * 128 / 1000)
#define HPWDT_MAX_TIMER TICKS_TO_SECS(65535)
@@ -55,7 +54,7 @@ static void __iomem *pci_mem_addr; /* the PCI-memory address */
static unsigned long __iomem *hpwdt_timer_reg;
static unsigned long __iomem *hpwdt_timer_con;
-static DEFINE_PCI_DEVICE_TABLE(hpwdt_devices) = {
+static const struct pci_device_id hpwdt_devices[] = {
{ PCI_DEVICE(PCI_VENDOR_ID_COMPAQ, 0xB203) }, /* iLO2 */
{ PCI_DEVICE(PCI_VENDOR_ID_HP, 0x3306) }, /* iLO3 */
{0}, /* terminate list */
@@ -146,9 +145,9 @@ struct cmn_registers {
} __attribute__((packed));
static unsigned int hpwdt_nmi_decoding;
-static unsigned int allow_kdump;
-static unsigned int priority; /* hpwdt at end of die_notify list */
+static unsigned int allow_kdump = 1;
static unsigned int is_icru;
+static unsigned int is_uefi;
static DEFINE_SPINLOCK(rom_lock);
static void *cru_rom_addr;
static struct cmn_registers cmn_regs;
@@ -162,7 +161,8 @@ extern asmlinkage void asminline_call(struct cmn_registers *pi86Regs,
#define HPWDT_ARCH 32
asm(".text \n\t"
- ".align 4 \n"
+ ".align 4 \n\t"
+ ".globl asminline_call \n"
"asminline_call: \n\t"
"pushl %ebp \n\t"
"movl %esp, %ebp \n\t"
@@ -213,7 +213,7 @@ asm(".text \n\t"
* 0 : SUCCESS
* <0 : FAILURE
*/
-static int __devinit cru_detect(unsigned long map_entry,
+static int cru_detect(unsigned long map_entry,
unsigned long map_offset)
{
void *bios32_map;
@@ -269,7 +269,7 @@ static int __devinit cru_detect(unsigned long map_entry,
/*
* bios_checksum
*/
-static int __devinit bios_checksum(const char __iomem *ptr, int len)
+static int bios_checksum(const char __iomem *ptr, int len)
{
char sum = 0;
int i;
@@ -294,7 +294,7 @@ static int __devinit bios_checksum(const char __iomem *ptr, int len)
* 0 : SUCCESS
* <0 : FAILURE
*/
-static int __devinit bios32_present(const char __iomem *p)
+static int bios32_present(const char __iomem *p)
{
struct bios32_service_dir *bios_32_ptr;
int length;
@@ -324,7 +324,7 @@ static int __devinit bios32_present(const char __iomem *p)
return -ENODEV;
}
-static int __devinit detect_cru_service(void)
+static int detect_cru_service(void)
{
char __iomem *p, *q;
int rc = -1;
@@ -352,7 +352,8 @@ static int __devinit detect_cru_service(void)
#define HPWDT_ARCH 64
asm(".text \n\t"
- ".align 4 \n"
+ ".align 4 \n\t"
+ ".globl asminline_call \n"
"asminline_call: \n\t"
"pushq %rbp \n\t"
"movq %rsp, %rbp \n\t"
@@ -396,7 +397,7 @@ asm(".text \n\t"
* This function checks whether or not a SMBIOS/DMI record is
* the 64bit CRU info or not
*/
-static void __devinit dmi_find_cru(const struct dmi_header *dm, void *dummy)
+static void dmi_find_cru(const struct dmi_header *dm, void *dummy)
{
struct smbios_cru64_info *smbios_cru64_ptr;
unsigned long cru_physical_address;
@@ -415,7 +416,7 @@ static void __devinit dmi_find_cru(const struct dmi_header *dm, void *dummy)
}
}
-static int __devinit detect_cru_service(void)
+static int detect_cru_service(void)
{
cru_rom_addr = NULL;
@@ -435,16 +436,16 @@ static void hpwdt_start(void)
{
reload = SECS_TO_TICKS(soft_margin);
iowrite16(reload, hpwdt_timer_reg);
- iowrite16(0x85, hpwdt_timer_con);
+ iowrite8(0x85, hpwdt_timer_con);
}
static void hpwdt_stop(void)
{
unsigned long data;
- data = ioread16(hpwdt_timer_con);
+ data = ioread8(hpwdt_timer_con);
data &= 0xFE;
- iowrite16(data, hpwdt_timer_con);
+ iowrite8(data, hpwdt_timer_con);
}
static void hpwdt_ping(void)
@@ -485,7 +486,7 @@ static int hpwdt_pretimeout(unsigned int ulReason, struct pt_regs *regs)
goto out;
spin_lock_irqsave(&rom_lock, rom_pl);
- if (!die_nmi_called && !is_icru)
+ if (!die_nmi_called && !is_icru && !is_uefi)
asminline_call(&cmn_regs, cru_rom_addr);
die_nmi_called = 1;
spin_unlock_irqrestore(&rom_lock, rom_pl);
@@ -493,14 +494,19 @@ static int hpwdt_pretimeout(unsigned int ulReason, struct pt_regs *regs)
if (allow_kdump)
hpwdt_stop();
- if (!is_icru) {
+ if (!is_icru && !is_uefi) {
if (cmn_regs.u1.ral == 0) {
panic("An NMI occurred, "
"but unable to determine source.\n");
}
}
- panic("An NMI occurred, please see the Integrated "
- "Management Log for details.\n");
+ panic("An NMI occurred. Depending on your system the reason "
+ "for the NMI is logged in any one of the following "
+ "resources:\n"
+ "1. Integrated Management Log (IML)\n"
+ "2. OA Syslog\n"
+ "3. OA Forward Progress Log\n"
+ "4. iLO Event Log");
out:
return NMI_DONE;
@@ -648,7 +654,7 @@ static struct miscdevice hpwdt_miscdev = {
#ifdef CONFIG_HPWDT_NMI_DECODING
#ifdef CONFIG_X86_LOCAL_APIC
-static void __devinit hpwdt_check_nmi_decoding(struct pci_dev *dev)
+static void hpwdt_check_nmi_decoding(struct pci_dev *dev)
{
/*
* If nmi_watchdog is turned off then we can turn on
@@ -657,7 +663,7 @@ static void __devinit hpwdt_check_nmi_decoding(struct pci_dev *dev)
hpwdt_nmi_decoding = 1;
}
#else
-static void __devinit hpwdt_check_nmi_decoding(struct pci_dev *dev)
+static void hpwdt_check_nmi_decoding(struct pci_dev *dev)
{
dev_warn(&dev->dev, "NMI decoding is disabled. "
"Your kernel does not support a NMI Watchdog.\n");
@@ -672,7 +678,7 @@ static void __devinit hpwdt_check_nmi_decoding(struct pci_dev *dev)
* This check is independent of architecture and needs to be made for
* any ProLiant system.
*/
-static void __devinit dmi_find_icru(const struct dmi_header *dm, void *dummy)
+static void dmi_find_icru(const struct dmi_header *dm, void *dummy)
{
struct smbios_proliant_info *smbios_proliant_ptr;
@@ -680,10 +686,12 @@ static void __devinit dmi_find_icru(const struct dmi_header *dm, void *dummy)
smbios_proliant_ptr = (struct smbios_proliant_info *) dm;
if (smbios_proliant_ptr->misc_features & 0x01)
is_icru = 1;
+ if (smbios_proliant_ptr->misc_features & 0x408)
+ is_uefi = 1;
}
}
-static int __devinit hpwdt_init_nmi_decoding(struct pci_dev *dev)
+static int hpwdt_init_nmi_decoding(struct pci_dev *dev)
{
int retval;
@@ -698,7 +706,7 @@ static int __devinit hpwdt_init_nmi_decoding(struct pci_dev *dev)
* the old cru detect code.
*/
dmi_walk(dmi_find_icru, NULL);
- if (!is_icru) {
+ if (!is_icru && !is_uefi) {
/*
* We need to map the ROM to get the CRU service.
@@ -723,42 +731,51 @@ static int __devinit hpwdt_init_nmi_decoding(struct pci_dev *dev)
}
/*
- * If the priority is set to 1, then we will be put first on the
- * die notify list to handle a critical NMI. The default is to
- * be last so other users of the NMI signal can function.
+ * Only one function can register for NMI_UNKNOWN
*/
- retval = register_nmi_handler(NMI_UNKNOWN, hpwdt_pretimeout,
- (priority) ? NMI_FLAG_FIRST : 0,
- "hpwdt");
- if (retval != 0) {
- dev_warn(&dev->dev,
- "Unable to register a die notifier (err=%d).\n",
- retval);
- if (cru_rom_addr)
- iounmap(cru_rom_addr);
- }
+ retval = register_nmi_handler(NMI_UNKNOWN, hpwdt_pretimeout, 0, "hpwdt");
+ if (retval)
+ goto error;
+ retval = register_nmi_handler(NMI_SERR, hpwdt_pretimeout, 0, "hpwdt");
+ if (retval)
+ goto error1;
+ retval = register_nmi_handler(NMI_IO_CHECK, hpwdt_pretimeout, 0, "hpwdt");
+ if (retval)
+ goto error2;
dev_info(&dev->dev,
"HP Watchdog Timer Driver: NMI decoding initialized"
- ", allow kernel dump: %s (default = 0/OFF)"
- ", priority: %s (default = 0/LAST).\n",
- (allow_kdump == 0) ? "OFF" : "ON",
- (priority == 0) ? "LAST" : "FIRST");
+ ", allow kernel dump: %s (default = 0/OFF)\n",
+ (allow_kdump == 0) ? "OFF" : "ON");
return 0;
+
+error2:
+ unregister_nmi_handler(NMI_SERR, "hpwdt");
+error1:
+ unregister_nmi_handler(NMI_UNKNOWN, "hpwdt");
+error:
+ dev_warn(&dev->dev,
+ "Unable to register a die notifier (err=%d).\n",
+ retval);
+ if (cru_rom_addr)
+ iounmap(cru_rom_addr);
+ return retval;
}
static void hpwdt_exit_nmi_decoding(void)
{
unregister_nmi_handler(NMI_UNKNOWN, "hpwdt");
+ unregister_nmi_handler(NMI_SERR, "hpwdt");
+ unregister_nmi_handler(NMI_IO_CHECK, "hpwdt");
if (cru_rom_addr)
iounmap(cru_rom_addr);
}
#else /* !CONFIG_HPWDT_NMI_DECODING */
-static void __devinit hpwdt_check_nmi_decoding(struct pci_dev *dev)
+static void hpwdt_check_nmi_decoding(struct pci_dev *dev)
{
}
-static int __devinit hpwdt_init_nmi_decoding(struct pci_dev *dev)
+static int hpwdt_init_nmi_decoding(struct pci_dev *dev)
{
return 0;
}
@@ -768,7 +785,7 @@ static void hpwdt_exit_nmi_decoding(void)
}
#endif /* CONFIG_HPWDT_NMI_DECODING */
-static int __devinit hpwdt_init_one(struct pci_dev *dev,
+static int hpwdt_init_one(struct pci_dev *dev,
const struct pci_device_id *ent)
{
int retval;
@@ -789,6 +806,12 @@ static int __devinit hpwdt_init_one(struct pci_dev *dev,
return -ENODEV;
}
+ /*
+ * Ignore all auxilary iLO devices with the following PCI ID
+ */
+ if (dev->subsystem_device == 0x1979)
+ return -ENODEV;
+
if (pci_enable_device(dev)) {
dev_warn(&dev->dev,
"Not possible to enable PCI Device: 0x%x:0x%x.\n",
@@ -806,6 +829,9 @@ static int __devinit hpwdt_init_one(struct pci_dev *dev,
hpwdt_timer_reg = pci_mem_addr + 0x70;
hpwdt_timer_con = pci_mem_addr + 0x72;
+ /* Make sure that timer is disabled until /dev/watchdog is opened */
+ hpwdt_stop();
+
/* Make sure that we have a valid soft_margin */
if (hpwdt_change_timer(soft_margin))
hpwdt_change_timer(DEFAULT_MARGIN);
@@ -837,7 +863,7 @@ error_pci_iomap:
return retval;
}
-static void __devexit hpwdt_exit(struct pci_dev *dev)
+static void hpwdt_exit(struct pci_dev *dev)
{
if (!nowayout)
hpwdt_stop();
@@ -852,24 +878,13 @@ static struct pci_driver hpwdt_driver = {
.name = "hpwdt",
.id_table = hpwdt_devices,
.probe = hpwdt_init_one,
- .remove = __devexit_p(hpwdt_exit),
+ .remove = hpwdt_exit,
};
-static void __exit hpwdt_cleanup(void)
-{
- pci_unregister_driver(&hpwdt_driver);
-}
-
-static int __init hpwdt_init(void)
-{
- return pci_register_driver(&hpwdt_driver);
-}
-
MODULE_AUTHOR("Tom Mingarelli");
MODULE_DESCRIPTION("hp watchdog driver");
MODULE_LICENSE("GPL");
MODULE_VERSION(HPWDT_VERSION);
-MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
module_param(soft_margin, int, 0);
MODULE_PARM_DESC(soft_margin, "Watchdog timeout in seconds");
@@ -881,11 +896,6 @@ MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default="
#ifdef CONFIG_HPWDT_NMI_DECODING
module_param(allow_kdump, int, 0);
MODULE_PARM_DESC(allow_kdump, "Start a kernel dump after NMI occurs");
-
-module_param(priority, int, 0);
-MODULE_PARM_DESC(priority, "The hpwdt driver handles NMIs first or last"
- " (default = 0/Last)\n");
#endif /* !CONFIG_HPWDT_NMI_DECODING */
-module_init(hpwdt_init);
-module_exit(hpwdt_cleanup);
+module_pci_driver(hpwdt_driver);
diff --git a/drivers/watchdog/i6300esb.c b/drivers/watchdog/i6300esb.c
index 738032a36bc..d7befd58b39 100644
--- a/drivers/watchdog/i6300esb.c
+++ b/drivers/watchdog/i6300esb.c
@@ -36,7 +36,6 @@
#include <linux/mm.h>
#include <linux/miscdevice.h>
#include <linux/watchdog.h>
-#include <linux/init.h>
#include <linux/pci.h>
#include <linux/ioport.h>
#include <linux/uaccess.h>
@@ -334,7 +333,7 @@ static struct miscdevice esb_miscdev = {
/*
* Data for PCI driver interface
*/
-static DEFINE_PCI_DEVICE_TABLE(esb_pci_tbl) = {
+static const struct pci_device_id esb_pci_tbl[] = {
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ESB_9), },
{ 0, }, /* End of list */
};
@@ -344,7 +343,7 @@ MODULE_DEVICE_TABLE(pci, esb_pci_tbl);
* Init & exit routines
*/
-static unsigned char __devinit esb_getdevice(struct pci_dev *pdev)
+static unsigned char esb_getdevice(struct pci_dev *pdev)
{
if (pci_enable_device(pdev)) {
pr_err("failed to enable device\n");
@@ -375,7 +374,7 @@ err_devput:
return 0;
}
-static void __devinit esb_initdevice(void)
+static void esb_initdevice(void)
{
u8 val1;
u16 val2;
@@ -416,7 +415,7 @@ static void __devinit esb_initdevice(void)
esb_timer_set_heartbeat(heartbeat);
}
-static int __devinit esb_probe(struct pci_dev *pdev,
+static int esb_probe(struct pci_dev *pdev,
const struct pci_device_id *ent)
{
int ret;
@@ -465,7 +464,7 @@ err_unmap:
return ret;
}
-static void __devexit esb_remove(struct pci_dev *pdev)
+static void esb_remove(struct pci_dev *pdev)
{
/* Stop the timer before we leave */
if (!nowayout)
@@ -488,25 +487,12 @@ static struct pci_driver esb_driver = {
.name = ESB_MODULE_NAME,
.id_table = esb_pci_tbl,
.probe = esb_probe,
- .remove = __devexit_p(esb_remove),
+ .remove = esb_remove,
.shutdown = esb_shutdown,
};
-static int __init watchdog_init(void)
-{
- return pci_register_driver(&esb_driver);
-}
-
-static void __exit watchdog_cleanup(void)
-{
- pci_unregister_driver(&esb_driver);
- pr_info("Watchdog Module Unloaded\n");
-}
-
-module_init(watchdog_init);
-module_exit(watchdog_cleanup);
+module_pci_driver(esb_driver);
MODULE_AUTHOR("Ross Biro and David Härdeman");
MODULE_DESCRIPTION("Watchdog driver for Intel 6300ESB chipsets");
MODULE_LICENSE("GPL");
-MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
diff --git a/drivers/watchdog/iTCO_vendor.h b/drivers/watchdog/iTCO_vendor.h
index 9e27e6422f6..3c57b45537a 100644
--- a/drivers/watchdog/iTCO_vendor.h
+++ b/drivers/watchdog/iTCO_vendor.h
@@ -1,8 +1,8 @@
/* iTCO Vendor Specific Support hooks */
#ifdef CONFIG_ITCO_VENDOR_SUPPORT
-extern void iTCO_vendor_pre_start(unsigned long, unsigned int);
-extern void iTCO_vendor_pre_stop(unsigned long);
-extern void iTCO_vendor_pre_keepalive(unsigned long, unsigned int);
+extern void iTCO_vendor_pre_start(struct resource *, unsigned int);
+extern void iTCO_vendor_pre_stop(struct resource *);
+extern void iTCO_vendor_pre_keepalive(struct resource *, unsigned int);
extern void iTCO_vendor_pre_set_heartbeat(unsigned int);
extern int iTCO_vendor_check_noreboot_on(void);
#else
diff --git a/drivers/watchdog/iTCO_vendor_support.c b/drivers/watchdog/iTCO_vendor_support.c
index 2721d29ce24..b6b2f90b5d4 100644
--- a/drivers/watchdog/iTCO_vendor_support.c
+++ b/drivers/watchdog/iTCO_vendor_support.c
@@ -35,11 +35,6 @@
#include "iTCO_vendor.h"
-/* iTCO defines */
-#define SMI_EN (acpibase + 0x30) /* SMI Control and Enable Register */
-#define TCOBASE (acpibase + 0x60) /* TCO base address */
-#define TCO1_STS (TCOBASE + 0x04) /* TCO1 Status Register */
-
/* List of vendor support modes */
/* SuperMicro Pentium 3 Era 370SSE+-OEM1/P3TSSE */
#define SUPERMICRO_OLD_BOARD 1
@@ -82,24 +77,24 @@ MODULE_PARM_DESC(vendorsupport, "iTCO vendor specific support mode, default="
* 20.6 seconds.
*/
-static void supermicro_old_pre_start(unsigned long acpibase)
+static void supermicro_old_pre_start(struct resource *smires)
{
unsigned long val32;
/* Bit 13: TCO_EN -> 0 = Disables TCO logic generating an SMI# */
- val32 = inl(SMI_EN);
+ val32 = inl(smires->start);
val32 &= 0xffffdfff; /* Turn off SMI clearing watchdog */
- outl(val32, SMI_EN); /* Needed to activate watchdog */
+ outl(val32, smires->start); /* Needed to activate watchdog */
}
-static void supermicro_old_pre_stop(unsigned long acpibase)
+static void supermicro_old_pre_stop(struct resource *smires)
{
unsigned long val32;
/* Bit 13: TCO_EN -> 1 = Enables the TCO logic to generate SMI# */
- val32 = inl(SMI_EN);
+ val32 = inl(smires->start);
val32 |= 0x00002000; /* Turn on SMI clearing watchdog */
- outl(val32, SMI_EN); /* Needed to deactivate watchdog */
+ outl(val32, smires->start); /* Needed to deactivate watchdog */
}
/*
@@ -270,66 +265,66 @@ static void supermicro_new_pre_set_heartbeat(unsigned int heartbeat)
* Don't use this fix if you don't need to!!!
*/
-static void broken_bios_start(unsigned long acpibase)
+static void broken_bios_start(struct resource *smires)
{
unsigned long val32;
- val32 = inl(SMI_EN);
+ val32 = inl(smires->start);
/* Bit 13: TCO_EN -> 0 = Disables TCO logic generating an SMI#
Bit 0: GBL_SMI_EN -> 0 = No SMI# will be generated by ICH. */
val32 &= 0xffffdffe;
- outl(val32, SMI_EN);
+ outl(val32, smires->start);
}
-static void broken_bios_stop(unsigned long acpibase)
+static void broken_bios_stop(struct resource *smires)
{
unsigned long val32;
- val32 = inl(SMI_EN);
+ val32 = inl(smires->start);
/* Bit 13: TCO_EN -> 1 = Enables TCO logic generating an SMI#
Bit 0: GBL_SMI_EN -> 1 = Turn global SMI on again. */
val32 |= 0x00002001;
- outl(val32, SMI_EN);
+ outl(val32, smires->start);
}
/*
* Generic Support Functions
*/
-void iTCO_vendor_pre_start(unsigned long acpibase,
+void iTCO_vendor_pre_start(struct resource *smires,
unsigned int heartbeat)
{
switch (vendorsupport) {
case SUPERMICRO_OLD_BOARD:
- supermicro_old_pre_start(acpibase);
+ supermicro_old_pre_start(smires);
break;
case SUPERMICRO_NEW_BOARD:
supermicro_new_pre_start(heartbeat);
break;
case BROKEN_BIOS:
- broken_bios_start(acpibase);
+ broken_bios_start(smires);
break;
}
}
EXPORT_SYMBOL(iTCO_vendor_pre_start);
-void iTCO_vendor_pre_stop(unsigned long acpibase)
+void iTCO_vendor_pre_stop(struct resource *smires)
{
switch (vendorsupport) {
case SUPERMICRO_OLD_BOARD:
- supermicro_old_pre_stop(acpibase);
+ supermicro_old_pre_stop(smires);
break;
case SUPERMICRO_NEW_BOARD:
supermicro_new_pre_stop();
break;
case BROKEN_BIOS:
- broken_bios_stop(acpibase);
+ broken_bios_stop(smires);
break;
}
}
EXPORT_SYMBOL(iTCO_vendor_pre_stop);
-void iTCO_vendor_pre_keepalive(unsigned long acpibase, unsigned int heartbeat)
+void iTCO_vendor_pre_keepalive(struct resource *smires, unsigned int heartbeat)
{
if (vendorsupport == SUPERMICRO_NEW_BOARD)
supermicro_new_pre_set_heartbeat(heartbeat);
diff --git a/drivers/watchdog/iTCO_wdt.c b/drivers/watchdog/iTCO_wdt.c
index 9fecb95645a..0ba1b7c9976 100644
--- a/drivers/watchdog/iTCO_wdt.c
+++ b/drivers/watchdog/iTCO_wdt.c
@@ -37,6 +37,7 @@
* document number TBD : DH89xxCC
* document number TBD : Panther Point
* document number TBD : Lynx Point
+ * document number TBD : Lynx Point-LP
*/
/*
@@ -47,7 +48,7 @@
/* Module and version information */
#define DRV_NAME "iTCO_wdt"
-#define DRV_VERSION "1.07"
+#define DRV_VERSION "1.11"
/* Includes */
#include <linux/module.h> /* For module specific items */
@@ -55,8 +56,6 @@
#include <linux/types.h> /* For standard types (like size_t) */
#include <linux/errno.h> /* For the -ENODEV/... values */
#include <linux/kernel.h> /* For printk/panic/... */
-#include <linux/miscdevice.h> /* For MODULE_ALIAS_MISCDEV
- (WATCHDOG_MINOR) */
#include <linux/watchdog.h> /* For the watchdog specific items */
#include <linux/init.h> /* For __init/__exit/... */
#include <linux/fs.h> /* For file operations */
@@ -66,316 +65,16 @@
#include <linux/spinlock.h> /* For spin_lock/spin_unlock/... */
#include <linux/uaccess.h> /* For copy_to_user/put_user/... */
#include <linux/io.h> /* For inb/outb/... */
+#include <linux/mfd/core.h>
+#include <linux/mfd/lpc_ich.h>
#include "iTCO_vendor.h"
-/* TCO related info */
-enum iTCO_chipsets {
- TCO_ICH = 0, /* ICH */
- TCO_ICH0, /* ICH0 */
- TCO_ICH2, /* ICH2 */
- TCO_ICH2M, /* ICH2-M */
- TCO_ICH3, /* ICH3-S */
- TCO_ICH3M, /* ICH3-M */
- TCO_ICH4, /* ICH4 */
- TCO_ICH4M, /* ICH4-M */
- TCO_CICH, /* C-ICH */
- TCO_ICH5, /* ICH5 & ICH5R */
- TCO_6300ESB, /* 6300ESB */
- TCO_ICH6, /* ICH6 & ICH6R */
- TCO_ICH6M, /* ICH6-M */
- TCO_ICH6W, /* ICH6W & ICH6RW */
- TCO_631XESB, /* 631xESB/632xESB */
- TCO_ICH7, /* ICH7 & ICH7R */
- TCO_ICH7DH, /* ICH7DH */
- TCO_ICH7M, /* ICH7-M & ICH7-U */
- TCO_ICH7MDH, /* ICH7-M DH */
- TCO_NM10, /* NM10 */
- TCO_ICH8, /* ICH8 & ICH8R */
- TCO_ICH8DH, /* ICH8DH */
- TCO_ICH8DO, /* ICH8DO */
- TCO_ICH8M, /* ICH8M */
- TCO_ICH8ME, /* ICH8M-E */
- TCO_ICH9, /* ICH9 */
- TCO_ICH9R, /* ICH9R */
- TCO_ICH9DH, /* ICH9DH */
- TCO_ICH9DO, /* ICH9DO */
- TCO_ICH9M, /* ICH9M */
- TCO_ICH9ME, /* ICH9M-E */
- TCO_ICH10, /* ICH10 */
- TCO_ICH10R, /* ICH10R */
- TCO_ICH10D, /* ICH10D */
- TCO_ICH10DO, /* ICH10DO */
- TCO_PCH, /* PCH Desktop Full Featured */
- TCO_PCHM, /* PCH Mobile Full Featured */
- TCO_P55, /* P55 */
- TCO_PM55, /* PM55 */
- TCO_H55, /* H55 */
- TCO_QM57, /* QM57 */
- TCO_H57, /* H57 */
- TCO_HM55, /* HM55 */
- TCO_Q57, /* Q57 */
- TCO_HM57, /* HM57 */
- TCO_PCHMSFF, /* PCH Mobile SFF Full Featured */
- TCO_QS57, /* QS57 */
- TCO_3400, /* 3400 */
- TCO_3420, /* 3420 */
- TCO_3450, /* 3450 */
- TCO_EP80579, /* EP80579 */
- TCO_CPT, /* Cougar Point */
- TCO_CPTD, /* Cougar Point Desktop */
- TCO_CPTM, /* Cougar Point Mobile */
- TCO_PBG, /* Patsburg */
- TCO_DH89XXCC, /* DH89xxCC */
- TCO_PPT, /* Panther Point */
- TCO_LPT, /* Lynx Point */
-};
-
-static struct {
- char *name;
- unsigned int iTCO_version;
-} iTCO_chipset_info[] __devinitdata = {
- {"ICH", 1},
- {"ICH0", 1},
- {"ICH2", 1},
- {"ICH2-M", 1},
- {"ICH3-S", 1},
- {"ICH3-M", 1},
- {"ICH4", 1},
- {"ICH4-M", 1},
- {"C-ICH", 1},
- {"ICH5 or ICH5R", 1},
- {"6300ESB", 1},
- {"ICH6 or ICH6R", 2},
- {"ICH6-M", 2},
- {"ICH6W or ICH6RW", 2},
- {"631xESB/632xESB", 2},
- {"ICH7 or ICH7R", 2},
- {"ICH7DH", 2},
- {"ICH7-M or ICH7-U", 2},
- {"ICH7-M DH", 2},
- {"NM10", 2},
- {"ICH8 or ICH8R", 2},
- {"ICH8DH", 2},
- {"ICH8DO", 2},
- {"ICH8M", 2},
- {"ICH8M-E", 2},
- {"ICH9", 2},
- {"ICH9R", 2},
- {"ICH9DH", 2},
- {"ICH9DO", 2},
- {"ICH9M", 2},
- {"ICH9M-E", 2},
- {"ICH10", 2},
- {"ICH10R", 2},
- {"ICH10D", 2},
- {"ICH10DO", 2},
- {"PCH Desktop Full Featured", 2},
- {"PCH Mobile Full Featured", 2},
- {"P55", 2},
- {"PM55", 2},
- {"H55", 2},
- {"QM57", 2},
- {"H57", 2},
- {"HM55", 2},
- {"Q57", 2},
- {"HM57", 2},
- {"PCH Mobile SFF Full Featured", 2},
- {"QS57", 2},
- {"3400", 2},
- {"3420", 2},
- {"3450", 2},
- {"EP80579", 2},
- {"Cougar Point", 2},
- {"Cougar Point Desktop", 2},
- {"Cougar Point Mobile", 2},
- {"Patsburg", 2},
- {"DH89xxCC", 2},
- {"Panther Point", 2},
- {"Lynx Point", 2},
- {NULL, 0}
-};
-
-/*
- * This data only exists for exporting the supported PCI ids
- * via MODULE_DEVICE_TABLE. We do not actually register a
- * pci_driver, because the I/O Controller Hub has also other
- * functions that probably will be registered by other drivers.
- */
-static DEFINE_PCI_DEVICE_TABLE(iTCO_wdt_pci_tbl) = {
- { PCI_VDEVICE(INTEL, 0x2410), TCO_ICH},
- { PCI_VDEVICE(INTEL, 0x2420), TCO_ICH0},
- { PCI_VDEVICE(INTEL, 0x2440), TCO_ICH2},
- { PCI_VDEVICE(INTEL, 0x244c), TCO_ICH2M},
- { PCI_VDEVICE(INTEL, 0x2480), TCO_ICH3},
- { PCI_VDEVICE(INTEL, 0x248c), TCO_ICH3M},
- { PCI_VDEVICE(INTEL, 0x24c0), TCO_ICH4},
- { PCI_VDEVICE(INTEL, 0x24cc), TCO_ICH4M},
- { PCI_VDEVICE(INTEL, 0x2450), TCO_CICH},
- { PCI_VDEVICE(INTEL, 0x24d0), TCO_ICH5},
- { PCI_VDEVICE(INTEL, 0x25a1), TCO_6300ESB},
- { PCI_VDEVICE(INTEL, 0x2640), TCO_ICH6},
- { PCI_VDEVICE(INTEL, 0x2641), TCO_ICH6M},
- { PCI_VDEVICE(INTEL, 0x2642), TCO_ICH6W},
- { PCI_VDEVICE(INTEL, 0x2670), TCO_631XESB},
- { PCI_VDEVICE(INTEL, 0x2671), TCO_631XESB},
- { PCI_VDEVICE(INTEL, 0x2672), TCO_631XESB},
- { PCI_VDEVICE(INTEL, 0x2673), TCO_631XESB},
- { PCI_VDEVICE(INTEL, 0x2674), TCO_631XESB},
- { PCI_VDEVICE(INTEL, 0x2675), TCO_631XESB},
- { PCI_VDEVICE(INTEL, 0x2676), TCO_631XESB},
- { PCI_VDEVICE(INTEL, 0x2677), TCO_631XESB},
- { PCI_VDEVICE(INTEL, 0x2678), TCO_631XESB},
- { PCI_VDEVICE(INTEL, 0x2679), TCO_631XESB},
- { PCI_VDEVICE(INTEL, 0x267a), TCO_631XESB},
- { PCI_VDEVICE(INTEL, 0x267b), TCO_631XESB},
- { PCI_VDEVICE(INTEL, 0x267c), TCO_631XESB},
- { PCI_VDEVICE(INTEL, 0x267d), TCO_631XESB},
- { PCI_VDEVICE(INTEL, 0x267e), TCO_631XESB},
- { PCI_VDEVICE(INTEL, 0x267f), TCO_631XESB},
- { PCI_VDEVICE(INTEL, 0x27b8), TCO_ICH7},
- { PCI_VDEVICE(INTEL, 0x27b0), TCO_ICH7DH},
- { PCI_VDEVICE(INTEL, 0x27b9), TCO_ICH7M},
- { PCI_VDEVICE(INTEL, 0x27bd), TCO_ICH7MDH},
- { PCI_VDEVICE(INTEL, 0x27bc), TCO_NM10},
- { PCI_VDEVICE(INTEL, 0x2810), TCO_ICH8},
- { PCI_VDEVICE(INTEL, 0x2812), TCO_ICH8DH},
- { PCI_VDEVICE(INTEL, 0x2814), TCO_ICH8DO},
- { PCI_VDEVICE(INTEL, 0x2815), TCO_ICH8M},
- { PCI_VDEVICE(INTEL, 0x2811), TCO_ICH8ME},
- { PCI_VDEVICE(INTEL, 0x2918), TCO_ICH9},
- { PCI_VDEVICE(INTEL, 0x2916), TCO_ICH9R},
- { PCI_VDEVICE(INTEL, 0x2912), TCO_ICH9DH},
- { PCI_VDEVICE(INTEL, 0x2914), TCO_ICH9DO},
- { PCI_VDEVICE(INTEL, 0x2919), TCO_ICH9M},
- { PCI_VDEVICE(INTEL, 0x2917), TCO_ICH9ME},
- { PCI_VDEVICE(INTEL, 0x3a18), TCO_ICH10},
- { PCI_VDEVICE(INTEL, 0x3a16), TCO_ICH10R},
- { PCI_VDEVICE(INTEL, 0x3a1a), TCO_ICH10D},
- { PCI_VDEVICE(INTEL, 0x3a14), TCO_ICH10DO},
- { PCI_VDEVICE(INTEL, 0x3b00), TCO_PCH},
- { PCI_VDEVICE(INTEL, 0x3b01), TCO_PCHM},
- { PCI_VDEVICE(INTEL, 0x3b02), TCO_P55},
- { PCI_VDEVICE(INTEL, 0x3b03), TCO_PM55},
- { PCI_VDEVICE(INTEL, 0x3b06), TCO_H55},
- { PCI_VDEVICE(INTEL, 0x3b07), TCO_QM57},
- { PCI_VDEVICE(INTEL, 0x3b08), TCO_H57},
- { PCI_VDEVICE(INTEL, 0x3b09), TCO_HM55},
- { PCI_VDEVICE(INTEL, 0x3b0a), TCO_Q57},
- { PCI_VDEVICE(INTEL, 0x3b0b), TCO_HM57},
- { PCI_VDEVICE(INTEL, 0x3b0d), TCO_PCHMSFF},
- { PCI_VDEVICE(INTEL, 0x3b0f), TCO_QS57},
- { PCI_VDEVICE(INTEL, 0x3b12), TCO_3400},
- { PCI_VDEVICE(INTEL, 0x3b14), TCO_3420},
- { PCI_VDEVICE(INTEL, 0x3b16), TCO_3450},
- { PCI_VDEVICE(INTEL, 0x5031), TCO_EP80579},
- { PCI_VDEVICE(INTEL, 0x1c41), TCO_CPT},
- { PCI_VDEVICE(INTEL, 0x1c42), TCO_CPTD},
- { PCI_VDEVICE(INTEL, 0x1c43), TCO_CPTM},
- { PCI_VDEVICE(INTEL, 0x1c44), TCO_CPT},
- { PCI_VDEVICE(INTEL, 0x1c45), TCO_CPT},
- { PCI_VDEVICE(INTEL, 0x1c46), TCO_CPT},
- { PCI_VDEVICE(INTEL, 0x1c47), TCO_CPT},
- { PCI_VDEVICE(INTEL, 0x1c48), TCO_CPT},
- { PCI_VDEVICE(INTEL, 0x1c49), TCO_CPT},
- { PCI_VDEVICE(INTEL, 0x1c4a), TCO_CPT},
- { PCI_VDEVICE(INTEL, 0x1c4b), TCO_CPT},
- { PCI_VDEVICE(INTEL, 0x1c4c), TCO_CPT},
- { PCI_VDEVICE(INTEL, 0x1c4d), TCO_CPT},
- { PCI_VDEVICE(INTEL, 0x1c4e), TCO_CPT},
- { PCI_VDEVICE(INTEL, 0x1c4f), TCO_CPT},
- { PCI_VDEVICE(INTEL, 0x1c50), TCO_CPT},
- { PCI_VDEVICE(INTEL, 0x1c51), TCO_CPT},
- { PCI_VDEVICE(INTEL, 0x1c52), TCO_CPT},
- { PCI_VDEVICE(INTEL, 0x1c53), TCO_CPT},
- { PCI_VDEVICE(INTEL, 0x1c54), TCO_CPT},
- { PCI_VDEVICE(INTEL, 0x1c55), TCO_CPT},
- { PCI_VDEVICE(INTEL, 0x1c56), TCO_CPT},
- { PCI_VDEVICE(INTEL, 0x1c57), TCO_CPT},
- { PCI_VDEVICE(INTEL, 0x1c58), TCO_CPT},
- { PCI_VDEVICE(INTEL, 0x1c59), TCO_CPT},
- { PCI_VDEVICE(INTEL, 0x1c5a), TCO_CPT},
- { PCI_VDEVICE(INTEL, 0x1c5b), TCO_CPT},
- { PCI_VDEVICE(INTEL, 0x1c5c), TCO_CPT},
- { PCI_VDEVICE(INTEL, 0x1c5d), TCO_CPT},
- { PCI_VDEVICE(INTEL, 0x1c5e), TCO_CPT},
- { PCI_VDEVICE(INTEL, 0x1c5f), TCO_CPT},
- { PCI_VDEVICE(INTEL, 0x1d40), TCO_PBG},
- { PCI_VDEVICE(INTEL, 0x1d41), TCO_PBG},
- { PCI_VDEVICE(INTEL, 0x2310), TCO_DH89XXCC},
- { PCI_VDEVICE(INTEL, 0x1e40), TCO_PPT},
- { PCI_VDEVICE(INTEL, 0x1e41), TCO_PPT},
- { PCI_VDEVICE(INTEL, 0x1e42), TCO_PPT},
- { PCI_VDEVICE(INTEL, 0x1e43), TCO_PPT},
- { PCI_VDEVICE(INTEL, 0x1e44), TCO_PPT},
- { PCI_VDEVICE(INTEL, 0x1e45), TCO_PPT},
- { PCI_VDEVICE(INTEL, 0x1e46), TCO_PPT},
- { PCI_VDEVICE(INTEL, 0x1e47), TCO_PPT},
- { PCI_VDEVICE(INTEL, 0x1e48), TCO_PPT},
- { PCI_VDEVICE(INTEL, 0x1e49), TCO_PPT},
- { PCI_VDEVICE(INTEL, 0x1e4a), TCO_PPT},
- { PCI_VDEVICE(INTEL, 0x1e4b), TCO_PPT},
- { PCI_VDEVICE(INTEL, 0x1e4c), TCO_PPT},
- { PCI_VDEVICE(INTEL, 0x1e4d), TCO_PPT},
- { PCI_VDEVICE(INTEL, 0x1e4e), TCO_PPT},
- { PCI_VDEVICE(INTEL, 0x1e4f), TCO_PPT},
- { PCI_VDEVICE(INTEL, 0x1e50), TCO_PPT},
- { PCI_VDEVICE(INTEL, 0x1e51), TCO_PPT},
- { PCI_VDEVICE(INTEL, 0x1e52), TCO_PPT},
- { PCI_VDEVICE(INTEL, 0x1e53), TCO_PPT},
- { PCI_VDEVICE(INTEL, 0x1e54), TCO_PPT},
- { PCI_VDEVICE(INTEL, 0x1e55), TCO_PPT},
- { PCI_VDEVICE(INTEL, 0x1e56), TCO_PPT},
- { PCI_VDEVICE(INTEL, 0x1e57), TCO_PPT},
- { PCI_VDEVICE(INTEL, 0x1e58), TCO_PPT},
- { PCI_VDEVICE(INTEL, 0x1e59), TCO_PPT},
- { PCI_VDEVICE(INTEL, 0x1e5a), TCO_PPT},
- { PCI_VDEVICE(INTEL, 0x1e5b), TCO_PPT},
- { PCI_VDEVICE(INTEL, 0x1e5c), TCO_PPT},
- { PCI_VDEVICE(INTEL, 0x1e5d), TCO_PPT},
- { PCI_VDEVICE(INTEL, 0x1e5e), TCO_PPT},
- { PCI_VDEVICE(INTEL, 0x1e5f), TCO_PPT},
- { PCI_VDEVICE(INTEL, 0x8c40), TCO_LPT},
- { PCI_VDEVICE(INTEL, 0x8c41), TCO_LPT},
- { PCI_VDEVICE(INTEL, 0x8c42), TCO_LPT},
- { PCI_VDEVICE(INTEL, 0x8c43), TCO_LPT},
- { PCI_VDEVICE(INTEL, 0x8c44), TCO_LPT},
- { PCI_VDEVICE(INTEL, 0x8c45), TCO_LPT},
- { PCI_VDEVICE(INTEL, 0x8c46), TCO_LPT},
- { PCI_VDEVICE(INTEL, 0x8c47), TCO_LPT},
- { PCI_VDEVICE(INTEL, 0x8c48), TCO_LPT},
- { PCI_VDEVICE(INTEL, 0x8c49), TCO_LPT},
- { PCI_VDEVICE(INTEL, 0x8c4a), TCO_LPT},
- { PCI_VDEVICE(INTEL, 0x8c4b), TCO_LPT},
- { PCI_VDEVICE(INTEL, 0x8c4c), TCO_LPT},
- { PCI_VDEVICE(INTEL, 0x8c4d), TCO_LPT},
- { PCI_VDEVICE(INTEL, 0x8c4e), TCO_LPT},
- { PCI_VDEVICE(INTEL, 0x8c4f), TCO_LPT},
- { PCI_VDEVICE(INTEL, 0x8c50), TCO_LPT},
- { PCI_VDEVICE(INTEL, 0x8c51), TCO_LPT},
- { PCI_VDEVICE(INTEL, 0x8c52), TCO_LPT},
- { PCI_VDEVICE(INTEL, 0x8c53), TCO_LPT},
- { PCI_VDEVICE(INTEL, 0x8c54), TCO_LPT},
- { PCI_VDEVICE(INTEL, 0x8c55), TCO_LPT},
- { PCI_VDEVICE(INTEL, 0x8c56), TCO_LPT},
- { PCI_VDEVICE(INTEL, 0x8c57), TCO_LPT},
- { PCI_VDEVICE(INTEL, 0x8c58), TCO_LPT},
- { PCI_VDEVICE(INTEL, 0x8c59), TCO_LPT},
- { PCI_VDEVICE(INTEL, 0x8c5a), TCO_LPT},
- { PCI_VDEVICE(INTEL, 0x8c5b), TCO_LPT},
- { PCI_VDEVICE(INTEL, 0x8c5c), TCO_LPT},
- { PCI_VDEVICE(INTEL, 0x8c5d), TCO_LPT},
- { PCI_VDEVICE(INTEL, 0x8c5e), TCO_LPT},
- { PCI_VDEVICE(INTEL, 0x8c5f), TCO_LPT},
- { 0, }, /* End of list */
-};
-MODULE_DEVICE_TABLE(pci, iTCO_wdt_pci_tbl);
-
/* Address definitions for the TCO */
/* TCO base address */
-#define TCOBASE (iTCO_wdt_private.ACPIBASE + 0x60)
+#define TCOBASE (iTCO_wdt_private.tco_res->start)
/* SMI Control and Enable Register */
-#define SMI_EN (iTCO_wdt_private.ACPIBASE + 0x30)
+#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 */
@@ -388,31 +87,31 @@ MODULE_DEVICE_TABLE(pci, iTCO_wdt_pci_tbl);
#define TCOv2_TMR (TCOBASE + 0x12) /* TCOv2 Timer Initial Value */
/* internal variables */
-static unsigned long is_active;
-static char expect_release;
static struct { /* this is private data for the iTCO_wdt device */
/* TCO version/generation */
unsigned int iTCO_version;
- /* The device's ACPIBASE address (TCOBASE = ACPIBASE+0x60) */
- unsigned long ACPIBASE;
- /* NO_REBOOT flag is Memory-Mapped GCS register bit 5 (TCO version 2)*/
- unsigned long __iomem *gcs;
+ struct resource *tco_res;
+ struct resource *smi_res;
+ /*
+ * NO_REBOOT flag is Memory-Mapped GCS register bit 5 (TCO version 2),
+ * or memory-mapped PMC register bit 4 (TCO version 3).
+ */
+ struct resource *gcs_pmc_res;
+ 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;
} iTCO_wdt_private;
-/* the watchdog platform device */
-static struct platform_device *iTCO_wdt_platform_device;
-
/* module parameters */
-#define WATCHDOG_HEARTBEAT 30 /* 30 sec default heartbeat */
-static int heartbeat = WATCHDOG_HEARTBEAT; /* in seconds */
+#define WATCHDOG_TIMEOUT 30 /* 30 sec default heartbeat */
+static int heartbeat = WATCHDOG_TIMEOUT; /* in seconds */
module_param(heartbeat, int, 0);
MODULE_PARM_DESC(heartbeat, "Watchdog timeout in seconds. "
"5..76 (TCO v1) or 3..614 (TCO v2), default="
- __MODULE_STRING(WATCHDOG_HEARTBEAT) ")");
+ __MODULE_STRING(WATCHDOG_TIMEOUT) ")");
static bool nowayout = WATCHDOG_NOWAYOUT;
module_param(nowayout, bool, 0);
@@ -429,11 +128,19 @@ MODULE_PARM_DESC(turn_SMI_watchdog_clear_off,
* Some TCO specific functions
*/
-static inline unsigned int seconds_to_ticks(int seconds)
+/*
+ * The iTCO v1 and v2's internal timer is stored as ticks which decrement
+ * 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)
+{
+ return iTCO_wdt_private.iTCO_version == 3 ? secs : (secs * 10) / 6;
+}
+
+static inline unsigned int ticks_to_seconds(int ticks)
{
- /* the internal timer is stored as ticks which decrement
- * every 0.6 seconds */
- return (seconds * 10) / 6;
+ return iTCO_wdt_private.iTCO_version == 3 ? ticks : (ticks * 6) / 10;
}
static void iTCO_wdt_set_NO_REBOOT_bit(void)
@@ -441,10 +148,14 @@ static void iTCO_wdt_set_NO_REBOOT_bit(void)
u32 val32;
/* Set the NO_REBOOT bit: this disables reboots */
- if (iTCO_wdt_private.iTCO_version == 2) {
- val32 = readl(iTCO_wdt_private.gcs);
+ if (iTCO_wdt_private.iTCO_version == 3) {
+ val32 = readl(iTCO_wdt_private.gcs_pmc);
+ val32 |= 0x00000010;
+ writel(val32, iTCO_wdt_private.gcs_pmc);
+ } else if (iTCO_wdt_private.iTCO_version == 2) {
+ val32 = readl(iTCO_wdt_private.gcs_pmc);
val32 |= 0x00000020;
- writel(val32, iTCO_wdt_private.gcs);
+ 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 |= 0x00000002;
@@ -458,12 +169,20 @@ static int iTCO_wdt_unset_NO_REBOOT_bit(void)
u32 val32;
/* Unset the NO_REBOOT bit: this enables reboots */
- if (iTCO_wdt_private.iTCO_version == 2) {
- val32 = readl(iTCO_wdt_private.gcs);
+ if (iTCO_wdt_private.iTCO_version == 3) {
+ val32 = readl(iTCO_wdt_private.gcs_pmc);
+ val32 &= 0xffffffef;
+ writel(val32, iTCO_wdt_private.gcs_pmc);
+
+ val32 = readl(iTCO_wdt_private.gcs_pmc);
+ if (val32 & 0x00000010)
+ ret = -EIO;
+ } else if (iTCO_wdt_private.iTCO_version == 2) {
+ val32 = readl(iTCO_wdt_private.gcs_pmc);
val32 &= 0xffffffdf;
- writel(val32, iTCO_wdt_private.gcs);
+ writel(val32, iTCO_wdt_private.gcs_pmc);
- val32 = readl(iTCO_wdt_private.gcs);
+ val32 = readl(iTCO_wdt_private.gcs_pmc);
if (val32 & 0x00000020)
ret = -EIO;
} else if (iTCO_wdt_private.iTCO_version == 1) {
@@ -479,13 +198,13 @@ static int iTCO_wdt_unset_NO_REBOOT_bit(void)
return ret; /* returns: 0 = OK, -EIO = Error */
}
-static int iTCO_wdt_start(void)
+static int iTCO_wdt_start(struct watchdog_device *wd_dev)
{
unsigned int val;
spin_lock(&iTCO_wdt_private.io_lock);
- iTCO_vendor_pre_start(iTCO_wdt_private.ACPIBASE, heartbeat);
+ iTCO_vendor_pre_start(iTCO_wdt_private.smi_res, wd_dev->timeout);
/* disable chipset's NO_REBOOT bit */
if (iTCO_wdt_unset_NO_REBOOT_bit()) {
@@ -496,7 +215,7 @@ static int iTCO_wdt_start(void)
/* Force the timer to its reload value by writing to the TCO_RLD
register */
- if (iTCO_wdt_private.iTCO_version == 2)
+ if (iTCO_wdt_private.iTCO_version >= 2)
outw(0x01, TCO_RLD);
else if (iTCO_wdt_private.iTCO_version == 1)
outb(0x01, TCO_RLD);
@@ -513,13 +232,13 @@ static int iTCO_wdt_start(void)
return 0;
}
-static int iTCO_wdt_stop(void)
+static int iTCO_wdt_stop(struct watchdog_device *wd_dev)
{
unsigned int val;
spin_lock(&iTCO_wdt_private.io_lock);
- iTCO_vendor_pre_stop(iTCO_wdt_private.ACPIBASE);
+ iTCO_vendor_pre_stop(iTCO_wdt_private.smi_res);
/* Bit 11: TCO Timer Halt -> 1 = The TCO timer is disabled */
val = inw(TCO1_CNT);
@@ -537,16 +256,16 @@ static int iTCO_wdt_stop(void)
return 0;
}
-static int iTCO_wdt_keepalive(void)
+static int iTCO_wdt_ping(struct watchdog_device *wd_dev)
{
spin_lock(&iTCO_wdt_private.io_lock);
- iTCO_vendor_pre_keepalive(iTCO_wdt_private.ACPIBASE, heartbeat);
+ iTCO_vendor_pre_keepalive(iTCO_wdt_private.smi_res, wd_dev->timeout);
/* Reload the timer by writing to the TCO Timer Counter register */
- if (iTCO_wdt_private.iTCO_version == 2)
+ if (iTCO_wdt_private.iTCO_version >= 2) {
outw(0x01, TCO_RLD);
- else if (iTCO_wdt_private.iTCO_version == 1) {
+ } else if (iTCO_wdt_private.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 */
@@ -558,7 +277,7 @@ static int iTCO_wdt_keepalive(void)
return 0;
}
-static int iTCO_wdt_set_heartbeat(int t)
+static int iTCO_wdt_set_timeout(struct watchdog_device *wd_dev, unsigned int t)
{
unsigned int val16;
unsigned char val8;
@@ -574,14 +293,14 @@ static int iTCO_wdt_set_heartbeat(int t)
/* "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)) ||
+ if (((iTCO_wdt_private.iTCO_version >= 2) && (tmrval > 0x3ff)) ||
((iTCO_wdt_private.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) {
+ if (iTCO_wdt_private.iTCO_version >= 2) {
spin_lock(&iTCO_wdt_private.io_lock);
val16 = inw(TCOv2_TMR);
val16 &= 0xfc00;
@@ -605,23 +324,24 @@ static int iTCO_wdt_set_heartbeat(int t)
return -EINVAL;
}
- heartbeat = t;
+ wd_dev->timeout = t;
return 0;
}
-static int iTCO_wdt_get_timeleft(int *time_left)
+static unsigned int iTCO_wdt_get_timeleft(struct watchdog_device *wd_dev)
{
unsigned int val16;
unsigned char val8;
+ unsigned int time_left = 0;
/* read the TCO Timer */
- if (iTCO_wdt_private.iTCO_version == 2) {
+ if (iTCO_wdt_private.iTCO_version >= 2) {
spin_lock(&iTCO_wdt_private.io_lock);
val16 = inw(TCO_RLD);
val16 &= 0x3ff;
spin_unlock(&iTCO_wdt_private.io_lock);
- *time_left = (val16 * 6) / 10;
+ 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);
@@ -630,260 +350,185 @@ static int iTCO_wdt_get_timeleft(int *time_left)
val8 += (inb(TCOv1_TMR) & 0x3f);
spin_unlock(&iTCO_wdt_private.io_lock);
- *time_left = (val8 * 6) / 10;
- } else
- return -EINVAL;
- return 0;
+ time_left = ticks_to_seconds(val8);
+ }
+ return time_left;
}
/*
- * /dev/watchdog handling
+ * Kernel Interfaces
*/
-static int iTCO_wdt_open(struct inode *inode, struct file *file)
-{
- /* /dev/watchdog can only be opened once */
- if (test_and_set_bit(0, &is_active))
- return -EBUSY;
+static const struct watchdog_info ident = {
+ .options = WDIOF_SETTIMEOUT |
+ WDIOF_KEEPALIVEPING |
+ WDIOF_MAGICCLOSE,
+ .firmware_version = 0,
+ .identity = DRV_NAME,
+};
- /*
- * Reload and activate timer
- */
- iTCO_wdt_start();
- return nonseekable_open(inode, file);
-}
+static const struct watchdog_ops iTCO_wdt_ops = {
+ .owner = THIS_MODULE,
+ .start = iTCO_wdt_start,
+ .stop = iTCO_wdt_stop,
+ .ping = iTCO_wdt_ping,
+ .set_timeout = iTCO_wdt_set_timeout,
+ .get_timeleft = iTCO_wdt_get_timeleft,
+};
-static int iTCO_wdt_release(struct inode *inode, struct file *file)
-{
- /*
- * Shut off the timer.
- */
- if (expect_release == 42) {
- iTCO_wdt_stop();
- } else {
- pr_crit("Unexpected close, not stopping watchdog!\n");
- iTCO_wdt_keepalive();
- }
- clear_bit(0, &is_active);
- expect_release = 0;
- return 0;
-}
+static struct watchdog_device iTCO_wdt_watchdog_dev = {
+ .info = &ident,
+ .ops = &iTCO_wdt_ops,
+};
-static ssize_t iTCO_wdt_write(struct file *file, const char __user *data,
- size_t len, loff_t *ppos)
+/*
+ * Init & exit routines
+ */
+
+static void iTCO_wdt_cleanup(void)
{
- /* See if we got the magic character 'V' and reload the timer */
- if (len) {
- if (!nowayout) {
- size_t i;
-
- /* note: just in case someone wrote the magic
- character five months ago... */
- expect_release = 0;
-
- /* scan to see whether or not we got the
- magic character */
- for (i = 0; i != len; i++) {
- char c;
- if (get_user(c, data + i))
- return -EFAULT;
- if (c == 'V')
- expect_release = 42;
- }
- }
+ /* Stop the timer before we leave */
+ if (!nowayout)
+ iTCO_wdt_stop(&iTCO_wdt_watchdog_dev);
- /* someone wrote to us, we should reload the timer */
- iTCO_wdt_keepalive();
+ /* 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));
}
- return len;
+
+ 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 long iTCO_wdt_ioctl(struct file *file, unsigned int cmd,
- unsigned long arg)
+static int iTCO_wdt_probe(struct platform_device *dev)
{
- int new_options, retval = -EINVAL;
- int new_heartbeat;
- void __user *argp = (void __user *)arg;
- int __user *p = argp;
- static const struct watchdog_info ident = {
- .options = WDIOF_SETTIMEOUT |
- WDIOF_KEEPALIVEPING |
- WDIOF_MAGICCLOSE,
- .firmware_version = 0,
- .identity = DRV_NAME,
- };
-
- switch (cmd) {
- case WDIOC_GETSUPPORT:
- return copy_to_user(argp, &ident, sizeof(ident)) ? -EFAULT : 0;
- case WDIOC_GETSTATUS:
- case WDIOC_GETBOOTSTATUS:
- return put_user(0, p);
-
- case WDIOC_SETOPTIONS:
- {
- if (get_user(new_options, p))
- return -EFAULT;
-
- if (new_options & WDIOS_DISABLECARD) {
- iTCO_wdt_stop();
- retval = 0;
- }
- if (new_options & WDIOS_ENABLECARD) {
- iTCO_wdt_keepalive();
- iTCO_wdt_start();
- retval = 0;
- }
- return retval;
- }
- case WDIOC_KEEPALIVE:
- iTCO_wdt_keepalive();
- return 0;
-
- case WDIOC_SETTIMEOUT:
- {
- if (get_user(new_heartbeat, p))
- return -EFAULT;
- if (iTCO_wdt_set_heartbeat(new_heartbeat))
- return -EINVAL;
- iTCO_wdt_keepalive();
- /* Fall */
- }
- case WDIOC_GETTIMEOUT:
- return put_user(heartbeat, p);
- case WDIOC_GETTIMELEFT:
- {
- int time_left;
- if (iTCO_wdt_get_timeleft(&time_left))
- return -EINVAL;
- return put_user(time_left, p);
- }
- default:
- return -ENOTTY;
- }
-}
+ int ret = -ENODEV;
+ unsigned long val32;
+ struct lpc_ich_info *ich_info = dev_get_platdata(&dev->dev);
-/*
- * Kernel Interfaces
- */
+ if (!ich_info)
+ goto out;
-static const struct file_operations iTCO_wdt_fops = {
- .owner = THIS_MODULE,
- .llseek = no_llseek,
- .write = iTCO_wdt_write,
- .unlocked_ioctl = iTCO_wdt_ioctl,
- .open = iTCO_wdt_open,
- .release = iTCO_wdt_release,
-};
+ spin_lock_init(&iTCO_wdt_private.io_lock);
-static struct miscdevice iTCO_wdt_miscdev = {
- .minor = WATCHDOG_MINOR,
- .name = "watchdog",
- .fops = &iTCO_wdt_fops,
-};
+ iTCO_wdt_private.tco_res =
+ platform_get_resource(dev, IORESOURCE_IO, ICH_RES_IO_TCO);
+ if (!iTCO_wdt_private.tco_res)
+ goto out;
-/*
- * Init & exit routines
- */
+ iTCO_wdt_private.smi_res =
+ platform_get_resource(dev, IORESOURCE_IO, ICH_RES_IO_SMI);
+ if (!iTCO_wdt_private.smi_res)
+ goto out;
-static int __devinit iTCO_wdt_init(struct pci_dev *pdev,
- const struct pci_device_id *ent, struct platform_device *dev)
-{
- int ret;
- u32 base_address;
- unsigned long RCBA;
- unsigned long val32;
+ iTCO_wdt_private.iTCO_version = ich_info->iTCO_version;
+ iTCO_wdt_private.dev = dev;
+ iTCO_wdt_private.pdev = to_pci_dev(dev->dev.parent);
/*
- * Find the ACPI/PM base I/O address which is the base
- * for the TCO registers (TCOBASE=ACPIBASE + 0x60)
- * ACPIBASE is bits [15:7] from 0x40-0x43
+ * Get the Memory-Mapped GCS or PMC register, we need it for the
+ * NO_REBOOT flag (TCO v2 and v3).
*/
- pci_read_config_dword(pdev, 0x40, &base_address);
- base_address &= 0x0000ff80;
- if (base_address == 0x00000000) {
- /* Something's wrong here, ACPIBASE has to be set */
- pr_err("failed to get TCOBASE address, device disabled by hardware/BIOS\n");
- return -ENODEV;
- }
- iTCO_wdt_private.iTCO_version =
- iTCO_chipset_info[ent->driver_data].iTCO_version;
- iTCO_wdt_private.ACPIBASE = base_address;
- iTCO_wdt_private.pdev = pdev;
-
- /* Get the Memory-Mapped GCS register, we need it for the
- NO_REBOOT flag (TCO v2). To get access to it you have to
- read RCBA from PCI Config space 0xf0 and use it as base.
- GCS = RCBA + ICH6_GCS(0x3410). */
- if (iTCO_wdt_private.iTCO_version == 2) {
- pci_read_config_dword(pdev, 0xf0, &base_address);
- if ((base_address & 1) == 0) {
- pr_err("RCBA is disabled by hardware/BIOS, device disabled\n");
- ret = -ENODEV;
+ 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;
}
- RCBA = base_address & 0xffffc000;
- iTCO_wdt_private.gcs = ioremap((RCBA + 0x3410), 4);
+ 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;
+ }
}
/* Check chipset's NO_REBOOT bit */
if (iTCO_wdt_unset_NO_REBOOT_bit() && 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 out_unmap;
+ goto unmap_gcs_pmc;
}
/* Set the NO_REBOOT bit to prevent later reboots, just for sure */
iTCO_wdt_set_NO_REBOOT_bit();
/* The TCO logic uses the TCO_EN bit in the SMI_EN register */
- if (!request_region(SMI_EN, 4, "iTCO_wdt")) {
- pr_err("I/O address 0x%04lx already in use, device disabled\n",
- SMI_EN);
- ret = -EIO;
- goto out_unmap;
+ if (!request_region(iTCO_wdt_private.smi_res->start,
+ resource_size(iTCO_wdt_private.smi_res), dev->name)) {
+ pr_err("I/O address 0x%04llx already in use, device disabled\n",
+ (u64)SMI_EN);
+ ret = -EBUSY;
+ goto unmap_gcs_pmc;
}
if (turn_SMI_watchdog_clear_off >= iTCO_wdt_private.iTCO_version) {
- /* Bit 13: TCO_EN -> 0 = Disables TCO logic generating an SMI# */
+ /*
+ * Bit 13: TCO_EN -> 0
+ * Disables TCO logic generating an SMI#
+ */
val32 = inl(SMI_EN);
val32 &= 0xffffdfff; /* Turn off SMI clearing watchdog */
outl(val32, SMI_EN);
}
- /* The TCO I/O registers reside in a 32-byte range pointed to
- by the TCOBASE value */
- if (!request_region(TCOBASE, 0x20, "iTCO_wdt")) {
- pr_err("I/O address 0x%04lx already in use, device disabled\n",
- TCOBASE);
- ret = -EIO;
- goto unreg_smi_en;
+ if (!request_region(iTCO_wdt_private.tco_res->start,
+ resource_size(iTCO_wdt_private.tco_res), dev->name)) {
+ pr_err("I/O address 0x%04llx already in use, device disabled\n",
+ (u64)TCOBASE);
+ ret = -EBUSY;
+ goto unreg_smi;
}
- pr_info("Found a %s TCO device (Version=%d, TCOBASE=0x%04lx)\n",
- iTCO_chipset_info[ent->driver_data].name,
- iTCO_chipset_info[ent->driver_data].iTCO_version,
- TCOBASE);
+ pr_info("Found a %s TCO device (Version=%d, TCOBASE=0x%04llx)\n",
+ ich_info->name, ich_info->iTCO_version, (u64)TCOBASE);
/* Clear out the (probably old) status */
- 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 */
+ if (iTCO_wdt_private.iTCO_version == 3) {
+ outl(0x20008, TCO1_STS);
+ } else {
+ 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 */
+ }
+
+ 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;
/* Make sure the watchdog is not running */
- iTCO_wdt_stop();
+ iTCO_wdt_stop(&iTCO_wdt_watchdog_dev);
/* Check that the heartbeat value is within it's range;
if not reset to the default */
- if (iTCO_wdt_set_heartbeat(heartbeat)) {
- iTCO_wdt_set_heartbeat(WATCHDOG_HEARTBEAT);
- pr_info("timeout value out of range, using %d\n", heartbeat);
+ if (iTCO_wdt_set_timeout(&iTCO_wdt_watchdog_dev, heartbeat)) {
+ iTCO_wdt_set_timeout(&iTCO_wdt_watchdog_dev, WATCHDOG_TIMEOUT);
+ pr_info("timeout value out of range, using %d\n",
+ WATCHDOG_TIMEOUT);
}
- ret = misc_register(&iTCO_wdt_miscdev);
+ ret = watchdog_register_device(&iTCO_wdt_watchdog_dev);
if (ret != 0) {
- pr_err("cannot register miscdev on minor=%d (err=%d)\n",
- WATCHDOG_MINOR, ret);
- goto unreg_region;
+ pr_err("cannot register watchdog device (err=%d)\n", ret);
+ goto unreg_tco;
}
pr_info("initialized. heartbeat=%d sec (nowayout=%d)\n",
@@ -891,62 +536,31 @@ static int __devinit iTCO_wdt_init(struct pci_dev *pdev,
return 0;
-unreg_region:
- release_region(TCOBASE, 0x20);
-unreg_smi_en:
- release_region(SMI_EN, 4);
-out_unmap:
- if (iTCO_wdt_private.iTCO_version == 2)
- iounmap(iTCO_wdt_private.gcs);
+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.ACPIBASE = 0;
- return ret;
-}
-
-static void __devexit iTCO_wdt_cleanup(void)
-{
- /* Stop the timer before we leave */
- if (!nowayout)
- iTCO_wdt_stop();
-
- /* Deregister */
- misc_deregister(&iTCO_wdt_miscdev);
- release_region(TCOBASE, 0x20);
- release_region(SMI_EN, 4);
- if (iTCO_wdt_private.iTCO_version == 2)
- iounmap(iTCO_wdt_private.gcs);
- pci_dev_put(iTCO_wdt_private.pdev);
- iTCO_wdt_private.ACPIBASE = 0;
-}
-
-static int __devinit iTCO_wdt_probe(struct platform_device *dev)
-{
- int ret = -ENODEV;
- int found = 0;
- struct pci_dev *pdev = NULL;
- const struct pci_device_id *ent;
-
- spin_lock_init(&iTCO_wdt_private.io_lock);
-
- for_each_pci_dev(pdev) {
- ent = pci_match_id(iTCO_wdt_pci_tbl, pdev);
- if (ent) {
- found++;
- ret = iTCO_wdt_init(pdev, ent, dev);
- if (!ret)
- break;
- }
- }
-
- if (!found)
- pr_info("No device detected\n");
+ 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 __devexit iTCO_wdt_remove(struct platform_device *dev)
+static int iTCO_wdt_remove(struct platform_device *dev)
{
- if (iTCO_wdt_private.ACPIBASE)
+ if (iTCO_wdt_private.tco_res || iTCO_wdt_private.smi_res)
iTCO_wdt_cleanup();
return 0;
@@ -954,12 +568,12 @@ static int __devexit iTCO_wdt_remove(struct platform_device *dev)
static void iTCO_wdt_shutdown(struct platform_device *dev)
{
- iTCO_wdt_stop();
+ iTCO_wdt_stop(NULL);
}
static struct platform_driver iTCO_wdt_driver = {
.probe = iTCO_wdt_probe,
- .remove = __devexit_p(iTCO_wdt_remove),
+ .remove = iTCO_wdt_remove,
.shutdown = iTCO_wdt_shutdown,
.driver = {
.owner = THIS_MODULE,
@@ -977,23 +591,11 @@ static int __init iTCO_wdt_init_module(void)
if (err)
return err;
- iTCO_wdt_platform_device = platform_device_register_simple(DRV_NAME,
- -1, NULL, 0);
- if (IS_ERR(iTCO_wdt_platform_device)) {
- err = PTR_ERR(iTCO_wdt_platform_device);
- goto unreg_platform_driver;
- }
-
return 0;
-
-unreg_platform_driver:
- platform_driver_unregister(&iTCO_wdt_driver);
- return err;
}
static void __exit iTCO_wdt_cleanup_module(void)
{
- platform_device_unregister(iTCO_wdt_platform_device);
platform_driver_unregister(&iTCO_wdt_driver);
pr_info("Watchdog Module Unloaded\n");
}
@@ -1005,4 +607,4 @@ MODULE_AUTHOR("Wim Van Sebroeck <wim@iguana.be>");
MODULE_DESCRIPTION("Intel TCO WatchDog Timer Driver");
MODULE_VERSION(DRV_VERSION);
MODULE_LICENSE("GPL");
-MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
+MODULE_ALIAS("platform:" DRV_NAME);
diff --git a/drivers/watchdog/ib700wdt.c b/drivers/watchdog/ib700wdt.c
index 184c0bfc87a..4247c498ee7 100644
--- a/drivers/watchdog/ib700wdt.c
+++ b/drivers/watchdog/ib700wdt.c
@@ -277,7 +277,7 @@ static struct miscdevice ibwdt_miscdev = {
* Init & exit routines
*/
-static int __devinit ibwdt_probe(struct platform_device *dev)
+static int __init ibwdt_probe(struct platform_device *dev)
{
int res;
@@ -319,7 +319,7 @@ out_nostopreg:
return res;
}
-static int __devexit ibwdt_remove(struct platform_device *dev)
+static int ibwdt_remove(struct platform_device *dev)
{
misc_deregister(&ibwdt_miscdev);
release_region(WDT_START, 1);
@@ -336,8 +336,7 @@ static void ibwdt_shutdown(struct platform_device *dev)
}
static struct platform_driver ibwdt_driver = {
- .probe = ibwdt_probe,
- .remove = __devexit_p(ibwdt_remove),
+ .remove = ibwdt_remove,
.shutdown = ibwdt_shutdown,
.driver = {
.owner = THIS_MODULE,
@@ -351,21 +350,19 @@ static int __init ibwdt_init(void)
pr_info("WDT driver for IB700 single board computer initialising\n");
- err = platform_driver_register(&ibwdt_driver);
- if (err)
- return err;
-
ibwdt_platform_device = platform_device_register_simple(DRV_NAME,
-1, NULL, 0);
- if (IS_ERR(ibwdt_platform_device)) {
- err = PTR_ERR(ibwdt_platform_device);
- goto unreg_platform_driver;
- }
+ if (IS_ERR(ibwdt_platform_device))
+ return PTR_ERR(ibwdt_platform_device);
+
+ err = platform_driver_probe(&ibwdt_driver, ibwdt_probe);
+ if (err)
+ goto unreg_platform_device;
return 0;
-unreg_platform_driver:
- platform_driver_unregister(&ibwdt_driver);
+unreg_platform_device:
+ platform_device_unregister(ibwdt_platform_device);
return err;
}
@@ -382,6 +379,5 @@ module_exit(ibwdt_exit);
MODULE_AUTHOR("Charles Howes <chowes@vsol.net>");
MODULE_DESCRIPTION("IB700 SBC watchdog driver");
MODULE_LICENSE("GPL");
-MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
/* end of ib700wdt.c */
diff --git a/drivers/watchdog/ibmasr.c b/drivers/watchdog/ibmasr.c
index bc3fb8fe89a..366b0474f27 100644
--- a/drivers/watchdog/ibmasr.c
+++ b/drivers/watchdog/ibmasr.c
@@ -360,7 +360,7 @@ struct ibmasr_id {
int type;
};
-static struct ibmasr_id __initdata ibmasr_id_table[] = {
+static struct ibmasr_id ibmasr_id_table[] __initdata = {
{ "IBM Automatic Server Restart - eserver xSeries 220", ASMTYPE_TOPAZ },
{ "IBM Automatic Server Restart - Machine Type 8673", ASMTYPE_PEARL },
{ "IBM Automatic Server Restart - Machine Type 8480", ASMTYPE_JASPER },
@@ -419,4 +419,3 @@ MODULE_PARM_DESC(nowayout,
MODULE_DESCRIPTION("IBM Automatic Server Restart driver");
MODULE_AUTHOR("Andrey Panin");
MODULE_LICENSE("GPL");
-MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
diff --git a/drivers/watchdog/ie6xx_wdt.c b/drivers/watchdog/ie6xx_wdt.c
new file mode 100644
index 00000000000..07f88f54e5c
--- /dev/null
+++ b/drivers/watchdog/ie6xx_wdt.c
@@ -0,0 +1,346 @@
+/*
+ * Intel Atom E6xx Watchdog driver
+ *
+ * Copyright (C) 2011 Alexander Stein
+ * <alexander.stein@systec-electronic.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General
+ * Public License as published by the Free Software Foundation.
+ *
+ * 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.
+ * You should have received a copy of the GNU General Public
+ * License along with this program; if not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ * The full GNU General Public License is included in this
+ * distribution in the file called COPYING.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/platform_device.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/watchdog.h>
+#include <linux/seq_file.h>
+#include <linux/debugfs.h>
+#include <linux/uaccess.h>
+#include <linux/spinlock.h>
+
+#define DRIVER_NAME "ie6xx_wdt"
+
+#define PV1 0x00
+#define PV2 0x04
+
+#define RR0 0x0c
+#define RR1 0x0d
+#define WDT_RELOAD 0x01
+#define WDT_TOUT 0x02
+
+#define WDTCR 0x10
+#define WDT_PRE_SEL 0x04
+#define WDT_RESET_SEL 0x08
+#define WDT_RESET_EN 0x10
+#define WDT_TOUT_EN 0x20
+
+#define DCR 0x14
+
+#define WDTLR 0x18
+#define WDT_LOCK 0x01
+#define WDT_ENABLE 0x02
+#define WDT_TOUT_CNF 0x03
+
+#define MIN_TIME 1
+#define MAX_TIME (10 * 60) /* 10 minutes */
+#define DEFAULT_TIME 60
+
+static unsigned int timeout = DEFAULT_TIME;
+module_param(timeout, uint, 0);
+MODULE_PARM_DESC(timeout,
+ "Default Watchdog timer setting ("
+ __MODULE_STRING(DEFAULT_TIME) "s)."
+ "The range is from 1 to 600");
+
+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) ")");
+
+static u8 resetmode = 0x10;
+module_param(resetmode, byte, 0);
+MODULE_PARM_DESC(resetmode,
+ "Resetmode bits: 0x08 warm reset (cold reset otherwise), "
+ "0x10 reset enable, 0x20 disable toggle GPIO[4] (default=0x10)");
+
+static struct {
+ unsigned short sch_wdtba;
+ struct spinlock unlock_sequence;
+#ifdef CONFIG_DEBUG_FS
+ struct dentry *debugfs;
+#endif
+} ie6xx_wdt_data;
+
+/*
+ * This is needed to write to preload and reload registers
+ * struct ie6xx_wdt_data.unlock_sequence must be used
+ * to prevent sequence interrupts
+ */
+static void ie6xx_wdt_unlock_registers(void)
+{
+ outb(0x80, ie6xx_wdt_data.sch_wdtba + RR0);
+ outb(0x86, ie6xx_wdt_data.sch_wdtba + RR0);
+}
+
+static int ie6xx_wdt_ping(struct watchdog_device *wdd)
+{
+ spin_lock(&ie6xx_wdt_data.unlock_sequence);
+ ie6xx_wdt_unlock_registers();
+ outb(WDT_RELOAD, ie6xx_wdt_data.sch_wdtba + RR1);
+ spin_unlock(&ie6xx_wdt_data.unlock_sequence);
+ return 0;
+}
+
+static int ie6xx_wdt_set_timeout(struct watchdog_device *wdd, unsigned int t)
+{
+ u32 preload;
+ u64 clock;
+ u8 wdtcr;
+
+ /* Watchdog clock is PCI Clock (33MHz) */
+ clock = 33000000;
+ /* and the preload value is loaded into [34:15] of the down counter */
+ preload = (t * clock) >> 15;
+ /*
+ * Manual states preload must be one less.
+ * Does not wrap as t is at least 1
+ */
+ preload -= 1;
+
+ spin_lock(&ie6xx_wdt_data.unlock_sequence);
+
+ /* Set ResetMode & Enable prescaler for range 10ms to 10 min */
+ wdtcr = resetmode & 0x38;
+ outb(wdtcr, ie6xx_wdt_data.sch_wdtba + WDTCR);
+
+ ie6xx_wdt_unlock_registers();
+ outl(0, ie6xx_wdt_data.sch_wdtba + PV1);
+
+ ie6xx_wdt_unlock_registers();
+ outl(preload, ie6xx_wdt_data.sch_wdtba + PV2);
+
+ ie6xx_wdt_unlock_registers();
+ outb(WDT_RELOAD | WDT_TOUT, ie6xx_wdt_data.sch_wdtba + RR1);
+
+ spin_unlock(&ie6xx_wdt_data.unlock_sequence);
+
+ wdd->timeout = t;
+ return 0;
+}
+
+static int ie6xx_wdt_start(struct watchdog_device *wdd)
+{
+ ie6xx_wdt_set_timeout(wdd, wdd->timeout);
+
+ /* Enable the watchdog timer */
+ spin_lock(&ie6xx_wdt_data.unlock_sequence);
+ outb(WDT_ENABLE, ie6xx_wdt_data.sch_wdtba + WDTLR);
+ spin_unlock(&ie6xx_wdt_data.unlock_sequence);
+
+ return 0;
+}
+
+static int ie6xx_wdt_stop(struct watchdog_device *wdd)
+{
+ if (inb(ie6xx_wdt_data.sch_wdtba + WDTLR) & WDT_LOCK)
+ return -1;
+
+ /* Disable the watchdog timer */
+ spin_lock(&ie6xx_wdt_data.unlock_sequence);
+ outb(0, ie6xx_wdt_data.sch_wdtba + WDTLR);
+ spin_unlock(&ie6xx_wdt_data.unlock_sequence);
+
+ return 0;
+}
+
+static const struct watchdog_info ie6xx_wdt_info = {
+ .identity = "Intel Atom E6xx Watchdog",
+ .options = WDIOF_SETTIMEOUT |
+ WDIOF_MAGICCLOSE |
+ WDIOF_KEEPALIVEPING,
+};
+
+static const struct watchdog_ops ie6xx_wdt_ops = {
+ .owner = THIS_MODULE,
+ .start = ie6xx_wdt_start,
+ .stop = ie6xx_wdt_stop,
+ .ping = ie6xx_wdt_ping,
+ .set_timeout = ie6xx_wdt_set_timeout,
+};
+
+static struct watchdog_device ie6xx_wdt_dev = {
+ .info = &ie6xx_wdt_info,
+ .ops = &ie6xx_wdt_ops,
+ .min_timeout = MIN_TIME,
+ .max_timeout = MAX_TIME,
+};
+
+#ifdef CONFIG_DEBUG_FS
+
+static int ie6xx_wdt_dbg_show(struct seq_file *s, void *unused)
+{
+ seq_printf(s, "PV1 = 0x%08x\n",
+ inl(ie6xx_wdt_data.sch_wdtba + PV1));
+ seq_printf(s, "PV2 = 0x%08x\n",
+ inl(ie6xx_wdt_data.sch_wdtba + PV2));
+ seq_printf(s, "RR = 0x%08x\n",
+ inw(ie6xx_wdt_data.sch_wdtba + RR0));
+ seq_printf(s, "WDTCR = 0x%08x\n",
+ inw(ie6xx_wdt_data.sch_wdtba + WDTCR));
+ seq_printf(s, "DCR = 0x%08x\n",
+ inl(ie6xx_wdt_data.sch_wdtba + DCR));
+ seq_printf(s, "WDTLR = 0x%08x\n",
+ inw(ie6xx_wdt_data.sch_wdtba + WDTLR));
+
+ seq_printf(s, "\n");
+ return 0;
+}
+
+static int ie6xx_wdt_dbg_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, ie6xx_wdt_dbg_show, NULL);
+}
+
+static const struct file_operations ie6xx_wdt_dbg_operations = {
+ .open = ie6xx_wdt_dbg_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+static void ie6xx_wdt_debugfs_init(void)
+{
+ /* /sys/kernel/debug/ie6xx_wdt */
+ ie6xx_wdt_data.debugfs = debugfs_create_file("ie6xx_wdt",
+ S_IFREG | S_IRUGO, NULL, NULL, &ie6xx_wdt_dbg_operations);
+}
+
+static void ie6xx_wdt_debugfs_exit(void)
+{
+ debugfs_remove(ie6xx_wdt_data.debugfs);
+}
+
+#else
+static void ie6xx_wdt_debugfs_init(void)
+{
+}
+
+static void ie6xx_wdt_debugfs_exit(void)
+{
+}
+#endif
+
+static int ie6xx_wdt_probe(struct platform_device *pdev)
+{
+ struct resource *res;
+ u8 wdtlr;
+ int ret;
+
+ res = platform_get_resource(pdev, IORESOURCE_IO, 0);
+ if (!res)
+ return -ENODEV;
+
+ if (!request_region(res->start, resource_size(res), pdev->name)) {
+ dev_err(&pdev->dev, "Watchdog region 0x%llx already in use!\n",
+ (u64)res->start);
+ return -EBUSY;
+ }
+
+ ie6xx_wdt_data.sch_wdtba = res->start;
+ dev_dbg(&pdev->dev, "WDT = 0x%X\n", ie6xx_wdt_data.sch_wdtba);
+
+ ie6xx_wdt_dev.timeout = timeout;
+ watchdog_set_nowayout(&ie6xx_wdt_dev, nowayout);
+
+ spin_lock_init(&ie6xx_wdt_data.unlock_sequence);
+
+ wdtlr = inb(ie6xx_wdt_data.sch_wdtba + WDTLR);
+ if (wdtlr & WDT_LOCK)
+ dev_warn(&pdev->dev,
+ "Watchdog Timer is Locked (Reg=0x%x)\n", wdtlr);
+
+ ie6xx_wdt_debugfs_init();
+
+ ret = watchdog_register_device(&ie6xx_wdt_dev);
+ if (ret) {
+ dev_err(&pdev->dev,
+ "Watchdog timer: cannot register device (err =%d)\n",
+ ret);
+ goto misc_register_error;
+ }
+
+ return 0;
+
+misc_register_error:
+ ie6xx_wdt_debugfs_exit();
+ release_region(res->start, resource_size(res));
+ ie6xx_wdt_data.sch_wdtba = 0;
+ return ret;
+}
+
+static int ie6xx_wdt_remove(struct platform_device *pdev)
+{
+ struct resource *res;
+
+ res = platform_get_resource(pdev, IORESOURCE_IO, 0);
+ ie6xx_wdt_stop(NULL);
+ watchdog_unregister_device(&ie6xx_wdt_dev);
+ ie6xx_wdt_debugfs_exit();
+ release_region(res->start, resource_size(res));
+ ie6xx_wdt_data.sch_wdtba = 0;
+
+ return 0;
+}
+
+static struct platform_driver ie6xx_wdt_driver = {
+ .probe = ie6xx_wdt_probe,
+ .remove = ie6xx_wdt_remove,
+ .driver = {
+ .name = DRIVER_NAME,
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init ie6xx_wdt_init(void)
+{
+ /* Check boot parameters to verify that their initial values */
+ /* are in range. */
+ if ((timeout < MIN_TIME) ||
+ (timeout > MAX_TIME)) {
+ pr_err("Watchdog timer: value of timeout %d (dec) "
+ "is out of range from %d to %d (dec)\n",
+ timeout, MIN_TIME, MAX_TIME);
+ return -EINVAL;
+ }
+
+ return platform_driver_register(&ie6xx_wdt_driver);
+}
+
+static void __exit ie6xx_wdt_exit(void)
+{
+ platform_driver_unregister(&ie6xx_wdt_driver);
+}
+
+late_initcall(ie6xx_wdt_init);
+module_exit(ie6xx_wdt_exit);
+
+MODULE_AUTHOR("Alexander Stein <alexander.stein@systec-electronic.com>");
+MODULE_DESCRIPTION("Intel Atom E6xx Watchdog Device Driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:" DRIVER_NAME);
diff --git a/drivers/watchdog/imx2_wdt.c b/drivers/watchdog/imx2_wdt.c
index 7a2b734fcdc..9d4874f0994 100644
--- a/drivers/watchdog/imx2_wdt.c
+++ b/drivers/watchdog/imx2_wdt.c
@@ -2,6 +2,7 @@
* Watchdog driver for IMX2 and later processors
*
* Copyright (C) 2010 Wolfram Sang, Pengutronix e.K. <w.sang@pengutronix.de>
+ * Copyright (C) 2014 Freescale Semiconductor, Inc.
*
* some parts adapted by similar drivers from Darius Augulis and Vladimir
* Zapolskiy, additional improvements by Wim Van Sebroeck.
@@ -20,20 +21,17 @@
* Halt on suspend: Manual Can be automatic
*/
+#include <linux/clk.h>
#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/jiffies.h>
#include <linux/kernel.h>
-#include <linux/miscdevice.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/platform_device.h>
-#include <linux/watchdog.h>
-#include <linux/clk.h>
-#include <linux/fs.h>
-#include <linux/io.h>
-#include <linux/uaccess.h>
+#include <linux/regmap.h>
#include <linux/timer.h>
-#include <linux/jiffies.h>
-#include <mach/hardware.h>
+#include <linux/watchdog.h>
#define DRIVER_NAME "imx2-wdt"
@@ -41,6 +39,7 @@
#define IMX2_WDT_WCR_WT (0xFF << 8) /* -> Watchdog Timeout Field */
#define IMX2_WDT_WCR_WRE (1 << 3) /* -> WDOG Reset Enable */
#define IMX2_WDT_WCR_WDE (1 << 2) /* -> Watchdog Enable */
+#define IMX2_WDT_WCR_WDZST (1 << 0) /* -> Watchdog timer Suspend */
#define IMX2_WDT_WSR 0x02 /* Service Register */
#define IMX2_WDT_SEQ1 0x5555 /* -> service sequence 1 */
@@ -54,19 +53,12 @@
#define WDOG_SEC_TO_COUNT(s) ((s * 2 - 1) << 8)
-#define IMX2_WDT_STATUS_OPEN 0
-#define IMX2_WDT_STATUS_STARTED 1
-#define IMX2_WDT_EXPECT_CLOSE 2
-
-static struct {
+struct imx2_wdt_device {
struct clk *clk;
- void __iomem *base;
- unsigned timeout;
- unsigned long status;
+ struct regmap *regmap;
struct timer_list timer; /* Pings the watchdog when closed */
-} imx2_wdt;
-
-static struct miscdevice imx2_wdt_miscdev;
+ struct watchdog_device wdog;
+};
static bool nowayout = WATCHDOG_NOWAYOUT;
module_param(nowayout, bool, 0);
@@ -84,10 +76,15 @@ static const struct watchdog_info imx2_wdt_info = {
.options = WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT | WDIOF_MAGICCLOSE,
};
-static inline void imx2_wdt_setup(void)
+static inline void imx2_wdt_setup(struct watchdog_device *wdog)
{
- u16 val = __raw_readw(imx2_wdt.base + IMX2_WDT_WCR);
+ struct imx2_wdt_device *wdev = watchdog_get_drvdata(wdog);
+ u32 val;
+
+ regmap_read(wdev->regmap, IMX2_WDT_WCR, &val);
+ /* Suspend timer in low power mode, write once-only */
+ val |= IMX2_WDT_WCR_WDZST;
/* Strip the old watchdog Time-Out value */
val &= ~IMX2_WDT_WCR_WT;
/* Generate reset if WDOG times out */
@@ -95,236 +92,199 @@ static inline void imx2_wdt_setup(void)
/* Keep Watchdog Disabled */
val &= ~IMX2_WDT_WCR_WDE;
/* Set the watchdog's Time-Out value */
- val |= WDOG_SEC_TO_COUNT(imx2_wdt.timeout);
+ val |= WDOG_SEC_TO_COUNT(wdog->timeout);
- __raw_writew(val, imx2_wdt.base + IMX2_WDT_WCR);
+ regmap_write(wdev->regmap, IMX2_WDT_WCR, val);
/* enable the watchdog */
val |= IMX2_WDT_WCR_WDE;
- __raw_writew(val, imx2_wdt.base + IMX2_WDT_WCR);
+ regmap_write(wdev->regmap, IMX2_WDT_WCR, val);
}
-static inline void imx2_wdt_ping(void)
+static inline bool imx2_wdt_is_running(struct imx2_wdt_device *wdev)
{
- __raw_writew(IMX2_WDT_SEQ1, imx2_wdt.base + IMX2_WDT_WSR);
- __raw_writew(IMX2_WDT_SEQ2, imx2_wdt.base + IMX2_WDT_WSR);
-}
+ u32 val;
-static void imx2_wdt_timer_ping(unsigned long arg)
-{
- /* ping it every imx2_wdt.timeout / 2 seconds to prevent reboot */
- imx2_wdt_ping();
- mod_timer(&imx2_wdt.timer, jiffies + imx2_wdt.timeout * HZ / 2);
+ regmap_read(wdev->regmap, IMX2_WDT_WCR, &val);
+
+ return val & IMX2_WDT_WCR_WDE;
}
-static void imx2_wdt_start(void)
+static int imx2_wdt_ping(struct watchdog_device *wdog)
{
- if (!test_and_set_bit(IMX2_WDT_STATUS_STARTED, &imx2_wdt.status)) {
- /* at our first start we enable clock and do initialisations */
- clk_enable(imx2_wdt.clk);
-
- imx2_wdt_setup();
- } else /* delete the timer that pings the watchdog after close */
- del_timer_sync(&imx2_wdt.timer);
+ struct imx2_wdt_device *wdev = watchdog_get_drvdata(wdog);
- /* Watchdog is enabled - time to reload the timeout value */
- imx2_wdt_ping();
+ regmap_write(wdev->regmap, IMX2_WDT_WSR, IMX2_WDT_SEQ1);
+ regmap_write(wdev->regmap, IMX2_WDT_WSR, IMX2_WDT_SEQ2);
+ return 0;
}
-static void imx2_wdt_stop(void)
+static void imx2_wdt_timer_ping(unsigned long arg)
{
- /* we don't need a clk_disable, it cannot be disabled once started.
- * We use a timer to ping the watchdog while /dev/watchdog is closed */
- imx2_wdt_timer_ping(0);
+ struct watchdog_device *wdog = (struct watchdog_device *)arg;
+ struct imx2_wdt_device *wdev = watchdog_get_drvdata(wdog);
+
+ /* ping it every wdog->timeout / 2 seconds to prevent reboot */
+ imx2_wdt_ping(wdog);
+ mod_timer(&wdev->timer, jiffies + wdog->timeout * HZ / 2);
}
-static void imx2_wdt_set_timeout(int new_timeout)
+static int imx2_wdt_set_timeout(struct watchdog_device *wdog,
+ unsigned int new_timeout)
{
- u16 val = __raw_readw(imx2_wdt.base + IMX2_WDT_WCR);
+ struct imx2_wdt_device *wdev = watchdog_get_drvdata(wdog);
- /* set the new timeout value in the WSR */
- val &= ~IMX2_WDT_WCR_WT;
- val |= WDOG_SEC_TO_COUNT(new_timeout);
- __raw_writew(val, imx2_wdt.base + IMX2_WDT_WCR);
+ regmap_update_bits(wdev->regmap, IMX2_WDT_WCR, IMX2_WDT_WCR_WT,
+ WDOG_SEC_TO_COUNT(new_timeout));
+ return 0;
}
-static int imx2_wdt_open(struct inode *inode, struct file *file)
+static int imx2_wdt_start(struct watchdog_device *wdog)
{
- if (test_and_set_bit(IMX2_WDT_STATUS_OPEN, &imx2_wdt.status))
- return -EBUSY;
+ struct imx2_wdt_device *wdev = watchdog_get_drvdata(wdog);
+
+ if (imx2_wdt_is_running(wdev)) {
+ /* delete the timer that pings the watchdog after close */
+ del_timer_sync(&wdev->timer);
+ imx2_wdt_set_timeout(wdog, wdog->timeout);
+ } else
+ imx2_wdt_setup(wdog);
- imx2_wdt_start();
- return nonseekable_open(inode, file);
+ return imx2_wdt_ping(wdog);
}
-static int imx2_wdt_close(struct inode *inode, struct file *file)
+static int imx2_wdt_stop(struct watchdog_device *wdog)
{
- if (test_bit(IMX2_WDT_EXPECT_CLOSE, &imx2_wdt.status) && !nowayout)
- imx2_wdt_stop();
- else {
- dev_crit(imx2_wdt_miscdev.parent,
- "Unexpected close: Expect reboot!\n");
- imx2_wdt_ping();
- }
-
- clear_bit(IMX2_WDT_EXPECT_CLOSE, &imx2_wdt.status);
- clear_bit(IMX2_WDT_STATUS_OPEN, &imx2_wdt.status);
+ /*
+ * We don't need a clk_disable, it cannot be disabled once started.
+ * We use a timer to ping the watchdog while /dev/watchdog is closed
+ */
+ imx2_wdt_timer_ping((unsigned long)wdog);
return 0;
}
-static long imx2_wdt_ioctl(struct file *file, unsigned int cmd,
- unsigned long arg)
+static inline void imx2_wdt_ping_if_active(struct watchdog_device *wdog)
{
- void __user *argp = (void __user *)arg;
- int __user *p = argp;
- int new_value;
- u16 val;
-
- switch (cmd) {
- case WDIOC_GETSUPPORT:
- return copy_to_user(argp, &imx2_wdt_info,
- sizeof(struct watchdog_info)) ? -EFAULT : 0;
-
- case WDIOC_GETSTATUS:
- return put_user(0, p);
-
- case WDIOC_GETBOOTSTATUS:
- val = __raw_readw(imx2_wdt.base + IMX2_WDT_WRSR);
- new_value = val & IMX2_WDT_WRSR_TOUT ? WDIOF_CARDRESET : 0;
- return put_user(new_value, p);
-
- case WDIOC_KEEPALIVE:
- imx2_wdt_ping();
- return 0;
-
- case WDIOC_SETTIMEOUT:
- if (get_user(new_value, p))
- return -EFAULT;
- if ((new_value < 1) || (new_value > IMX2_WDT_MAX_TIME))
- return -EINVAL;
- imx2_wdt_set_timeout(new_value);
- imx2_wdt.timeout = new_value;
- imx2_wdt_ping();
-
- /* Fallthrough to return current value */
- case WDIOC_GETTIMEOUT:
- return put_user(imx2_wdt.timeout, p);
-
- default:
- return -ENOTTY;
- }
-}
+ struct imx2_wdt_device *wdev = watchdog_get_drvdata(wdog);
-static ssize_t imx2_wdt_write(struct file *file, const char __user *data,
- size_t len, loff_t *ppos)
-{
- size_t i;
- char c;
-
- if (len == 0) /* Can we see this even ? */
- return 0;
-
- clear_bit(IMX2_WDT_EXPECT_CLOSE, &imx2_wdt.status);
- /* scan to see whether or not we got the magic character */
- for (i = 0; i != len; i++) {
- if (get_user(c, data + i))
- return -EFAULT;
- if (c == 'V')
- set_bit(IMX2_WDT_EXPECT_CLOSE, &imx2_wdt.status);
+ if (imx2_wdt_is_running(wdev)) {
+ imx2_wdt_set_timeout(wdog, wdog->timeout);
+ imx2_wdt_timer_ping((unsigned long)wdog);
}
-
- imx2_wdt_ping();
- return len;
}
-static const struct file_operations imx2_wdt_fops = {
+static struct watchdog_ops imx2_wdt_ops = {
.owner = THIS_MODULE,
- .llseek = no_llseek,
- .unlocked_ioctl = imx2_wdt_ioctl,
- .open = imx2_wdt_open,
- .release = imx2_wdt_close,
- .write = imx2_wdt_write,
+ .start = imx2_wdt_start,
+ .stop = imx2_wdt_stop,
+ .ping = imx2_wdt_ping,
+ .set_timeout = imx2_wdt_set_timeout,
};
-static struct miscdevice imx2_wdt_miscdev = {
- .minor = WATCHDOG_MINOR,
- .name = "watchdog",
- .fops = &imx2_wdt_fops,
+static struct regmap_config imx2_wdt_regmap_config = {
+ .reg_bits = 16,
+ .reg_stride = 2,
+ .val_bits = 16,
+ .max_register = 0x8,
};
static int __init imx2_wdt_probe(struct platform_device *pdev)
{
- int ret;
+ struct imx2_wdt_device *wdev;
+ struct watchdog_device *wdog;
struct resource *res;
+ void __iomem *base;
+ int ret;
+ u32 val;
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (!res) {
- dev_err(&pdev->dev, "can't get device resources\n");
- return -ENODEV;
- }
-
- imx2_wdt.base = devm_request_and_ioremap(&pdev->dev, res);
- if (!imx2_wdt.base) {
- dev_err(&pdev->dev, "ioremap failed\n");
+ wdev = devm_kzalloc(&pdev->dev, sizeof(*wdev), GFP_KERNEL);
+ if (!wdev)
return -ENOMEM;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ base = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(base))
+ return PTR_ERR(base);
+
+ wdev->regmap = devm_regmap_init_mmio_clk(&pdev->dev, NULL, base,
+ &imx2_wdt_regmap_config);
+ if (IS_ERR(wdev->regmap)) {
+ dev_err(&pdev->dev, "regmap init failed\n");
+ return PTR_ERR(wdev->regmap);
}
- imx2_wdt.clk = clk_get(&pdev->dev, NULL);
- if (IS_ERR(imx2_wdt.clk)) {
+ wdev->clk = devm_clk_get(&pdev->dev, NULL);
+ if (IS_ERR(wdev->clk)) {
dev_err(&pdev->dev, "can't get Watchdog clock\n");
- return PTR_ERR(imx2_wdt.clk);
+ return PTR_ERR(wdev->clk);
}
- imx2_wdt.timeout = clamp_t(unsigned, timeout, 1, IMX2_WDT_MAX_TIME);
- if (imx2_wdt.timeout != timeout)
- dev_warn(&pdev->dev, "Initial timeout out of range! "
- "Clamped from %u to %u\n", timeout, imx2_wdt.timeout);
+ wdog = &wdev->wdog;
+ wdog->info = &imx2_wdt_info;
+ wdog->ops = &imx2_wdt_ops;
+ wdog->min_timeout = 1;
+ wdog->max_timeout = IMX2_WDT_MAX_TIME;
- setup_timer(&imx2_wdt.timer, imx2_wdt_timer_ping, 0);
+ clk_prepare_enable(wdev->clk);
- imx2_wdt_miscdev.parent = &pdev->dev;
- ret = misc_register(&imx2_wdt_miscdev);
- if (ret)
- goto fail;
+ regmap_read(wdev->regmap, IMX2_WDT_WRSR, &val);
+ wdog->bootstatus = val & IMX2_WDT_WRSR_TOUT ? WDIOF_CARDRESET : 0;
- dev_info(&pdev->dev,
- "IMX2+ Watchdog Timer enabled. timeout=%ds (nowayout=%d)\n",
- imx2_wdt.timeout, nowayout);
- return 0;
+ wdog->timeout = clamp_t(unsigned, timeout, 1, IMX2_WDT_MAX_TIME);
+ if (wdog->timeout != timeout)
+ dev_warn(&pdev->dev, "Initial timeout out of range! Clamped from %u to %u\n",
+ timeout, wdog->timeout);
+
+ platform_set_drvdata(pdev, wdog);
+ watchdog_set_drvdata(wdog, wdev);
+ watchdog_set_nowayout(wdog, nowayout);
+ watchdog_init_timeout(wdog, timeout, &pdev->dev);
+
+ setup_timer(&wdev->timer, imx2_wdt_timer_ping, (unsigned long)wdog);
-fail:
- imx2_wdt_miscdev.parent = NULL;
- clk_put(imx2_wdt.clk);
- return ret;
+ imx2_wdt_ping_if_active(wdog);
+
+ ret = watchdog_register_device(wdog);
+ if (ret) {
+ dev_err(&pdev->dev, "cannot register watchdog device\n");
+ return ret;
+ }
+
+ dev_info(&pdev->dev, "timeout %d sec (nowayout=%d)\n",
+ wdog->timeout, nowayout);
+
+ return 0;
}
static int __exit imx2_wdt_remove(struct platform_device *pdev)
{
- misc_deregister(&imx2_wdt_miscdev);
-
- if (test_bit(IMX2_WDT_STATUS_STARTED, &imx2_wdt.status)) {
- del_timer_sync(&imx2_wdt.timer);
+ struct watchdog_device *wdog = platform_get_drvdata(pdev);
+ struct imx2_wdt_device *wdev = watchdog_get_drvdata(wdog);
- dev_crit(imx2_wdt_miscdev.parent,
- "Device removed: Expect reboot!\n");
- } else
- clk_put(imx2_wdt.clk);
+ watchdog_unregister_device(wdog);
- imx2_wdt_miscdev.parent = NULL;
+ if (imx2_wdt_is_running(wdev)) {
+ del_timer_sync(&wdev->timer);
+ imx2_wdt_ping(wdog);
+ dev_crit(&pdev->dev, "Device removed: Expect reboot!\n");
+ }
return 0;
}
static void imx2_wdt_shutdown(struct platform_device *pdev)
{
- if (test_bit(IMX2_WDT_STATUS_STARTED, &imx2_wdt.status)) {
- /* we are running, we need to delete the timer but will give
- * max timeout before reboot will take place */
- del_timer_sync(&imx2_wdt.timer);
- imx2_wdt_set_timeout(IMX2_WDT_MAX_TIME);
- imx2_wdt_ping();
-
- dev_crit(imx2_wdt_miscdev.parent,
- "Device shutdown: Expect reboot!\n");
+ struct watchdog_device *wdog = platform_get_drvdata(pdev);
+ struct imx2_wdt_device *wdev = watchdog_get_drvdata(wdog);
+
+ if (imx2_wdt_is_running(wdev)) {
+ /*
+ * We are running, we need to delete the timer but will
+ * give max timeout before reboot will take place
+ */
+ del_timer_sync(&wdev->timer);
+ imx2_wdt_set_timeout(wdog, IMX2_WDT_MAX_TIME);
+ imx2_wdt_ping(wdog);
+ dev_crit(&pdev->dev, "Device shutdown: Expect reboot!\n");
}
}
@@ -332,6 +292,7 @@ static const struct of_device_id imx2_wdt_dt_ids[] = {
{ .compatible = "fsl,imx21-wdt", },
{ /* sentinel */ }
};
+MODULE_DEVICE_TABLE(of, imx2_wdt_dt_ids);
static struct platform_driver imx2_wdt_driver = {
.remove = __exit_p(imx2_wdt_remove),
@@ -343,20 +304,9 @@ static struct platform_driver imx2_wdt_driver = {
},
};
-static int __init imx2_wdt_init(void)
-{
- return platform_driver_probe(&imx2_wdt_driver, imx2_wdt_probe);
-}
-module_init(imx2_wdt_init);
-
-static void __exit imx2_wdt_exit(void)
-{
- platform_driver_unregister(&imx2_wdt_driver);
-}
-module_exit(imx2_wdt_exit);
+module_platform_driver_probe(imx2_wdt_driver, imx2_wdt_probe);
MODULE_AUTHOR("Wolfram Sang");
MODULE_DESCRIPTION("Watchdog driver for IMX2 and later");
MODULE_LICENSE("GPL v2");
-MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
MODULE_ALIAS("platform:" DRIVER_NAME);
diff --git a/drivers/watchdog/indydog.c b/drivers/watchdog/indydog.c
index 6d90f7a2ce2..5d20cdd30ef 100644
--- a/drivers/watchdog/indydog.c
+++ b/drivers/watchdog/indydog.c
@@ -41,24 +41,15 @@ MODULE_PARM_DESC(nowayout,
static void indydog_start(void)
{
- u32 mc_ctrl0;
-
spin_lock(&indydog_lock);
- mc_ctrl0 = sgimc->cpuctrl0;
- mc_ctrl0 = sgimc->cpuctrl0 | SGIMC_CCTRL0_WDOG;
- sgimc->cpuctrl0 = mc_ctrl0;
+ sgimc->cpuctrl0 |= SGIMC_CCTRL0_WDOG;
spin_unlock(&indydog_lock);
}
static void indydog_stop(void)
{
- u32 mc_ctrl0;
-
spin_lock(&indydog_lock);
-
- mc_ctrl0 = sgimc->cpuctrl0;
- mc_ctrl0 &= ~SGIMC_CCTRL0_WDOG;
- sgimc->cpuctrl0 = mc_ctrl0;
+ sgimc->cpuctrl0 &= ~SGIMC_CCTRL0_WDOG;
spin_unlock(&indydog_lock);
pr_info("Stopped watchdog timer\n");
@@ -214,4 +205,3 @@ module_exit(watchdog_exit);
MODULE_AUTHOR("Guido Guenther <agx@sigxcpu.org>");
MODULE_DESCRIPTION("Hardware Watchdog Device for SGI IP22");
MODULE_LICENSE("GPL");
-MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
diff --git a/drivers/watchdog/intel-mid_wdt.c b/drivers/watchdog/intel-mid_wdt.c
new file mode 100644
index 00000000000..ca66e8e7463
--- /dev/null
+++ b/drivers/watchdog/intel-mid_wdt.c
@@ -0,0 +1,184 @@
+/*
+ * intel-mid_wdt: generic Intel MID SCU watchdog driver
+ *
+ * Platforms supported so far:
+ * - Merrifield only
+ *
+ * Copyright (C) 2014 Intel Corporation. All rights reserved.
+ * Contact: David Cohen <david.a.cohen@linux.intel.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General
+ * Public License as published by the Free Software Foundation.
+ */
+
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/nmi.h>
+#include <linux/platform_device.h>
+#include <linux/watchdog.h>
+#include <linux/platform_data/intel-mid_wdt.h>
+
+#include <asm/intel_scu_ipc.h>
+#include <asm/intel-mid.h>
+
+#define IPC_WATCHDOG 0xf8
+
+#define MID_WDT_PRETIMEOUT 15
+#define MID_WDT_TIMEOUT_MIN (1 + MID_WDT_PRETIMEOUT)
+#define MID_WDT_TIMEOUT_MAX 170
+#define MID_WDT_DEFAULT_TIMEOUT 90
+
+/* SCU watchdog messages */
+enum {
+ SCU_WATCHDOG_START = 0,
+ SCU_WATCHDOG_STOP,
+ SCU_WATCHDOG_KEEPALIVE,
+};
+
+static inline int wdt_command(int sub, u32 *in, int inlen)
+{
+ return intel_scu_ipc_command(IPC_WATCHDOG, sub, in, inlen, NULL, 0);
+}
+
+static int wdt_start(struct watchdog_device *wd)
+{
+ int ret, in_size;
+ int timeout = wd->timeout;
+ struct ipc_wd_start {
+ u32 pretimeout;
+ u32 timeout;
+ } ipc_wd_start = { timeout - MID_WDT_PRETIMEOUT, timeout };
+
+ /*
+ * SCU expects the input size for watchdog IPC to
+ * be based on 4 bytes
+ */
+ in_size = DIV_ROUND_UP(sizeof(ipc_wd_start), 4);
+
+ ret = wdt_command(SCU_WATCHDOG_START, (u32 *)&ipc_wd_start, in_size);
+ if (ret) {
+ struct device *dev = watchdog_get_drvdata(wd);
+ dev_crit(dev, "error starting watchdog: %d\n", ret);
+ }
+
+ return ret;
+}
+
+static int wdt_ping(struct watchdog_device *wd)
+{
+ int ret;
+
+ ret = wdt_command(SCU_WATCHDOG_KEEPALIVE, NULL, 0);
+ if (ret) {
+ struct device *dev = watchdog_get_drvdata(wd);
+ dev_crit(dev, "Error executing keepalive: 0x%x\n", ret);
+ }
+
+ return ret;
+}
+
+static int wdt_stop(struct watchdog_device *wd)
+{
+ int ret;
+
+ ret = wdt_command(SCU_WATCHDOG_STOP, NULL, 0);
+ if (ret) {
+ struct device *dev = watchdog_get_drvdata(wd);
+ dev_crit(dev, "Error stopping watchdog: 0x%x\n", ret);
+ }
+
+ return ret;
+}
+
+static irqreturn_t mid_wdt_irq(int irq, void *dev_id)
+{
+ panic("Kernel Watchdog");
+
+ /* This code should not be reached */
+ return IRQ_HANDLED;
+}
+
+static const struct watchdog_info mid_wdt_info = {
+ .identity = "Intel MID SCU watchdog",
+ .options = WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT,
+};
+
+static const struct watchdog_ops mid_wdt_ops = {
+ .owner = THIS_MODULE,
+ .start = wdt_start,
+ .stop = wdt_stop,
+ .ping = wdt_ping,
+};
+
+static int mid_wdt_probe(struct platform_device *pdev)
+{
+ struct watchdog_device *wdt_dev;
+ struct intel_mid_wdt_pdata *pdata = pdev->dev.platform_data;
+ int ret;
+
+ if (!pdata) {
+ dev_err(&pdev->dev, "missing platform data\n");
+ return -EINVAL;
+ }
+
+ if (pdata->probe) {
+ ret = pdata->probe(pdev);
+ if (ret)
+ return ret;
+ }
+
+ wdt_dev = devm_kzalloc(&pdev->dev, sizeof(*wdt_dev), GFP_KERNEL);
+ if (!wdt_dev)
+ return -ENOMEM;
+
+ wdt_dev->info = &mid_wdt_info;
+ wdt_dev->ops = &mid_wdt_ops;
+ wdt_dev->min_timeout = MID_WDT_TIMEOUT_MIN;
+ wdt_dev->max_timeout = MID_WDT_TIMEOUT_MAX;
+ wdt_dev->timeout = MID_WDT_DEFAULT_TIMEOUT;
+
+ 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",
+ wdt_dev);
+ if (ret) {
+ dev_err(&pdev->dev, "error requesting warning irq %d\n",
+ pdata->irq);
+ return ret;
+ }
+
+ ret = watchdog_register_device(wdt_dev);
+ if (ret) {
+ dev_err(&pdev->dev, "error registering watchdog device\n");
+ return ret;
+ }
+
+ dev_info(&pdev->dev, "Intel MID watchdog device probed\n");
+
+ 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 = {
+ .owner = THIS_MODULE,
+ .name = "intel_mid_wdt",
+ },
+};
+
+module_platform_driver(mid_wdt_driver);
+
+MODULE_AUTHOR("David Cohen <david.a.cohen@linux.intel.com>");
+MODULE_DESCRIPTION("Watchdog Driver for Intel MID platform");
+MODULE_LICENSE("GPL");
diff --git a/drivers/watchdog/intel_scu_watchdog.c b/drivers/watchdog/intel_scu_watchdog.c
index 9dda2d08af9..0caab6241eb 100644
--- a/drivers/watchdog/intel_scu_watchdog.c
+++ b/drivers/watchdog/intel_scu_watchdog.c
@@ -48,7 +48,7 @@
#include <linux/atomic.h>
#include <asm/intel_scu_ipc.h>
#include <asm/apb_timer.h>
-#include <asm/mrst.h>
+#include <asm/intel-mid.h>
#include "intel_scu_watchdog.h"
@@ -211,7 +211,6 @@ static int intel_scu_set_heartbeat(u32 t)
int ipc_ret;
int retry_count;
u32 soft_value;
- u32 hw_pre_value;
u32 hw_value;
watchdog_device.timer_set = t;
@@ -273,8 +272,7 @@ static int intel_scu_set_heartbeat(u32 t)
watchdog_device.timer_load_count_addr);
/* read count value before starting timer */
- hw_pre_value = ioread32(watchdog_device.timer_load_count_addr);
- hw_pre_value = hw_pre_value & 0xFFFF0000;
+ ioread32(watchdog_device.timer_load_count_addr);
/* Start the timer */
iowrite32(0x00000003, watchdog_device.timer_control_addr);
@@ -445,7 +443,7 @@ static int __init intel_scu_watchdog_init(void)
*
* If it isn't an intel MID device then it doesn't have this watchdog
*/
- if (!mrst_identify_cpu())
+ if (!intel_mid_identify_cpu())
return -ENODEV;
/* Check boot parameters to verify that their initial values */
@@ -564,5 +562,4 @@ module_exit(intel_scu_watchdog_exit);
MODULE_AUTHOR("Intel Corporation");
MODULE_DESCRIPTION("Intel SCU Watchdog Device Driver");
MODULE_LICENSE("GPL");
-MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
MODULE_VERSION(WDT_VER);
diff --git a/drivers/watchdog/iop_wdt.c b/drivers/watchdog/iop_wdt.c
index d964faf1a25..b16013ffacc 100644
--- a/drivers/watchdog/iop_wdt.c
+++ b/drivers/watchdog/iop_wdt.c
@@ -259,4 +259,3 @@ MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started");
MODULE_AUTHOR("Curt E Bruns <curt.e.bruns@intel.com>");
MODULE_DESCRIPTION("iop watchdog timer driver");
MODULE_LICENSE("GPL");
-MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
diff --git a/drivers/watchdog/it8712f_wdt.c b/drivers/watchdog/it8712f_wdt.c
index f4cce6d66a5..41b3979a9d8 100644
--- a/drivers/watchdog/it8712f_wdt.c
+++ b/drivers/watchdog/it8712f_wdt.c
@@ -41,7 +41,6 @@
MODULE_AUTHOR("Jorge Boncompte - DTI2 <jorge@dti2.net>");
MODULE_DESCRIPTION("IT8712F Watchdog Driver");
MODULE_LICENSE("GPL");
-MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
static int max_units = 255;
static int margin = 60; /* in seconds */
diff --git a/drivers/watchdog/it87_wdt.c b/drivers/watchdog/it87_wdt.c
index 8a741bcb512..0b93739c010 100644
--- a/drivers/watchdog/it87_wdt.c
+++ b/drivers/watchdog/it87_wdt.c
@@ -12,7 +12,8 @@
* http://www.ite.com.tw/
*
* Support of the watchdog timers, which are available on
- * IT8702, IT8712, IT8716, IT8718, IT8720, IT8721 and IT8726.
+ * IT8702, IT8712, IT8716, IT8718, IT8720, IT8721, IT8726
+ * and IT8728.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@@ -53,6 +54,7 @@
/* Defaults for Module Parameter */
#define DEFAULT_NOGAMEPORT 0
+#define DEFAULT_NOCIR 0
#define DEFAULT_EXCLUSIVE 1
#define DEFAULT_TIMEOUT 60
#define DEFAULT_TESTMODE 0
@@ -84,6 +86,7 @@
#define IT8720_ID 0x8720
#define IT8721_ID 0x8721
#define IT8726_ID 0x8726 /* the data sheet suggest wrongly 0x8716 */
+#define IT8728_ID 0x8728
/* GPIO Configuration Registers LDN=0x07 */
#define WDTCTRL 0x71
@@ -95,7 +98,7 @@
#define WDT_CIRINT 0x80
#define WDT_MOUSEINT 0x40
#define WDT_KYBINT 0x20
-#define WDT_GAMEPORT 0x10 /* not in it8718, it8720, it8721 */
+#define WDT_GAMEPORT 0x10 /* not in it8718, it8720, it8721, it8728 */
#define WDT_FORCE 0x02
#define WDT_ZERO 0x01
@@ -134,11 +137,13 @@
#define WDTS_LOCKED 3
#define WDTS_USE_GP 4
#define WDTS_EXPECTED 5
+#define WDTS_USE_CIR 6
static unsigned int base, gpact, ciract, max_units, chip_type;
static unsigned long wdt_status;
static int nogameport = DEFAULT_NOGAMEPORT;
+static int nocir = DEFAULT_NOCIR;
static int exclusive = DEFAULT_EXCLUSIVE;
static int timeout = DEFAULT_TIMEOUT;
static int testmode = DEFAULT_TESTMODE;
@@ -147,6 +152,9 @@ static bool nowayout = DEFAULT_NOWAYOUT;
module_param(nogameport, int, 0);
MODULE_PARM_DESC(nogameport, "Forbid the activation of game port, default="
__MODULE_STRING(DEFAULT_NOGAMEPORT));
+module_param(nocir, int, 0);
+MODULE_PARM_DESC(nocir, "Forbid the use of Consumer IR interrupts to reset timer, default="
+ __MODULE_STRING(DEFAULT_NOCIR));
module_param(exclusive, int, 0);
MODULE_PARM_DESC(exclusive, "Watchdog exclusive device open, default="
__MODULE_STRING(DEFAULT_EXCLUSIVE));
@@ -256,9 +264,17 @@ static void wdt_keepalive(void)
{
if (test_bit(WDTS_USE_GP, &wdt_status))
inb(base);
- else
+ else if (test_bit(WDTS_USE_CIR, &wdt_status))
/* The timer reloads with around 5 msec delay */
outb(0x55, CIR_DR(base));
+ else {
+ if (superio_enter())
+ return;
+
+ superio_select(GPIO);
+ wdt_update_timeout();
+ superio_exit();
+ }
set_bit(WDTS_KEEPALIVE, &wdt_status);
}
@@ -271,7 +287,7 @@ static int wdt_start(void)
superio_select(GPIO);
if (test_bit(WDTS_USE_GP, &wdt_status))
superio_outb(WDT_GAMEPORT, WDTCTRL);
- else
+ else if (test_bit(WDTS_USE_CIR, &wdt_status))
superio_outb(WDT_CIRINT, WDTCTRL);
wdt_update_timeout();
@@ -616,6 +632,7 @@ static int __init it87_wdt_init(void)
case IT8718_ID:
case IT8720_ID:
case IT8721_ID:
+ case IT8728_ID:
max_units = 65535;
try_gameport = 0;
break;
@@ -657,7 +674,7 @@ static int __init it87_wdt_init(void)
}
/* If we haven't Gameport support, try to get CIR support */
- if (!test_bit(WDTS_USE_GP, &wdt_status)) {
+ if (!nocir && !test_bit(WDTS_USE_GP, &wdt_status)) {
if (!request_region(CIR_BASE, 8, WATCHDOG_NAME)) {
if (gp_rreq_fail)
pr_err("I/O Address 0x%04x and 0x%04x already in use\n",
@@ -679,6 +696,7 @@ static int __init it87_wdt_init(void)
superio_select(GAMEPORT);
superio_outb(gpact, ACTREG);
}
+ set_bit(WDTS_USE_CIR, &wdt_status);
}
if (timeout < 1 || timeout > max_units * 60) {
@@ -704,7 +722,7 @@ static int __init it87_wdt_init(void)
}
/* Initialize CIR to use it as keepalive source */
- if (!test_bit(WDTS_USE_GP, &wdt_status)) {
+ if (test_bit(WDTS_USE_CIR, &wdt_status)) {
outb(0x00, CIR_RCR(base));
outb(0xc0, CIR_TCR1(base));
outb(0x5c, CIR_TCR2(base));
@@ -714,9 +732,9 @@ static int __init it87_wdt_init(void)
outb(0x09, CIR_IER(base));
}
- pr_info("Chip IT%04x revision %d initialized. timeout=%d sec (nowayout=%d testmode=%d exclusive=%d nogameport=%d)\n",
+ pr_info("Chip IT%04x revision %d initialized. timeout=%d sec (nowayout=%d testmode=%d exclusive=%d nogameport=%d nocir=%d)\n",
chip_type, chip_rev, timeout,
- nowayout, testmode, exclusive, nogameport);
+ nowayout, testmode, exclusive, nogameport, nocir);
superio_exit();
return 0;
@@ -724,8 +742,10 @@ static int __init it87_wdt_init(void)
err_out_reboot:
unregister_reboot_notifier(&wdt_notifier);
err_out_region:
- release_region(base, test_bit(WDTS_USE_GP, &wdt_status) ? 1 : 8);
- if (!test_bit(WDTS_USE_GP, &wdt_status)) {
+ if (test_bit(WDTS_USE_GP, &wdt_status))
+ release_region(base, 1);
+ else if (test_bit(WDTS_USE_CIR, &wdt_status)) {
+ release_region(base, 8);
superio_select(CIR);
superio_outb(ciract, ACTREG);
}
@@ -751,7 +771,7 @@ static void __exit it87_wdt_exit(void)
if (test_bit(WDTS_USE_GP, &wdt_status)) {
superio_select(GAMEPORT);
superio_outb(gpact, ACTREG);
- } else {
+ } else if (test_bit(WDTS_USE_CIR, &wdt_status)) {
superio_select(CIR);
superio_outb(ciract, ACTREG);
}
@@ -760,7 +780,11 @@ static void __exit it87_wdt_exit(void)
misc_deregister(&wdt_miscdev);
unregister_reboot_notifier(&wdt_notifier);
- release_region(base, test_bit(WDTS_USE_GP, &wdt_status) ? 1 : 8);
+
+ if (test_bit(WDTS_USE_GP, &wdt_status))
+ release_region(base, 1);
+ else if (test_bit(WDTS_USE_CIR, &wdt_status))
+ release_region(base, 8);
}
module_init(it87_wdt_init);
@@ -769,4 +793,3 @@ module_exit(it87_wdt_exit);
MODULE_AUTHOR("Oliver Schuster");
MODULE_DESCRIPTION("Hardware Watchdog Device Driver for IT87xx EC-LPC I/O");
MODULE_LICENSE("GPL");
-MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
diff --git a/drivers/watchdog/ixp2000_wdt.c b/drivers/watchdog/ixp2000_wdt.c
deleted file mode 100644
index 3f047a58d3a..00000000000
--- a/drivers/watchdog/ixp2000_wdt.c
+++ /dev/null
@@ -1,215 +0,0 @@
-/*
- * drivers/char/watchdog/ixp2000_wdt.c
- *
- * Watchdog driver for Intel IXP2000 network processors
- *
- * Adapted from the IXP4xx watchdog driver by Lennert Buytenhek.
- * The original version carries these notices:
- *
- * Author: Deepak Saxena <dsaxena@plexity.net>
- *
- * Copyright 2004 (c) MontaVista, Software, Inc.
- * Based on sa1100 driver, Copyright (C) 2000 Oleg Drokin <green@crimea.edu>
- *
- * This file is licensed under the terms of the GNU General Public
- * License version 2. This program is licensed "as is" without any
- * warranty of any kind, whether express or implied.
- */
-
-#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
-
-#include <linux/module.h>
-#include <linux/moduleparam.h>
-#include <linux/types.h>
-#include <linux/timer.h>
-#include <linux/kernel.h>
-#include <linux/fs.h>
-#include <linux/miscdevice.h>
-#include <linux/watchdog.h>
-#include <linux/init.h>
-#include <linux/bitops.h>
-#include <linux/uaccess.h>
-#include <mach/hardware.h>
-
-static bool nowayout = WATCHDOG_NOWAYOUT;
-static unsigned int heartbeat = 60; /* (secs) Default is 1 minute */
-static unsigned long wdt_status;
-static DEFINE_SPINLOCK(wdt_lock);
-
-#define WDT_IN_USE 0
-#define WDT_OK_TO_CLOSE 1
-
-static unsigned long wdt_tick_rate;
-
-static void wdt_enable(void)
-{
- spin_lock(&wdt_lock);
- ixp2000_reg_write(IXP2000_RESET0, *(IXP2000_RESET0) | WDT_RESET_ENABLE);
- ixp2000_reg_write(IXP2000_TWDE, WDT_ENABLE);
- ixp2000_reg_write(IXP2000_T4_CLD, heartbeat * wdt_tick_rate);
- ixp2000_reg_write(IXP2000_T4_CTL, TIMER_DIVIDER_256 | TIMER_ENABLE);
- spin_unlock(&wdt_lock);
-}
-
-static void wdt_disable(void)
-{
- spin_lock(&wdt_lock);
- ixp2000_reg_write(IXP2000_T4_CTL, 0);
- spin_unlock(&wdt_lock);
-}
-
-static void wdt_keepalive(void)
-{
- spin_lock(&wdt_lock);
- ixp2000_reg_write(IXP2000_T4_CLD, heartbeat * wdt_tick_rate);
- spin_unlock(&wdt_lock);
-}
-
-static int ixp2000_wdt_open(struct inode *inode, struct file *file)
-{
- if (test_and_set_bit(WDT_IN_USE, &wdt_status))
- return -EBUSY;
-
- clear_bit(WDT_OK_TO_CLOSE, &wdt_status);
-
- wdt_enable();
-
- return nonseekable_open(inode, file);
-}
-
-static ssize_t ixp2000_wdt_write(struct file *file, const char *data,
- size_t len, loff_t *ppos)
-{
- if (len) {
- if (!nowayout) {
- size_t i;
-
- clear_bit(WDT_OK_TO_CLOSE, &wdt_status);
-
- for (i = 0; i != len; i++) {
- char c;
-
- if (get_user(c, data + i))
- return -EFAULT;
- if (c == 'V')
- set_bit(WDT_OK_TO_CLOSE, &wdt_status);
- }
- }
- wdt_keepalive();
- }
-
- return len;
-}
-
-
-static const struct watchdog_info ident = {
- .options = WDIOF_MAGICCLOSE | WDIOF_SETTIMEOUT |
- WDIOF_KEEPALIVEPING,
- .identity = "IXP2000 Watchdog",
-};
-
-static long ixp2000_wdt_ioctl(struct file *file, unsigned int cmd,
- unsigned long arg)
-{
- int ret = -ENOTTY;
- int time;
-
- switch (cmd) {
- case WDIOC_GETSUPPORT:
- ret = copy_to_user((struct watchdog_info *)arg, &ident,
- sizeof(ident)) ? -EFAULT : 0;
- break;
-
- case WDIOC_GETSTATUS:
- ret = put_user(0, (int *)arg);
- break;
-
- case WDIOC_GETBOOTSTATUS:
- ret = put_user(0, (int *)arg);
- break;
-
- case WDIOC_KEEPALIVE:
- wdt_enable();
- ret = 0;
- break;
-
- case WDIOC_SETTIMEOUT:
- ret = get_user(time, (int *)arg);
- if (ret)
- break;
-
- if (time <= 0 || time > 60) {
- ret = -EINVAL;
- break;
- }
-
- heartbeat = time;
- wdt_keepalive();
- /* Fall through */
-
- case WDIOC_GETTIMEOUT:
- ret = put_user(heartbeat, (int *)arg);
- break;
- }
-
- return ret;
-}
-
-static int ixp2000_wdt_release(struct inode *inode, struct file *file)
-{
- if (test_bit(WDT_OK_TO_CLOSE, &wdt_status))
- wdt_disable();
- else
- pr_crit("Device closed unexpectedly - timer will not stop\n");
- clear_bit(WDT_IN_USE, &wdt_status);
- clear_bit(WDT_OK_TO_CLOSE, &wdt_status);
-
- return 0;
-}
-
-
-static const struct file_operations ixp2000_wdt_fops = {
- .owner = THIS_MODULE,
- .llseek = no_llseek,
- .write = ixp2000_wdt_write,
- .unlocked_ioctl = ixp2000_wdt_ioctl,
- .open = ixp2000_wdt_open,
- .release = ixp2000_wdt_release,
-};
-
-static struct miscdevice ixp2000_wdt_miscdev = {
- .minor = WATCHDOG_MINOR,
- .name = "watchdog",
- .fops = &ixp2000_wdt_fops,
-};
-
-static int __init ixp2000_wdt_init(void)
-{
- if ((*IXP2000_PRODUCT_ID & 0x001ffef0) == 0x00000000) {
- pr_info("Unable to use IXP2000 watchdog due to IXP2800 erratum #25\n");
- return -EIO;
- }
- wdt_tick_rate = (*IXP2000_T1_CLD * HZ) / 256;
- return misc_register(&ixp2000_wdt_miscdev);
-}
-
-static void __exit ixp2000_wdt_exit(void)
-{
- misc_deregister(&ixp2000_wdt_miscdev);
-}
-
-module_init(ixp2000_wdt_init);
-module_exit(ixp2000_wdt_exit);
-
-MODULE_AUTHOR("Deepak Saxena <dsaxena@plexity.net>");
-MODULE_DESCRIPTION("IXP2000 Network Processor Watchdog");
-
-module_param(heartbeat, int, 0);
-MODULE_PARM_DESC(heartbeat, "Watchdog heartbeat in seconds (default 60s)");
-
-module_param(nowayout, bool, 0);
-MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started");
-
-MODULE_LICENSE("GPL");
-MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
-
diff --git a/drivers/watchdog/ixp4xx_wdt.c b/drivers/watchdog/ixp4xx_wdt.c
index 5580b4fff7f..f20cc53ff71 100644
--- a/drivers/watchdog/ixp4xx_wdt.c
+++ b/drivers/watchdog/ixp4xx_wdt.c
@@ -208,5 +208,3 @@ module_param(nowayout, bool, 0);
MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started");
MODULE_LICENSE("GPL");
-MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
-
diff --git a/drivers/watchdog/jz4740_wdt.c b/drivers/watchdog/jz4740_wdt.c
index 978615ef899..91e45ca589e 100644
--- a/drivers/watchdog/jz4740_wdt.c
+++ b/drivers/watchdog/jz4740_wdt.c
@@ -17,9 +17,7 @@
#include <linux/moduleparam.h>
#include <linux/types.h>
#include <linux/kernel.h>
-#include <linux/miscdevice.h>
#include <linux/watchdog.h>
-#include <linux/init.h>
#include <linux/platform_device.h>
#include <linux/io.h>
#include <linux/device.h>
@@ -144,7 +142,7 @@ static const struct watchdog_ops jz4740_wdt_ops = {
.set_timeout = jz4740_wdt_set_timeout,
};
-static int __devinit jz4740_wdt_probe(struct platform_device *pdev)
+static int jz4740_wdt_probe(struct platform_device *pdev)
{
struct jz4740_wdt_drvdata *drvdata;
struct watchdog_device *jz4740_wdt;
@@ -171,13 +169,13 @@ static int __devinit jz4740_wdt_probe(struct platform_device *pdev)
watchdog_set_drvdata(jz4740_wdt, drvdata);
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- drvdata->base = devm_request_and_ioremap(&pdev->dev, res);
- if (drvdata->base == NULL) {
- ret = -EBUSY;
+ drvdata->base = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(drvdata->base)) {
+ ret = PTR_ERR(drvdata->base);
goto err_out;
}
- drvdata->rtc_clk = clk_get(NULL, "rtc");
+ drvdata->rtc_clk = clk_get(&pdev->dev, "rtc");
if (IS_ERR(drvdata->rtc_clk)) {
dev_err(&pdev->dev, "cannot find RTC clock\n");
ret = PTR_ERR(drvdata->rtc_clk);
@@ -197,7 +195,7 @@ err_out:
return ret;
}
-static int __devexit jz4740_wdt_remove(struct platform_device *pdev)
+static int jz4740_wdt_remove(struct platform_device *pdev)
{
struct jz4740_wdt_drvdata *drvdata = platform_get_drvdata(pdev);
@@ -210,7 +208,7 @@ static int __devexit jz4740_wdt_remove(struct platform_device *pdev)
static struct platform_driver jz4740_wdt_driver = {
.probe = jz4740_wdt_probe,
- .remove = __devexit_p(jz4740_wdt_remove),
+ .remove = jz4740_wdt_remove,
.driver = {
.name = "jz4740-wdt",
.owner = THIS_MODULE,
@@ -222,5 +220,4 @@ module_platform_driver(jz4740_wdt_driver);
MODULE_AUTHOR("Paul Cercueil <paul@crapouillou.net>");
MODULE_DESCRIPTION("jz4740 Watchdog Driver");
MODULE_LICENSE("GPL");
-MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
MODULE_ALIAS("platform:jz4740-wdt");
diff --git a/drivers/watchdog/kempld_wdt.c b/drivers/watchdog/kempld_wdt.c
new file mode 100644
index 00000000000..d9c1a160192
--- /dev/null
+++ b/drivers/watchdog/kempld_wdt.c
@@ -0,0 +1,579 @@
+/*
+ * Kontron PLD watchdog driver
+ *
+ * Copyright (c) 2010-2013 Kontron Europe GmbH
+ * Author: Michael Brunner <michael.brunner@kontron.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License 2 as published
+ * by the Free Software Foundation.
+ *
+ * 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.
+ *
+ * Note: From the PLD watchdog point of view timeout and pretimeout are
+ * defined differently than in the kernel.
+ * First the pretimeout stage runs out before the timeout stage gets
+ * active.
+ *
+ * Kernel/API: P-----| pretimeout
+ * |-----------------------T timeout
+ * Watchdog: |-----------------P pretimeout_stage
+ * |-----T timeout_stage
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/uaccess.h>
+#include <linux/watchdog.h>
+#include <linux/platform_device.h>
+#include <linux/mfd/kempld.h>
+
+#define KEMPLD_WDT_STAGE_TIMEOUT(x) (0x1b + (x) * 4)
+#define KEMPLD_WDT_STAGE_CFG(x) (0x18 + (x))
+#define STAGE_CFG_GET_PRESCALER(x) (((x) & 0x30) >> 4)
+#define STAGE_CFG_SET_PRESCALER(x) (((x) & 0x3) << 4)
+#define STAGE_CFG_PRESCALER_MASK 0x30
+#define STAGE_CFG_ACTION_MASK 0x7
+#define STAGE_CFG_ASSERT (1 << 3)
+
+#define KEMPLD_WDT_MAX_STAGES 2
+#define KEMPLD_WDT_KICK 0x16
+#define KEMPLD_WDT_CFG 0x17
+#define KEMPLD_WDT_CFG_ENABLE 0x10
+#define KEMPLD_WDT_CFG_ENABLE_LOCK 0x8
+#define KEMPLD_WDT_CFG_GLOBAL_LOCK 0x80
+
+enum {
+ ACTION_NONE = 0,
+ ACTION_RESET,
+ ACTION_NMI,
+ ACTION_SMI,
+ ACTION_SCI,
+ ACTION_DELAY,
+};
+
+enum {
+ STAGE_TIMEOUT = 0,
+ STAGE_PRETIMEOUT,
+};
+
+enum {
+ PRESCALER_21 = 0,
+ PRESCALER_17,
+ PRESCALER_12,
+};
+
+static const u32 kempld_prescaler[] = {
+ [PRESCALER_21] = (1 << 21) - 1,
+ [PRESCALER_17] = (1 << 17) - 1,
+ [PRESCALER_12] = (1 << 12) - 1,
+ 0,
+};
+
+struct kempld_wdt_stage {
+ unsigned int id;
+ u32 mask;
+};
+
+struct kempld_wdt_data {
+ struct kempld_device_data *pld;
+ struct watchdog_device wdd;
+ unsigned int pretimeout;
+ struct kempld_wdt_stage stage[KEMPLD_WDT_MAX_STAGES];
+#ifdef CONFIG_PM
+ u8 pm_status_store;
+#endif
+};
+
+#define DEFAULT_TIMEOUT 30 /* seconds */
+#define DEFAULT_PRETIMEOUT 0
+
+static unsigned int timeout = DEFAULT_TIMEOUT;
+module_param(timeout, uint, 0);
+MODULE_PARM_DESC(timeout,
+ "Watchdog timeout in seconds. (>=0, default="
+ __MODULE_STRING(DEFAULT_TIMEOUT) ")");
+
+static unsigned int pretimeout = DEFAULT_PRETIMEOUT;
+module_param(pretimeout, uint, 0);
+MODULE_PARM_DESC(pretimeout,
+ "Watchdog pretimeout in seconds. (>=0, default="
+ __MODULE_STRING(DEFAULT_PRETIMEOUT) ")");
+
+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) ")");
+
+static int kempld_wdt_set_stage_action(struct kempld_wdt_data *wdt_data,
+ struct kempld_wdt_stage *stage,
+ u8 action)
+{
+ struct kempld_device_data *pld = wdt_data->pld;
+ u8 stage_cfg;
+
+ if (!stage || !stage->mask)
+ return -EINVAL;
+
+ kempld_get_mutex(pld);
+ stage_cfg = kempld_read8(pld, KEMPLD_WDT_STAGE_CFG(stage->id));
+ stage_cfg &= ~STAGE_CFG_ACTION_MASK;
+ stage_cfg |= (action & STAGE_CFG_ACTION_MASK);
+
+ if (action == ACTION_RESET)
+ stage_cfg |= STAGE_CFG_ASSERT;
+ else
+ stage_cfg &= ~STAGE_CFG_ASSERT;
+
+ kempld_write8(pld, KEMPLD_WDT_STAGE_CFG(stage->id), stage_cfg);
+ kempld_release_mutex(pld);
+
+ return 0;
+}
+
+static int kempld_wdt_set_stage_timeout(struct kempld_wdt_data *wdt_data,
+ struct kempld_wdt_stage *stage,
+ unsigned int timeout)
+{
+ struct kempld_device_data *pld = wdt_data->pld;
+ u32 prescaler = kempld_prescaler[PRESCALER_21];
+ u64 stage_timeout64;
+ u32 stage_timeout;
+ u32 remainder;
+ u8 stage_cfg;
+
+ if (!stage)
+ return -EINVAL;
+
+ stage_timeout64 = (u64)timeout * pld->pld_clock;
+ remainder = do_div(stage_timeout64, prescaler);
+ if (remainder)
+ stage_timeout64++;
+
+ if (stage_timeout64 > stage->mask)
+ return -EINVAL;
+
+ stage_timeout = stage_timeout64 & stage->mask;
+
+ kempld_get_mutex(pld);
+ stage_cfg = kempld_read8(pld, KEMPLD_WDT_STAGE_CFG(stage->id));
+ stage_cfg &= ~STAGE_CFG_PRESCALER_MASK;
+ stage_cfg |= STAGE_CFG_SET_PRESCALER(PRESCALER_21);
+ kempld_write8(pld, KEMPLD_WDT_STAGE_CFG(stage->id), stage_cfg);
+ kempld_write32(pld, KEMPLD_WDT_STAGE_TIMEOUT(stage->id),
+ stage_timeout);
+ kempld_release_mutex(pld);
+
+ return 0;
+}
+
+/*
+ * kempld_get_mutex must be called prior to calling this function.
+ */
+static unsigned int kempld_wdt_get_timeout(struct kempld_wdt_data *wdt_data,
+ struct kempld_wdt_stage *stage)
+{
+ struct kempld_device_data *pld = wdt_data->pld;
+ unsigned int timeout;
+ u64 stage_timeout;
+ u32 prescaler;
+ u32 remainder;
+ u8 stage_cfg;
+
+ if (!stage->mask)
+ return 0;
+
+ stage_cfg = kempld_read8(pld, KEMPLD_WDT_STAGE_CFG(stage->id));
+ stage_timeout = kempld_read32(pld, KEMPLD_WDT_STAGE_TIMEOUT(stage->id));
+ prescaler = kempld_prescaler[STAGE_CFG_GET_PRESCALER(stage_cfg)];
+
+ stage_timeout = (stage_timeout & stage->mask) * prescaler;
+ remainder = do_div(stage_timeout, pld->pld_clock);
+ if (remainder)
+ stage_timeout++;
+
+ timeout = stage_timeout;
+ WARN_ON_ONCE(timeout != stage_timeout);
+
+ return timeout;
+}
+
+static int kempld_wdt_set_timeout(struct watchdog_device *wdd,
+ unsigned int timeout)
+{
+ struct kempld_wdt_data *wdt_data = watchdog_get_drvdata(wdd);
+ struct kempld_wdt_stage *pretimeout_stage;
+ struct kempld_wdt_stage *timeout_stage;
+ int ret;
+
+ timeout_stage = &wdt_data->stage[STAGE_TIMEOUT];
+ pretimeout_stage = &wdt_data->stage[STAGE_PRETIMEOUT];
+
+ if (pretimeout_stage->mask && wdt_data->pretimeout > 0)
+ timeout = wdt_data->pretimeout;
+
+ ret = kempld_wdt_set_stage_action(wdt_data, timeout_stage,
+ ACTION_RESET);
+ if (ret)
+ return ret;
+ ret = kempld_wdt_set_stage_timeout(wdt_data, timeout_stage,
+ timeout);
+ if (ret)
+ return ret;
+
+ wdd->timeout = timeout;
+ return 0;
+}
+
+static int kempld_wdt_set_pretimeout(struct watchdog_device *wdd,
+ unsigned int pretimeout)
+{
+ struct kempld_wdt_data *wdt_data = watchdog_get_drvdata(wdd);
+ struct kempld_wdt_stage *pretimeout_stage;
+ u8 action = ACTION_NONE;
+ int ret;
+
+ pretimeout_stage = &wdt_data->stage[STAGE_PRETIMEOUT];
+
+ if (!pretimeout_stage->mask)
+ return -ENXIO;
+
+ if (pretimeout > wdd->timeout)
+ return -EINVAL;
+
+ if (pretimeout > 0)
+ action = ACTION_NMI;
+
+ ret = kempld_wdt_set_stage_action(wdt_data, pretimeout_stage,
+ action);
+ if (ret)
+ return ret;
+ ret = kempld_wdt_set_stage_timeout(wdt_data, pretimeout_stage,
+ wdd->timeout - pretimeout);
+ if (ret)
+ return ret;
+
+ wdt_data->pretimeout = pretimeout;
+ return 0;
+}
+
+static void kempld_wdt_update_timeouts(struct kempld_wdt_data *wdt_data)
+{
+ struct kempld_device_data *pld = wdt_data->pld;
+ struct kempld_wdt_stage *pretimeout_stage;
+ struct kempld_wdt_stage *timeout_stage;
+ unsigned int pretimeout, timeout;
+
+ pretimeout_stage = &wdt_data->stage[STAGE_PRETIMEOUT];
+ timeout_stage = &wdt_data->stage[STAGE_TIMEOUT];
+
+ kempld_get_mutex(pld);
+ pretimeout = kempld_wdt_get_timeout(wdt_data, pretimeout_stage);
+ timeout = kempld_wdt_get_timeout(wdt_data, timeout_stage);
+ kempld_release_mutex(pld);
+
+ if (pretimeout)
+ wdt_data->pretimeout = timeout;
+ else
+ wdt_data->pretimeout = 0;
+
+ wdt_data->wdd.timeout = pretimeout + timeout;
+}
+
+static int kempld_wdt_start(struct watchdog_device *wdd)
+{
+ struct kempld_wdt_data *wdt_data = watchdog_get_drvdata(wdd);
+ struct kempld_device_data *pld = wdt_data->pld;
+ u8 status;
+ int ret;
+
+ ret = kempld_wdt_set_timeout(wdd, wdd->timeout);
+ if (ret)
+ return ret;
+
+ kempld_get_mutex(pld);
+ status = kempld_read8(pld, KEMPLD_WDT_CFG);
+ status |= KEMPLD_WDT_CFG_ENABLE;
+ kempld_write8(pld, KEMPLD_WDT_CFG, status);
+ status = kempld_read8(pld, KEMPLD_WDT_CFG);
+ kempld_release_mutex(pld);
+
+ /* Check if the watchdog was enabled */
+ if (!(status & KEMPLD_WDT_CFG_ENABLE))
+ return -EACCES;
+
+ return 0;
+}
+
+static int kempld_wdt_stop(struct watchdog_device *wdd)
+{
+ struct kempld_wdt_data *wdt_data = watchdog_get_drvdata(wdd);
+ struct kempld_device_data *pld = wdt_data->pld;
+ u8 status;
+
+ kempld_get_mutex(pld);
+ status = kempld_read8(pld, KEMPLD_WDT_CFG);
+ status &= ~KEMPLD_WDT_CFG_ENABLE;
+ kempld_write8(pld, KEMPLD_WDT_CFG, status);
+ status = kempld_read8(pld, KEMPLD_WDT_CFG);
+ kempld_release_mutex(pld);
+
+ /* Check if the watchdog was disabled */
+ if (status & KEMPLD_WDT_CFG_ENABLE)
+ return -EACCES;
+
+ return 0;
+}
+
+static int kempld_wdt_keepalive(struct watchdog_device *wdd)
+{
+ struct kempld_wdt_data *wdt_data = watchdog_get_drvdata(wdd);
+ struct kempld_device_data *pld = wdt_data->pld;
+
+ kempld_get_mutex(pld);
+ kempld_write8(pld, KEMPLD_WDT_KICK, 'K');
+ kempld_release_mutex(pld);
+
+ return 0;
+}
+
+static long kempld_wdt_ioctl(struct watchdog_device *wdd, unsigned int cmd,
+ unsigned long arg)
+{
+ struct kempld_wdt_data *wdt_data = watchdog_get_drvdata(wdd);
+ void __user *argp = (void __user *)arg;
+ int ret = -ENOIOCTLCMD;
+ int __user *p = argp;
+ int new_value;
+
+ switch (cmd) {
+ case WDIOC_SETPRETIMEOUT:
+ if (get_user(new_value, p))
+ return -EFAULT;
+ ret = kempld_wdt_set_pretimeout(wdd, new_value);
+ if (ret)
+ return ret;
+ ret = kempld_wdt_keepalive(wdd);
+ break;
+ case WDIOC_GETPRETIMEOUT:
+ ret = put_user(wdt_data->pretimeout, (int __user *)arg);
+ break;
+ }
+
+ return ret;
+}
+
+static int kempld_wdt_probe_stages(struct watchdog_device *wdd)
+{
+ struct kempld_wdt_data *wdt_data = watchdog_get_drvdata(wdd);
+ struct kempld_device_data *pld = wdt_data->pld;
+ struct kempld_wdt_stage *pretimeout_stage;
+ struct kempld_wdt_stage *timeout_stage;
+ u8 index, data, data_orig;
+ u32 mask;
+ int i, j;
+
+ pretimeout_stage = &wdt_data->stage[STAGE_PRETIMEOUT];
+ timeout_stage = &wdt_data->stage[STAGE_TIMEOUT];
+
+ pretimeout_stage->mask = 0;
+ timeout_stage->mask = 0;
+
+ for (i = 0; i < 3; i++) {
+ index = KEMPLD_WDT_STAGE_TIMEOUT(i);
+ mask = 0;
+
+ kempld_get_mutex(pld);
+ /* Probe each byte individually. */
+ for (j = 0; j < 4; j++) {
+ data_orig = kempld_read8(pld, index + j);
+ kempld_write8(pld, index + j, 0x00);
+ data = kempld_read8(pld, index + j);
+ /* A failed write means this byte is reserved */
+ if (data != 0x00)
+ break;
+ kempld_write8(pld, index + j, data_orig);
+ mask |= 0xff << (j * 8);
+ }
+ kempld_release_mutex(pld);
+
+ /* Assign available stages to timeout and pretimeout */
+ if (!timeout_stage->mask) {
+ timeout_stage->mask = mask;
+ timeout_stage->id = i;
+ } else {
+ if (pld->feature_mask & KEMPLD_FEATURE_BIT_NMI) {
+ pretimeout_stage->mask = timeout_stage->mask;
+ timeout_stage->mask = mask;
+ pretimeout_stage->id = timeout_stage->id;
+ timeout_stage->id = i;
+ }
+ break;
+ }
+ }
+
+ if (!timeout_stage->mask)
+ return -ENODEV;
+
+ return 0;
+}
+
+static struct watchdog_info kempld_wdt_info = {
+ .identity = "KEMPLD Watchdog",
+ .options = WDIOF_SETTIMEOUT |
+ WDIOF_KEEPALIVEPING |
+ WDIOF_MAGICCLOSE |
+ WDIOF_PRETIMEOUT
+};
+
+static struct watchdog_ops kempld_wdt_ops = {
+ .owner = THIS_MODULE,
+ .start = kempld_wdt_start,
+ .stop = kempld_wdt_stop,
+ .ping = kempld_wdt_keepalive,
+ .set_timeout = kempld_wdt_set_timeout,
+ .ioctl = kempld_wdt_ioctl,
+};
+
+static int kempld_wdt_probe(struct platform_device *pdev)
+{
+ struct kempld_device_data *pld = dev_get_drvdata(pdev->dev.parent);
+ struct kempld_wdt_data *wdt_data;
+ struct device *dev = &pdev->dev;
+ struct watchdog_device *wdd;
+ u8 status;
+ int ret = 0;
+
+ wdt_data = devm_kzalloc(dev, sizeof(*wdt_data), GFP_KERNEL);
+ if (!wdt_data)
+ return -ENOMEM;
+
+ wdt_data->pld = pld;
+ wdd = &wdt_data->wdd;
+ wdd->parent = dev;
+
+ kempld_get_mutex(pld);
+ status = kempld_read8(pld, KEMPLD_WDT_CFG);
+ kempld_release_mutex(pld);
+
+ /* Enable nowayout if watchdog is already locked */
+ if (status & (KEMPLD_WDT_CFG_ENABLE_LOCK |
+ KEMPLD_WDT_CFG_GLOBAL_LOCK)) {
+ if (!nowayout)
+ dev_warn(dev,
+ "Forcing nowayout - watchdog lock enabled!\n");
+ nowayout = true;
+ }
+
+ wdd->info = &kempld_wdt_info;
+ wdd->ops = &kempld_wdt_ops;
+
+ watchdog_set_drvdata(wdd, wdt_data);
+ watchdog_set_nowayout(wdd, nowayout);
+
+ ret = kempld_wdt_probe_stages(wdd);
+ if (ret)
+ return ret;
+
+ kempld_wdt_set_timeout(wdd, timeout);
+ kempld_wdt_set_pretimeout(wdd, pretimeout);
+
+ /* Check if watchdog is already enabled */
+ if (status & KEMPLD_WDT_CFG_ENABLE) {
+ /* Get current watchdog settings */
+ kempld_wdt_update_timeouts(wdt_data);
+ dev_info(dev, "Watchdog was already enabled\n");
+ }
+
+ platform_set_drvdata(pdev, wdt_data);
+ ret = watchdog_register_device(wdd);
+ if (ret)
+ return ret;
+
+ dev_info(dev, "Watchdog registered with %ds timeout\n", wdd->timeout);
+
+ return 0;
+}
+
+static void kempld_wdt_shutdown(struct platform_device *pdev)
+{
+ struct kempld_wdt_data *wdt_data = platform_get_drvdata(pdev);
+
+ kempld_wdt_stop(&wdt_data->wdd);
+}
+
+static int kempld_wdt_remove(struct platform_device *pdev)
+{
+ struct kempld_wdt_data *wdt_data = platform_get_drvdata(pdev);
+ struct watchdog_device *wdd = &wdt_data->wdd;
+ int ret = 0;
+
+ if (!nowayout)
+ ret = kempld_wdt_stop(wdd);
+ watchdog_unregister_device(wdd);
+
+ return ret;
+}
+
+#ifdef CONFIG_PM
+/* Disable watchdog if it is active during suspend */
+static int kempld_wdt_suspend(struct platform_device *pdev,
+ pm_message_t message)
+{
+ struct kempld_wdt_data *wdt_data = platform_get_drvdata(pdev);
+ struct kempld_device_data *pld = wdt_data->pld;
+ struct watchdog_device *wdd = &wdt_data->wdd;
+
+ kempld_get_mutex(pld);
+ wdt_data->pm_status_store = kempld_read8(pld, KEMPLD_WDT_CFG);
+ kempld_release_mutex(pld);
+
+ kempld_wdt_update_timeouts(wdt_data);
+
+ if (wdt_data->pm_status_store & KEMPLD_WDT_CFG_ENABLE)
+ return kempld_wdt_stop(wdd);
+
+ return 0;
+}
+
+/* Enable watchdog and configure it if necessary */
+static int kempld_wdt_resume(struct platform_device *pdev)
+{
+ struct kempld_wdt_data *wdt_data = platform_get_drvdata(pdev);
+ struct watchdog_device *wdd = &wdt_data->wdd;
+
+ /*
+ * If watchdog was stopped before suspend be sure it gets disabled
+ * again, for the case BIOS has enabled it during resume
+ */
+ if (wdt_data->pm_status_store & KEMPLD_WDT_CFG_ENABLE)
+ return kempld_wdt_start(wdd);
+ else
+ return kempld_wdt_stop(wdd);
+}
+#else
+#define kempld_wdt_suspend NULL
+#define kempld_wdt_resume NULL
+#endif
+
+static struct platform_driver kempld_wdt_driver = {
+ .driver = {
+ .name = "kempld-wdt",
+ .owner = THIS_MODULE,
+ },
+ .probe = kempld_wdt_probe,
+ .remove = kempld_wdt_remove,
+ .shutdown = kempld_wdt_shutdown,
+ .suspend = kempld_wdt_suspend,
+ .resume = kempld_wdt_resume,
+};
+
+module_platform_driver(kempld_wdt_driver);
+
+MODULE_DESCRIPTION("KEM PLD Watchdog Driver");
+MODULE_AUTHOR("Michael Brunner <michael.brunner@kontron.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/watchdog/ks8695_wdt.c b/drivers/watchdog/ks8695_wdt.c
index 59e75d9a6b7..40ca5594a33 100644
--- a/drivers/watchdog/ks8695_wdt.c
+++ b/drivers/watchdog/ks8695_wdt.c
@@ -24,7 +24,19 @@
#include <linux/io.h>
#include <linux/uaccess.h>
#include <mach/hardware.h>
-#include <mach/regs-timer.h>
+
+#define KS8695_TMR_OFFSET (0xF0000 + 0xE400)
+#define KS8695_TMR_VA (KS8695_IO_VA + KS8695_TMR_OFFSET)
+
+/*
+ * Timer registers
+ */
+#define KS8695_TMCON (0x00) /* Timer Control Register */
+#define KS8695_T0TC (0x08) /* Timer 0 Timeout Count Register */
+#define TMCON_T0EN (1 << 0) /* Timer 0 Enable */
+
+/* Timer0 Timeout Counter Register */
+#define T0TC_WATCHDOG (0xff) /* Enable watchdog mode */
#define WDT_DEFAULT_TIME 5 /* seconds */
#define WDT_MAX_TIME 171 /* seconds */
@@ -223,7 +235,7 @@ static struct miscdevice ks8695wdt_miscdev = {
.fops = &ks8695wdt_fops,
};
-static int __devinit ks8695wdt_probe(struct platform_device *pdev)
+static int ks8695wdt_probe(struct platform_device *pdev)
{
int res;
@@ -240,7 +252,7 @@ static int __devinit ks8695wdt_probe(struct platform_device *pdev)
return 0;
}
-static int __devexit ks8695wdt_remove(struct platform_device *pdev)
+static int ks8695wdt_remove(struct platform_device *pdev)
{
int res;
@@ -278,7 +290,7 @@ static int ks8695wdt_resume(struct platform_device *pdev)
static struct platform_driver ks8695wdt_driver = {
.probe = ks8695wdt_probe,
- .remove = __devexit_p(ks8695wdt_remove),
+ .remove = ks8695wdt_remove,
.shutdown = ks8695wdt_shutdown,
.suspend = ks8695wdt_suspend,
.resume = ks8695wdt_resume,
@@ -311,5 +323,4 @@ module_exit(ks8695_wdt_exit);
MODULE_AUTHOR("Andrew Victor");
MODULE_DESCRIPTION("Watchdog driver for KS8695");
MODULE_LICENSE("GPL");
-MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
MODULE_ALIAS("platform:ks8695_wdt");
diff --git a/drivers/watchdog/lantiq_wdt.c b/drivers/watchdog/lantiq_wdt.c
index a9593a3a32a..3b3148c764a 100644
--- a/drivers/watchdog/lantiq_wdt.c
+++ b/drivers/watchdog/lantiq_wdt.c
@@ -13,14 +13,15 @@
#include <linux/fs.h>
#include <linux/miscdevice.h>
#include <linux/watchdog.h>
-#include <linux/platform_device.h>
+#include <linux/of_platform.h>
#include <linux/uaccess.h>
#include <linux/clk.h>
#include <linux/io.h>
-#include <lantiq.h>
+#include <lantiq_soc.h>
-/* Section 3.4 of the datasheet
+/*
+ * Section 3.4 of the datasheet
* The password sequence protects the WDT control register from unintended
* write actions, which might cause malfunction of the WDT.
*
@@ -70,7 +71,8 @@ ltq_wdt_disable(void)
{
/* write the first password magic */
ltq_w32(LTQ_WDT_PW1, ltq_wdt_membase + LTQ_WDT_CR);
- /* write the second password magic with no config
+ /*
+ * write the second password magic with no config
* this turns the watchdog off
*/
ltq_w32(LTQ_WDT_PW2, ltq_wdt_membase + LTQ_WDT_CR);
@@ -184,7 +186,7 @@ static struct miscdevice ltq_wdt_miscdev = {
.fops = &ltq_wdt_fops,
};
-static int __init
+static int
ltq_wdt_probe(struct platform_device *pdev)
{
struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
@@ -194,32 +196,29 @@ ltq_wdt_probe(struct platform_device *pdev)
dev_err(&pdev->dev, "cannot obtain I/O memory region");
return -ENOENT;
}
- res = devm_request_mem_region(&pdev->dev, res->start,
- resource_size(res), dev_name(&pdev->dev));
- if (!res) {
- dev_err(&pdev->dev, "cannot request I/O memory region");
- return -EBUSY;
- }
- ltq_wdt_membase = devm_ioremap_nocache(&pdev->dev, res->start,
- resource_size(res));
- if (!ltq_wdt_membase) {
- dev_err(&pdev->dev, "cannot remap I/O memory region\n");
- return -ENOMEM;
- }
+
+ ltq_wdt_membase = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(ltq_wdt_membase))
+ return PTR_ERR(ltq_wdt_membase);
/* we do not need to enable the clock as it is always running */
- clk = clk_get(&pdev->dev, "io");
- WARN_ON(!clk);
+ clk = clk_get_io();
+ if (IS_ERR(clk)) {
+ dev_err(&pdev->dev, "Failed to get clock\n");
+ return -ENOENT;
+ }
ltq_io_region_clk_rate = clk_get_rate(clk);
clk_put(clk);
+ /* find out if the watchdog caused the last reboot */
if (ltq_reset_cause() == LTQ_RST_CAUSE_WDTRST)
ltq_wdt_bootstatus = WDIOF_CARDRESET;
+ dev_info(&pdev->dev, "Init done\n");
return misc_register(&ltq_wdt_miscdev);
}
-static int __devexit
+static int
ltq_wdt_remove(struct platform_device *pdev)
{
misc_deregister(&ltq_wdt_miscdev);
@@ -227,34 +226,26 @@ ltq_wdt_remove(struct platform_device *pdev)
return 0;
}
+static const struct of_device_id ltq_wdt_match[] = {
+ { .compatible = "lantiq,wdt" },
+ {},
+};
+MODULE_DEVICE_TABLE(of, ltq_wdt_match);
static struct platform_driver ltq_wdt_driver = {
- .remove = __devexit_p(ltq_wdt_remove),
+ .probe = ltq_wdt_probe,
+ .remove = ltq_wdt_remove,
.driver = {
- .name = "ltq_wdt",
+ .name = "wdt",
.owner = THIS_MODULE,
+ .of_match_table = ltq_wdt_match,
},
};
-static int __init
-init_ltq_wdt(void)
-{
- return platform_driver_probe(&ltq_wdt_driver, ltq_wdt_probe);
-}
-
-static void __exit
-exit_ltq_wdt(void)
-{
- return platform_driver_unregister(&ltq_wdt_driver);
-}
-
-module_init(init_ltq_wdt);
-module_exit(exit_ltq_wdt);
+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_DESCRIPTION("Lantiq SoC Watchdog");
MODULE_LICENSE("GPL");
-MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
diff --git a/drivers/watchdog/m54xx_wdt.c b/drivers/watchdog/m54xx_wdt.c
index 663cad86c63..da6fa2b6807 100644
--- a/drivers/watchdog/m54xx_wdt.c
+++ b/drivers/watchdog/m54xx_wdt.c
@@ -46,17 +46,17 @@ static void wdt_enable(void)
unsigned int gms0;
/* preserve GPIO usage, if any */
- gms0 = __raw_readl(MCF_MBAR + MCF_GPT_GMS0);
+ gms0 = __raw_readl(MCF_GPT_GMS0);
if (gms0 & MCF_GPT_GMS_TMS_GPIO)
gms0 &= (MCF_GPT_GMS_TMS_GPIO | MCF_GPT_GMS_GPIO_MASK
| MCF_GPT_GMS_OD);
else
gms0 = MCF_GPT_GMS_TMS_GPIO | MCF_GPT_GMS_OD;
- __raw_writel(gms0, MCF_MBAR + MCF_GPT_GMS0);
+ __raw_writel(gms0, MCF_GPT_GMS0);
__raw_writel(MCF_GPT_GCIR_PRE(heartbeat*(MCF_BUSCLK/0xffff)) |
- MCF_GPT_GCIR_CNT(0xffff), MCF_MBAR + MCF_GPT_GCIR0);
+ MCF_GPT_GCIR_CNT(0xffff), MCF_GPT_GCIR0);
gms0 |= MCF_GPT_GMS_OCPW(0xA5) | MCF_GPT_GMS_WDEN | MCF_GPT_GMS_CE;
- __raw_writel(gms0, MCF_MBAR + MCF_GPT_GMS0);
+ __raw_writel(gms0, MCF_GPT_GMS0);
}
static void wdt_disable(void)
@@ -64,18 +64,18 @@ static void wdt_disable(void)
unsigned int gms0;
/* disable watchdog */
- gms0 = __raw_readl(MCF_MBAR + MCF_GPT_GMS0);
+ gms0 = __raw_readl(MCF_GPT_GMS0);
gms0 &= ~(MCF_GPT_GMS_WDEN | MCF_GPT_GMS_CE);
- __raw_writel(gms0, MCF_MBAR + MCF_GPT_GMS0);
+ __raw_writel(gms0, MCF_GPT_GMS0);
}
static void wdt_keepalive(void)
{
unsigned int gms0;
- gms0 = __raw_readl(MCF_MBAR + MCF_GPT_GMS0);
+ gms0 = __raw_readl(MCF_GPT_GMS0);
gms0 |= MCF_GPT_GMS_OCPW(0xA5);
- __raw_writel(gms0, MCF_MBAR + MCF_GPT_GMS0);
+ __raw_writel(gms0, MCF_GPT_GMS0);
}
static int m54xx_wdt_open(struct inode *inode, struct file *file)
@@ -195,8 +195,7 @@ static struct miscdevice m54xx_wdt_miscdev = {
static int __init m54xx_wdt_init(void)
{
- if (!request_mem_region(MCF_MBAR + MCF_GPT_GCIR0, 4,
- "Coldfire M54xx Watchdog")) {
+ if (!request_mem_region(MCF_GPT_GCIR0, 4, "Coldfire M54xx Watchdog")) {
pr_warn("I/O region busy\n");
return -EBUSY;
}
@@ -208,7 +207,7 @@ static int __init m54xx_wdt_init(void)
static void __exit m54xx_wdt_exit(void)
{
misc_deregister(&m54xx_wdt_miscdev);
- release_mem_region(MCF_MBAR + MCF_GPT_GCIR0, 4);
+ release_mem_region(MCF_GPT_GCIR0, 4);
}
module_init(m54xx_wdt_init);
@@ -224,4 +223,3 @@ module_param(nowayout, bool, 0);
MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started");
MODULE_LICENSE("GPL");
-MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
diff --git a/drivers/watchdog/machzwd.c b/drivers/watchdog/machzwd.c
index bf84f788e59..9826b59ef73 100644
--- a/drivers/watchdog/machzwd.c
+++ b/drivers/watchdog/machzwd.c
@@ -92,7 +92,6 @@ static unsigned short zf_readw(unsigned char port)
MODULE_AUTHOR("Fernando Fuganti <fuganti@conectiva.com.br>");
MODULE_DESCRIPTION("MachZ ZF-Logic Watchdog driver");
MODULE_LICENSE("GPL");
-MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
static bool nowayout = WATCHDOG_NOWAYOUT;
module_param(nowayout, bool, 0);
diff --git a/drivers/watchdog/max63xx_wdt.c b/drivers/watchdog/max63xx_wdt.c
index 8f4a74e9161..0e9cc6f5a91 100644
--- a/drivers/watchdog/max63xx_wdt.c
+++ b/drivers/watchdog/max63xx_wdt.c
@@ -14,13 +14,12 @@
* another interface, some abstraction will have to be introduced.
*/
+#include <linux/err.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/types.h>
#include <linux/kernel.h>
-#include <linux/miscdevice.h>
#include <linux/watchdog.h>
-#include <linux/init.h>
#include <linux/bitops.h>
#include <linux/platform_device.h>
#include <linux/spinlock.h>
@@ -174,7 +173,7 @@ static struct watchdog_device max63xx_wdt_dev = {
.ops = &max63xx_wdt_ops,
};
-static int __devinit max63xx_wdt_probe(struct platform_device *pdev)
+static int max63xx_wdt_probe(struct platform_device *pdev)
{
struct resource *wdt_mem;
struct max63xx_timeout *table;
@@ -198,9 +197,9 @@ static int __devinit max63xx_wdt_probe(struct platform_device *pdev)
heartbeat = current_timeout->twd;
wdt_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- wdt_base = devm_request_and_ioremap(&pdev->dev, wdt_mem);
- if (!wdt_base)
- return -ENOMEM;
+ wdt_base = devm_ioremap_resource(&pdev->dev, wdt_mem);
+ if (IS_ERR(wdt_base))
+ return PTR_ERR(wdt_base);
max63xx_wdt_dev.timeout = heartbeat;
watchdog_set_nowayout(&max63xx_wdt_dev, nowayout);
@@ -209,7 +208,7 @@ static int __devinit max63xx_wdt_probe(struct platform_device *pdev)
return watchdog_register_device(&max63xx_wdt_dev);
}
-static int __devexit max63xx_wdt_remove(struct platform_device *pdev)
+static int max63xx_wdt_remove(struct platform_device *pdev)
{
watchdog_unregister_device(&max63xx_wdt_dev);
return 0;
@@ -228,7 +227,7 @@ MODULE_DEVICE_TABLE(platform, max63xx_id_table);
static struct platform_driver max63xx_wdt_driver = {
.probe = max63xx_wdt_probe,
- .remove = __devexit_p(max63xx_wdt_remove),
+ .remove = max63xx_wdt_remove,
.id_table = max63xx_id_table,
.driver = {
.name = "max63xx_wdt",
@@ -257,4 +256,3 @@ MODULE_PARM_DESC(nodelay,
"(max6373/74 only, default=0)");
MODULE_LICENSE("GPL");
-MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
diff --git a/drivers/watchdog/mena21_wdt.c b/drivers/watchdog/mena21_wdt.c
new file mode 100644
index 00000000000..96dbba98057
--- /dev/null
+++ b/drivers/watchdog/mena21_wdt.c
@@ -0,0 +1,270 @@
+/*
+ * Watchdog driver for the A21 VME CPU Boards
+ *
+ * Copyright (C) 2013 MEN Mikro Elektronik Nuernberg GmbH
+ *
+ * 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
+ */
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/platform_device.h>
+#include <linux/watchdog.h>
+#include <linux/uaccess.h>
+#include <linux/gpio.h>
+#include <linux/of_gpio.h>
+#include <linux/delay.h>
+#include <linux/bitops.h>
+
+#define NUM_GPIOS 6
+
+enum a21_wdt_gpios {
+ GPIO_WD_ENAB,
+ GPIO_WD_FAST,
+ GPIO_WD_TRIG,
+ GPIO_WD_RST0,
+ GPIO_WD_RST1,
+ GPIO_WD_RST2,
+};
+
+struct a21_wdt_drv {
+ struct watchdog_device wdt;
+ struct mutex lock;
+ unsigned gpios[NUM_GPIOS];
+};
+
+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) ")");
+
+static unsigned int a21_wdt_get_bootstatus(struct a21_wdt_drv *drv)
+{
+ int reset = 0;
+
+ reset |= gpio_get_value(drv->gpios[GPIO_WD_RST0]) ? (1 << 0) : 0;
+ reset |= gpio_get_value(drv->gpios[GPIO_WD_RST1]) ? (1 << 1) : 0;
+ reset |= gpio_get_value(drv->gpios[GPIO_WD_RST2]) ? (1 << 2) : 0;
+
+ return reset;
+}
+
+static int a21_wdt_start(struct watchdog_device *wdt)
+{
+ struct a21_wdt_drv *drv = watchdog_get_drvdata(wdt);
+
+ mutex_lock(&drv->lock);
+
+ gpio_set_value(drv->gpios[GPIO_WD_ENAB], 1);
+
+ mutex_unlock(&drv->lock);
+
+ return 0;
+}
+
+static int a21_wdt_stop(struct watchdog_device *wdt)
+{
+ struct a21_wdt_drv *drv = watchdog_get_drvdata(wdt);
+
+ mutex_lock(&drv->lock);
+
+ gpio_set_value(drv->gpios[GPIO_WD_ENAB], 0);
+
+ mutex_unlock(&drv->lock);
+
+ return 0;
+}
+
+static int a21_wdt_ping(struct watchdog_device *wdt)
+{
+ struct a21_wdt_drv *drv = watchdog_get_drvdata(wdt);
+
+ mutex_lock(&drv->lock);
+
+ gpio_set_value(drv->gpios[GPIO_WD_TRIG], 0);
+ ndelay(10);
+ gpio_set_value(drv->gpios[GPIO_WD_TRIG], 1);
+
+ mutex_unlock(&drv->lock);
+
+ return 0;
+}
+
+static int a21_wdt_set_timeout(struct watchdog_device *wdt,
+ unsigned int timeout)
+{
+ struct a21_wdt_drv *drv = watchdog_get_drvdata(wdt);
+
+ if (timeout != 1 && timeout != 30) {
+ dev_err(wdt->dev, "Only 1 and 30 allowed as timeout\n");
+ return -EINVAL;
+ }
+
+ if (timeout == 30 && wdt->timeout == 1) {
+ dev_err(wdt->dev,
+ "Transition from fast to slow mode not allowed\n");
+ return -EINVAL;
+ }
+
+ mutex_lock(&drv->lock);
+
+ if (timeout == 1)
+ gpio_set_value(drv->gpios[GPIO_WD_FAST], 1);
+ else
+ gpio_set_value(drv->gpios[GPIO_WD_FAST], 0);
+
+ wdt->timeout = timeout;
+
+ mutex_unlock(&drv->lock);
+
+ return 0;
+}
+
+static const struct watchdog_info a21_wdt_info = {
+ .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE,
+ .identity = "MEN A21 Watchdog",
+};
+
+static const struct watchdog_ops a21_wdt_ops = {
+ .owner = THIS_MODULE,
+ .start = a21_wdt_start,
+ .stop = a21_wdt_stop,
+ .ping = a21_wdt_ping,
+ .set_timeout = a21_wdt_set_timeout,
+};
+
+static struct watchdog_device a21_wdt = {
+ .info = &a21_wdt_info,
+ .ops = &a21_wdt_ops,
+ .min_timeout = 1,
+ .max_timeout = 30,
+};
+
+static int a21_wdt_probe(struct platform_device *pdev)
+{
+ struct device_node *node;
+ struct a21_wdt_drv *drv;
+ unsigned int reset = 0;
+ int num_gpios;
+ int ret;
+ int i;
+
+ drv = devm_kzalloc(&pdev->dev, sizeof(struct a21_wdt_drv), GFP_KERNEL);
+ if (!drv)
+ return -ENOMEM;
+
+ /* Fill GPIO pin array */
+ node = pdev->dev.of_node;
+
+ num_gpios = of_gpio_count(node);
+ if (num_gpios != NUM_GPIOS) {
+ dev_err(&pdev->dev, "gpios DT property wrong, got %d want %d",
+ num_gpios, NUM_GPIOS);
+ return -ENODEV;
+ }
+
+ for (i = 0; i < num_gpios; i++) {
+ int val;
+
+ val = of_get_gpio(node, i);
+ if (val < 0)
+ return val;
+
+ drv->gpios[i] = val;
+ }
+
+ /* Request the used GPIOs */
+ for (i = 0; i < num_gpios; i++) {
+ ret = devm_gpio_request(&pdev->dev, drv->gpios[i],
+ "MEN A21 Watchdog");
+ if (ret)
+ return ret;
+
+ if (i < GPIO_WD_RST0)
+ ret = gpio_direction_output(drv->gpios[i],
+ gpio_get_value(drv->gpios[i]));
+ else /* GPIO_WD_RST[0..2] are inputs */
+ ret = gpio_direction_input(drv->gpios[i]);
+ if (ret)
+ return ret;
+ }
+
+ mutex_init(&drv->lock);
+ watchdog_init_timeout(&a21_wdt, 30, &pdev->dev);
+ watchdog_set_nowayout(&a21_wdt, nowayout);
+ watchdog_set_drvdata(&a21_wdt, drv);
+
+ reset = a21_wdt_get_bootstatus(drv);
+ if (reset == 2)
+ a21_wdt.bootstatus |= WDIOF_EXTERN1;
+ else if (reset == 4)
+ a21_wdt.bootstatus |= WDIOF_CARDRESET;
+ else if (reset == 5)
+ a21_wdt.bootstatus |= WDIOF_POWERUNDER;
+ else if (reset == 7)
+ a21_wdt.bootstatus |= WDIOF_EXTERN2;
+
+ ret = watchdog_register_device(&a21_wdt);
+ if (ret) {
+ dev_err(&pdev->dev, "Cannot register watchdog device\n");
+ goto err_register_wd;
+ }
+
+ dev_set_drvdata(&pdev->dev, drv);
+
+ 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;
+}
+
+static void a21_wdt_shutdown(struct platform_device *pdev)
+{
+ struct a21_wdt_drv *drv = dev_get_drvdata(&pdev->dev);
+
+ gpio_set_value(drv->gpios[GPIO_WD_ENAB], 0);
+}
+
+static const struct of_device_id a21_wdt_ids[] = {
+ { .compatible = "men,a021-wdt" },
+ { },
+};
+
+static struct platform_driver a21_wdt_driver = {
+ .probe = a21_wdt_probe,
+ .remove = a21_wdt_remove,
+ .shutdown = a21_wdt_shutdown,
+ .driver = {
+ .name = "a21-watchdog",
+ .of_match_table = a21_wdt_ids,
+ },
+};
+
+module_platform_driver(a21_wdt_driver);
+
+MODULE_AUTHOR("MEN Mikro Elektronik");
+MODULE_DESCRIPTION("MEN A21 Watchdog");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:a21-watchdog");
diff --git a/drivers/watchdog/mixcomwd.c b/drivers/watchdog/mixcomwd.c
index 37e4b52dbce..be86ea359ee 100644
--- a/drivers/watchdog/mixcomwd.c
+++ b/drivers/watchdog/mixcomwd.c
@@ -73,7 +73,7 @@
static struct {
int ioport;
int id;
-} mixcomwd_io_info[] __devinitdata = {
+} mixcomwd_io_info[] = {
/* The Mixcom cards */
{0x0d90, MIXCOM_ID},
{0x0e90, MIXCOM_ID},
@@ -315,4 +315,3 @@ MODULE_AUTHOR("Gergely Madarasz <gorgo@itc.hu>");
MODULE_DESCRIPTION("MixCom Watchdog driver");
MODULE_VERSION(VERSION);
MODULE_LICENSE("GPL");
-MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
diff --git a/drivers/watchdog/moxart_wdt.c b/drivers/watchdog/moxart_wdt.c
new file mode 100644
index 00000000000..4aa3a8a876f
--- /dev/null
+++ b/drivers/watchdog/moxart_wdt.c
@@ -0,0 +1,180 @@
+/*
+ * MOXA ART SoCs watchdog driver.
+ *
+ * Copyright (C) 2013 Jonas Jensen
+ *
+ * Jonas Jensen <jonas.jensen@gmail.com>
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/err.h>
+#include <linux/kernel.h>
+#include <linux/platform_device.h>
+#include <linux/watchdog.h>
+#include <linux/moduleparam.h>
+
+#include <asm/system_misc.h>
+
+#define REG_COUNT 0x4
+#define REG_MODE 0x8
+#define REG_ENABLE 0xC
+
+struct moxart_wdt_dev {
+ struct watchdog_device dev;
+ void __iomem *base;
+ unsigned int clock_frequency;
+};
+
+static struct moxart_wdt_dev *moxart_restart_ctx;
+
+static int heartbeat;
+
+static void moxart_wdt_restart(enum reboot_mode reboot_mode, const char *cmd)
+{
+ writel(1, moxart_restart_ctx->base + REG_COUNT);
+ writel(0x5ab9, moxart_restart_ctx->base + REG_MODE);
+ writel(0x03, moxart_restart_ctx->base + REG_ENABLE);
+}
+
+static int moxart_wdt_stop(struct watchdog_device *wdt_dev)
+{
+ struct moxart_wdt_dev *moxart_wdt = watchdog_get_drvdata(wdt_dev);
+
+ writel(0, moxart_wdt->base + REG_ENABLE);
+
+ return 0;
+}
+
+static int moxart_wdt_start(struct watchdog_device *wdt_dev)
+{
+ struct moxart_wdt_dev *moxart_wdt = watchdog_get_drvdata(wdt_dev);
+
+ writel(moxart_wdt->clock_frequency * wdt_dev->timeout,
+ moxart_wdt->base + REG_COUNT);
+ writel(0x5ab9, moxart_wdt->base + REG_MODE);
+ writel(0x03, moxart_wdt->base + REG_ENABLE);
+
+ return 0;
+}
+
+static int moxart_wdt_set_timeout(struct watchdog_device *wdt_dev,
+ unsigned int timeout)
+{
+ wdt_dev->timeout = timeout;
+
+ return 0;
+}
+
+static const struct watchdog_info moxart_wdt_info = {
+ .identity = "moxart-wdt",
+ .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING |
+ WDIOF_MAGICCLOSE,
+};
+
+static const struct watchdog_ops moxart_wdt_ops = {
+ .owner = THIS_MODULE,
+ .start = moxart_wdt_start,
+ .stop = moxart_wdt_stop,
+ .set_timeout = moxart_wdt_set_timeout,
+};
+
+static int moxart_wdt_probe(struct platform_device *pdev)
+{
+ struct moxart_wdt_dev *moxart_wdt;
+ struct device *dev = &pdev->dev;
+ struct device_node *node = dev->of_node;
+ struct resource *res;
+ struct clk *clk;
+ int err;
+ unsigned int max_timeout;
+ bool nowayout = WATCHDOG_NOWAYOUT;
+
+ moxart_wdt = devm_kzalloc(dev, sizeof(*moxart_wdt), GFP_KERNEL);
+ if (!moxart_wdt)
+ return -ENOMEM;
+
+ platform_set_drvdata(pdev, moxart_wdt);
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ moxart_wdt->base = devm_ioremap_resource(dev, res);
+ if (IS_ERR(moxart_wdt->base))
+ return PTR_ERR(moxart_wdt->base);
+
+ clk = of_clk_get(node, 0);
+ if (IS_ERR(clk)) {
+ pr_err("%s: of_clk_get failed\n", __func__);
+ return PTR_ERR(clk);
+ }
+
+ moxart_wdt->clock_frequency = clk_get_rate(clk);
+ if (moxart_wdt->clock_frequency == 0) {
+ pr_err("%s: incorrect clock frequency\n", __func__);
+ return -EINVAL;
+ }
+
+ max_timeout = UINT_MAX / moxart_wdt->clock_frequency;
+
+ moxart_wdt->dev.info = &moxart_wdt_info;
+ moxart_wdt->dev.ops = &moxart_wdt_ops;
+ moxart_wdt->dev.timeout = max_timeout;
+ moxart_wdt->dev.min_timeout = 1;
+ moxart_wdt->dev.max_timeout = max_timeout;
+ moxart_wdt->dev.parent = dev;
+
+ watchdog_init_timeout(&moxart_wdt->dev, heartbeat, dev);
+ watchdog_set_nowayout(&moxart_wdt->dev, nowayout);
+
+ watchdog_set_drvdata(&moxart_wdt->dev, moxart_wdt);
+
+ err = watchdog_register_device(&moxart_wdt->dev);
+ if (err)
+ return err;
+
+ moxart_restart_ctx = moxart_wdt;
+ arm_pm_restart = moxart_wdt_restart;
+
+ dev_dbg(dev, "Watchdog enabled (heartbeat=%d sec, nowayout=%d)\n",
+ moxart_wdt->dev.timeout, nowayout);
+
+ return 0;
+}
+
+static int moxart_wdt_remove(struct platform_device *pdev)
+{
+ struct moxart_wdt_dev *moxart_wdt = platform_get_drvdata(pdev);
+
+ arm_pm_restart = NULL;
+ moxart_wdt_stop(&moxart_wdt->dev);
+ watchdog_unregister_device(&moxart_wdt->dev);
+
+ return 0;
+}
+
+static const struct of_device_id moxart_watchdog_match[] = {
+ { .compatible = "moxa,moxart-watchdog" },
+ { },
+};
+
+static struct platform_driver moxart_wdt_driver = {
+ .probe = moxart_wdt_probe,
+ .remove = moxart_wdt_remove,
+ .driver = {
+ .name = "moxart-watchdog",
+ .owner = THIS_MODULE,
+ .of_match_table = moxart_watchdog_match,
+ },
+};
+module_platform_driver(moxart_wdt_driver);
+
+module_param(heartbeat, int, 0);
+MODULE_PARM_DESC(heartbeat, "Watchdog heartbeat in seconds");
+
+MODULE_DESCRIPTION("MOXART watchdog driver");
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Jonas Jensen <jonas.jensen@gmail.com>");
diff --git a/drivers/watchdog/mpc8xxx_wdt.c b/drivers/watchdog/mpc8xxx_wdt.c
index 40f7bf1f865..7831955cd9e 100644
--- a/drivers/watchdog/mpc8xxx_wdt.c
+++ b/drivers/watchdog/mpc8xxx_wdt.c
@@ -24,6 +24,7 @@
#include <linux/kernel.h>
#include <linux/timer.h>
#include <linux/miscdevice.h>
+#include <linux/of_address.h>
#include <linux/of_platform.h>
#include <linux/module.h>
#include <linux/watchdog.h>
@@ -72,9 +73,7 @@ MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started "
* to 0
*/
static int prescale = 1;
-static unsigned int timeout_sec;
-static unsigned long wdt_is_open;
static DEFINE_SPINLOCK(wdt_spinlock);
static void mpc8xxx_wdt_keepalive(void)
@@ -86,39 +85,23 @@ static void mpc8xxx_wdt_keepalive(void)
spin_unlock(&wdt_spinlock);
}
+static struct watchdog_device mpc8xxx_wdt_dev;
static void mpc8xxx_wdt_timer_ping(unsigned long arg);
-static DEFINE_TIMER(wdt_timer, mpc8xxx_wdt_timer_ping, 0, 0);
+static DEFINE_TIMER(wdt_timer, mpc8xxx_wdt_timer_ping, 0,
+ (unsigned long)&mpc8xxx_wdt_dev);
static void mpc8xxx_wdt_timer_ping(unsigned long arg)
{
+ struct watchdog_device *w = (struct watchdog_device *)arg;
+
mpc8xxx_wdt_keepalive();
/* We're pinging it twice faster than needed, just to be sure. */
- mod_timer(&wdt_timer, jiffies + HZ * timeout_sec / 2);
-}
-
-static void mpc8xxx_wdt_pr_warn(const char *msg)
-{
- pr_crit("%s, expect the %s soon!\n", msg,
- reset ? "reset" : "machine check exception");
+ mod_timer(&wdt_timer, jiffies + HZ * w->timeout / 2);
}
-static ssize_t mpc8xxx_wdt_write(struct file *file, const char __user *buf,
- size_t count, loff_t *ppos)
-{
- if (count)
- mpc8xxx_wdt_keepalive();
- return count;
-}
-
-static int mpc8xxx_wdt_open(struct inode *inode, struct file *file)
+static int mpc8xxx_wdt_start(struct watchdog_device *w)
{
u32 tmp = SWCRR_SWEN;
- if (test_and_set_bit(0, &wdt_is_open))
- return -EBUSY;
-
- /* Once we start the watchdog we can't stop it */
- if (nowayout)
- __module_get(THIS_MODULE);
/* Good, fire up the show */
if (prescale)
@@ -132,70 +115,49 @@ static int mpc8xxx_wdt_open(struct inode *inode, struct file *file)
del_timer_sync(&wdt_timer);
- return nonseekable_open(inode, file);
+ return 0;
}
-static int mpc8xxx_wdt_release(struct inode *inode, struct file *file)
+static int mpc8xxx_wdt_ping(struct watchdog_device *w)
{
- if (!nowayout)
- mpc8xxx_wdt_timer_ping(0);
- else
- mpc8xxx_wdt_pr_warn("watchdog closed");
- clear_bit(0, &wdt_is_open);
+ mpc8xxx_wdt_keepalive();
return 0;
}
-static long mpc8xxx_wdt_ioctl(struct file *file, unsigned int cmd,
- unsigned long arg)
+static int mpc8xxx_wdt_stop(struct watchdog_device *w)
{
- void __user *argp = (void __user *)arg;
- int __user *p = argp;
- static const struct watchdog_info ident = {
- .options = WDIOF_KEEPALIVEPING,
- .firmware_version = 1,
- .identity = "MPC8xxx",
- };
-
- switch (cmd) {
- case WDIOC_GETSUPPORT:
- return copy_to_user(argp, &ident, sizeof(ident)) ? -EFAULT : 0;
- case WDIOC_GETSTATUS:
- case WDIOC_GETBOOTSTATUS:
- return put_user(0, p);
- case WDIOC_KEEPALIVE:
- mpc8xxx_wdt_keepalive();
- return 0;
- case WDIOC_GETTIMEOUT:
- return put_user(timeout_sec, p);
- default:
- return -ENOTTY;
- }
+ mod_timer(&wdt_timer, jiffies);
+ return 0;
}
-static const struct file_operations mpc8xxx_wdt_fops = {
- .owner = THIS_MODULE,
- .llseek = no_llseek,
- .write = mpc8xxx_wdt_write,
- .unlocked_ioctl = mpc8xxx_wdt_ioctl,
- .open = mpc8xxx_wdt_open,
- .release = mpc8xxx_wdt_release,
+static struct watchdog_info mpc8xxx_wdt_info = {
+ .options = WDIOF_KEEPALIVEPING,
+ .firmware_version = 1,
+ .identity = "MPC8xxx",
};
-static struct miscdevice mpc8xxx_wdt_miscdev = {
- .minor = WATCHDOG_MINOR,
- .name = "watchdog",
- .fops = &mpc8xxx_wdt_fops,
+static struct watchdog_ops mpc8xxx_wdt_ops = {
+ .owner = THIS_MODULE,
+ .start = mpc8xxx_wdt_start,
+ .ping = mpc8xxx_wdt_ping,
+ .stop = mpc8xxx_wdt_stop,
+};
+
+static struct watchdog_device mpc8xxx_wdt_dev = {
+ .info = &mpc8xxx_wdt_info,
+ .ops = &mpc8xxx_wdt_ops,
};
static const struct of_device_id mpc8xxx_wdt_match[];
-static int __devinit mpc8xxx_wdt_probe(struct platform_device *ofdev)
+static int mpc8xxx_wdt_probe(struct platform_device *ofdev)
{
int ret;
const struct of_device_id *match;
struct device_node *np = ofdev->dev.of_node;
- struct mpc8xxx_wdt_type *wdt_type;
+ const struct mpc8xxx_wdt_type *wdt_type;
u32 freq = fsl_get_sys_freq();
bool enabled;
+ unsigned int timeout_sec;
match = of_match_device(mpc8xxx_wdt_match, &ofdev->dev);
if (!match)
@@ -222,6 +184,7 @@ static int __devinit mpc8xxx_wdt_probe(struct platform_device *ofdev)
else
timeout_sec = timeout / freq;
+ mpc8xxx_wdt_dev.timeout = timeout_sec;
#ifdef MODULE
ret = mpc8xxx_wdt_init_late();
if (ret)
@@ -237,7 +200,7 @@ static int __devinit mpc8xxx_wdt_probe(struct platform_device *ofdev)
* userspace handles it.
*/
if (enabled)
- mpc8xxx_wdt_timer_ping(0);
+ mod_timer(&wdt_timer, jiffies);
return 0;
err_unmap:
iounmap(wd_base);
@@ -245,11 +208,12 @@ err_unmap:
return ret;
}
-static int __devexit mpc8xxx_wdt_remove(struct platform_device *ofdev)
+static int mpc8xxx_wdt_remove(struct platform_device *ofdev)
{
- mpc8xxx_wdt_pr_warn("watchdog removed");
+ pr_crit("Watchdog removed, expect the %s soon!\n",
+ reset ? "reset" : "machine check exception");
del_timer_sync(&wdt_timer);
- misc_deregister(&mpc8xxx_wdt_miscdev);
+ watchdog_unregister_device(&mpc8xxx_wdt_dev);
iounmap(wd_base);
return 0;
@@ -273,6 +237,7 @@ static const struct of_device_id mpc8xxx_wdt_match[] = {
.compatible = "fsl,mpc823-wdt",
.data = &(struct mpc8xxx_wdt_type) {
.prescaler = 0x800,
+ .hw_enabled = true,
},
},
{},
@@ -281,7 +246,7 @@ MODULE_DEVICE_TABLE(of, mpc8xxx_wdt_match);
static struct platform_driver mpc8xxx_wdt_driver = {
.probe = mpc8xxx_wdt_probe,
- .remove = __devexit_p(mpc8xxx_wdt_remove),
+ .remove = mpc8xxx_wdt_remove,
.driver = {
.name = "mpc8xxx_wdt",
.owner = THIS_MODULE,
@@ -301,10 +266,11 @@ static int mpc8xxx_wdt_init_late(void)
if (!wd_base)
return -ENODEV;
- ret = misc_register(&mpc8xxx_wdt_miscdev);
+ watchdog_set_nowayout(&mpc8xxx_wdt_dev, nowayout);
+
+ ret = watchdog_register_device(&mpc8xxx_wdt_dev);
if (ret) {
- pr_err("cannot register miscdev on minor=%d (err=%d)\n",
- WATCHDOG_MINOR, ret);
+ pr_err("cannot register watchdog device (err=%d)\n", ret);
return ret;
}
return 0;
@@ -329,4 +295,3 @@ MODULE_AUTHOR("Dave Updegraff, Kumar Gala");
MODULE_DESCRIPTION("Driver for watchdog timer in MPC8xx/MPC83xx/MPC86xx "
"uProcessors");
MODULE_LICENSE("GPL");
-MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
diff --git a/drivers/watchdog/mpcore_wdt.c b/drivers/watchdog/mpcore_wdt.c
deleted file mode 100644
index 7c741dc987b..00000000000
--- a/drivers/watchdog/mpcore_wdt.c
+++ /dev/null
@@ -1,457 +0,0 @@
-/*
- * Watchdog driver for the mpcore watchdog timer
- *
- * (c) Copyright 2004 ARM Limited
- *
- * Based on the SoftDog driver:
- * (c) Copyright 1996 Alan Cox <alan@lxorguk.ukuu.org.uk>,
- * All Rights Reserved.
- *
- * 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.
- *
- * Neither Alan Cox nor CymruNet Ltd. admit liability nor provide
- * warranty for any of this software. This material is provided
- * "AS-IS" and at no charge.
- *
- * (c) Copyright 1995 Alan Cox <alan@lxorguk.ukuu.org.uk>
- *
- */
-
-#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
-
-#include <linux/module.h>
-#include <linux/moduleparam.h>
-#include <linux/types.h>
-#include <linux/miscdevice.h>
-#include <linux/watchdog.h>
-#include <linux/fs.h>
-#include <linux/reboot.h>
-#include <linux/init.h>
-#include <linux/interrupt.h>
-#include <linux/platform_device.h>
-#include <linux/uaccess.h>
-#include <linux/slab.h>
-#include <linux/io.h>
-
-#include <asm/smp_twd.h>
-
-struct mpcore_wdt {
- unsigned long timer_alive;
- struct device *dev;
- void __iomem *base;
- int irq;
- unsigned int perturb;
- char expect_close;
-};
-
-static struct platform_device *mpcore_wdt_pdev;
-static DEFINE_SPINLOCK(wdt_lock);
-
-#define TIMER_MARGIN 60
-static int mpcore_margin = TIMER_MARGIN;
-module_param(mpcore_margin, int, 0);
-MODULE_PARM_DESC(mpcore_margin,
- "MPcore timer margin in seconds. (0 < mpcore_margin < 65536, default="
- __MODULE_STRING(TIMER_MARGIN) ")");
-
-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) ")");
-
-#define ONLY_TESTING 0
-static int mpcore_noboot = ONLY_TESTING;
-module_param(mpcore_noboot, int, 0);
-MODULE_PARM_DESC(mpcore_noboot, "MPcore watchdog action, "
- "set to 1 to ignore reboots, 0 to reboot (default="
- __MODULE_STRING(ONLY_TESTING) ")");
-
-/*
- * This is the interrupt handler. Note that we only use this
- * in testing mode, so don't actually do a reboot here.
- */
-static irqreturn_t mpcore_wdt_fire(int irq, void *arg)
-{
- struct mpcore_wdt *wdt = arg;
-
- /* Check it really was our interrupt */
- if (readl(wdt->base + TWD_WDOG_INTSTAT)) {
- dev_printk(KERN_CRIT, wdt->dev,
- "Triggered - Reboot ignored.\n");
- /* Clear the interrupt on the watchdog */
- writel(1, wdt->base + TWD_WDOG_INTSTAT);
- return IRQ_HANDLED;
- }
- return IRQ_NONE;
-}
-
-/*
- * mpcore_wdt_keepalive - reload the timer
- *
- * Note that the spec says a DIFFERENT value must be written to the reload
- * register each time. The "perturb" variable deals with this by adding 1
- * to the count every other time the function is called.
- */
-static void mpcore_wdt_keepalive(struct mpcore_wdt *wdt)
-{
- unsigned long count;
-
- spin_lock(&wdt_lock);
- /* Assume prescale is set to 256 */
- count = __raw_readl(wdt->base + TWD_WDOG_COUNTER);
- count = (0xFFFFFFFFU - count) * (HZ / 5);
- count = (count / 256) * mpcore_margin;
-
- /* Reload the counter */
- writel(count + wdt->perturb, wdt->base + TWD_WDOG_LOAD);
- wdt->perturb = wdt->perturb ? 0 : 1;
- spin_unlock(&wdt_lock);
-}
-
-static void mpcore_wdt_stop(struct mpcore_wdt *wdt)
-{
- spin_lock(&wdt_lock);
- writel(0x12345678, wdt->base + TWD_WDOG_DISABLE);
- writel(0x87654321, wdt->base + TWD_WDOG_DISABLE);
- writel(0x0, wdt->base + TWD_WDOG_CONTROL);
- spin_unlock(&wdt_lock);
-}
-
-static void mpcore_wdt_start(struct mpcore_wdt *wdt)
-{
- dev_printk(KERN_INFO, wdt->dev, "enabling watchdog.\n");
-
- /* This loads the count register but does NOT start the count yet */
- mpcore_wdt_keepalive(wdt);
-
- if (mpcore_noboot) {
- /* Enable watchdog - prescale=256, watchdog mode=0, enable=1 */
- writel(0x0000FF01, wdt->base + TWD_WDOG_CONTROL);
- } else {
- /* Enable watchdog - prescale=256, watchdog mode=1, enable=1 */
- writel(0x0000FF09, wdt->base + TWD_WDOG_CONTROL);
- }
-}
-
-static int mpcore_wdt_set_heartbeat(int t)
-{
- if (t < 0x0001 || t > 0xFFFF)
- return -EINVAL;
-
- mpcore_margin = t;
- return 0;
-}
-
-/*
- * /dev/watchdog handling
- */
-static int mpcore_wdt_open(struct inode *inode, struct file *file)
-{
- struct mpcore_wdt *wdt = platform_get_drvdata(mpcore_wdt_pdev);
-
- if (test_and_set_bit(0, &wdt->timer_alive))
- return -EBUSY;
-
- if (nowayout)
- __module_get(THIS_MODULE);
-
- file->private_data = wdt;
-
- /*
- * Activate timer
- */
- mpcore_wdt_start(wdt);
-
- return nonseekable_open(inode, file);
-}
-
-static int mpcore_wdt_release(struct inode *inode, struct file *file)
-{
- struct mpcore_wdt *wdt = file->private_data;
-
- /*
- * Shut off the timer.
- * Lock it in if it's a module and we set nowayout
- */
- if (wdt->expect_close == 42)
- mpcore_wdt_stop(wdt);
- else {
- dev_printk(KERN_CRIT, wdt->dev,
- "unexpected close, not stopping watchdog!\n");
- mpcore_wdt_keepalive(wdt);
- }
- clear_bit(0, &wdt->timer_alive);
- wdt->expect_close = 0;
- return 0;
-}
-
-static ssize_t mpcore_wdt_write(struct file *file, const char *data,
- size_t len, loff_t *ppos)
-{
- struct mpcore_wdt *wdt = file->private_data;
-
- /*
- * Refresh the timer.
- */
- if (len) {
- if (!nowayout) {
- size_t i;
-
- /* In case it was set long ago */
- wdt->expect_close = 0;
-
- for (i = 0; i != len; i++) {
- char c;
-
- if (get_user(c, data + i))
- return -EFAULT;
- if (c == 'V')
- wdt->expect_close = 42;
- }
- }
- mpcore_wdt_keepalive(wdt);
- }
- return len;
-}
-
-static const struct watchdog_info ident = {
- .options = WDIOF_SETTIMEOUT |
- WDIOF_KEEPALIVEPING |
- WDIOF_MAGICCLOSE,
- .identity = "MPcore Watchdog",
-};
-
-static long mpcore_wdt_ioctl(struct file *file, unsigned int cmd,
- unsigned long arg)
-{
- struct mpcore_wdt *wdt = file->private_data;
- int ret;
- union {
- struct watchdog_info ident;
- int i;
- } uarg;
-
- if (_IOC_DIR(cmd) && _IOC_SIZE(cmd) > sizeof(uarg))
- return -ENOTTY;
-
- if (_IOC_DIR(cmd) & _IOC_WRITE) {
- ret = copy_from_user(&uarg, (void __user *)arg, _IOC_SIZE(cmd));
- if (ret)
- return -EFAULT;
- }
-
- switch (cmd) {
- case WDIOC_GETSUPPORT:
- uarg.ident = ident;
- ret = 0;
- break;
-
- case WDIOC_GETSTATUS:
- case WDIOC_GETBOOTSTATUS:
- uarg.i = 0;
- ret = 0;
- break;
-
- case WDIOC_SETOPTIONS:
- ret = -EINVAL;
- if (uarg.i & WDIOS_DISABLECARD) {
- mpcore_wdt_stop(wdt);
- ret = 0;
- }
- if (uarg.i & WDIOS_ENABLECARD) {
- mpcore_wdt_start(wdt);
- ret = 0;
- }
- break;
-
- case WDIOC_KEEPALIVE:
- mpcore_wdt_keepalive(wdt);
- ret = 0;
- break;
-
- case WDIOC_SETTIMEOUT:
- ret = mpcore_wdt_set_heartbeat(uarg.i);
- if (ret)
- break;
-
- mpcore_wdt_keepalive(wdt);
- /* Fall */
- case WDIOC_GETTIMEOUT:
- uarg.i = mpcore_margin;
- ret = 0;
- break;
-
- default:
- return -ENOTTY;
- }
-
- if (ret == 0 && _IOC_DIR(cmd) & _IOC_READ) {
- ret = copy_to_user((void __user *)arg, &uarg, _IOC_SIZE(cmd));
- if (ret)
- ret = -EFAULT;
- }
- return ret;
-}
-
-/*
- * System shutdown handler. Turn off the watchdog if we're
- * restarting or halting the system.
- */
-static void mpcore_wdt_shutdown(struct platform_device *pdev)
-{
- struct mpcore_wdt *wdt = platform_get_drvdata(pdev);
-
- if (system_state == SYSTEM_RESTART || system_state == SYSTEM_HALT)
- mpcore_wdt_stop(wdt);
-}
-
-/*
- * Kernel Interfaces
- */
-static const struct file_operations mpcore_wdt_fops = {
- .owner = THIS_MODULE,
- .llseek = no_llseek,
- .write = mpcore_wdt_write,
- .unlocked_ioctl = mpcore_wdt_ioctl,
- .open = mpcore_wdt_open,
- .release = mpcore_wdt_release,
-};
-
-static struct miscdevice mpcore_wdt_miscdev = {
- .minor = WATCHDOG_MINOR,
- .name = "watchdog",
- .fops = &mpcore_wdt_fops,
-};
-
-static int __devinit mpcore_wdt_probe(struct platform_device *pdev)
-{
- struct mpcore_wdt *wdt;
- struct resource *res;
- int ret;
-
- /* We only accept one device, and it must have an id of -1 */
- if (pdev->id != -1)
- return -ENODEV;
-
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (!res)
- return -ENODEV;
-
- wdt = devm_kzalloc(&pdev->dev, sizeof(struct mpcore_wdt), GFP_KERNEL);
- if (!wdt)
- return -ENOMEM;
-
- wdt->dev = &pdev->dev;
- wdt->irq = platform_get_irq(pdev, 0);
- if (wdt->irq >= 0) {
- ret = devm_request_irq(wdt->dev, wdt->irq, mpcore_wdt_fire, 0,
- "mpcore_wdt", wdt);
- if (ret) {
- dev_printk(KERN_ERR, wdt->dev,
- "cannot register IRQ%d for watchdog\n",
- wdt->irq);
- return ret;
- }
- }
-
- wdt->base = devm_ioremap(wdt->dev, res->start, resource_size(res));
- if (!wdt->base)
- return -ENOMEM;
-
- mpcore_wdt_miscdev.parent = &pdev->dev;
- ret = misc_register(&mpcore_wdt_miscdev);
- if (ret) {
- dev_printk(KERN_ERR, wdt->dev,
- "cannot register miscdev on minor=%d (err=%d)\n",
- WATCHDOG_MINOR, ret);
- return ret;
- }
-
- mpcore_wdt_stop(wdt);
- platform_set_drvdata(pdev, wdt);
- mpcore_wdt_pdev = pdev;
-
- return 0;
-}
-
-static int __devexit mpcore_wdt_remove(struct platform_device *pdev)
-{
- platform_set_drvdata(pdev, NULL);
-
- misc_deregister(&mpcore_wdt_miscdev);
-
- mpcore_wdt_pdev = NULL;
-
- return 0;
-}
-
-#ifdef CONFIG_PM
-static int mpcore_wdt_suspend(struct platform_device *pdev, pm_message_t msg)
-{
- struct mpcore_wdt *wdt = platform_get_drvdata(pdev);
- mpcore_wdt_stop(wdt); /* Turn the WDT off */
- return 0;
-}
-
-static int mpcore_wdt_resume(struct platform_device *pdev)
-{
- struct mpcore_wdt *wdt = platform_get_drvdata(pdev);
- /* re-activate timer */
- if (test_bit(0, &wdt->timer_alive))
- mpcore_wdt_start(wdt);
- return 0;
-}
-#else
-#define mpcore_wdt_suspend NULL
-#define mpcore_wdt_resume NULL
-#endif
-
-/* work with hotplug and coldplug */
-MODULE_ALIAS("platform:mpcore_wdt");
-
-static struct platform_driver mpcore_wdt_driver = {
- .probe = mpcore_wdt_probe,
- .remove = __devexit_p(mpcore_wdt_remove),
- .suspend = mpcore_wdt_suspend,
- .resume = mpcore_wdt_resume,
- .shutdown = mpcore_wdt_shutdown,
- .driver = {
- .owner = THIS_MODULE,
- .name = "mpcore_wdt",
- },
-};
-
-static int __init mpcore_wdt_init(void)
-{
- /*
- * Check that the margin value is within it's range;
- * if not reset to the default
- */
- if (mpcore_wdt_set_heartbeat(mpcore_margin)) {
- mpcore_wdt_set_heartbeat(TIMER_MARGIN);
- pr_info("mpcore_margin value must be 0 < mpcore_margin < 65536, using %d\n",
- TIMER_MARGIN);
- }
-
- pr_info("MPcore Watchdog Timer: 0.1. mpcore_noboot=%d mpcore_margin=%d sec (nowayout= %d)\n",
- mpcore_noboot, mpcore_margin, nowayout);
-
- return platform_driver_register(&mpcore_wdt_driver);
-}
-
-static void __exit mpcore_wdt_exit(void)
-{
- platform_driver_unregister(&mpcore_wdt_driver);
-}
-
-module_init(mpcore_wdt_init);
-module_exit(mpcore_wdt_exit);
-
-MODULE_AUTHOR("ARM Limited");
-MODULE_DESCRIPTION("MPcore Watchdog Device Driver");
-MODULE_LICENSE("GPL");
-MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
diff --git a/drivers/watchdog/mtx-1_wdt.c b/drivers/watchdog/mtx-1_wdt.c
index c29e31d99fe..ff27c4ac96e 100644
--- a/drivers/watchdog/mtx-1_wdt.c
+++ b/drivers/watchdog/mtx-1_wdt.c
@@ -40,7 +40,6 @@
#include <linux/errno.h>
#include <linux/miscdevice.h>
#include <linux/fs.h>
-#include <linux/init.h>
#include <linux/ioport.h>
#include <linux/timer.h>
#include <linux/completion.h>
@@ -204,12 +203,12 @@ static struct miscdevice mtx1_wdt_misc = {
};
-static int __devinit mtx1_wdt_probe(struct platform_device *pdev)
+static int mtx1_wdt_probe(struct platform_device *pdev)
{
int ret;
mtx1_wdt_device.gpio = pdev->resource[0].start;
- ret = gpio_request_one(mtx1_wdt_device.gpio,
+ ret = devm_gpio_request_one(&pdev->dev, mtx1_wdt_device.gpio,
GPIOF_OUT_INIT_HIGH, "mtx1-wdt");
if (ret < 0) {
dev_err(&pdev->dev, "failed to request gpio");
@@ -233,7 +232,7 @@ static int __devinit mtx1_wdt_probe(struct platform_device *pdev)
return 0;
}
-static int __devexit mtx1_wdt_remove(struct platform_device *pdev)
+static int mtx1_wdt_remove(struct platform_device *pdev)
{
/* FIXME: do we need to lock this test ? */
if (mtx1_wdt_device.queue) {
@@ -241,14 +240,13 @@ static int __devexit mtx1_wdt_remove(struct platform_device *pdev)
wait_for_completion(&mtx1_wdt_device.stop);
}
- gpio_free(mtx1_wdt_device.gpio);
misc_deregister(&mtx1_wdt_misc);
return 0;
}
static struct platform_driver mtx1_wdt_driver = {
.probe = mtx1_wdt_probe,
- .remove = __devexit_p(mtx1_wdt_remove),
+ .remove = mtx1_wdt_remove,
.driver.name = "mtx1-wdt",
.driver.owner = THIS_MODULE,
};
@@ -258,5 +256,4 @@ module_platform_driver(mtx1_wdt_driver);
MODULE_AUTHOR("Michael Stickel, Florian Fainelli");
MODULE_DESCRIPTION("Driver for the MTX-1 watchdog");
MODULE_LICENSE("GPL");
-MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
MODULE_ALIAS("platform:mtx1-wdt");
diff --git a/drivers/watchdog/mv64x60_wdt.c b/drivers/watchdog/mv64x60_wdt.c
index c53d025e70d..f9fa5840939 100644
--- a/drivers/watchdog/mv64x60_wdt.c
+++ b/drivers/watchdog/mv64x60_wdt.c
@@ -253,9 +253,9 @@ static struct miscdevice mv64x60_wdt_miscdev = {
.fops = &mv64x60_wdt_fops,
};
-static int __devinit mv64x60_wdt_probe(struct platform_device *dev)
+static int mv64x60_wdt_probe(struct platform_device *dev)
{
- struct mv64x60_wdt_pdata *pdata = dev->dev.platform_data;
+ struct mv64x60_wdt_pdata *pdata = dev_get_platdata(&dev->dev);
struct resource *r;
int timeout = 10;
@@ -276,7 +276,7 @@ static int __devinit mv64x60_wdt_probe(struct platform_device *dev)
if (!r)
return -ENODEV;
- mv64x60_wdt_regs = ioremap(r->start, resource_size(r));
+ mv64x60_wdt_regs = devm_ioremap(&dev->dev, r->start, resource_size(r));
if (mv64x60_wdt_regs == NULL)
return -ENOMEM;
@@ -287,20 +287,18 @@ static int __devinit mv64x60_wdt_probe(struct platform_device *dev)
return misc_register(&mv64x60_wdt_miscdev);
}
-static int __devexit mv64x60_wdt_remove(struct platform_device *dev)
+static int mv64x60_wdt_remove(struct platform_device *dev)
{
misc_deregister(&mv64x60_wdt_miscdev);
mv64x60_wdt_handler_disable();
- iounmap(mv64x60_wdt_regs);
-
return 0;
}
static struct platform_driver mv64x60_wdt_driver = {
.probe = mv64x60_wdt_probe,
- .remove = __devexit_p(mv64x60_wdt_remove),
+ .remove = mv64x60_wdt_remove,
.driver = {
.owner = THIS_MODULE,
.name = MV64x60_WDT_NAME,
@@ -325,5 +323,4 @@ module_exit(mv64x60_wdt_exit);
MODULE_AUTHOR("James Chapman <jchapman@katalix.com>");
MODULE_DESCRIPTION("MV64x60 watchdog driver");
MODULE_LICENSE("GPL");
-MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
MODULE_ALIAS("platform:" MV64x60_WDT_NAME);
diff --git a/drivers/watchdog/nuc900_wdt.c b/drivers/watchdog/nuc900_wdt.c
index ea4c7448b75..7135803ca1a 100644
--- a/drivers/watchdog/nuc900_wdt.c
+++ b/drivers/watchdog/nuc900_wdt.c
@@ -12,7 +12,6 @@
#include <linux/bitops.h>
#include <linux/errno.h>
#include <linux/fs.h>
-#include <linux/init.h>
#include <linux/io.h>
#include <linux/clk.h>
#include <linux/kernel.h>
@@ -61,7 +60,6 @@ MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started "
"(default=" __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
struct nuc900_wdt {
- struct resource *res;
struct clk *wdt_clock;
struct platform_device *pdev;
void __iomem *wdt_base;
@@ -242,11 +240,13 @@ static struct miscdevice nuc900wdt_miscdev = {
.fops = &nuc900wdt_fops,
};
-static int __devinit nuc900wdt_probe(struct platform_device *pdev)
+static int nuc900wdt_probe(struct platform_device *pdev)
{
+ struct resource *res;
int ret = 0;
- nuc900_wdt = kzalloc(sizeof(struct nuc900_wdt), GFP_KERNEL);
+ nuc900_wdt = devm_kzalloc(&pdev->dev, sizeof(*nuc900_wdt),
+ GFP_KERNEL);
if (!nuc900_wdt)
return -ENOMEM;
@@ -254,33 +254,15 @@ static int __devinit nuc900wdt_probe(struct platform_device *pdev)
spin_lock_init(&nuc900_wdt->wdt_lock);
- nuc900_wdt->res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (nuc900_wdt->res == NULL) {
- dev_err(&pdev->dev, "no memory resource specified\n");
- ret = -ENOENT;
- goto err_get;
- }
-
- if (!request_mem_region(nuc900_wdt->res->start,
- resource_size(nuc900_wdt->res), pdev->name)) {
- dev_err(&pdev->dev, "failed to get memory region\n");
- ret = -ENOENT;
- goto err_get;
- }
-
- nuc900_wdt->wdt_base = ioremap(nuc900_wdt->res->start,
- resource_size(nuc900_wdt->res));
- if (nuc900_wdt->wdt_base == NULL) {
- dev_err(&pdev->dev, "failed to ioremap() region\n");
- ret = -EINVAL;
- goto err_req;
- }
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ nuc900_wdt->wdt_base = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(nuc900_wdt->wdt_base))
+ return PTR_ERR(nuc900_wdt->wdt_base);
- nuc900_wdt->wdt_clock = clk_get(&pdev->dev, NULL);
+ nuc900_wdt->wdt_clock = devm_clk_get(&pdev->dev, NULL);
if (IS_ERR(nuc900_wdt->wdt_clock)) {
dev_err(&pdev->dev, "failed to find watchdog clock source\n");
- ret = PTR_ERR(nuc900_wdt->wdt_clock);
- goto err_map;
+ return PTR_ERR(nuc900_wdt->wdt_clock);
}
clk_enable(nuc900_wdt->wdt_clock);
@@ -298,37 +280,21 @@ static int __devinit nuc900wdt_probe(struct platform_device *pdev)
err_clk:
clk_disable(nuc900_wdt->wdt_clock);
- clk_put(nuc900_wdt->wdt_clock);
-err_map:
- iounmap(nuc900_wdt->wdt_base);
-err_req:
- release_mem_region(nuc900_wdt->res->start,
- resource_size(nuc900_wdt->res));
-err_get:
- kfree(nuc900_wdt);
return ret;
}
-static int __devexit nuc900wdt_remove(struct platform_device *pdev)
+static int nuc900wdt_remove(struct platform_device *pdev)
{
misc_deregister(&nuc900wdt_miscdev);
clk_disable(nuc900_wdt->wdt_clock);
- clk_put(nuc900_wdt->wdt_clock);
-
- iounmap(nuc900_wdt->wdt_base);
-
- release_mem_region(nuc900_wdt->res->start,
- resource_size(nuc900_wdt->res));
-
- kfree(nuc900_wdt);
return 0;
}
static struct platform_driver nuc900wdt_driver = {
.probe = nuc900wdt_probe,
- .remove = __devexit_p(nuc900wdt_remove),
+ .remove = nuc900wdt_remove,
.driver = {
.name = "nuc900-wdt",
.owner = THIS_MODULE,
@@ -340,5 +306,4 @@ module_platform_driver(nuc900wdt_driver);
MODULE_AUTHOR("Wan ZongShun <mcuos.com@gmail.com>");
MODULE_DESCRIPTION("Watchdog driver for NUC900");
MODULE_LICENSE("GPL");
-MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
MODULE_ALIAS("platform:nuc900-wdt");
diff --git a/drivers/watchdog/nv_tco.c b/drivers/watchdog/nv_tco.c
index 6bbb9efc612..0b9ec61e131 100644
--- a/drivers/watchdog/nv_tco.c
+++ b/drivers/watchdog/nv_tco.c
@@ -289,7 +289,7 @@ static struct miscdevice nv_tco_miscdev = {
* register a pci_driver, because someone else might one day
* want to register another driver on the same PCI id.
*/
-static DEFINE_PCI_DEVICE_TABLE(tco_pci_tbl) = {
+static const struct pci_device_id tco_pci_tbl[] = {
{ PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_MCP51_SMBUS,
PCI_ANY_ID, PCI_ANY_ID, },
{ PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_MCP55_SMBUS,
@@ -302,7 +302,7 @@ MODULE_DEVICE_TABLE(pci, tco_pci_tbl);
* Init & exit routines
*/
-static unsigned char __devinit nv_tco_getdevice(void)
+static unsigned char nv_tco_getdevice(void)
{
struct pci_dev *dev = NULL;
u32 val;
@@ -376,7 +376,7 @@ out:
return 0;
}
-static int __devinit nv_tco_init(struct platform_device *dev)
+static int nv_tco_init(struct platform_device *dev)
{
int ret;
@@ -423,7 +423,7 @@ unreg_region:
return ret;
}
-static void __devexit nv_tco_cleanup(void)
+static void nv_tco_cleanup(void)
{
u32 val;
@@ -445,7 +445,7 @@ static void __devexit nv_tco_cleanup(void)
release_region(tcobase, 0x10);
}
-static int __devexit nv_tco_remove(struct platform_device *dev)
+static int nv_tco_remove(struct platform_device *dev)
{
if (tcobase)
nv_tco_cleanup();
@@ -468,7 +468,7 @@ static void nv_tco_shutdown(struct platform_device *dev)
static struct platform_driver nv_tco_driver = {
.probe = nv_tco_init,
- .remove = __devexit_p(nv_tco_remove),
+ .remove = nv_tco_remove,
.shutdown = nv_tco_shutdown,
.driver = {
.owner = THIS_MODULE,
@@ -513,4 +513,3 @@ module_exit(nv_tco_cleanup_module);
MODULE_AUTHOR("Mike Waychison");
MODULE_DESCRIPTION("TCO timer driver for NV chipsets");
MODULE_LICENSE("GPL");
-MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
diff --git a/drivers/watchdog/octeon-wdt-main.c b/drivers/watchdog/octeon-wdt-main.c
index 46120883142..4baf2d78892 100644
--- a/drivers/watchdog/octeon-wdt-main.c
+++ b/drivers/watchdog/octeon-wdt-main.c
@@ -708,10 +708,13 @@ static int __init octeon_wdt_init(void)
cpumask_clear(&irq_enabled_cpus);
+ cpu_notifier_register_begin();
for_each_online_cpu(cpu)
octeon_wdt_setup_interrupt(cpu);
- register_hotcpu_notifier(&octeon_wdt_cpu_notifier);
+ __register_hotcpu_notifier(&octeon_wdt_cpu_notifier);
+ cpu_notifier_register_done();
+
out:
return ret;
}
@@ -725,7 +728,8 @@ static void __exit octeon_wdt_cleanup(void)
misc_deregister(&octeon_wdt_miscdev);
- unregister_hotcpu_notifier(&octeon_wdt_cpu_notifier);
+ cpu_notifier_register_begin();
+ __unregister_hotcpu_notifier(&octeon_wdt_cpu_notifier);
for_each_online_cpu(cpu) {
int core = cpu2core(cpu);
@@ -734,6 +738,9 @@ static void __exit octeon_wdt_cleanup(void)
/* Free the interrupt handler */
free_irq(OCTEON_IRQ_WDOG0 + core, octeon_wdt_poke_irq);
}
+
+ cpu_notifier_register_done();
+
/*
* Disable the boot-bus memory, the code it points to is soon
* to go missing.
diff --git a/drivers/watchdog/of_xilinx_wdt.c b/drivers/watchdog/of_xilinx_wdt.c
index 55d2f66dbea..1e6e28df5d7 100644
--- a/drivers/watchdog/of_xilinx_wdt.c
+++ b/drivers/watchdog/of_xilinx_wdt.c
@@ -1,36 +1,22 @@
/*
-* of_xilinx_wdt.c 1.01 A Watchdog Device Driver for Xilinx xps_timebase_wdt
-*
-* (C) Copyright 2011 (Alejandro Cabrera <aldaya@gmail.com>)
-*
-* -----------------------
-*
-* 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.
-*
-* -----------------------
-* 30-May-2011 Alejandro Cabrera <aldaya@gmail.com>
-* - If "xlnx,wdt-enable-once" wasn't found on device tree the
-* module will use CONFIG_WATCHDOG_NOWAYOUT
-* - If the device tree parameters ("clock-frequency" and
-* "xlnx,wdt-interval") wasn't found the driver won't
-* know the wdt reset interval
-*/
-
-#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+ * Watchdog Device Driver for Xilinx axi/xps_timebase_wdt
+ *
+ * (C) Copyright 2013 - 2014 Xilinx, Inc.
+ * (C) Copyright 2011 (Alejandro Cabrera <aldaya@gmail.com>)
+ *
+ * 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.
+ */
+#include <linux/err.h>
#include <linux/module.h>
#include <linux/types.h>
#include <linux/kernel.h>
-#include <linux/fs.h>
-#include <linux/miscdevice.h>
-#include <linux/init.h>
#include <linux/ioport.h>
#include <linux/watchdog.h>
#include <linux/io.h>
-#include <linux/uaccess.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/of_address.h>
@@ -53,102 +39,103 @@
#define XWT_TIMER_FAILED 0xFFFFFFFF
#define WATCHDOG_NAME "Xilinx Watchdog"
-#define PFX WATCHDOG_NAME ": "
struct xwdt_device {
- struct resource res;
void __iomem *base;
- u32 nowayout;
u32 wdt_interval;
- u32 boot_status;
+ spinlock_t spinlock;
+ struct watchdog_device xilinx_wdt_wdd;
};
-static struct xwdt_device xdev;
-
-static u32 timeout;
-static u32 control_status_reg;
-static u8 expect_close;
-static u8 no_timeout;
-static unsigned long driver_open;
-
-static DEFINE_SPINLOCK(spinlock);
-
-static void xwdt_start(void)
+static int xilinx_wdt_start(struct watchdog_device *wdd)
{
- spin_lock(&spinlock);
+ u32 control_status_reg;
+ struct xwdt_device *xdev = watchdog_get_drvdata(wdd);
+
+ spin_lock(&xdev->spinlock);
/* Clean previous status and enable the watchdog timer */
- control_status_reg = ioread32(xdev.base + XWT_TWCSR0_OFFSET);
+ control_status_reg = ioread32(xdev->base + XWT_TWCSR0_OFFSET);
control_status_reg |= (XWT_CSR0_WRS_MASK | XWT_CSR0_WDS_MASK);
iowrite32((control_status_reg | XWT_CSR0_EWDT1_MASK),
- xdev.base + XWT_TWCSR0_OFFSET);
+ xdev->base + XWT_TWCSR0_OFFSET);
+
+ iowrite32(XWT_CSRX_EWDT2_MASK, xdev->base + XWT_TWCSR1_OFFSET);
- iowrite32(XWT_CSRX_EWDT2_MASK, xdev.base + XWT_TWCSR1_OFFSET);
+ spin_unlock(&xdev->spinlock);
- spin_unlock(&spinlock);
+ return 0;
}
-static void xwdt_stop(void)
+static int xilinx_wdt_stop(struct watchdog_device *wdd)
{
- spin_lock(&spinlock);
+ u32 control_status_reg;
+ struct xwdt_device *xdev = watchdog_get_drvdata(wdd);
- control_status_reg = ioread32(xdev.base + XWT_TWCSR0_OFFSET);
+ spin_lock(&xdev->spinlock);
+
+ control_status_reg = ioread32(xdev->base + XWT_TWCSR0_OFFSET);
iowrite32((control_status_reg & ~XWT_CSR0_EWDT1_MASK),
- xdev.base + XWT_TWCSR0_OFFSET);
+ xdev->base + XWT_TWCSR0_OFFSET);
- iowrite32(0, xdev.base + XWT_TWCSR1_OFFSET);
+ iowrite32(0, xdev->base + XWT_TWCSR1_OFFSET);
- spin_unlock(&spinlock);
+ spin_unlock(&xdev->spinlock);
pr_info("Stopped!\n");
+
+ return 0;
}
-static void xwdt_keepalive(void)
+static int xilinx_wdt_keepalive(struct watchdog_device *wdd)
{
- spin_lock(&spinlock);
+ u32 control_status_reg;
+ struct xwdt_device *xdev = watchdog_get_drvdata(wdd);
- control_status_reg = ioread32(xdev.base + XWT_TWCSR0_OFFSET);
- control_status_reg |= (XWT_CSR0_WRS_MASK | XWT_CSR0_WDS_MASK);
- iowrite32(control_status_reg, xdev.base + XWT_TWCSR0_OFFSET);
+ spin_lock(&xdev->spinlock);
- spin_unlock(&spinlock);
-}
+ control_status_reg = ioread32(xdev->base + XWT_TWCSR0_OFFSET);
+ control_status_reg |= (XWT_CSR0_WRS_MASK | XWT_CSR0_WDS_MASK);
+ iowrite32(control_status_reg, xdev->base + XWT_TWCSR0_OFFSET);
-static void xwdt_get_status(int *status)
-{
- int new_status;
+ spin_unlock(&xdev->spinlock);
- spin_lock(&spinlock);
+ return 0;
+}
- control_status_reg = ioread32(xdev.base + XWT_TWCSR0_OFFSET);
- new_status = ((control_status_reg &
- (XWT_CSR0_WRS_MASK | XWT_CSR0_WDS_MASK)) != 0);
- spin_unlock(&spinlock);
+static const struct watchdog_info xilinx_wdt_ident = {
+ .options = WDIOF_MAGICCLOSE |
+ WDIOF_KEEPALIVEPING,
+ .firmware_version = 1,
+ .identity = WATCHDOG_NAME,
+};
- *status = 0;
- if (new_status & 1)
- *status |= WDIOF_CARDRESET;
-}
+static const struct watchdog_ops xilinx_wdt_ops = {
+ .owner = THIS_MODULE,
+ .start = xilinx_wdt_start,
+ .stop = xilinx_wdt_stop,
+ .ping = xilinx_wdt_keepalive,
+};
-static u32 xwdt_selftest(void)
+static u32 xwdt_selftest(struct xwdt_device *xdev)
{
int i;
u32 timer_value1;
u32 timer_value2;
- spin_lock(&spinlock);
+ spin_lock(&xdev->spinlock);
- timer_value1 = ioread32(xdev.base + XWT_TBR_OFFSET);
- timer_value2 = ioread32(xdev.base + XWT_TBR_OFFSET);
+ timer_value1 = ioread32(xdev->base + XWT_TBR_OFFSET);
+ timer_value2 = ioread32(xdev->base + XWT_TBR_OFFSET);
for (i = 0;
((i <= XWT_MAX_SELFTEST_LOOP_COUNT) &&
(timer_value2 == timer_value1)); i++) {
- timer_value2 = ioread32(xdev.base + XWT_TBR_OFFSET);
+ timer_value2 = ioread32(xdev->base + XWT_TBR_OFFSET);
}
- spin_unlock(&spinlock);
+ spin_unlock(&xdev->spinlock);
if (timer_value2 != timer_value1)
return ~XWT_TIMER_FAILED;
@@ -156,244 +143,90 @@ static u32 xwdt_selftest(void)
return XWT_TIMER_FAILED;
}
-static int xwdt_open(struct inode *inode, struct file *file)
-{
- /* Only one process can handle the wdt at a time */
- if (test_and_set_bit(0, &driver_open))
- return -EBUSY;
-
- /* Make sure that the module are always loaded...*/
- if (xdev.nowayout)
- __module_get(THIS_MODULE);
-
- xwdt_start();
- pr_info("Started...\n");
-
- return nonseekable_open(inode, file);
-}
-
-static int xwdt_release(struct inode *inode, struct file *file)
-{
- if (expect_close == 42) {
- xwdt_stop();
- } else {
- pr_crit("Unexpected close, not stopping watchdog!\n");
- xwdt_keepalive();
- }
-
- clear_bit(0, &driver_open);
- expect_close = 0;
- return 0;
-}
-
-/*
- * xwdt_write:
- * @file: file handle to the watchdog
- * @buf: buffer to write (unused as data does not matter here
- * @count: count of bytes
- * @ppos: pointer to the position to write. No seeks allowed
- *
- * A write to a watchdog device is defined as a keepalive signal. Any
- * write of data will do, as we don't define content meaning.
- */
-static ssize_t xwdt_write(struct file *file, const char __user *buf,
- size_t len, loff_t *ppos)
-{
- if (len) {
- if (!xdev.nowayout) {
- size_t i;
-
- /* In case it was set long ago */
- expect_close = 0;
-
- for (i = 0; i != len; i++) {
- char c;
-
- if (get_user(c, buf + i))
- return -EFAULT;
- if (c == 'V')
- expect_close = 42;
- }
- }
- xwdt_keepalive();
- }
- return len;
-}
-
-static const struct watchdog_info ident = {
- .options = WDIOF_MAGICCLOSE |
- WDIOF_KEEPALIVEPING,
- .firmware_version = 1,
- .identity = WATCHDOG_NAME,
-};
-
-/*
- * xwdt_ioctl:
- * @file: file handle to the device
- * @cmd: watchdog command
- * @arg: argument pointer
- *
- * The watchdog API defines a common set of functions for all watchdogs
- * according to their available features.
- */
-static long xwdt_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
-{
- int status;
-
- union {
- struct watchdog_info __user *ident;
- int __user *i;
- } uarg;
-
- uarg.i = (int __user *)arg;
-
- switch (cmd) {
- case WDIOC_GETSUPPORT:
- return copy_to_user(uarg.ident, &ident,
- sizeof(ident)) ? -EFAULT : 0;
-
- case WDIOC_GETBOOTSTATUS:
- return put_user(xdev.boot_status, uarg.i);
-
- case WDIOC_GETSTATUS:
- xwdt_get_status(&status);
- return put_user(status, uarg.i);
-
- case WDIOC_KEEPALIVE:
- xwdt_keepalive();
- return 0;
-
- case WDIOC_GETTIMEOUT:
- if (no_timeout)
- return -ENOTTY;
- else
- return put_user(timeout, uarg.i);
-
- default:
- return -ENOTTY;
- }
-}
-
-static const struct file_operations xwdt_fops = {
- .owner = THIS_MODULE,
- .llseek = no_llseek,
- .write = xwdt_write,
- .open = xwdt_open,
- .release = xwdt_release,
- .unlocked_ioctl = xwdt_ioctl,
-};
-
-static struct miscdevice xwdt_miscdev = {
- .minor = WATCHDOG_MINOR,
- .name = "watchdog",
- .fops = &xwdt_fops,
-};
-
-static int __devinit xwdt_probe(struct platform_device *pdev)
+static int xwdt_probe(struct platform_device *pdev)
{
int rc;
- u32 *tmptr;
- u32 *pfreq;
-
- no_timeout = 0;
-
- pfreq = (u32 *)of_get_property(pdev->dev.of_node->parent,
- "clock-frequency", NULL);
-
- if (pfreq == NULL) {
- pr_warn("The watchdog clock frequency cannot be obtained!\n");
- no_timeout = 1;
- }
-
- rc = of_address_to_resource(pdev->dev.of_node, 0, &xdev.res);
- if (rc) {
- pr_warn("invalid address!\n");
- return rc;
- }
-
- tmptr = (u32 *)of_get_property(pdev->dev.of_node,
- "xlnx,wdt-interval", NULL);
- if (tmptr == NULL) {
- pr_warn("Parameter \"xlnx,wdt-interval\" not found in device tree!\n");
- no_timeout = 1;
- } else {
- xdev.wdt_interval = *tmptr;
- }
-
- tmptr = (u32 *)of_get_property(pdev->dev.of_node,
- "xlnx,wdt-enable-once", NULL);
- if (tmptr == NULL) {
- pr_warn("Parameter \"xlnx,wdt-enable-once\" not found in device tree!\n");
- xdev.nowayout = WATCHDOG_NOWAYOUT;
- }
-
-/*
- * Twice of the 2^wdt_interval / freq because the first wdt overflow is
- * ignored (interrupt), reset is only generated at second wdt overflow
- */
- if (!no_timeout)
- timeout = 2 * ((1<<xdev.wdt_interval) / *pfreq);
-
- if (!request_mem_region(xdev.res.start,
- xdev.res.end - xdev.res.start + 1, WATCHDOG_NAME)) {
- rc = -ENXIO;
- pr_err("memory request failure!\n");
- goto err_out;
- }
-
- xdev.base = ioremap(xdev.res.start, xdev.res.end - xdev.res.start + 1);
- if (xdev.base == NULL) {
- rc = -ENOMEM;
- pr_err("ioremap failure!\n");
- goto release_mem;
- }
-
- rc = xwdt_selftest();
+ u32 pfreq = 0, enable_once = 0;
+ struct resource *res;
+ struct xwdt_device *xdev;
+ struct watchdog_device *xilinx_wdt_wdd;
+
+ xdev = devm_kzalloc(&pdev->dev, sizeof(*xdev), GFP_KERNEL);
+ if (!xdev)
+ return -ENOMEM;
+
+ xilinx_wdt_wdd = &xdev->xilinx_wdt_wdd;
+ xilinx_wdt_wdd->info = &xilinx_wdt_ident;
+ xilinx_wdt_wdd->ops = &xilinx_wdt_ops;
+ xilinx_wdt_wdd->parent = &pdev->dev;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ xdev->base = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(xdev->base))
+ return PTR_ERR(xdev->base);
+
+ rc = of_property_read_u32(pdev->dev.of_node, "clock-frequency", &pfreq);
+ if (rc)
+ dev_warn(&pdev->dev,
+ "The watchdog clock frequency cannot be obtained\n");
+
+ rc = of_property_read_u32(pdev->dev.of_node, "xlnx,wdt-interval",
+ &xdev->wdt_interval);
+ if (rc)
+ dev_warn(&pdev->dev,
+ "Parameter \"xlnx,wdt-interval\" not found\n");
+
+ rc = of_property_read_u32(pdev->dev.of_node, "xlnx,wdt-enable-once",
+ &enable_once);
+ if (rc)
+ dev_warn(&pdev->dev,
+ "Parameter \"xlnx,wdt-enable-once\" not found\n");
+
+ watchdog_set_nowayout(xilinx_wdt_wdd, enable_once);
+
+ /*
+ * Twice of the 2^wdt_interval / freq because the first wdt overflow is
+ * ignored (interrupt), reset is only generated at second wdt overflow
+ */
+ if (pfreq && xdev->wdt_interval)
+ xilinx_wdt_wdd->timeout = 2 * ((1 << xdev->wdt_interval) /
+ pfreq);
+
+ spin_lock_init(&xdev->spinlock);
+ watchdog_set_drvdata(xilinx_wdt_wdd, xdev);
+
+ rc = xwdt_selftest(xdev);
if (rc == XWT_TIMER_FAILED) {
- pr_err("SelfTest routine error!\n");
- goto unmap_io;
+ dev_err(&pdev->dev, "SelfTest routine error\n");
+ return rc;
}
- xwdt_get_status(&xdev.boot_status);
-
- rc = misc_register(&xwdt_miscdev);
+ rc = watchdog_register_device(xilinx_wdt_wdd);
if (rc) {
- pr_err("cannot register miscdev on minor=%d (err=%d)\n",
- xwdt_miscdev.minor, rc);
- goto unmap_io;
+ dev_err(&pdev->dev, "Cannot register watchdog (err=%d)\n", rc);
+ return rc;
}
- if (no_timeout)
- pr_info("driver loaded (timeout=? sec, nowayout=%d)\n",
- xdev.nowayout);
- else
- pr_info("driver loaded (timeout=%d sec, nowayout=%d)\n",
- timeout, xdev.nowayout);
+ dev_info(&pdev->dev, "Xilinx Watchdog Timer at %p with timeout %ds\n",
+ xdev->base, xilinx_wdt_wdd->timeout);
- expect_close = 0;
- clear_bit(0, &driver_open);
+ platform_set_drvdata(pdev, xdev);
return 0;
-
-unmap_io:
- iounmap(xdev.base);
-release_mem:
- release_mem_region(xdev.res.start, resource_size(&xdev.res));
-err_out:
- return rc;
}
-static int __devexit xwdt_remove(struct platform_device *dev)
+static int xwdt_remove(struct platform_device *pdev)
{
- misc_deregister(&xwdt_miscdev);
- iounmap(xdev.base);
- release_mem_region(xdev.res.start, resource_size(&xdev.res));
+ struct xwdt_device *xdev = platform_get_drvdata(pdev);
+
+ watchdog_unregister_device(&xdev->xilinx_wdt_wdd);
return 0;
}
/* Match table for of_platform binding */
-static struct of_device_id __devinitdata xwdt_of_match[] = {
+static const struct of_device_id xwdt_of_match[] = {
+ { .compatible = "xlnx,xps-timebase-wdt-1.00.a", },
{ .compatible = "xlnx,xps-timebase-wdt-1.01.a", },
{},
};
@@ -401,7 +234,7 @@ MODULE_DEVICE_TABLE(of, xwdt_of_match);
static struct platform_driver xwdt_driver = {
.probe = xwdt_probe,
- .remove = __devexit_p(xwdt_remove),
+ .remove = xwdt_remove,
.driver = {
.owner = THIS_MODULE,
.name = WATCHDOG_NAME,
@@ -413,5 +246,4 @@ module_platform_driver(xwdt_driver);
MODULE_AUTHOR("Alejandro Cabrera <aldaya@gmail.com>");
MODULE_DESCRIPTION("Xilinx Watchdog driver");
-MODULE_LICENSE("GPL");
-MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/watchdog/omap_wdt.c b/drivers/watchdog/omap_wdt.c
index 8285d65cd20..3691b157516 100644
--- a/drivers/watchdog/omap_wdt.c
+++ b/drivers/watchdog/omap_wdt.c
@@ -31,55 +31,49 @@
#include <linux/module.h>
#include <linux/types.h>
#include <linux/kernel.h>
-#include <linux/fs.h>
#include <linux/mm.h>
-#include <linux/miscdevice.h>
#include <linux/watchdog.h>
#include <linux/reboot.h>
-#include <linux/init.h>
#include <linux/err.h>
#include <linux/platform_device.h>
#include <linux/moduleparam.h>
-#include <linux/bitops.h>
#include <linux/io.h>
-#include <linux/uaccess.h>
#include <linux/slab.h>
#include <linux/pm_runtime.h>
-#include <mach/hardware.h>
-#include <plat/prcm.h>
+#include <linux/platform_data/omap-wd-timer.h>
#include "omap_wdt.h"
-static struct platform_device *omap_wdt_dev;
+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) ")");
static unsigned timer_margin;
module_param(timer_margin, uint, 0);
MODULE_PARM_DESC(timer_margin, "initial watchdog timeout (in seconds)");
-static unsigned int wdt_trgr_pattern = 0x1234;
-static DEFINE_SPINLOCK(wdt_lock);
-
struct omap_wdt_dev {
void __iomem *base; /* physical */
struct device *dev;
- int omap_wdt_users;
- struct resource *mem;
- struct miscdevice omap_wdt_miscdev;
+ bool omap_wdt_users;
+ int wdt_trgr_pattern;
+ struct mutex lock; /* to avoid races with PM */
};
-static void omap_wdt_ping(struct omap_wdt_dev *wdev)
+static void omap_wdt_reload(struct omap_wdt_dev *wdev)
{
void __iomem *base = wdev->base;
/* wait for posted write to complete */
- while ((__raw_readl(base + OMAP_WATCHDOG_WPS)) & 0x08)
+ while ((readl_relaxed(base + OMAP_WATCHDOG_WPS)) & 0x08)
cpu_relax();
- wdt_trgr_pattern = ~wdt_trgr_pattern;
- __raw_writel(wdt_trgr_pattern, (base + OMAP_WATCHDOG_TGR));
+ wdev->wdt_trgr_pattern = ~wdev->wdt_trgr_pattern;
+ writel_relaxed(wdev->wdt_trgr_pattern, (base + OMAP_WATCHDOG_TGR));
/* wait for posted write to complete */
- while ((__raw_readl(base + OMAP_WATCHDOG_WPS)) & 0x08)
+ while ((readl_relaxed(base + OMAP_WATCHDOG_WPS)) & 0x08)
cpu_relax();
/* reloaded WCRR from WLDR */
}
@@ -89,12 +83,12 @@ static void omap_wdt_enable(struct omap_wdt_dev *wdev)
void __iomem *base = wdev->base;
/* Sequence to enable the watchdog */
- __raw_writel(0xBBBB, base + OMAP_WATCHDOG_SPR);
- while ((__raw_readl(base + OMAP_WATCHDOG_WPS)) & 0x10)
+ writel_relaxed(0xBBBB, base + OMAP_WATCHDOG_SPR);
+ while ((readl_relaxed(base + OMAP_WATCHDOG_WPS)) & 0x10)
cpu_relax();
- __raw_writel(0x4444, base + OMAP_WATCHDOG_SPR);
- while ((__raw_readl(base + OMAP_WATCHDOG_WPS)) & 0x10)
+ writel_relaxed(0x4444, base + OMAP_WATCHDOG_SPR);
+ while ((readl_relaxed(base + OMAP_WATCHDOG_WPS)) & 0x10)
cpu_relax();
}
@@ -103,289 +97,201 @@ static void omap_wdt_disable(struct omap_wdt_dev *wdev)
void __iomem *base = wdev->base;
/* sequence required to disable watchdog */
- __raw_writel(0xAAAA, base + OMAP_WATCHDOG_SPR); /* TIMER_MODE */
- while (__raw_readl(base + OMAP_WATCHDOG_WPS) & 0x10)
+ writel_relaxed(0xAAAA, base + OMAP_WATCHDOG_SPR); /* TIMER_MODE */
+ while (readl_relaxed(base + OMAP_WATCHDOG_WPS) & 0x10)
cpu_relax();
- __raw_writel(0x5555, base + OMAP_WATCHDOG_SPR); /* TIMER_MODE */
- while (__raw_readl(base + OMAP_WATCHDOG_WPS) & 0x10)
+ writel_relaxed(0x5555, base + OMAP_WATCHDOG_SPR); /* TIMER_MODE */
+ while (readl_relaxed(base + OMAP_WATCHDOG_WPS) & 0x10)
cpu_relax();
}
-static void omap_wdt_adjust_timeout(unsigned new_timeout)
+static void omap_wdt_set_timer(struct omap_wdt_dev *wdev,
+ unsigned int timeout)
{
- if (new_timeout < TIMER_MARGIN_MIN)
- new_timeout = TIMER_MARGIN_DEFAULT;
- if (new_timeout > TIMER_MARGIN_MAX)
- new_timeout = TIMER_MARGIN_MAX;
- timer_margin = new_timeout;
-}
-
-static void omap_wdt_set_timeout(struct omap_wdt_dev *wdev)
-{
- u32 pre_margin = GET_WLDR_VAL(timer_margin);
+ u32 pre_margin = GET_WLDR_VAL(timeout);
void __iomem *base = wdev->base;
- pm_runtime_get_sync(wdev->dev);
-
/* just count up at 32 KHz */
- while (__raw_readl(base + OMAP_WATCHDOG_WPS) & 0x04)
+ while (readl_relaxed(base + OMAP_WATCHDOG_WPS) & 0x04)
cpu_relax();
- __raw_writel(pre_margin, base + OMAP_WATCHDOG_LDR);
- while (__raw_readl(base + OMAP_WATCHDOG_WPS) & 0x04)
+ writel_relaxed(pre_margin, base + OMAP_WATCHDOG_LDR);
+ while (readl_relaxed(base + OMAP_WATCHDOG_WPS) & 0x04)
cpu_relax();
-
- pm_runtime_put_sync(wdev->dev);
}
-/*
- * Allow only one task to hold it open
- */
-static int omap_wdt_open(struct inode *inode, struct file *file)
+static int omap_wdt_start(struct watchdog_device *wdog)
{
- struct omap_wdt_dev *wdev = platform_get_drvdata(omap_wdt_dev);
+ struct omap_wdt_dev *wdev = watchdog_get_drvdata(wdog);
void __iomem *base = wdev->base;
- if (test_and_set_bit(1, (unsigned long *)&(wdev->omap_wdt_users)))
- return -EBUSY;
+ mutex_lock(&wdev->lock);
+
+ wdev->omap_wdt_users = true;
pm_runtime_get_sync(wdev->dev);
/* initialize prescaler */
- while (__raw_readl(base + OMAP_WATCHDOG_WPS) & 0x01)
+ while (readl_relaxed(base + OMAP_WATCHDOG_WPS) & 0x01)
cpu_relax();
- __raw_writel((1 << 5) | (PTV << 2), base + OMAP_WATCHDOG_CNTRL);
- while (__raw_readl(base + OMAP_WATCHDOG_WPS) & 0x01)
+ writel_relaxed((1 << 5) | (PTV << 2), base + OMAP_WATCHDOG_CNTRL);
+ while (readl_relaxed(base + OMAP_WATCHDOG_WPS) & 0x01)
cpu_relax();
- file->private_data = (void *) wdev;
-
- omap_wdt_set_timeout(wdev);
- omap_wdt_ping(wdev); /* trigger loading of new timeout value */
+ omap_wdt_set_timer(wdev, wdog->timeout);
+ omap_wdt_reload(wdev); /* trigger loading of new timeout value */
omap_wdt_enable(wdev);
- pm_runtime_put_sync(wdev->dev);
+ mutex_unlock(&wdev->lock);
- return nonseekable_open(inode, file);
+ return 0;
}
-static int omap_wdt_release(struct inode *inode, struct file *file)
+static int omap_wdt_stop(struct watchdog_device *wdog)
{
- struct omap_wdt_dev *wdev = file->private_data;
-
- /*
- * Shut off the timer unless NOWAYOUT is defined.
- */
-#ifndef CONFIG_WATCHDOG_NOWAYOUT
- pm_runtime_get_sync(wdev->dev);
+ struct omap_wdt_dev *wdev = watchdog_get_drvdata(wdog);
+ mutex_lock(&wdev->lock);
omap_wdt_disable(wdev);
-
pm_runtime_put_sync(wdev->dev);
-#else
- pr_crit("Unexpected close, not stopping!\n");
-#endif
- wdev->omap_wdt_users = 0;
-
+ wdev->omap_wdt_users = false;
+ mutex_unlock(&wdev->lock);
return 0;
}
-static ssize_t omap_wdt_write(struct file *file, const char __user *data,
- size_t len, loff_t *ppos)
+static int omap_wdt_ping(struct watchdog_device *wdog)
{
- struct omap_wdt_dev *wdev = file->private_data;
+ struct omap_wdt_dev *wdev = watchdog_get_drvdata(wdog);
- /* Refresh LOAD_TIME. */
- if (len) {
- pm_runtime_get_sync(wdev->dev);
- spin_lock(&wdt_lock);
- omap_wdt_ping(wdev);
- spin_unlock(&wdt_lock);
- pm_runtime_put_sync(wdev->dev);
- }
- return len;
+ mutex_lock(&wdev->lock);
+ omap_wdt_reload(wdev);
+ mutex_unlock(&wdev->lock);
+
+ return 0;
}
-static long omap_wdt_ioctl(struct file *file, unsigned int cmd,
- unsigned long arg)
+static int omap_wdt_set_timeout(struct watchdog_device *wdog,
+ unsigned int timeout)
{
- struct omap_wdt_dev *wdev;
- int new_margin;
- static const struct watchdog_info ident = {
- .identity = "OMAP Watchdog",
- .options = WDIOF_SETTIMEOUT,
- .firmware_version = 0,
- };
-
- wdev = file->private_data;
-
- switch (cmd) {
- case WDIOC_GETSUPPORT:
- return copy_to_user((struct watchdog_info __user *)arg, &ident,
- sizeof(ident));
- case WDIOC_GETSTATUS:
- return put_user(0, (int __user *)arg);
- case WDIOC_GETBOOTSTATUS:
- if (cpu_is_omap16xx())
- return put_user(__raw_readw(ARM_SYSST),
- (int __user *)arg);
- if (cpu_is_omap24xx())
- return put_user(omap_prcm_get_reset_sources(),
- (int __user *)arg);
- return put_user(0, (int __user *)arg);
- case WDIOC_KEEPALIVE:
- pm_runtime_get_sync(wdev->dev);
- spin_lock(&wdt_lock);
- omap_wdt_ping(wdev);
- spin_unlock(&wdt_lock);
- pm_runtime_put_sync(wdev->dev);
- return 0;
- case WDIOC_SETTIMEOUT:
- if (get_user(new_margin, (int __user *)arg))
- return -EFAULT;
- omap_wdt_adjust_timeout(new_margin);
+ struct omap_wdt_dev *wdev = watchdog_get_drvdata(wdog);
- pm_runtime_get_sync(wdev->dev);
- spin_lock(&wdt_lock);
- omap_wdt_disable(wdev);
- omap_wdt_set_timeout(wdev);
- omap_wdt_enable(wdev);
+ mutex_lock(&wdev->lock);
+ omap_wdt_disable(wdev);
+ omap_wdt_set_timer(wdev, timeout);
+ omap_wdt_enable(wdev);
+ omap_wdt_reload(wdev);
+ wdog->timeout = timeout;
+ mutex_unlock(&wdev->lock);
- omap_wdt_ping(wdev);
- spin_unlock(&wdt_lock);
- pm_runtime_put_sync(wdev->dev);
- /* Fall */
- case WDIOC_GETTIMEOUT:
- return put_user(timer_margin, (int __user *)arg);
- default:
- return -ENOTTY;
- }
+ return 0;
}
-static const struct file_operations omap_wdt_fops = {
- .owner = THIS_MODULE,
- .write = omap_wdt_write,
- .unlocked_ioctl = omap_wdt_ioctl,
- .open = omap_wdt_open,
- .release = omap_wdt_release,
- .llseek = no_llseek,
+static const struct watchdog_info omap_wdt_info = {
+ .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING,
+ .identity = "OMAP Watchdog",
+};
+
+static const struct watchdog_ops omap_wdt_ops = {
+ .owner = THIS_MODULE,
+ .start = omap_wdt_start,
+ .stop = omap_wdt_stop,
+ .ping = omap_wdt_ping,
+ .set_timeout = omap_wdt_set_timeout,
};
-static int __devinit omap_wdt_probe(struct platform_device *pdev)
+static int omap_wdt_probe(struct platform_device *pdev)
{
- struct resource *res, *mem;
+ struct omap_wd_timer_platform_data *pdata = dev_get_platdata(&pdev->dev);
+ struct watchdog_device *omap_wdt;
+ struct resource *res;
struct omap_wdt_dev *wdev;
+ u32 rs;
int ret;
- /* reserve static register mappings */
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (!res) {
- ret = -ENOENT;
- goto err_get_resource;
- }
+ omap_wdt = devm_kzalloc(&pdev->dev, sizeof(*omap_wdt), GFP_KERNEL);
+ if (!omap_wdt)
+ return -ENOMEM;
- if (omap_wdt_dev) {
- ret = -EBUSY;
- goto err_busy;
- }
+ wdev = devm_kzalloc(&pdev->dev, sizeof(*wdev), GFP_KERNEL);
+ if (!wdev)
+ return -ENOMEM;
- mem = request_mem_region(res->start, resource_size(res), pdev->name);
- if (!mem) {
- ret = -EBUSY;
- goto err_busy;
- }
+ wdev->omap_wdt_users = false;
+ wdev->dev = &pdev->dev;
+ wdev->wdt_trgr_pattern = 0x1234;
+ mutex_init(&wdev->lock);
- wdev = kzalloc(sizeof(struct omap_wdt_dev), GFP_KERNEL);
- if (!wdev) {
- ret = -ENOMEM;
- goto err_kzalloc;
- }
+ /* reserve static register mappings */
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ wdev->base = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(wdev->base))
+ return PTR_ERR(wdev->base);
- wdev->omap_wdt_users = 0;
- wdev->mem = mem;
- wdev->dev = &pdev->dev;
+ omap_wdt->info = &omap_wdt_info;
+ omap_wdt->ops = &omap_wdt_ops;
+ omap_wdt->min_timeout = TIMER_MARGIN_MIN;
+ omap_wdt->max_timeout = TIMER_MARGIN_MAX;
- wdev->base = ioremap(res->start, resource_size(res));
- if (!wdev->base) {
- ret = -ENOMEM;
- goto err_ioremap;
- }
+ if (timer_margin >= TIMER_MARGIN_MIN &&
+ timer_margin <= TIMER_MARGIN_MAX)
+ omap_wdt->timeout = timer_margin;
+ else
+ omap_wdt->timeout = TIMER_MARGIN_DEFAULT;
+
+ watchdog_set_drvdata(omap_wdt, wdev);
+ watchdog_set_nowayout(omap_wdt, nowayout);
- platform_set_drvdata(pdev, wdev);
+ platform_set_drvdata(pdev, omap_wdt);
pm_runtime_enable(wdev->dev);
pm_runtime_get_sync(wdev->dev);
- omap_wdt_disable(wdev);
- omap_wdt_adjust_timeout(timer_margin);
+ if (pdata && pdata->read_reset_sources)
+ rs = pdata->read_reset_sources();
+ else
+ rs = 0;
+ omap_wdt->bootstatus = (rs & (1 << OMAP_MPU_WD_RST_SRC_ID_SHIFT)) ?
+ WDIOF_CARDRESET : 0;
- wdev->omap_wdt_miscdev.parent = &pdev->dev;
- wdev->omap_wdt_miscdev.minor = WATCHDOG_MINOR;
- wdev->omap_wdt_miscdev.name = "watchdog";
- wdev->omap_wdt_miscdev.fops = &omap_wdt_fops;
+ omap_wdt_disable(wdev);
- ret = misc_register(&(wdev->omap_wdt_miscdev));
- if (ret)
- goto err_misc;
+ ret = watchdog_register_device(omap_wdt);
+ if (ret) {
+ pm_runtime_disable(wdev->dev);
+ return ret;
+ }
pr_info("OMAP Watchdog Timer Rev 0x%02x: initial timeout %d sec\n",
- __raw_readl(wdev->base + OMAP_WATCHDOG_REV) & 0xFF,
- timer_margin);
+ readl_relaxed(wdev->base + OMAP_WATCHDOG_REV) & 0xFF,
+ omap_wdt->timeout);
pm_runtime_put_sync(wdev->dev);
- omap_wdt_dev = pdev;
-
return 0;
-
-err_misc:
- pm_runtime_disable(wdev->dev);
- platform_set_drvdata(pdev, NULL);
- iounmap(wdev->base);
-
-err_ioremap:
- wdev->base = NULL;
- kfree(wdev);
-
-err_kzalloc:
- release_mem_region(res->start, resource_size(res));
-
-err_busy:
-err_get_resource:
-
- return ret;
}
static void omap_wdt_shutdown(struct platform_device *pdev)
{
- struct omap_wdt_dev *wdev = platform_get_drvdata(pdev);
+ struct watchdog_device *wdog = platform_get_drvdata(pdev);
+ struct omap_wdt_dev *wdev = watchdog_get_drvdata(wdog);
+ mutex_lock(&wdev->lock);
if (wdev->omap_wdt_users) {
- pm_runtime_get_sync(wdev->dev);
omap_wdt_disable(wdev);
pm_runtime_put_sync(wdev->dev);
}
+ mutex_unlock(&wdev->lock);
}
-static int __devexit omap_wdt_remove(struct platform_device *pdev)
+static int omap_wdt_remove(struct platform_device *pdev)
{
- struct omap_wdt_dev *wdev = platform_get_drvdata(pdev);
- struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ struct watchdog_device *wdog = platform_get_drvdata(pdev);
+ struct omap_wdt_dev *wdev = watchdog_get_drvdata(wdog);
pm_runtime_disable(wdev->dev);
- if (!res)
- return -ENOENT;
-
- misc_deregister(&(wdev->omap_wdt_miscdev));
- release_mem_region(res->start, resource_size(res));
- platform_set_drvdata(pdev, NULL);
-
- iounmap(wdev->base);
-
- kfree(wdev);
- omap_wdt_dev = NULL;
+ watchdog_unregister_device(wdog);
return 0;
}
@@ -400,27 +306,31 @@ static int __devexit omap_wdt_remove(struct platform_device *pdev)
static int omap_wdt_suspend(struct platform_device *pdev, pm_message_t state)
{
- struct omap_wdt_dev *wdev = platform_get_drvdata(pdev);
+ struct watchdog_device *wdog = platform_get_drvdata(pdev);
+ struct omap_wdt_dev *wdev = watchdog_get_drvdata(wdog);
+ mutex_lock(&wdev->lock);
if (wdev->omap_wdt_users) {
- pm_runtime_get_sync(wdev->dev);
omap_wdt_disable(wdev);
pm_runtime_put_sync(wdev->dev);
}
+ mutex_unlock(&wdev->lock);
return 0;
}
static int omap_wdt_resume(struct platform_device *pdev)
{
- struct omap_wdt_dev *wdev = platform_get_drvdata(pdev);
+ struct watchdog_device *wdog = platform_get_drvdata(pdev);
+ struct omap_wdt_dev *wdev = watchdog_get_drvdata(wdog);
+ mutex_lock(&wdev->lock);
if (wdev->omap_wdt_users) {
pm_runtime_get_sync(wdev->dev);
omap_wdt_enable(wdev);
- omap_wdt_ping(wdev);
- pm_runtime_put_sync(wdev->dev);
+ omap_wdt_reload(wdev);
}
+ mutex_unlock(&wdev->lock);
return 0;
}
@@ -430,15 +340,22 @@ static int omap_wdt_resume(struct platform_device *pdev)
#define omap_wdt_resume NULL
#endif
+static const struct of_device_id omap_wdt_of_match[] = {
+ { .compatible = "ti,omap3-wdt", },
+ {},
+};
+MODULE_DEVICE_TABLE(of, omap_wdt_of_match);
+
static struct platform_driver omap_wdt_driver = {
.probe = omap_wdt_probe,
- .remove = __devexit_p(omap_wdt_remove),
+ .remove = omap_wdt_remove,
.shutdown = omap_wdt_shutdown,
.suspend = omap_wdt_suspend,
.resume = omap_wdt_resume,
.driver = {
.owner = THIS_MODULE,
.name = "omap_wdt",
+ .of_match_table = omap_wdt_of_match,
},
};
@@ -446,5 +363,4 @@ module_platform_driver(omap_wdt_driver);
MODULE_AUTHOR("George G. Davis");
MODULE_LICENSE("GPL");
-MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
MODULE_ALIAS("platform:omap_wdt");
diff --git a/drivers/watchdog/orion_wdt.c b/drivers/watchdog/orion_wdt.c
index 788aa158e78..00d0741228f 100644
--- a/drivers/watchdog/orion_wdt.c
+++ b/drivers/watchdog/orion_wdt.c
@@ -16,288 +16,586 @@
#include <linux/moduleparam.h>
#include <linux/types.h>
#include <linux/kernel.h>
-#include <linux/fs.h>
-#include <linux/miscdevice.h>
#include <linux/platform_device.h>
#include <linux/watchdog.h>
-#include <linux/init.h>
-#include <linux/uaccess.h>
+#include <linux/interrupt.h>
#include <linux/io.h>
-#include <linux/spinlock.h>
-#include <mach/bridge-regs.h>
-#include <plat/orion_wdt.h>
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+
+/* RSTOUT mask register physical address for Orion5x, Kirkwood and Dove */
+#define ORION_RSTOUT_MASK_OFFSET 0x20108
+
+/* Internal registers can be configured at any 1 MiB aligned address */
+#define INTERNAL_REGS_MASK ~(SZ_1M - 1)
/*
* Watchdog timer block registers.
*/
#define TIMER_CTRL 0x0000
-#define WDT_EN 0x0010
-#define WDT_VAL 0x0024
+#define TIMER_A370_STATUS 0x04
#define WDT_MAX_CYCLE_COUNT 0xffffffff
-#define WDT_IN_USE 0
-#define WDT_OK_TO_CLOSE 1
+
+#define WDT_A370_RATIO_MASK(v) ((v) << 16)
+#define WDT_A370_RATIO_SHIFT 5
+#define WDT_A370_RATIO (1 << WDT_A370_RATIO_SHIFT)
+
+#define WDT_AXP_FIXED_ENABLE_BIT BIT(10)
+#define WDT_A370_EXPIRED BIT(31)
static bool nowayout = WATCHDOG_NOWAYOUT;
static int heartbeat = -1; /* module parameter (seconds) */
-static unsigned int wdt_max_duration; /* (seconds) */
-static unsigned int wdt_tclk;
-static void __iomem *wdt_reg;
-static unsigned long wdt_status;
-static DEFINE_SPINLOCK(wdt_lock);
-static void orion_wdt_ping(void)
+struct orion_watchdog;
+
+struct orion_watchdog_data {
+ int wdt_counter_offset;
+ int wdt_enable_bit;
+ int rstout_enable_bit;
+ int rstout_mask_bit;
+ int (*clock_init)(struct platform_device *,
+ struct orion_watchdog *);
+ int (*enabled)(struct orion_watchdog *);
+ int (*start)(struct watchdog_device *);
+ int (*stop)(struct watchdog_device *);
+};
+
+struct orion_watchdog {
+ struct watchdog_device wdt;
+ void __iomem *reg;
+ void __iomem *rstout;
+ void __iomem *rstout_mask;
+ unsigned long clk_rate;
+ struct clk *clk;
+ const struct orion_watchdog_data *data;
+};
+
+static int orion_wdt_clock_init(struct platform_device *pdev,
+ struct orion_watchdog *dev)
{
- spin_lock(&wdt_lock);
+ int ret;
- /* Reload watchdog duration */
- writel(wdt_tclk * heartbeat, wdt_reg + WDT_VAL);
+ dev->clk = clk_get(&pdev->dev, NULL);
+ if (IS_ERR(dev->clk))
+ return PTR_ERR(dev->clk);
+ ret = clk_prepare_enable(dev->clk);
+ if (ret) {
+ clk_put(dev->clk);
+ return ret;
+ }
- spin_unlock(&wdt_lock);
+ dev->clk_rate = clk_get_rate(dev->clk);
+ return 0;
}
-static void orion_wdt_enable(void)
+static int armada370_wdt_clock_init(struct platform_device *pdev,
+ struct orion_watchdog *dev)
{
- u32 reg;
+ int ret;
+
+ dev->clk = clk_get(&pdev->dev, NULL);
+ if (IS_ERR(dev->clk))
+ return PTR_ERR(dev->clk);
+ ret = clk_prepare_enable(dev->clk);
+ if (ret) {
+ clk_put(dev->clk);
+ return ret;
+ }
+
+ /* Setup watchdog input clock */
+ atomic_io_modify(dev->reg + TIMER_CTRL,
+ WDT_A370_RATIO_MASK(WDT_A370_RATIO_SHIFT),
+ WDT_A370_RATIO_MASK(WDT_A370_RATIO_SHIFT));
+
+ dev->clk_rate = clk_get_rate(dev->clk) / WDT_A370_RATIO;
+ return 0;
+}
+
+static int armadaxp_wdt_clock_init(struct platform_device *pdev,
+ struct orion_watchdog *dev)
+{
+ int ret;
+
+ dev->clk = of_clk_get_by_name(pdev->dev.of_node, "fixed");
+ if (IS_ERR(dev->clk))
+ return PTR_ERR(dev->clk);
+ ret = clk_prepare_enable(dev->clk);
+ if (ret) {
+ clk_put(dev->clk);
+ return ret;
+ }
+
+ /* Enable the fixed watchdog clock input */
+ atomic_io_modify(dev->reg + TIMER_CTRL,
+ WDT_AXP_FIXED_ENABLE_BIT,
+ WDT_AXP_FIXED_ENABLE_BIT);
+
+ dev->clk_rate = clk_get_rate(dev->clk);
+ return 0;
+}
+
+static int orion_wdt_ping(struct watchdog_device *wdt_dev)
+{
+ struct orion_watchdog *dev = watchdog_get_drvdata(wdt_dev);
+ /* Reload watchdog duration */
+ writel(dev->clk_rate * wdt_dev->timeout,
+ dev->reg + dev->data->wdt_counter_offset);
+ return 0;
+}
- spin_lock(&wdt_lock);
+static int armada375_start(struct watchdog_device *wdt_dev)
+{
+ struct orion_watchdog *dev = watchdog_get_drvdata(wdt_dev);
+ u32 reg;
/* Set watchdog duration */
- writel(wdt_tclk * heartbeat, wdt_reg + WDT_VAL);
+ writel(dev->clk_rate * wdt_dev->timeout,
+ dev->reg + dev->data->wdt_counter_offset);
- /* Clear watchdog timer interrupt */
- reg = readl(BRIDGE_CAUSE);
- reg &= ~WDT_INT_REQ;
- writel(reg, BRIDGE_CAUSE);
+ /* Clear the watchdog expiration bit */
+ atomic_io_modify(dev->reg + TIMER_A370_STATUS, WDT_A370_EXPIRED, 0);
/* Enable watchdog timer */
- reg = readl(wdt_reg + TIMER_CTRL);
- reg |= WDT_EN;
- writel(reg, wdt_reg + TIMER_CTRL);
+ atomic_io_modify(dev->reg + TIMER_CTRL, dev->data->wdt_enable_bit,
+ dev->data->wdt_enable_bit);
/* Enable reset on watchdog */
- reg = readl(RSTOUTn_MASK);
- reg |= WDT_RESET_OUT_EN;
- writel(reg, RSTOUTn_MASK);
+ reg = readl(dev->rstout);
+ reg |= dev->data->rstout_enable_bit;
+ writel(reg, dev->rstout);
- spin_unlock(&wdt_lock);
+ atomic_io_modify(dev->rstout_mask, dev->data->rstout_mask_bit, 0);
+ return 0;
}
-static void orion_wdt_disable(void)
+static int armada370_start(struct watchdog_device *wdt_dev)
{
+ struct orion_watchdog *dev = watchdog_get_drvdata(wdt_dev);
u32 reg;
- spin_lock(&wdt_lock);
+ /* Set watchdog duration */
+ writel(dev->clk_rate * wdt_dev->timeout,
+ dev->reg + dev->data->wdt_counter_offset);
- /* Disable reset on watchdog */
- reg = readl(RSTOUTn_MASK);
- reg &= ~WDT_RESET_OUT_EN;
- writel(reg, RSTOUTn_MASK);
+ /* Clear the watchdog expiration bit */
+ atomic_io_modify(dev->reg + TIMER_A370_STATUS, WDT_A370_EXPIRED, 0);
- /* Disable watchdog timer */
- reg = readl(wdt_reg + TIMER_CTRL);
- reg &= ~WDT_EN;
- writel(reg, wdt_reg + TIMER_CTRL);
+ /* Enable watchdog timer */
+ atomic_io_modify(dev->reg + TIMER_CTRL, dev->data->wdt_enable_bit,
+ dev->data->wdt_enable_bit);
- spin_unlock(&wdt_lock);
+ /* Enable reset on watchdog */
+ reg = readl(dev->rstout);
+ reg |= dev->data->rstout_enable_bit;
+ writel(reg, dev->rstout);
+ return 0;
}
-static int orion_wdt_get_timeleft(int *time_left)
+static int orion_start(struct watchdog_device *wdt_dev)
{
- spin_lock(&wdt_lock);
- *time_left = readl(wdt_reg + WDT_VAL) / wdt_tclk;
- spin_unlock(&wdt_lock);
+ struct orion_watchdog *dev = watchdog_get_drvdata(wdt_dev);
+
+ /* Set watchdog duration */
+ writel(dev->clk_rate * wdt_dev->timeout,
+ dev->reg + dev->data->wdt_counter_offset);
+
+ /* Enable watchdog timer */
+ atomic_io_modify(dev->reg + TIMER_CTRL, dev->data->wdt_enable_bit,
+ dev->data->wdt_enable_bit);
+
+ /* Enable reset on watchdog */
+ atomic_io_modify(dev->rstout, dev->data->rstout_enable_bit,
+ dev->data->rstout_enable_bit);
+
return 0;
}
-static int orion_wdt_open(struct inode *inode, struct file *file)
+static int orion_wdt_start(struct watchdog_device *wdt_dev)
{
- if (test_and_set_bit(WDT_IN_USE, &wdt_status))
- return -EBUSY;
- clear_bit(WDT_OK_TO_CLOSE, &wdt_status);
- orion_wdt_enable();
- return nonseekable_open(inode, file);
+ struct orion_watchdog *dev = watchdog_get_drvdata(wdt_dev);
+
+ /* There are some per-SoC quirks to handle */
+ return dev->data->start(wdt_dev);
}
-static ssize_t orion_wdt_write(struct file *file, const char *data,
- size_t len, loff_t *ppos)
+static int orion_stop(struct watchdog_device *wdt_dev)
{
- if (len) {
- if (!nowayout) {
- size_t i;
-
- clear_bit(WDT_OK_TO_CLOSE, &wdt_status);
- for (i = 0; i != len; i++) {
- char c;
-
- if (get_user(c, data + i))
- return -EFAULT;
- if (c == 'V')
- set_bit(WDT_OK_TO_CLOSE, &wdt_status);
- }
- }
- orion_wdt_ping();
- }
- return len;
+ struct orion_watchdog *dev = watchdog_get_drvdata(wdt_dev);
+
+ /* Disable reset on watchdog */
+ atomic_io_modify(dev->rstout, dev->data->rstout_enable_bit, 0);
+
+ /* Disable watchdog timer */
+ atomic_io_modify(dev->reg + TIMER_CTRL, dev->data->wdt_enable_bit, 0);
+
+ return 0;
}
-static int orion_wdt_settimeout(int new_time)
+static int armada375_stop(struct watchdog_device *wdt_dev)
{
- if ((new_time <= 0) || (new_time > wdt_max_duration))
- return -EINVAL;
+ struct orion_watchdog *dev = watchdog_get_drvdata(wdt_dev);
+ u32 reg;
+
+ /* Disable reset on watchdog */
+ atomic_io_modify(dev->rstout_mask, dev->data->rstout_mask_bit,
+ dev->data->rstout_mask_bit);
+ reg = readl(dev->rstout);
+ reg &= ~dev->data->rstout_enable_bit;
+ writel(reg, dev->rstout);
+
+ /* Disable watchdog timer */
+ atomic_io_modify(dev->reg + TIMER_CTRL, dev->data->wdt_enable_bit, 0);
- /* Set new watchdog time to be used when
- * orion_wdt_enable() or orion_wdt_ping() is called. */
- heartbeat = new_time;
return 0;
}
-static const struct watchdog_info ident = {
- .options = WDIOF_MAGICCLOSE | WDIOF_SETTIMEOUT |
- WDIOF_KEEPALIVEPING,
- .identity = "Orion Watchdog",
-};
+static int armada370_stop(struct watchdog_device *wdt_dev)
+{
+ struct orion_watchdog *dev = watchdog_get_drvdata(wdt_dev);
+ u32 reg;
+
+ /* Disable reset on watchdog */
+ reg = readl(dev->rstout);
+ reg &= ~dev->data->rstout_enable_bit;
+ writel(reg, dev->rstout);
+
+ /* Disable watchdog timer */
+ atomic_io_modify(dev->reg + TIMER_CTRL, dev->data->wdt_enable_bit, 0);
-static long orion_wdt_ioctl(struct file *file, unsigned int cmd,
- unsigned long arg)
+ return 0;
+}
+
+static int orion_wdt_stop(struct watchdog_device *wdt_dev)
{
- int ret = -ENOTTY;
- int time;
-
- switch (cmd) {
- case WDIOC_GETSUPPORT:
- ret = copy_to_user((struct watchdog_info *)arg, &ident,
- sizeof(ident)) ? -EFAULT : 0;
- break;
-
- case WDIOC_GETSTATUS:
- case WDIOC_GETBOOTSTATUS:
- ret = put_user(0, (int *)arg);
- break;
-
- case WDIOC_KEEPALIVE:
- orion_wdt_ping();
- ret = 0;
- break;
-
- case WDIOC_SETTIMEOUT:
- ret = get_user(time, (int *)arg);
- if (ret)
- break;
-
- if (orion_wdt_settimeout(time)) {
- ret = -EINVAL;
- break;
- }
- orion_wdt_ping();
- /* Fall through */
+ struct orion_watchdog *dev = watchdog_get_drvdata(wdt_dev);
+
+ return dev->data->stop(wdt_dev);
+}
- case WDIOC_GETTIMEOUT:
- ret = put_user(heartbeat, (int *)arg);
- break;
+static int orion_enabled(struct orion_watchdog *dev)
+{
+ bool enabled, running;
- case WDIOC_GETTIMELEFT:
- if (orion_wdt_get_timeleft(&time)) {
- ret = -EINVAL;
- break;
- }
- ret = put_user(time, (int *)arg);
- break;
- }
- return ret;
+ enabled = readl(dev->rstout) & dev->data->rstout_enable_bit;
+ running = readl(dev->reg + TIMER_CTRL) & dev->data->wdt_enable_bit;
+
+ return enabled && running;
}
-static int orion_wdt_release(struct inode *inode, struct file *file)
+static int armada375_enabled(struct orion_watchdog *dev)
{
- if (test_bit(WDT_OK_TO_CLOSE, &wdt_status))
- orion_wdt_disable();
- else
- pr_crit("Device closed unexpectedly - timer will not stop\n");
- clear_bit(WDT_IN_USE, &wdt_status);
- clear_bit(WDT_OK_TO_CLOSE, &wdt_status);
+ bool masked, enabled, running;
+
+ masked = readl(dev->rstout_mask) & dev->data->rstout_mask_bit;
+ enabled = readl(dev->rstout) & dev->data->rstout_enable_bit;
+ running = readl(dev->reg + TIMER_CTRL) & dev->data->wdt_enable_bit;
+ return !masked && enabled && running;
+}
+
+static int orion_wdt_enabled(struct watchdog_device *wdt_dev)
+{
+ struct orion_watchdog *dev = watchdog_get_drvdata(wdt_dev);
+
+ return dev->data->enabled(dev);
+}
+
+static unsigned int orion_wdt_get_timeleft(struct watchdog_device *wdt_dev)
+{
+ struct orion_watchdog *dev = watchdog_get_drvdata(wdt_dev);
+ return readl(dev->reg + dev->data->wdt_counter_offset) / dev->clk_rate;
+}
+
+static int orion_wdt_set_timeout(struct watchdog_device *wdt_dev,
+ unsigned int timeout)
+{
+ wdt_dev->timeout = timeout;
return 0;
}
+static const struct watchdog_info orion_wdt_info = {
+ .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE,
+ .identity = "Orion Watchdog",
+};
+
+static const struct watchdog_ops orion_wdt_ops = {
+ .owner = THIS_MODULE,
+ .start = orion_wdt_start,
+ .stop = orion_wdt_stop,
+ .ping = orion_wdt_ping,
+ .set_timeout = orion_wdt_set_timeout,
+ .get_timeleft = orion_wdt_get_timeleft,
+};
+
+static irqreturn_t orion_wdt_irq(int irq, void *devid)
+{
+ panic("Watchdog Timeout");
+ return IRQ_HANDLED;
+}
+
+/*
+ * The original devicetree binding for this driver specified only
+ * one memory resource, so in order to keep DT backwards compatibility
+ * we try to fallback to a hardcoded register address, if the resource
+ * is missing from the devicetree.
+ */
+static void __iomem *orion_wdt_ioremap_rstout(struct platform_device *pdev,
+ phys_addr_t internal_regs)
+{
+ struct resource *res;
+ phys_addr_t rstout;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+ if (res)
+ return devm_ioremap(&pdev->dev, res->start,
+ resource_size(res));
+
+ rstout = internal_regs + ORION_RSTOUT_MASK_OFFSET;
+
+ WARN(1, FW_BUG "falling back to harcoded RSTOUT reg %pa\n", &rstout);
+ return devm_ioremap(&pdev->dev, rstout, 0x4);
+}
-static const struct file_operations orion_wdt_fops = {
- .owner = THIS_MODULE,
- .llseek = no_llseek,
- .write = orion_wdt_write,
- .unlocked_ioctl = orion_wdt_ioctl,
- .open = orion_wdt_open,
- .release = orion_wdt_release,
+static const struct orion_watchdog_data orion_data = {
+ .rstout_enable_bit = BIT(1),
+ .wdt_enable_bit = BIT(4),
+ .wdt_counter_offset = 0x24,
+ .clock_init = orion_wdt_clock_init,
+ .enabled = orion_enabled,
+ .start = orion_start,
+ .stop = orion_stop,
};
-static struct miscdevice orion_wdt_miscdev = {
- .minor = WATCHDOG_MINOR,
- .name = "watchdog",
- .fops = &orion_wdt_fops,
+static const struct orion_watchdog_data armada370_data = {
+ .rstout_enable_bit = BIT(8),
+ .wdt_enable_bit = BIT(8),
+ .wdt_counter_offset = 0x34,
+ .clock_init = armada370_wdt_clock_init,
+ .enabled = orion_enabled,
+ .start = armada370_start,
+ .stop = armada370_stop,
};
-static int __devinit orion_wdt_probe(struct platform_device *pdev)
+static const struct orion_watchdog_data armadaxp_data = {
+ .rstout_enable_bit = BIT(8),
+ .wdt_enable_bit = BIT(8),
+ .wdt_counter_offset = 0x34,
+ .clock_init = armadaxp_wdt_clock_init,
+ .enabled = orion_enabled,
+ .start = armada370_start,
+ .stop = armada370_stop,
+};
+
+static const struct orion_watchdog_data armada375_data = {
+ .rstout_enable_bit = BIT(8),
+ .rstout_mask_bit = BIT(10),
+ .wdt_enable_bit = BIT(8),
+ .wdt_counter_offset = 0x34,
+ .clock_init = armada370_wdt_clock_init,
+ .enabled = armada375_enabled,
+ .start = armada375_start,
+ .stop = armada375_stop,
+};
+
+static const struct orion_watchdog_data armada380_data = {
+ .rstout_enable_bit = BIT(8),
+ .rstout_mask_bit = BIT(10),
+ .wdt_enable_bit = BIT(8),
+ .wdt_counter_offset = 0x34,
+ .clock_init = armadaxp_wdt_clock_init,
+ .enabled = armada375_enabled,
+ .start = armada375_start,
+ .stop = armada375_stop,
+};
+
+static const struct of_device_id orion_wdt_of_match_table[] = {
+ {
+ .compatible = "marvell,orion-wdt",
+ .data = &orion_data,
+ },
+ {
+ .compatible = "marvell,armada-370-wdt",
+ .data = &armada370_data,
+ },
+ {
+ .compatible = "marvell,armada-xp-wdt",
+ .data = &armadaxp_data,
+ },
+ {
+ .compatible = "marvell,armada-375-wdt",
+ .data = &armada375_data,
+ },
+ {
+ .compatible = "marvell,armada-380-wdt",
+ .data = &armada380_data,
+ },
+ {},
+};
+MODULE_DEVICE_TABLE(of, orion_wdt_of_match_table);
+
+static int orion_wdt_get_regs(struct platform_device *pdev,
+ struct orion_watchdog *dev)
{
- struct orion_wdt_platform_data *pdata = pdev->dev.platform_data;
+ struct device_node *node = pdev->dev.of_node;
struct resource *res;
- int ret;
- if (pdata) {
- wdt_tclk = pdata->tclk;
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res)
+ return -ENODEV;
+ dev->reg = devm_ioremap(&pdev->dev, res->start,
+ resource_size(res));
+ if (!dev->reg)
+ return -ENOMEM;
+
+ /* Each supported compatible has some RSTOUT register quirk */
+ if (of_device_is_compatible(node, "marvell,orion-wdt")) {
+
+ dev->rstout = orion_wdt_ioremap_rstout(pdev, res->start &
+ INTERNAL_REGS_MASK);
+ if (!dev->rstout)
+ return -ENODEV;
+
+ } else if (of_device_is_compatible(node, "marvell,armada-370-wdt") ||
+ of_device_is_compatible(node, "marvell,armada-xp-wdt")) {
+
+ /* Dedicated RSTOUT register, can be requested. */
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+ dev->rstout = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(dev->rstout))
+ return PTR_ERR(dev->rstout);
+
+ } else if (of_device_is_compatible(node, "marvell,armada-375-wdt") ||
+ of_device_is_compatible(node, "marvell,armada-380-wdt")) {
+
+ /* Dedicated RSTOUT register, can be requested. */
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+ dev->rstout = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(dev->rstout))
+ return PTR_ERR(dev->rstout);
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 2);
+ if (!res)
+ return -ENODEV;
+ dev->rstout_mask = devm_ioremap(&pdev->dev, res->start,
+ resource_size(res));
+ if (!dev->rstout_mask)
+ return -ENOMEM;
+
} else {
- pr_err("misses platform data\n");
return -ENODEV;
}
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ return 0;
+}
- wdt_reg = ioremap(res->start, resource_size(res));
+static int orion_wdt_probe(struct platform_device *pdev)
+{
+ struct orion_watchdog *dev;
+ const struct of_device_id *match;
+ unsigned int wdt_max_duration; /* (seconds) */
+ int ret, irq;
+
+ dev = devm_kzalloc(&pdev->dev, sizeof(struct orion_watchdog),
+ GFP_KERNEL);
+ if (!dev)
+ return -ENOMEM;
+
+ match = of_match_device(orion_wdt_of_match_table, &pdev->dev);
+ if (!match)
+ /* Default legacy match */
+ match = &orion_wdt_of_match_table[0];
+
+ dev->wdt.info = &orion_wdt_info;
+ dev->wdt.ops = &orion_wdt_ops;
+ dev->wdt.min_timeout = 1;
+ dev->data = match->data;
+
+ ret = orion_wdt_get_regs(pdev, dev);
+ if (ret)
+ return ret;
- if (orion_wdt_miscdev.parent)
- return -EBUSY;
- orion_wdt_miscdev.parent = &pdev->dev;
+ ret = dev->data->clock_init(pdev, dev);
+ if (ret) {
+ dev_err(&pdev->dev, "cannot initialize clock\n");
+ return ret;
+ }
- wdt_max_duration = WDT_MAX_CYCLE_COUNT / wdt_tclk;
- if (orion_wdt_settimeout(heartbeat))
- heartbeat = wdt_max_duration;
+ wdt_max_duration = WDT_MAX_CYCLE_COUNT / dev->clk_rate;
+
+ dev->wdt.timeout = wdt_max_duration;
+ dev->wdt.max_timeout = wdt_max_duration;
+ watchdog_init_timeout(&dev->wdt, heartbeat, &pdev->dev);
+
+ platform_set_drvdata(pdev, &dev->wdt);
+ watchdog_set_drvdata(&dev->wdt, dev);
+
+ /*
+ * Let's make sure the watchdog is fully stopped, unless it's
+ * explicitly enabled. This may be the case if the module was
+ * removed and re-insterted, or if the bootloader explicitly
+ * set a running watchdog before booting the kernel.
+ */
+ if (!orion_wdt_enabled(&dev->wdt))
+ orion_wdt_stop(&dev->wdt);
+
+ /* Request the IRQ only after the watchdog is disabled */
+ irq = platform_get_irq(pdev, 0);
+ if (irq > 0) {
+ /*
+ * Not all supported platforms specify an interrupt for the
+ * watchdog, so let's make it optional.
+ */
+ ret = devm_request_irq(&pdev->dev, irq, orion_wdt_irq, 0,
+ pdev->name, dev);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "failed to request IRQ\n");
+ goto disable_clk;
+ }
+ }
- ret = misc_register(&orion_wdt_miscdev);
+ watchdog_set_nowayout(&dev->wdt, nowayout);
+ ret = watchdog_register_device(&dev->wdt);
if (ret)
- return ret;
+ goto disable_clk;
pr_info("Initial timeout %d sec%s\n",
- heartbeat, nowayout ? ", nowayout" : "");
+ dev->wdt.timeout, nowayout ? ", nowayout" : "");
return 0;
+
+disable_clk:
+ clk_disable_unprepare(dev->clk);
+ clk_put(dev->clk);
+ return ret;
}
-static int __devexit orion_wdt_remove(struct platform_device *pdev)
+static int orion_wdt_remove(struct platform_device *pdev)
{
- int ret;
+ struct watchdog_device *wdt_dev = platform_get_drvdata(pdev);
+ struct orion_watchdog *dev = watchdog_get_drvdata(wdt_dev);
- if (test_bit(WDT_IN_USE, &wdt_status)) {
- orion_wdt_disable();
- clear_bit(WDT_IN_USE, &wdt_status);
- }
-
- ret = misc_deregister(&orion_wdt_miscdev);
- if (!ret)
- orion_wdt_miscdev.parent = NULL;
-
- return ret;
+ watchdog_unregister_device(wdt_dev);
+ clk_disable_unprepare(dev->clk);
+ clk_put(dev->clk);
+ return 0;
}
static void orion_wdt_shutdown(struct platform_device *pdev)
{
- if (test_bit(WDT_IN_USE, &wdt_status))
- orion_wdt_disable();
+ struct watchdog_device *wdt_dev = platform_get_drvdata(pdev);
+ orion_wdt_stop(wdt_dev);
}
static struct platform_driver orion_wdt_driver = {
.probe = orion_wdt_probe,
- .remove = __devexit_p(orion_wdt_remove),
+ .remove = orion_wdt_remove,
.shutdown = orion_wdt_shutdown,
.driver = {
.owner = THIS_MODULE,
.name = "orion_wdt",
+ .of_match_table = orion_wdt_of_match_table,
},
};
@@ -314,4 +612,4 @@ MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default="
__MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
MODULE_LICENSE("GPL");
-MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
+MODULE_ALIAS("platform:orion_wdt");
diff --git a/drivers/watchdog/pc87413_wdt.c b/drivers/watchdog/pc87413_wdt.c
index 5afb89b4865..9f15dd9435d 100644
--- a/drivers/watchdog/pc87413_wdt.c
+++ b/drivers/watchdog/pc87413_wdt.c
@@ -512,9 +512,8 @@ static int __init pc87413_init(void)
return -EBUSY;
ret = register_reboot_notifier(&pc87413_notifier);
- if (ret != 0) {
+ if (ret != 0)
pr_err("cannot register reboot notifier (err=%d)\n", ret);
- }
ret = misc_register(&pc87413_miscdev);
if (ret != 0) {
@@ -575,13 +574,11 @@ static void __exit pc87413_exit(void)
module_init(pc87413_init);
module_exit(pc87413_exit);
-MODULE_AUTHOR("Sven Anders <anders@anduras.de>, "
- "Marcus Junker <junker@anduras.de>,");
+MODULE_AUTHOR("Sven Anders <anders@anduras.de>");
+MODULE_AUTHOR("Marcus Junker <junker@anduras.de>");
MODULE_DESCRIPTION("PC87413 WDT driver");
MODULE_LICENSE("GPL");
-MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
-
module_param(io, int, 0);
MODULE_PARM_DESC(io, MODNAME " I/O port (default: "
__MODULE_STRING(IO_DEFAULT) ").");
diff --git a/drivers/watchdog/pcwd.c b/drivers/watchdog/pcwd.c
index 75694cf24f8..e936f15dc7c 100644
--- a/drivers/watchdog/pcwd.c
+++ b/drivers/watchdog/pcwd.c
@@ -61,7 +61,7 @@
#include <linux/delay.h> /* For mdelay function */
#include <linux/timer.h> /* For timer related operations */
#include <linux/jiffies.h> /* For jiffies stuff */
-#include <linux/miscdevice.h> /* For MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR) */
+#include <linux/miscdevice.h> /* For struct miscdevice */
#include <linux/watchdog.h> /* For the watchdog specific items */
#include <linux/reboot.h> /* For kernel_power_off() */
#include <linux/init.h> /* For __init/__exit/... */
@@ -801,7 +801,7 @@ static inline int get_revision(void)
* The initial rate is once per second at board start up, then twice
* per second for normal operation.
*/
-static int __devinit pcwd_isa_match(struct device *dev, unsigned int id)
+static int pcwd_isa_match(struct device *dev, unsigned int id)
{
int base_addr = pcwd_ioports[id];
int port0, last_port0; /* Reg 0, in case it's REV A */
@@ -846,7 +846,7 @@ static int __devinit pcwd_isa_match(struct device *dev, unsigned int id)
return retval;
}
-static int __devinit pcwd_isa_probe(struct device *dev, unsigned int id)
+static int pcwd_isa_probe(struct device *dev, unsigned int id)
{
int ret;
@@ -949,7 +949,7 @@ error_request_region:
return ret;
}
-static int __devexit pcwd_isa_remove(struct device *dev, unsigned int id)
+static int pcwd_isa_remove(struct device *dev, unsigned int id)
{
if (debug >= DEBUG)
pr_debug("pcwd_isa_remove id=%d\n", id);
@@ -984,7 +984,7 @@ static void pcwd_isa_shutdown(struct device *dev, unsigned int id)
static struct isa_driver pcwd_isa_driver = {
.match = pcwd_isa_match,
.probe = pcwd_isa_probe,
- .remove = __devexit_p(pcwd_isa_remove),
+ .remove = pcwd_isa_remove,
.shutdown = pcwd_isa_shutdown,
.driver = {
.owner = THIS_MODULE,
@@ -1011,5 +1011,3 @@ MODULE_AUTHOR("Ken Hollis <kenji@bitgate.com>, "
MODULE_DESCRIPTION("Berkshire ISA-PC Watchdog driver");
MODULE_VERSION(WATCHDOG_VERSION);
MODULE_LICENSE("GPL");
-MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
-MODULE_ALIAS_MISCDEV(TEMP_MINOR);
diff --git a/drivers/watchdog/pcwd_pci.c b/drivers/watchdog/pcwd_pci.c
index c891399bed6..c0d07eef264 100644
--- a/drivers/watchdog/pcwd_pci.c
+++ b/drivers/watchdog/pcwd_pci.c
@@ -40,7 +40,7 @@
#include <linux/errno.h> /* For the -ENODEV/... values */
#include <linux/kernel.h> /* For printk/panic/... */
#include <linux/delay.h> /* For mdelay function */
-#include <linux/miscdevice.h> /* For MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR) */
+#include <linux/miscdevice.h> /* For struct miscdevice */
#include <linux/watchdog.h> /* For the watchdog specific items */
#include <linux/notifier.h> /* For notifier support */
#include <linux/reboot.h> /* For reboot_notifier stuff */
@@ -682,7 +682,7 @@ static struct notifier_block pcipcwd_notifier = {
* Init & exit routines
*/
-static int __devinit pcipcwd_card_init(struct pci_dev *pdev,
+static int pcipcwd_card_init(struct pci_dev *pdev,
const struct pci_device_id *ent)
{
int ret = -EIO;
@@ -707,6 +707,7 @@ static int __devinit pcipcwd_card_init(struct pci_dev *pdev,
goto err_out_disable_device;
}
+ spin_lock_init(&pcipcwd_private.io_lock);
pcipcwd_private.pdev = pdev;
pcipcwd_private.io_addr = pci_resource_start(pdev, 0);
@@ -784,7 +785,7 @@ err_out_disable_device:
return ret;
}
-static void __devexit pcipcwd_card_exit(struct pci_dev *pdev)
+static void pcipcwd_card_exit(struct pci_dev *pdev)
{
/* Stop the timer before we leave */
if (!nowayout)
@@ -800,7 +801,7 @@ static void __devexit pcipcwd_card_exit(struct pci_dev *pdev)
cards_found--;
}
-static DEFINE_PCI_DEVICE_TABLE(pcipcwd_pci_tbl) = {
+static const struct pci_device_id pcipcwd_pci_tbl[] = {
{ PCI_VENDOR_ID_QUICKLOGIC, PCI_DEVICE_ID_WATCHDOG_PCIPCWD,
PCI_ANY_ID, PCI_ANY_ID, },
{ 0 }, /* End of list */
@@ -811,28 +812,11 @@ static struct pci_driver pcipcwd_driver = {
.name = WATCHDOG_NAME,
.id_table = pcipcwd_pci_tbl,
.probe = pcipcwd_card_init,
- .remove = __devexit_p(pcipcwd_card_exit),
+ .remove = pcipcwd_card_exit,
};
-static int __init pcipcwd_init_module(void)
-{
- spin_lock_init(&pcipcwd_private.io_lock);
-
- return pci_register_driver(&pcipcwd_driver);
-}
-
-static void __exit pcipcwd_cleanup_module(void)
-{
- pci_unregister_driver(&pcipcwd_driver);
-
- pr_info("Watchdog Module Unloaded\n");
-}
-
-module_init(pcipcwd_init_module);
-module_exit(pcipcwd_cleanup_module);
+module_pci_driver(pcipcwd_driver);
MODULE_AUTHOR("Wim Van Sebroeck <wim@iguana.be>");
MODULE_DESCRIPTION("Berkshire PCI-PC Watchdog driver");
MODULE_LICENSE("GPL");
-MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
-MODULE_ALIAS_MISCDEV(TEMP_MINOR);
diff --git a/drivers/watchdog/pcwd_usb.c b/drivers/watchdog/pcwd_usb.c
index 7b14d184792..1a11aedc4fe 100644
--- a/drivers/watchdog/pcwd_usb.c
+++ b/drivers/watchdog/pcwd_usb.c
@@ -32,7 +32,7 @@
#include <linux/errno.h> /* For the -ENODEV/... values */
#include <linux/kernel.h> /* For printk/panic/... */
#include <linux/delay.h> /* For mdelay function */
-#include <linux/miscdevice.h> /* For MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR) */
+#include <linux/miscdevice.h> /* For struct miscdevice */
#include <linux/watchdog.h> /* For the watchdog specific items */
#include <linux/notifier.h> /* For notifier support */
#include <linux/reboot.h> /* For reboot_notifier stuff */
@@ -44,23 +44,6 @@
#include <linux/hid.h> /* For HID_REQ_SET_REPORT & HID_DT_REPORT */
#include <linux/uaccess.h> /* For copy_to_user/put_user/... */
-#ifdef CONFIG_USB_DEBUG
-static int debug = 1;
-#else
-static int debug;
-#endif
-
-/* Use our own dbg macro */
-
-#undef dbg
-#ifndef DEBUG
-#define DEBUG
-#endif
-#define dbg(format, ...) \
-do { \
- if (debug) \
- pr_debug(format "\n", ##__VA_ARGS__); \
-} while (0)
/* Module and Version Information */
#define DRIVER_VERSION "1.02"
@@ -72,12 +55,6 @@ do { \
MODULE_AUTHOR(DRIVER_AUTHOR);
MODULE_DESCRIPTION(DRIVER_DESC);
MODULE_LICENSE(DRIVER_LICENSE);
-MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
-MODULE_ALIAS_MISCDEV(TEMP_MINOR);
-
-/* Module Parameters */
-module_param(debug, int, 0);
-MODULE_PARM_DESC(debug, "Debug enabled or not");
#define WATCHDOG_HEARTBEAT 0 /* default heartbeat =
delay-time from dip-switches */
@@ -195,6 +172,7 @@ static void usb_pcwd_intr_done(struct urb *urb)
struct usb_pcwd_private *usb_pcwd =
(struct usb_pcwd_private *)urb->context;
unsigned char *data = usb_pcwd->intr_buffer;
+ struct device *dev = &usb_pcwd->interface->dev;
int retval;
switch (urb->status) {
@@ -204,17 +182,17 @@ static void usb_pcwd_intr_done(struct urb *urb)
case -ENOENT:
case -ESHUTDOWN:
/* this urb is terminated, clean up */
- dbg("%s - urb shutting down with status: %d", __func__,
- urb->status);
+ dev_dbg(dev, "%s - urb shutting down with status: %d",
+ __func__, urb->status);
return;
/* -EPIPE: should clear the halt */
default: /* error */
- dbg("%s - nonzero urb status received: %d", __func__,
- urb->status);
+ dev_dbg(dev, "%s - nonzero urb status received: %d",
+ __func__, urb->status);
goto resubmit;
}
- dbg("received following data cmd=0x%02x msb=0x%02x lsb=0x%02x",
+ dev_dbg(dev, "received following data cmd=0x%02x msb=0x%02x lsb=0x%02x",
data[0], data[1], data[2]);
usb_pcwd->cmd_command = data[0];
@@ -235,13 +213,17 @@ static int usb_pcwd_send_command(struct usb_pcwd_private *usb_pcwd,
unsigned char cmd, unsigned char *msb, unsigned char *lsb)
{
int got_response, count;
- unsigned char buf[6];
+ unsigned char *buf;
/* We will not send any commands if the USB PCWD device does
* not exist */
if ((!usb_pcwd) || (!usb_pcwd->exists))
return -1;
+ buf = kmalloc(6, GFP_KERNEL);
+ if (buf == NULL)
+ return 0;
+
/* The USB PC Watchdog uses a 6 byte report format.
* The board currently uses only 3 of the six bytes of the report. */
buf[0] = cmd; /* Byte 0 = CMD */
@@ -249,17 +231,19 @@ static int usb_pcwd_send_command(struct usb_pcwd_private *usb_pcwd,
buf[2] = *lsb; /* Byte 2 = Data LSB */
buf[3] = buf[4] = buf[5] = 0; /* All other bytes not used */
- dbg("sending following data cmd=0x%02x msb=0x%02x lsb=0x%02x",
+ dev_dbg(&usb_pcwd->interface->dev,
+ "sending following data cmd=0x%02x msb=0x%02x lsb=0x%02x",
buf[0], buf[1], buf[2]);
atomic_set(&usb_pcwd->cmd_received, 0);
if (usb_control_msg(usb_pcwd->udev, usb_sndctrlpipe(usb_pcwd->udev, 0),
HID_REQ_SET_REPORT, HID_DT_REPORT,
- 0x0200, usb_pcwd->interface_number, buf, sizeof(buf),
- USB_COMMAND_TIMEOUT) != sizeof(buf)) {
- dbg("usb_pcwd_send_command: error in usb_control_msg for "
- "cmd 0x%x 0x%x 0x%x\n", cmd, *msb, *lsb);
+ 0x0200, usb_pcwd->interface_number, buf, 6,
+ USB_COMMAND_TIMEOUT) != 6) {
+ dev_dbg(&usb_pcwd->interface->dev,
+ "usb_pcwd_send_command: error in usb_control_msg for cmd 0x%x 0x%x 0x%x\n",
+ cmd, *msb, *lsb);
}
/* wait till the usb card processed the command,
* with a max. timeout of USB_COMMAND_TIMEOUT */
@@ -277,6 +261,8 @@ static int usb_pcwd_send_command(struct usb_pcwd_private *usb_pcwd,
*lsb = usb_pcwd->cmd_data_lsb;
}
+ kfree(buf);
+
return got_response;
}
@@ -659,10 +645,8 @@ static int usb_pcwd_probe(struct usb_interface *interface,
/* allocate memory for our device and initialize it */
usb_pcwd = kzalloc(sizeof(struct usb_pcwd_private), GFP_KERNEL);
- if (usb_pcwd == NULL) {
- pr_err("Out of memory\n");
+ if (usb_pcwd == NULL)
goto error;
- }
usb_pcwd_device = usb_pcwd;
diff --git a/drivers/watchdog/pika_wdt.c b/drivers/watchdog/pika_wdt.c
index 7d3d471f810..0cdfee26669 100644
--- a/drivers/watchdog/pika_wdt.c
+++ b/drivers/watchdog/pika_wdt.c
@@ -22,6 +22,7 @@
#include <linux/bitops.h>
#include <linux/uaccess.h>
#include <linux/io.h>
+#include <linux/of_address.h>
#include <linux/of_platform.h>
#define DRV_NAME "PIKA-WDT"
@@ -298,5 +299,3 @@ module_exit(pikawdt_exit);
MODULE_AUTHOR("Sean MacLennan <smaclennan@pikatech.com>");
MODULE_DESCRIPTION("PIKA FPGA based Watchdog Timer");
MODULE_LICENSE("GPL");
-MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
-
diff --git a/drivers/watchdog/pnx4008_wdt.c b/drivers/watchdog/pnx4008_wdt.c
index 6b8432f61d0..15fb316e943 100644
--- a/drivers/watchdog/pnx4008_wdt.c
+++ b/drivers/watchdog/pnx4008_wdt.c
@@ -23,15 +23,14 @@
#include <linux/moduleparam.h>
#include <linux/types.h>
#include <linux/kernel.h>
-#include <linux/miscdevice.h>
#include <linux/watchdog.h>
-#include <linux/init.h>
#include <linux/platform_device.h>
#include <linux/clk.h>
#include <linux/spinlock.h>
#include <linux/io.h>
#include <linux/slab.h>
#include <linux/err.h>
+#include <linux/of.h>
#include <mach/hardware.h>
/* WatchDog Timer - Chapter 23 Page 207 */
@@ -141,32 +140,31 @@ static const struct watchdog_ops pnx4008_wdt_ops = {
static struct watchdog_device pnx4008_wdd = {
.info = &pnx4008_wdt_ident,
.ops = &pnx4008_wdt_ops,
+ .timeout = DEFAULT_HEARTBEAT,
.min_timeout = 1,
.max_timeout = MAX_HEARTBEAT,
};
-static int __devinit pnx4008_wdt_probe(struct platform_device *pdev)
+static int pnx4008_wdt_probe(struct platform_device *pdev)
{
struct resource *r;
int ret = 0;
- if (heartbeat < 1 || heartbeat > MAX_HEARTBEAT)
- heartbeat = DEFAULT_HEARTBEAT;
+ watchdog_init_timeout(&pnx4008_wdd, heartbeat, &pdev->dev);
r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- wdt_base = devm_request_and_ioremap(&pdev->dev, r);
- if (!wdt_base)
- return -EADDRINUSE;
+ wdt_base = devm_ioremap_resource(&pdev->dev, r);
+ if (IS_ERR(wdt_base))
+ return PTR_ERR(wdt_base);
- wdt_clk = clk_get(&pdev->dev, NULL);
+ wdt_clk = devm_clk_get(&pdev->dev, NULL);
if (IS_ERR(wdt_clk))
return PTR_ERR(wdt_clk);
ret = clk_enable(wdt_clk);
if (ret)
- goto out;
+ return ret;
- pnx4008_wdd.timeout = heartbeat;
pnx4008_wdd.bootstatus = (readl(WDTIM_RES(wdt_base)) & WDOG_RESET) ?
WDIOF_CARDRESET : 0;
watchdog_set_nowayout(&pnx4008_wdd, nowayout);
@@ -180,34 +178,40 @@ static int __devinit pnx4008_wdt_probe(struct platform_device *pdev)
}
dev_info(&pdev->dev, "PNX4008 Watchdog Timer: heartbeat %d sec\n",
- heartbeat);
+ pnx4008_wdd.timeout);
return 0;
disable_clk:
clk_disable(wdt_clk);
-out:
- clk_put(wdt_clk);
return ret;
}
-static int __devexit pnx4008_wdt_remove(struct platform_device *pdev)
+static int pnx4008_wdt_remove(struct platform_device *pdev)
{
watchdog_unregister_device(&pnx4008_wdd);
clk_disable(wdt_clk);
- clk_put(wdt_clk);
return 0;
}
+#ifdef CONFIG_OF
+static const struct of_device_id pnx4008_wdt_match[] = {
+ { .compatible = "nxp,pnx4008-wdt" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, pnx4008_wdt_match);
+#endif
+
static struct platform_driver platform_wdt_driver = {
.driver = {
.name = "pnx4008-watchdog",
.owner = THIS_MODULE,
+ .of_match_table = of_match_ptr(pnx4008_wdt_match),
},
.probe = pnx4008_wdt_probe,
- .remove = __devexit_p(pnx4008_wdt_remove),
+ .remove = pnx4008_wdt_remove,
};
module_platform_driver(platform_wdt_driver);
@@ -227,5 +231,4 @@ MODULE_PARM_DESC(nowayout,
"Set to 1 to keep watchdog running after device release");
MODULE_LICENSE("GPL");
-MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
MODULE_ALIAS("platform:pnx4008-watchdog");
diff --git a/drivers/watchdog/pnx833x_wdt.c b/drivers/watchdog/pnx833x_wdt.c
index 1b62a7dfcc9..882fdcb46ad 100644
--- a/drivers/watchdog/pnx833x_wdt.c
+++ b/drivers/watchdog/pnx833x_wdt.c
@@ -278,4 +278,3 @@ module_exit(watchdog_exit);
MODULE_AUTHOR("Daniel Laird/Andre McCurdy");
MODULE_DESCRIPTION("Hardware Watchdog Device for PNX833x");
MODULE_LICENSE("GPL");
-MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
diff --git a/drivers/watchdog/rc32434_wdt.c b/drivers/watchdog/rc32434_wdt.c
index 547353a50eb..71e78ef4b73 100644
--- a/drivers/watchdog/rc32434_wdt.c
+++ b/drivers/watchdog/rc32434_wdt.c
@@ -25,13 +25,13 @@
#include <linux/errno.h> /* For the -ENODEV/... values */
#include <linux/kernel.h> /* For printk/panic/... */
#include <linux/fs.h> /* For file operations */
-#include <linux/miscdevice.h> /* For MODULE_ALIAS_MISCDEV
- (WATCHDOG_MINOR) */
+#include <linux/miscdevice.h> /* For struct miscdevice */
#include <linux/watchdog.h> /* For the watchdog specific items */
#include <linux/init.h> /* For __init/__exit/... */
#include <linux/platform_device.h> /* For platform_driver framework */
#include <linux/spinlock.h> /* For spin_lock/spin_unlock/... */
#include <linux/uaccess.h> /* For copy_to_user/put_user/... */
+#include <linux/io.h> /* For devm_ioremap_nocache */
#include <asm/mach-rc32434/integ.h> /* For the Watchdog registers */
@@ -260,7 +260,7 @@ static struct miscdevice rc32434_wdt_miscdev = {
.fops = &rc32434_wdt_fops,
};
-static int __devinit rc32434_wdt_probe(struct platform_device *pdev)
+static int rc32434_wdt_probe(struct platform_device *pdev)
{
int ret;
struct resource *r;
@@ -271,7 +271,7 @@ static int __devinit rc32434_wdt_probe(struct platform_device *pdev)
return -ENODEV;
}
- wdt_reg = ioremap_nocache(r->start, resource_size(r));
+ wdt_reg = devm_ioremap_nocache(&pdev->dev, r->start, resource_size(r));
if (!wdt_reg) {
pr_err("failed to remap I/O resources\n");
return -ENXIO;
@@ -293,23 +293,18 @@ static int __devinit rc32434_wdt_probe(struct platform_device *pdev)
ret = misc_register(&rc32434_wdt_miscdev);
if (ret < 0) {
pr_err("failed to register watchdog device\n");
- goto unmap;
+ return ret;
}
pr_info("Watchdog Timer version " VERSION ", timer margin: %d sec\n",
timeout);
return 0;
-
-unmap:
- iounmap(wdt_reg);
- return ret;
}
-static int __devexit rc32434_wdt_remove(struct platform_device *pdev)
+static int rc32434_wdt_remove(struct platform_device *pdev)
{
misc_deregister(&rc32434_wdt_miscdev);
- iounmap(wdt_reg);
return 0;
}
@@ -320,7 +315,7 @@ static void rc32434_wdt_shutdown(struct platform_device *pdev)
static struct platform_driver rc32434_wdt_driver = {
.probe = rc32434_wdt_probe,
- .remove = __devexit_p(rc32434_wdt_remove),
+ .remove = rc32434_wdt_remove,
.shutdown = rc32434_wdt_shutdown,
.driver = {
.name = "rc32434_wdt",
@@ -333,4 +328,3 @@ MODULE_AUTHOR("Ondrej Zajicek <santiago@crfreenet.org>,"
"Florian Fainelli <florian@openwrt.org>");
MODULE_DESCRIPTION("Driver for the IDT RC32434 SoC watchdog");
MODULE_LICENSE("GPL");
-MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
diff --git a/drivers/watchdog/rdc321x_wdt.c b/drivers/watchdog/rdc321x_wdt.c
index 042ccc56ae2..29cf4dcbc59 100644
--- a/drivers/watchdog/rdc321x_wdt.c
+++ b/drivers/watchdog/rdc321x_wdt.c
@@ -27,7 +27,6 @@
#include <linux/errno.h>
#include <linux/miscdevice.h>
#include <linux/fs.h>
-#include <linux/init.h>
#include <linux/ioport.h>
#include <linux/timer.h>
#include <linux/completion.h>
@@ -225,13 +224,13 @@ static struct miscdevice rdc321x_wdt_misc = {
.fops = &rdc321x_wdt_fops,
};
-static int __devinit rdc321x_wdt_probe(struct platform_device *pdev)
+static int rdc321x_wdt_probe(struct platform_device *pdev)
{
int err;
struct resource *r;
struct rdc321x_wdt_pdata *pdata;
- pdata = pdev->dev.platform_data;
+ pdata = dev_get_platdata(&pdev->dev);
if (!pdata) {
dev_err(&pdev->dev, "no platform data supplied\n");
return -ENODEV;
@@ -272,7 +271,7 @@ static int __devinit rdc321x_wdt_probe(struct platform_device *pdev)
return 0;
}
-static int __devexit rdc321x_wdt_remove(struct platform_device *pdev)
+static int rdc321x_wdt_remove(struct platform_device *pdev)
{
if (rdc321x_wdt_device.queue) {
rdc321x_wdt_device.queue = 0;
@@ -286,7 +285,7 @@ static int __devexit rdc321x_wdt_remove(struct platform_device *pdev)
static struct platform_driver rdc321x_wdt_driver = {
.probe = rdc321x_wdt_probe,
- .remove = __devexit_p(rdc321x_wdt_remove),
+ .remove = rdc321x_wdt_remove,
.driver = {
.owner = THIS_MODULE,
.name = "rdc321x-wdt",
@@ -298,4 +297,3 @@ module_platform_driver(rdc321x_wdt_driver);
MODULE_AUTHOR("Florian Fainelli <florian@openwrt.org>");
MODULE_DESCRIPTION("RDC321x watchdog driver");
MODULE_LICENSE("GPL");
-MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
diff --git a/drivers/watchdog/retu_wdt.c b/drivers/watchdog/retu_wdt.c
new file mode 100644
index 00000000000..a7a0695971e
--- /dev/null
+++ b/drivers/watchdog/retu_wdt.c
@@ -0,0 +1,177 @@
+/*
+ * Retu watchdog driver
+ *
+ * Copyright (C) 2004, 2005 Nokia Corporation
+ *
+ * Based on code written by Amit Kucheria and Michael Buesch.
+ * Rewritten by Aaro Koskinen.
+ *
+ * This file is subject to the terms and conditions of the GNU General
+ * Public License. See the file "COPYING" in the main directory of this
+ * archive for more details.
+ *
+ * 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/slab.h>
+#include <linux/errno.h>
+#include <linux/device.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/mfd/retu.h>
+#include <linux/watchdog.h>
+#include <linux/platform_device.h>
+
+/* Watchdog timer values in seconds */
+#define RETU_WDT_MAX_TIMER 63
+
+struct retu_wdt_dev {
+ struct retu_dev *rdev;
+ struct device *dev;
+ struct delayed_work ping_work;
+};
+
+/*
+ * Since Retu watchdog cannot be disabled in hardware, we must kick it
+ * with a timer until userspace watchdog software takes over. If
+ * CONFIG_WATCHDOG_NOWAYOUT is set, we never start the feeding.
+ */
+static void retu_wdt_ping_enable(struct retu_wdt_dev *wdev)
+{
+ retu_write(wdev->rdev, RETU_REG_WATCHDOG, RETU_WDT_MAX_TIMER);
+ schedule_delayed_work(&wdev->ping_work,
+ round_jiffies_relative(RETU_WDT_MAX_TIMER * HZ / 2));
+}
+
+static void retu_wdt_ping_disable(struct retu_wdt_dev *wdev)
+{
+ retu_write(wdev->rdev, RETU_REG_WATCHDOG, RETU_WDT_MAX_TIMER);
+ cancel_delayed_work_sync(&wdev->ping_work);
+}
+
+static void retu_wdt_ping_work(struct work_struct *work)
+{
+ struct retu_wdt_dev *wdev = container_of(to_delayed_work(work),
+ struct retu_wdt_dev, ping_work);
+ retu_wdt_ping_enable(wdev);
+}
+
+static int retu_wdt_start(struct watchdog_device *wdog)
+{
+ struct retu_wdt_dev *wdev = watchdog_get_drvdata(wdog);
+
+ retu_wdt_ping_disable(wdev);
+
+ return retu_write(wdev->rdev, RETU_REG_WATCHDOG, wdog->timeout);
+}
+
+static int retu_wdt_stop(struct watchdog_device *wdog)
+{
+ struct retu_wdt_dev *wdev = watchdog_get_drvdata(wdog);
+
+ retu_wdt_ping_enable(wdev);
+
+ return 0;
+}
+
+static int retu_wdt_ping(struct watchdog_device *wdog)
+{
+ struct retu_wdt_dev *wdev = watchdog_get_drvdata(wdog);
+
+ return retu_write(wdev->rdev, RETU_REG_WATCHDOG, wdog->timeout);
+}
+
+static int retu_wdt_set_timeout(struct watchdog_device *wdog,
+ unsigned int timeout)
+{
+ struct retu_wdt_dev *wdev = watchdog_get_drvdata(wdog);
+
+ wdog->timeout = timeout;
+ return retu_write(wdev->rdev, RETU_REG_WATCHDOG, wdog->timeout);
+}
+
+static const struct watchdog_info retu_wdt_info = {
+ .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING,
+ .identity = "Retu watchdog",
+};
+
+static const struct watchdog_ops retu_wdt_ops = {
+ .owner = THIS_MODULE,
+ .start = retu_wdt_start,
+ .stop = retu_wdt_stop,
+ .ping = retu_wdt_ping,
+ .set_timeout = retu_wdt_set_timeout,
+};
+
+static int retu_wdt_probe(struct platform_device *pdev)
+{
+ struct retu_dev *rdev = dev_get_drvdata(pdev->dev.parent);
+ bool nowayout = WATCHDOG_NOWAYOUT;
+ struct watchdog_device *retu_wdt;
+ struct retu_wdt_dev *wdev;
+ int ret;
+
+ retu_wdt = devm_kzalloc(&pdev->dev, sizeof(*retu_wdt), GFP_KERNEL);
+ if (!retu_wdt)
+ return -ENOMEM;
+
+ wdev = devm_kzalloc(&pdev->dev, sizeof(*wdev), GFP_KERNEL);
+ if (!wdev)
+ return -ENOMEM;
+
+ retu_wdt->info = &retu_wdt_info;
+ retu_wdt->ops = &retu_wdt_ops;
+ retu_wdt->timeout = RETU_WDT_MAX_TIMER;
+ retu_wdt->min_timeout = 0;
+ retu_wdt->max_timeout = RETU_WDT_MAX_TIMER;
+
+ watchdog_set_drvdata(retu_wdt, wdev);
+ watchdog_set_nowayout(retu_wdt, nowayout);
+
+ wdev->rdev = rdev;
+ wdev->dev = &pdev->dev;
+
+ INIT_DELAYED_WORK(&wdev->ping_work, retu_wdt_ping_work);
+
+ ret = watchdog_register_device(retu_wdt);
+ if (ret < 0)
+ return ret;
+
+ if (nowayout)
+ retu_wdt_ping(retu_wdt);
+ else
+ retu_wdt_ping_enable(wdev);
+
+ platform_set_drvdata(pdev, retu_wdt);
+
+ return 0;
+}
+
+static int retu_wdt_remove(struct platform_device *pdev)
+{
+ struct watchdog_device *wdog = platform_get_drvdata(pdev);
+ struct retu_wdt_dev *wdev = watchdog_get_drvdata(wdog);
+
+ watchdog_unregister_device(wdog);
+ cancel_delayed_work_sync(&wdev->ping_work);
+
+ return 0;
+}
+
+static struct platform_driver retu_wdt_driver = {
+ .probe = retu_wdt_probe,
+ .remove = retu_wdt_remove,
+ .driver = {
+ .name = "retu-wdt",
+ },
+};
+module_platform_driver(retu_wdt_driver);
+
+MODULE_ALIAS("platform:retu-wdt");
+MODULE_DESCRIPTION("Retu watchdog");
+MODULE_AUTHOR("Amit Kucheria");
+MODULE_AUTHOR("Aaro Koskinen <aaro.koskinen@iki.fi>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/watchdog/riowd.c b/drivers/watchdog/riowd.c
index 49e1b1c2135..cfed0fe264d 100644
--- a/drivers/watchdog/riowd.c
+++ b/drivers/watchdog/riowd.c
@@ -10,7 +10,6 @@
#include <linux/types.h>
#include <linux/fs.h>
#include <linux/errno.h>
-#include <linux/init.h>
#include <linux/miscdevice.h>
#include <linux/watchdog.h>
#include <linux/of.h>
@@ -174,7 +173,7 @@ static struct miscdevice riowd_miscdev = {
.fops = &riowd_fops
};
-static int __devinit riowd_probe(struct platform_device *op)
+static int riowd_probe(struct platform_device *op)
{
struct riowd *p;
int err = -EINVAL;
@@ -183,7 +182,7 @@ static int __devinit riowd_probe(struct platform_device *op)
goto out;
err = -ENOMEM;
- p = kzalloc(sizeof(*p), GFP_KERNEL);
+ p = devm_kzalloc(&op->dev, sizeof(*p), GFP_KERNEL);
if (!p)
goto out;
@@ -192,7 +191,7 @@ static int __devinit riowd_probe(struct platform_device *op)
p->regs = of_ioremap(&op->resource[0], 0, 2, DRIVER_NAME);
if (!p->regs) {
pr_err("Cannot map registers\n");
- goto out_free;
+ goto out;
}
/* Make miscdev useable right away */
riowd_device = p;
@@ -206,27 +205,23 @@ static int __devinit riowd_probe(struct platform_device *op)
pr_info("Hardware watchdog [%i minutes], regs at %p\n",
riowd_timeout, p->regs);
- dev_set_drvdata(&op->dev, p);
+ platform_set_drvdata(op, p);
return 0;
out_iounmap:
riowd_device = NULL;
of_iounmap(&op->resource[0], p->regs, 2);
-out_free:
- kfree(p);
-
out:
return err;
}
-static int __devexit riowd_remove(struct platform_device *op)
+static int riowd_remove(struct platform_device *op)
{
- struct riowd *p = dev_get_drvdata(&op->dev);
+ struct riowd *p = platform_get_drvdata(op);
misc_deregister(&riowd_miscdev);
of_iounmap(&op->resource[0], p->regs, 2);
- kfree(p);
return 0;
}
@@ -246,7 +241,7 @@ static struct platform_driver riowd_driver = {
.of_match_table = riowd_match,
},
.probe = riowd_probe,
- .remove = __devexit_p(riowd_remove),
+ .remove = riowd_remove,
};
module_platform_driver(riowd_driver);
diff --git a/drivers/watchdog/rt2880_wdt.c b/drivers/watchdog/rt2880_wdt.c
new file mode 100644
index 00000000000..d92c2d5859c
--- /dev/null
+++ b/drivers/watchdog/rt2880_wdt.c
@@ -0,0 +1,206 @@
+/*
+ * 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>
+ *
+ * This driver was based on: drivers/watchdog/softdog.c
+ *
+ * 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/clk.h>
+#include <linux/reset.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/watchdog.h>
+#include <linux/moduleparam.h>
+#include <linux/platform_device.h>
+
+#include <asm/mach-ralink/ralink_regs.h>
+
+#define SYSC_RSTSTAT 0x38
+#define WDT_RST_CAUSE BIT(1)
+
+#define RALINK_WDT_TIMEOUT 30
+#define RALINK_WDT_PRESCALE 65536
+
+#define TIMER_REG_TMR1LOAD 0x00
+#define TIMER_REG_TMR1CTL 0x08
+
+#define TMRSTAT_TMR1RST BIT(5)
+
+#define TMR1CTL_ENABLE BIT(7)
+#define TMR1CTL_MODE_SHIFT 4
+#define TMR1CTL_MODE_MASK 0x3
+#define TMR1CTL_MODE_FREE_RUNNING 0x0
+#define TMR1CTL_MODE_PERIODIC 0x1
+#define TMR1CTL_MODE_TIMEOUT 0x2
+#define TMR1CTL_MODE_WDT 0x3
+#define TMR1CTL_PRESCALE_MASK 0xf
+#define TMR1CTL_PRESCALE_65536 0xf
+
+static struct clk *rt288x_wdt_clk;
+static unsigned long rt288x_wdt_freq;
+static void __iomem *rt288x_wdt_base;
+
+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) ")");
+
+static inline void rt_wdt_w32(unsigned reg, u32 val)
+{
+ iowrite32(val, rt288x_wdt_base + reg);
+}
+
+static inline u32 rt_wdt_r32(unsigned reg)
+{
+ return ioread32(rt288x_wdt_base + reg);
+}
+
+static int rt288x_wdt_ping(struct watchdog_device *w)
+{
+ rt_wdt_w32(TIMER_REG_TMR1LOAD, w->timeout * rt288x_wdt_freq);
+
+ return 0;
+}
+
+static int rt288x_wdt_start(struct watchdog_device *w)
+{
+ u32 t;
+
+ t = rt_wdt_r32(TIMER_REG_TMR1CTL);
+ t &= ~(TMR1CTL_MODE_MASK << TMR1CTL_MODE_SHIFT |
+ TMR1CTL_PRESCALE_MASK);
+ t |= (TMR1CTL_MODE_WDT << TMR1CTL_MODE_SHIFT |
+ TMR1CTL_PRESCALE_65536);
+ rt_wdt_w32(TIMER_REG_TMR1CTL, t);
+
+ rt288x_wdt_ping(w);
+
+ t = rt_wdt_r32(TIMER_REG_TMR1CTL);
+ t |= TMR1CTL_ENABLE;
+ rt_wdt_w32(TIMER_REG_TMR1CTL, t);
+
+ return 0;
+}
+
+static int rt288x_wdt_stop(struct watchdog_device *w)
+{
+ u32 t;
+
+ rt288x_wdt_ping(w);
+
+ t = rt_wdt_r32(TIMER_REG_TMR1CTL);
+ t &= ~TMR1CTL_ENABLE;
+ rt_wdt_w32(TIMER_REG_TMR1CTL, t);
+
+ return 0;
+}
+
+static int rt288x_wdt_set_timeout(struct watchdog_device *w, unsigned int t)
+{
+ w->timeout = t;
+ rt288x_wdt_ping(w);
+
+ return 0;
+}
+
+static int rt288x_wdt_bootcause(void)
+{
+ if (rt_sysc_r32(SYSC_RSTSTAT) & WDT_RST_CAUSE)
+ return WDIOF_CARDRESET;
+
+ return 0;
+}
+
+static struct watchdog_info rt288x_wdt_info = {
+ .identity = "Ralink Watchdog",
+ .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE,
+};
+
+static struct watchdog_ops rt288x_wdt_ops = {
+ .owner = THIS_MODULE,
+ .start = rt288x_wdt_start,
+ .stop = rt288x_wdt_stop,
+ .ping = rt288x_wdt_ping,
+ .set_timeout = rt288x_wdt_set_timeout,
+};
+
+static struct watchdog_device rt288x_wdt_dev = {
+ .info = &rt288x_wdt_info,
+ .ops = &rt288x_wdt_ops,
+ .min_timeout = 1,
+};
+
+static int rt288x_wdt_probe(struct platform_device *pdev)
+{
+ struct resource *res;
+ int ret;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ rt288x_wdt_base = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(rt288x_wdt_base))
+ return PTR_ERR(rt288x_wdt_base);
+
+ rt288x_wdt_clk = devm_clk_get(&pdev->dev, NULL);
+ if (IS_ERR(rt288x_wdt_clk))
+ return PTR_ERR(rt288x_wdt_clk);
+
+ device_reset(&pdev->dev);
+
+ rt288x_wdt_freq = clk_get_rate(rt288x_wdt_clk) / RALINK_WDT_PRESCALE;
+
+ rt288x_wdt_dev.dev = &pdev->dev;
+ rt288x_wdt_dev.bootstatus = rt288x_wdt_bootcause();
+
+ rt288x_wdt_dev.max_timeout = (0xfffful / rt288x_wdt_freq);
+ rt288x_wdt_dev.timeout = rt288x_wdt_dev.max_timeout;
+
+ watchdog_set_nowayout(&rt288x_wdt_dev, nowayout);
+
+ ret = watchdog_register_device(&rt288x_wdt_dev);
+ if (!ret)
+ dev_info(&pdev->dev, "Initialized\n");
+
+ return 0;
+}
+
+static int rt288x_wdt_remove(struct platform_device *pdev)
+{
+ watchdog_unregister_device(&rt288x_wdt_dev);
+
+ return 0;
+}
+
+static void rt288x_wdt_shutdown(struct platform_device *pdev)
+{
+ rt288x_wdt_stop(&rt288x_wdt_dev);
+}
+
+static const struct of_device_id rt288x_wdt_match[] = {
+ { .compatible = "ralink,rt2880-wdt" },
+ {},
+};
+MODULE_DEVICE_TABLE(of, rt288x_wdt_match);
+
+static struct platform_driver rt288x_wdt_driver = {
+ .probe = rt288x_wdt_probe,
+ .remove = rt288x_wdt_remove,
+ .shutdown = rt288x_wdt_shutdown,
+ .driver = {
+ .name = KBUILD_MODNAME,
+ .owner = THIS_MODULE,
+ .of_match_table = rt288x_wdt_match,
+ },
+};
+
+module_platform_driver(rt288x_wdt_driver);
+
+MODULE_DESCRIPTION("MediaTek/Ralink RT288x/RT3xxx hardware watchdog driver");
+MODULE_AUTHOR("Gabor Juhos <juhosg@openwrt.org");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/watchdog/s3c2410_wdt.c b/drivers/watchdog/s3c2410_wdt.c
index 04e5a6de47d..7c6ccd071ba 100644
--- a/drivers/watchdog/s3c2410_wdt.c
+++ b/drivers/watchdog/s3c2410_wdt.c
@@ -29,9 +29,7 @@
#include <linux/moduleparam.h>
#include <linux/types.h>
#include <linux/timer.h>
-#include <linux/miscdevice.h> /* for MODULE_ALIAS_MISCDEV */
#include <linux/watchdog.h>
-#include <linux/init.h>
#include <linux/platform_device.h>
#include <linux/interrupt.h>
#include <linux/clk.h>
@@ -40,19 +38,41 @@
#include <linux/cpufreq.h>
#include <linux/slab.h>
#include <linux/err.h>
+#include <linux/of.h>
+#include <linux/mfd/syscon.h>
+#include <linux/regmap.h>
-#include <mach/map.h>
+#define S3C2410_WTCON 0x00
+#define S3C2410_WTDAT 0x04
+#define S3C2410_WTCNT 0x08
-#undef S3C_VA_WATCHDOG
-#define S3C_VA_WATCHDOG (0)
+#define S3C2410_WTCON_RSTEN (1 << 0)
+#define S3C2410_WTCON_INTEN (1 << 2)
+#define S3C2410_WTCON_ENABLE (1 << 5)
-#include <plat/regs-watchdog.h>
+#define S3C2410_WTCON_DIV16 (0 << 3)
+#define S3C2410_WTCON_DIV32 (1 << 3)
+#define S3C2410_WTCON_DIV64 (2 << 3)
+#define S3C2410_WTCON_DIV128 (3 << 3)
+
+#define S3C2410_WTCON_PRESCALE(x) ((x) << 8)
+#define S3C2410_WTCON_PRESCALE_MASK (0xff << 8)
#define CONFIG_S3C2410_WATCHDOG_ATBOOT (0)
#define CONFIG_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)
+
+/* These quirks require that we have a PMU register map */
+#define QUIRKS_HAVE_PMUREG (QUIRK_HAS_PMU_CONFIG | \
+ QUIRK_HAS_RST_STAT)
+
static bool nowayout = WATCHDOG_NOWAYOUT;
-static int tmr_margin = CONFIG_S3C2410_WATCHDOG_DEFAULT_TIME;
+static int tmr_margin;
static int tmr_atboot = CONFIG_S3C2410_WATCHDOG_ATBOOT;
static int soft_noboot;
static int debug;
@@ -74,13 +94,87 @@ 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)");
-static struct device *wdt_dev; /* platform device attached to */
-static struct resource *wdt_mem;
-static struct resource *wdt_irq;
-static struct clk *wdt_clock;
-static void __iomem *wdt_base;
-static unsigned int wdt_count;
-static DEFINE_SPINLOCK(wdt_lock);
+/**
+ * struct s3c2410_wdt_variant - Per-variant config data
+ *
+ * @disable_reg: Offset in pmureg for the register that disables the watchdog
+ * timer reset functionality.
+ * @mask_reset_reg: Offset in pmureg for the register that masks the watchdog
+ * timer reset functionality.
+ * @mask_bit: Bit number for the watchdog timer in the disable register and the
+ * mask reset register.
+ * @rst_stat_reg: Offset in pmureg for the register that has the reset status.
+ * @rst_stat_bit: Bit number in the rst_stat register indicating a watchdog
+ * reset.
+ * @quirks: A bitfield of quirks.
+ */
+
+struct s3c2410_wdt_variant {
+ int disable_reg;
+ int mask_reset_reg;
+ int mask_bit;
+ int rst_stat_reg;
+ int rst_stat_bit;
+ u32 quirks;
+};
+
+struct s3c2410_wdt {
+ struct device *dev;
+ struct clk *clock;
+ void __iomem *reg_base;
+ unsigned int count;
+ spinlock_t lock;
+ unsigned long wtcon_save;
+ unsigned long wtdat_save;
+ struct watchdog_device wdt_device;
+ struct notifier_block freq_transition;
+ struct s3c2410_wdt_variant *drv_data;
+ struct regmap *pmureg;
+};
+
+static const struct s3c2410_wdt_variant drv_data_s3c2410 = {
+ .quirks = 0
+};
+
+#ifdef CONFIG_OF
+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,
+};
+
+static const struct s3c2410_wdt_variant drv_data_exynos5420 = {
+ .disable_reg = EXYNOS5_WDT_DISABLE_REG_OFFSET,
+ .mask_reset_reg = EXYNOS5_WDT_MASK_RESET_REG_OFFSET,
+ .mask_bit = 0,
+ .rst_stat_reg = EXYNOS5_RST_STAT_REG_OFFSET,
+ .rst_stat_bit = 9,
+ .quirks = QUIRK_HAS_PMU_CONFIG | QUIRK_HAS_RST_STAT,
+};
+
+static const struct of_device_id s3c2410_wdt_match[] = {
+ { .compatible = "samsung,s3c2410-wdt",
+ .data = &drv_data_s3c2410 },
+ { .compatible = "samsung,exynos5250-wdt",
+ .data = &drv_data_exynos5250 },
+ { .compatible = "samsung,exynos5420-wdt",
+ .data = &drv_data_exynos5420 },
+ {},
+};
+MODULE_DEVICE_TABLE(of, s3c2410_wdt_match);
+#endif
+
+static const struct platform_device_id s3c2410_wdt_ids[] = {
+ {
+ .name = "s3c2410-wdt",
+ .driver_data = (unsigned long)&drv_data_s3c2410,
+ },
+ {}
+};
+MODULE_DEVICE_TABLE(platform, s3c2410_wdt_ids);
/* watchdog control routines */
@@ -92,29 +186,67 @@ do { \
/* functions */
+static inline struct s3c2410_wdt *freq_to_wdt(struct notifier_block *nb)
+{
+ return container_of(nb, struct s3c2410_wdt, freq_transition);
+}
+
+static int s3c2410wdt_mask_and_disable_reset(struct s3c2410_wdt *wdt, bool mask)
+{
+ int ret;
+ u32 mask_val = 1 << wdt->drv_data->mask_bit;
+ u32 val = 0;
+
+ /* No need to do anything if no PMU CONFIG needed */
+ if (!(wdt->drv_data->quirks & QUIRK_HAS_PMU_CONFIG))
+ return 0;
+
+ if (mask)
+ val = mask_val;
+
+ ret = regmap_update_bits(wdt->pmureg,
+ wdt->drv_data->disable_reg,
+ mask_val, val);
+ if (ret < 0)
+ goto error;
+
+ ret = regmap_update_bits(wdt->pmureg,
+ wdt->drv_data->mask_reset_reg,
+ mask_val, val);
+ error:
+ if (ret < 0)
+ dev_err(wdt->dev, "failed to update reg(%d)\n", ret);
+
+ return ret;
+}
+
static int s3c2410wdt_keepalive(struct watchdog_device *wdd)
{
- spin_lock(&wdt_lock);
- writel(wdt_count, wdt_base + S3C2410_WTCNT);
- spin_unlock(&wdt_lock);
+ struct s3c2410_wdt *wdt = watchdog_get_drvdata(wdd);
+
+ spin_lock(&wdt->lock);
+ writel(wdt->count, wdt->reg_base + S3C2410_WTCNT);
+ spin_unlock(&wdt->lock);
return 0;
}
-static void __s3c2410wdt_stop(void)
+static void __s3c2410wdt_stop(struct s3c2410_wdt *wdt)
{
unsigned long wtcon;
- wtcon = readl(wdt_base + S3C2410_WTCON);
+ wtcon = readl(wdt->reg_base + S3C2410_WTCON);
wtcon &= ~(S3C2410_WTCON_ENABLE | S3C2410_WTCON_RSTEN);
- writel(wtcon, wdt_base + S3C2410_WTCON);
+ writel(wtcon, wdt->reg_base + S3C2410_WTCON);
}
static int s3c2410wdt_stop(struct watchdog_device *wdd)
{
- spin_lock(&wdt_lock);
- __s3c2410wdt_stop();
- spin_unlock(&wdt_lock);
+ struct s3c2410_wdt *wdt = watchdog_get_drvdata(wdd);
+
+ spin_lock(&wdt->lock);
+ __s3c2410wdt_stop(wdt);
+ spin_unlock(&wdt->lock);
return 0;
}
@@ -122,12 +254,13 @@ static int s3c2410wdt_stop(struct watchdog_device *wdd)
static int s3c2410wdt_start(struct watchdog_device *wdd)
{
unsigned long wtcon;
+ struct s3c2410_wdt *wdt = watchdog_get_drvdata(wdd);
- spin_lock(&wdt_lock);
+ spin_lock(&wdt->lock);
- __s3c2410wdt_stop();
+ __s3c2410wdt_stop(wdt);
- wtcon = readl(wdt_base + S3C2410_WTCON);
+ wtcon = readl(wdt->reg_base + S3C2410_WTCON);
wtcon |= S3C2410_WTCON_ENABLE | S3C2410_WTCON_DIV128;
if (soft_noboot) {
@@ -138,25 +271,26 @@ static int s3c2410wdt_start(struct watchdog_device *wdd)
wtcon |= S3C2410_WTCON_RSTEN;
}
- DBG("%s: wdt_count=0x%08x, wtcon=%08lx\n",
- __func__, wdt_count, wtcon);
+ DBG("%s: count=0x%08x, wtcon=%08lx\n",
+ __func__, wdt->count, wtcon);
- writel(wdt_count, wdt_base + S3C2410_WTDAT);
- writel(wdt_count, wdt_base + S3C2410_WTCNT);
- writel(wtcon, wdt_base + S3C2410_WTCON);
- spin_unlock(&wdt_lock);
+ writel(wdt->count, wdt->reg_base + S3C2410_WTDAT);
+ writel(wdt->count, wdt->reg_base + S3C2410_WTCNT);
+ writel(wtcon, wdt->reg_base + S3C2410_WTCON);
+ spin_unlock(&wdt->lock);
return 0;
}
-static inline int s3c2410wdt_is_running(void)
+static inline int s3c2410wdt_is_running(struct s3c2410_wdt *wdt)
{
- return readl(wdt_base + S3C2410_WTCON) & S3C2410_WTCON_ENABLE;
+ return readl(wdt->reg_base + S3C2410_WTCON) & S3C2410_WTCON_ENABLE;
}
static int s3c2410wdt_set_heartbeat(struct watchdog_device *wdd, unsigned timeout)
{
- unsigned long freq = clk_get_rate(wdt_clock);
+ struct s3c2410_wdt *wdt = watchdog_get_drvdata(wdd);
+ unsigned long freq = clk_get_rate(wdt->clock);
unsigned int count;
unsigned int divisor = 1;
unsigned long wtcon;
@@ -164,7 +298,7 @@ static int s3c2410wdt_set_heartbeat(struct watchdog_device *wdd, unsigned timeou
if (timeout < 1)
return -EINVAL;
- freq /= 128;
+ freq = DIV_ROUND_UP(freq, 128);
count = timeout * freq;
DBG("%s: count=%d, timeout=%d, freq=%lu\n",
@@ -176,32 +310,29 @@ static int s3c2410wdt_set_heartbeat(struct watchdog_device *wdd, unsigned timeou
*/
if (count >= 0x10000) {
- for (divisor = 1; divisor <= 0x100; divisor++) {
- if ((count / divisor) < 0x10000)
- break;
- }
+ divisor = DIV_ROUND_UP(count, 0xffff);
- if ((count / divisor) >= 0x10000) {
- dev_err(wdt_dev, "timeout %d too big\n", timeout);
+ if (divisor > 0x100) {
+ dev_err(wdt->dev, "timeout %d too big\n", timeout);
return -EINVAL;
}
}
DBG("%s: timeout=%d, divisor=%d, count=%d (%08x)\n",
- __func__, timeout, divisor, count, count/divisor);
+ __func__, timeout, divisor, count, DIV_ROUND_UP(count, divisor));
- count /= divisor;
- wdt_count = count;
+ count = DIV_ROUND_UP(count, divisor);
+ wdt->count = count;
/* update the pre-scaler */
- wtcon = readl(wdt_base + S3C2410_WTCON);
+ wtcon = readl(wdt->reg_base + S3C2410_WTCON);
wtcon &= ~S3C2410_WTCON_PRESCALE_MASK;
wtcon |= S3C2410_WTCON_PRESCALE(divisor-1);
- writel(count, wdt_base + S3C2410_WTDAT);
- writel(wtcon, wdt_base + S3C2410_WTCON);
+ writel(count, wdt->reg_base + S3C2410_WTDAT);
+ writel(wtcon, wdt->reg_base + S3C2410_WTCON);
- wdd->timeout = timeout;
+ wdd->timeout = (count * divisor) / freq;
return 0;
}
@@ -225,27 +356,30 @@ 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,
};
/* interrupt handler code */
static irqreturn_t s3c2410wdt_irq(int irqno, void *param)
{
- dev_info(wdt_dev, "watchdog timer expired (irq)\n");
+ struct s3c2410_wdt *wdt = platform_get_drvdata(param);
- s3c2410wdt_keepalive(&s3c2410_wdd);
+ dev_info(wdt->dev, "watchdog timer expired (irq)\n");
+
+ s3c2410wdt_keepalive(&wdt->wdt_device);
return IRQ_HANDLED;
}
-
-#ifdef CONFIG_CPU_FREQ
+#ifdef CONFIG_ARM_S3C24XX_CPUFREQ
static int s3c2410wdt_cpufreq_transition(struct notifier_block *nb,
unsigned long val, void *data)
{
int ret;
+ struct s3c2410_wdt *wdt = freq_to_wdt(nb);
- if (!s3c2410wdt_is_running())
+ if (!s3c2410wdt_is_running(wdt))
goto done;
if (val == CPUFREQ_PRECHANGE) {
@@ -254,14 +388,15 @@ static int s3c2410wdt_cpufreq_transition(struct notifier_block *nb,
* the watchdog is running.
*/
- s3c2410wdt_keepalive(&s3c2410_wdd);
+ s3c2410wdt_keepalive(&wdt->wdt_device);
} else if (val == CPUFREQ_POSTCHANGE) {
- s3c2410wdt_stop(&s3c2410_wdd);
+ s3c2410wdt_stop(&wdt->wdt_device);
- ret = s3c2410wdt_set_heartbeat(&s3c2410_wdd, s3c2410_wdd.timeout);
+ ret = s3c2410wdt_set_heartbeat(&wdt->wdt_device,
+ wdt->wdt_device.timeout);
if (ret >= 0)
- s3c2410wdt_start(&s3c2410_wdd);
+ s3c2410wdt_start(&wdt->wdt_device);
else
goto err;
}
@@ -270,55 +405,100 @@ done:
return 0;
err:
- dev_err(wdt_dev, "cannot set new value for timeout %d\n",
- s3c2410_wdd.timeout);
+ dev_err(wdt->dev, "cannot set new value for timeout %d\n",
+ wdt->wdt_device.timeout);
return ret;
}
-static struct notifier_block s3c2410wdt_cpufreq_transition_nb = {
- .notifier_call = s3c2410wdt_cpufreq_transition,
-};
-
-static inline int s3c2410wdt_cpufreq_register(void)
+static inline int s3c2410wdt_cpufreq_register(struct s3c2410_wdt *wdt)
{
- return cpufreq_register_notifier(&s3c2410wdt_cpufreq_transition_nb,
+ wdt->freq_transition.notifier_call = s3c2410wdt_cpufreq_transition;
+
+ return cpufreq_register_notifier(&wdt->freq_transition,
CPUFREQ_TRANSITION_NOTIFIER);
}
-static inline void s3c2410wdt_cpufreq_deregister(void)
+static inline void s3c2410wdt_cpufreq_deregister(struct s3c2410_wdt *wdt)
{
- cpufreq_unregister_notifier(&s3c2410wdt_cpufreq_transition_nb,
+ wdt->freq_transition.notifier_call = s3c2410wdt_cpufreq_transition;
+
+ cpufreq_unregister_notifier(&wdt->freq_transition,
CPUFREQ_TRANSITION_NOTIFIER);
}
#else
-static inline int s3c2410wdt_cpufreq_register(void)
+
+static inline int s3c2410wdt_cpufreq_register(struct s3c2410_wdt *wdt)
{
return 0;
}
-static inline void s3c2410wdt_cpufreq_deregister(void)
+static inline void s3c2410wdt_cpufreq_deregister(struct s3c2410_wdt *wdt)
{
}
#endif
-static int __devinit s3c2410wdt_probe(struct platform_device *pdev)
+static inline unsigned int s3c2410wdt_get_bootstatus(struct s3c2410_wdt *wdt)
+{
+ unsigned int rst_stat;
+ int ret;
+
+ if (!(wdt->drv_data->quirks & QUIRK_HAS_RST_STAT))
+ return 0;
+
+ ret = regmap_read(wdt->pmureg, wdt->drv_data->rst_stat_reg, &rst_stat);
+ if (ret)
+ dev_warn(wdt->dev, "Couldn't get RST_STAT register\n");
+ else if (rst_stat & BIT(wdt->drv_data->rst_stat_bit))
+ return WDIOF_CARDRESET;
+
+ return 0;
+}
+
+/* s3c2410_get_wdt_driver_data */
+static inline struct s3c2410_wdt_variant *
+get_wdt_drv_data(struct platform_device *pdev)
+{
+ if (pdev->dev.of_node) {
+ const struct of_device_id *match;
+ match = of_match_node(s3c2410_wdt_match, pdev->dev.of_node);
+ return (struct s3c2410_wdt_variant *)match->data;
+ } else {
+ return (struct s3c2410_wdt_variant *)
+ platform_get_device_id(pdev)->driver_data;
+ }
+}
+
+static int s3c2410wdt_probe(struct platform_device *pdev)
{
struct device *dev;
+ struct s3c2410_wdt *wdt;
+ struct resource *wdt_mem;
+ struct resource *wdt_irq;
unsigned int wtcon;
int started = 0;
int ret;
- int size;
DBG("%s: probe=%p\n", __func__, pdev);
dev = &pdev->dev;
- wdt_dev = &pdev->dev;
- wdt_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (wdt_mem == NULL) {
- dev_err(dev, "no memory resource specified\n");
- return -ENOENT;
+ wdt = devm_kzalloc(dev, sizeof(*wdt), GFP_KERNEL);
+ if (!wdt)
+ return -ENOMEM;
+
+ wdt->dev = &pdev->dev;
+ spin_lock_init(&wdt->lock);
+ wdt->wdt_device = s3c2410_wdd;
+
+ wdt->drv_data = 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");
+ if (IS_ERR(wdt->pmureg)) {
+ dev_err(dev, "syscon regmap lookup failed.\n");
+ return PTR_ERR(wdt->pmureg);
+ }
}
wdt_irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
@@ -329,43 +509,44 @@ static int __devinit s3c2410wdt_probe(struct platform_device *pdev)
}
/* get the memory region for the watchdog timer */
-
- size = resource_size(wdt_mem);
- if (!request_mem_region(wdt_mem->start, size, pdev->name)) {
- dev_err(dev, "failed to get memory region\n");
- ret = -EBUSY;
+ wdt_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ wdt->reg_base = devm_ioremap_resource(dev, wdt_mem);
+ if (IS_ERR(wdt->reg_base)) {
+ ret = PTR_ERR(wdt->reg_base);
goto err;
}
- wdt_base = ioremap(wdt_mem->start, size);
- if (wdt_base == NULL) {
- dev_err(dev, "failed to ioremap() region\n");
- ret = -EINVAL;
- goto err_req;
- }
-
- DBG("probe: mapped wdt_base=%p\n", wdt_base);
+ DBG("probe: mapped reg_base=%p\n", wdt->reg_base);
- wdt_clock = clk_get(&pdev->dev, "watchdog");
- if (IS_ERR(wdt_clock)) {
+ wdt->clock = devm_clk_get(dev, "watchdog");
+ if (IS_ERR(wdt->clock)) {
dev_err(dev, "failed to find watchdog clock source\n");
- ret = PTR_ERR(wdt_clock);
- goto err_map;
+ ret = PTR_ERR(wdt->clock);
+ goto err;
}
- clk_enable(wdt_clock);
+ ret = clk_prepare_enable(wdt->clock);
+ if (ret < 0) {
+ dev_err(dev, "failed to enable clock\n");
+ return ret;
+ }
- ret = s3c2410wdt_cpufreq_register();
+ ret = s3c2410wdt_cpufreq_register(wdt);
if (ret < 0) {
- pr_err("failed to register cpufreq\n");
+ dev_err(dev, "failed to register cpufreq\n");
goto err_clk;
}
+ watchdog_set_drvdata(&wdt->wdt_device, wdt);
+
/* see if we can actually set the requested timer margin, and if
* not, try the default value */
- if (s3c2410wdt_set_heartbeat(&s3c2410_wdd, tmr_margin)) {
- started = s3c2410wdt_set_heartbeat(&s3c2410_wdd,
+ watchdog_init_timeout(&wdt->wdt_device, tmr_margin, &pdev->dev);
+ ret = s3c2410wdt_set_heartbeat(&wdt->wdt_device,
+ wdt->wdt_device.timeout);
+ if (ret) {
+ started = s3c2410wdt_set_heartbeat(&wdt->wdt_device,
CONFIG_S3C2410_WATCHDOG_DEFAULT_TIME);
if (started == 0)
@@ -377,34 +558,43 @@ static int __devinit s3c2410wdt_probe(struct platform_device *pdev)
"cannot start\n");
}
- ret = request_irq(wdt_irq->start, s3c2410wdt_irq, 0, pdev->name, pdev);
+ ret = devm_request_irq(dev, wdt_irq->start, s3c2410wdt_irq, 0,
+ pdev->name, pdev);
if (ret != 0) {
dev_err(dev, "failed to install irq (%d)\n", ret);
goto err_cpufreq;
}
- watchdog_set_nowayout(&s3c2410_wdd, nowayout);
+ watchdog_set_nowayout(&wdt->wdt_device, nowayout);
+
+ wdt->wdt_device.bootstatus = s3c2410wdt_get_bootstatus(wdt);
- ret = watchdog_register_device(&s3c2410_wdd);
+ ret = watchdog_register_device(&wdt->wdt_device);
if (ret) {
dev_err(dev, "cannot register watchdog (%d)\n", ret);
- goto err_irq;
+ goto err_cpufreq;
}
+ ret = s3c2410wdt_mask_and_disable_reset(wdt, false);
+ if (ret < 0)
+ goto err_unregister;
+
if (tmr_atboot && started == 0) {
dev_info(dev, "starting watchdog timer\n");
- s3c2410wdt_start(&s3c2410_wdd);
+ s3c2410wdt_start(&wdt->wdt_device);
} else if (!tmr_atboot) {
/* if we're not enabling the watchdog, then ensure it is
* disabled if it has been left running from the bootloader
* or other source */
- s3c2410wdt_stop(&s3c2410_wdd);
+ s3c2410wdt_stop(&wdt->wdt_device);
}
+ platform_set_drvdata(pdev, wdt);
+
/* print out a statement of readiness */
- wtcon = readl(wdt_base + S3C2410_WTCON);
+ wtcon = readl(wdt->reg_base + S3C2410_WTCON);
dev_info(dev, "watchdog %sactive, reset %sabled, irq %sabled\n",
(wtcon & S3C2410_WTCON_ENABLE) ? "" : "in",
@@ -413,132 +603,107 @@ static int __devinit s3c2410wdt_probe(struct platform_device *pdev)
return 0;
- err_irq:
- free_irq(wdt_irq->start, pdev);
+ err_unregister:
+ watchdog_unregister_device(&wdt->wdt_device);
err_cpufreq:
- s3c2410wdt_cpufreq_deregister();
+ s3c2410wdt_cpufreq_deregister(wdt);
err_clk:
- clk_disable(wdt_clock);
- clk_put(wdt_clock);
- wdt_clock = NULL;
-
- err_map:
- iounmap(wdt_base);
-
- err_req:
- release_mem_region(wdt_mem->start, size);
+ clk_disable_unprepare(wdt->clock);
err:
- wdt_irq = NULL;
- wdt_mem = NULL;
return ret;
}
-static int __devexit s3c2410wdt_remove(struct platform_device *dev)
+static int s3c2410wdt_remove(struct platform_device *dev)
{
- watchdog_unregister_device(&s3c2410_wdd);
+ int ret;
+ struct s3c2410_wdt *wdt = platform_get_drvdata(dev);
- free_irq(wdt_irq->start, dev);
+ ret = s3c2410wdt_mask_and_disable_reset(wdt, true);
+ if (ret < 0)
+ return ret;
- s3c2410wdt_cpufreq_deregister();
+ watchdog_unregister_device(&wdt->wdt_device);
- clk_disable(wdt_clock);
- clk_put(wdt_clock);
- wdt_clock = NULL;
+ s3c2410wdt_cpufreq_deregister(wdt);
- iounmap(wdt_base);
+ clk_disable_unprepare(wdt->clock);
- release_mem_region(wdt_mem->start, resource_size(wdt_mem));
- wdt_irq = NULL;
- wdt_mem = NULL;
return 0;
}
static void s3c2410wdt_shutdown(struct platform_device *dev)
{
- s3c2410wdt_stop(&s3c2410_wdd);
-}
+ struct s3c2410_wdt *wdt = platform_get_drvdata(dev);
-#ifdef CONFIG_PM
+ s3c2410wdt_mask_and_disable_reset(wdt, true);
-static unsigned long wtcon_save;
-static unsigned long wtdat_save;
+ s3c2410wdt_stop(&wdt->wdt_device);
+}
+
+#ifdef CONFIG_PM_SLEEP
-static int s3c2410wdt_suspend(struct platform_device *dev, pm_message_t state)
+static int s3c2410wdt_suspend(struct device *dev)
{
+ int ret;
+ struct s3c2410_wdt *wdt = dev_get_drvdata(dev);
+
/* Save watchdog state, and turn it off. */
- wtcon_save = readl(wdt_base + S3C2410_WTCON);
- wtdat_save = readl(wdt_base + S3C2410_WTDAT);
+ wdt->wtcon_save = readl(wdt->reg_base + S3C2410_WTCON);
+ wdt->wtdat_save = readl(wdt->reg_base + S3C2410_WTDAT);
+
+ ret = s3c2410wdt_mask_and_disable_reset(wdt, true);
+ if (ret < 0)
+ return ret;
/* Note that WTCNT doesn't need to be saved. */
- s3c2410wdt_stop(&s3c2410_wdd);
+ s3c2410wdt_stop(&wdt->wdt_device);
return 0;
}
-static int s3c2410wdt_resume(struct platform_device *dev)
+static int s3c2410wdt_resume(struct device *dev)
{
+ int ret;
+ struct s3c2410_wdt *wdt = dev_get_drvdata(dev);
+
/* Restore watchdog state. */
+ writel(wdt->wtdat_save, wdt->reg_base + S3C2410_WTDAT);
+ writel(wdt->wtdat_save, wdt->reg_base + S3C2410_WTCNT);/* Reset count */
+ writel(wdt->wtcon_save, wdt->reg_base + S3C2410_WTCON);
- writel(wtdat_save, wdt_base + S3C2410_WTDAT);
- writel(wtdat_save, wdt_base + S3C2410_WTCNT); /* Reset count */
- writel(wtcon_save, wdt_base + S3C2410_WTCON);
+ ret = s3c2410wdt_mask_and_disable_reset(wdt, false);
+ if (ret < 0)
+ return ret;
- pr_info("watchdog %sabled\n",
- (wtcon_save & S3C2410_WTCON_ENABLE) ? "en" : "dis");
+ dev_info(dev, "watchdog %sabled\n",
+ (wdt->wtcon_save & S3C2410_WTCON_ENABLE) ? "en" : "dis");
return 0;
}
-
-#else
-#define s3c2410wdt_suspend NULL
-#define s3c2410wdt_resume NULL
-#endif /* CONFIG_PM */
-
-#ifdef CONFIG_OF
-static const struct of_device_id s3c2410_wdt_match[] = {
- { .compatible = "samsung,s3c2410-wdt" },
- {},
-};
-MODULE_DEVICE_TABLE(of, s3c2410_wdt_match);
-#else
-#define s3c2410_wdt_match NULL
#endif
+static SIMPLE_DEV_PM_OPS(s3c2410wdt_pm_ops, s3c2410wdt_suspend,
+ s3c2410wdt_resume);
+
static struct platform_driver s3c2410wdt_driver = {
.probe = s3c2410wdt_probe,
- .remove = __devexit_p(s3c2410wdt_remove),
+ .remove = s3c2410wdt_remove,
.shutdown = s3c2410wdt_shutdown,
- .suspend = s3c2410wdt_suspend,
- .resume = s3c2410wdt_resume,
+ .id_table = s3c2410_wdt_ids,
.driver = {
.owner = THIS_MODULE,
.name = "s3c2410-wdt",
- .of_match_table = s3c2410_wdt_match,
+ .pm = &s3c2410wdt_pm_ops,
+ .of_match_table = of_match_ptr(s3c2410_wdt_match),
},
};
-
-static int __init watchdog_init(void)
-{
- pr_info("S3C2410 Watchdog Timer, (c) 2004 Simtec Electronics\n");
-
- return platform_driver_register(&s3c2410wdt_driver);
-}
-
-static void __exit watchdog_exit(void)
-{
- platform_driver_unregister(&s3c2410wdt_driver);
-}
-
-module_init(watchdog_init);
-module_exit(watchdog_exit);
+module_platform_driver(s3c2410wdt_driver);
MODULE_AUTHOR("Ben Dooks <ben@simtec.co.uk>, "
"Dimitry Andric <dimitry.andric@tomtom.com>");
MODULE_DESCRIPTION("S3C2410 Watchdog Device Driver");
MODULE_LICENSE("GPL");
-MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
-MODULE_ALIAS("platform:s3c2410-wdt");
diff --git a/drivers/watchdog/sa1100_wdt.c b/drivers/watchdog/sa1100_wdt.c
index 54984deb856..e1d39a1e962 100644
--- a/drivers/watchdog/sa1100_wdt.c
+++ b/drivers/watchdog/sa1100_wdt.c
@@ -54,10 +54,10 @@ static int sa1100dog_open(struct inode *inode, struct file *file)
return -EBUSY;
/* Activate SA1100 Watchdog timer */
- OSMR3 = OSCR + pre_margin;
- OSSR = OSSR_M3;
- OWER = OWER_WME;
- OIER |= OIER_E3;
+ writel_relaxed(readl_relaxed(OSCR) + pre_margin, OSMR3);
+ writel_relaxed(OSSR_M3, OSSR);
+ writel_relaxed(OWER_WME, OWER);
+ writel_relaxed(readl_relaxed(OIER) | OIER_E3, OIER);
return nonseekable_open(inode, file);
}
@@ -80,7 +80,7 @@ static ssize_t sa1100dog_write(struct file *file, const char __user *data,
{
if (len)
/* Refresh OSMR3 timer. */
- OSMR3 = OSCR + pre_margin;
+ writel_relaxed(readl_relaxed(OSCR) + pre_margin, OSMR3);
return len;
}
@@ -114,7 +114,7 @@ static long sa1100dog_ioctl(struct file *file, unsigned int cmd,
break;
case WDIOC_KEEPALIVE:
- OSMR3 = OSCR + pre_margin;
+ writel_relaxed(readl_relaxed(OSCR) + pre_margin, OSMR3);
ret = 0;
break;
@@ -129,7 +129,7 @@ static long sa1100dog_ioctl(struct file *file, unsigned int cmd,
}
pre_margin = oscr_freq * time;
- OSMR3 = OSCR + pre_margin;
+ writel_relaxed(readl_relaxed(OSCR) + pre_margin, OSMR3);
/*fall through*/
case WDIOC_GETTIMEOUT:
@@ -193,4 +193,3 @@ module_param(margin, int, 0);
MODULE_PARM_DESC(margin, "Watchdog margin in seconds (default 60s)");
MODULE_LICENSE("GPL");
-MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
diff --git a/drivers/watchdog/sb_wdog.c b/drivers/watchdog/sb_wdog.c
index 25c7a3f9652..3abae50773b 100644
--- a/drivers/watchdog/sb_wdog.c
+++ b/drivers/watchdog/sb_wdog.c
@@ -208,7 +208,7 @@ static long sbwdog_ioctl(struct file *file, unsigned int cmd,
* get the remaining count from the ... count register
* which is 1*8 before the config register
*/
- ret = put_user(__raw_readq(user_dog - 8) / 1000000, p);
+ ret = put_user((u32)__raw_readq(user_dog - 8) / 1000000, p);
break;
}
return ret;
@@ -341,7 +341,6 @@ MODULE_PARM_DESC(timeout,
"Watchdog timeout in microseconds (max/default 8388607 or 8.3ish secs)");
MODULE_LICENSE("GPL");
-MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
/*
* example code that can be put in a platform code area to utilize the
diff --git a/drivers/watchdog/sbc60xxwdt.c b/drivers/watchdog/sbc60xxwdt.c
index 63632ec87c7..2eef58a0cf0 100644
--- a/drivers/watchdog/sbc60xxwdt.c
+++ b/drivers/watchdog/sbc60xxwdt.c
@@ -387,4 +387,3 @@ module_exit(sbc60xxwdt_unload);
MODULE_AUTHOR("Jakob Oestergaard <jakob@unthought.net>");
MODULE_DESCRIPTION("60xx Single Board Computer Watchdog Timer driver");
MODULE_LICENSE("GPL");
-MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
diff --git a/drivers/watchdog/sbc7240_wdt.c b/drivers/watchdog/sbc7240_wdt.c
index 719edc8fdeb..5f268add17c 100644
--- a/drivers/watchdog/sbc7240_wdt.c
+++ b/drivers/watchdog/sbc7240_wdt.c
@@ -309,5 +309,3 @@ MODULE_AUTHOR("Gilles Gigan");
MODULE_DESCRIPTION("Watchdog device driver for single board"
" computers EPIC Nano 7240 from iEi");
MODULE_LICENSE("GPL");
-MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
-
diff --git a/drivers/watchdog/sbc8360.c b/drivers/watchdog/sbc8360.c
index d4781e05f01..da60560ca44 100644
--- a/drivers/watchdog/sbc8360.c
+++ b/drivers/watchdog/sbc8360.c
@@ -404,6 +404,5 @@ MODULE_AUTHOR("Ian E. Morgan <imorgan@webcon.ca>");
MODULE_DESCRIPTION("SBC8360 watchdog driver");
MODULE_LICENSE("GPL");
MODULE_VERSION("1.01");
-MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
/* end of sbc8360.c */
diff --git a/drivers/watchdog/sbc_epx_c3.c b/drivers/watchdog/sbc_epx_c3.c
index 0c3e9f66ef7..a1c502e0d8e 100644
--- a/drivers/watchdog/sbc_epx_c3.c
+++ b/drivers/watchdog/sbc_epx_c3.c
@@ -220,4 +220,3 @@ MODULE_DESCRIPTION("Hardware Watchdog Device for Winsystems EPX-C3 SBC. "
"so only use it if you are *sure* you are running on this specific "
"SBC system from Winsystems! It writes to IO ports 0x1ee and 0x1ef!");
MODULE_LICENSE("GPL");
-MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
diff --git a/drivers/watchdog/sbc_fitpc2_wdt.c b/drivers/watchdog/sbc_fitpc2_wdt.c
index 90d5527ca88..a517d8bae75 100644
--- a/drivers/watchdog/sbc_fitpc2_wdt.c
+++ b/drivers/watchdog/sbc_fitpc2_wdt.c
@@ -263,5 +263,3 @@ module_param(nowayout, bool, 0);
MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started");
MODULE_LICENSE("GPL");
-MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
-
diff --git a/drivers/watchdog/sc1200wdt.c b/drivers/watchdog/sc1200wdt.c
index 3fb83b0c28c..131193a7acd 100644
--- a/drivers/watchdog/sc1200wdt.c
+++ b/drivers/watchdog/sc1200wdt.c
@@ -409,8 +409,9 @@ static int __init sc1200wdt_init(void)
#if defined CONFIG_PNP
/* now that the user has specified an IO port and we haven't detected
* any devices, disable pnp support */
+ if (isapnp)
+ pnp_unregister_driver(&scl200wdt_pnp_driver);
isapnp = 0;
- pnp_unregister_driver(&scl200wdt_pnp_driver);
#endif
if (!request_region(io, io_len, SC1200_MODULE_NAME)) {
@@ -476,4 +477,3 @@ MODULE_AUTHOR("Zwane Mwaikambo <zwane@commfireservices.com>");
MODULE_DESCRIPTION(
"Driver for National Semiconductor PC87307/PC97307 watchdog component");
MODULE_LICENSE("GPL");
-MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
diff --git a/drivers/watchdog/sc520_wdt.c b/drivers/watchdog/sc520_wdt.c
index 707e027e500..1cfd3f6a13d 100644
--- a/drivers/watchdog/sc520_wdt.c
+++ b/drivers/watchdog/sc520_wdt.c
@@ -158,12 +158,11 @@ static void wdt_timer_ping(unsigned long data)
static void wdt_config(int writeval)
{
- __u16 dummy;
unsigned long flags;
/* buy some time (ping) */
spin_lock_irqsave(&wdt_spinlock, flags);
- dummy = readw(wdtmrctl); /* ensure write synchronization */
+ readw(wdtmrctl); /* ensure write synchronization */
writew(0xAAAA, wdtmrctl);
writew(0x5555, wdtmrctl);
/* unlock WDT = make WDT configuration register writable one time */
@@ -433,4 +432,3 @@ MODULE_AUTHOR("Scott and Bill Jennings");
MODULE_DESCRIPTION(
"Driver for watchdog timer in AMD \"Elan\" SC520 uProcessor");
MODULE_LICENSE("GPL");
-MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
diff --git a/drivers/watchdog/sch311x_wdt.c b/drivers/watchdog/sch311x_wdt.c
index bd86f32d63a..b96127ea3de 100644
--- a/drivers/watchdog/sch311x_wdt.c
+++ b/drivers/watchdog/sch311x_wdt.c
@@ -26,8 +26,7 @@
#include <linux/types.h> /* For standard types (like size_t) */
#include <linux/errno.h> /* For the -ENODEV/... values */
#include <linux/kernel.h> /* For printk/... */
-#include <linux/miscdevice.h> /* For MODULE_ALIAS_MISCDEV
- (WATCHDOG_MINOR) */
+#include <linux/miscdevice.h> /* For struct miscdevice */
#include <linux/watchdog.h> /* For the watchdog specific items */
#include <linux/init.h> /* For __init/__exit/... */
#include <linux/fs.h> /* For file operations */
@@ -41,7 +40,6 @@
#define DRV_NAME "sch311x_wdt"
/* Runtime registers */
-#define RESGEN 0x1d
#define GP60 0x47
#define WDT_TIME_OUT 0x65
#define WDT_VAL 0x66
@@ -69,10 +67,6 @@ static unsigned short force_id;
module_param(force_id, ushort, 0);
MODULE_PARM_DESC(force_id, "Override the detected device ID");
-static unsigned short therm_trip;
-module_param(therm_trip, ushort, 0);
-MODULE_PARM_DESC(therm_trip, "Should a ThermTrip trigger the reset generator");
-
#define WATCHDOG_TIMEOUT 60 /* 60 sec default timeout */
static int timeout = WATCHDOG_TIMEOUT; /* in seconds */
module_param(timeout, int, 0);
@@ -141,6 +135,8 @@ static void sch311x_wdt_set_timeout(int t)
static void sch311x_wdt_start(void)
{
+ unsigned char t;
+
spin_lock(&sch311x_wdt_data.io_lock);
/* set watchdog's timeout */
@@ -154,7 +150,8 @@ static void sch311x_wdt_start(void)
* Bit 4-6 (Reserved)
* Bit 7, Output Type: 0 = Push Pull Bit, 1 = Open Drain
*/
- outb(0x0e, sch311x_wdt_data.runtime_reg + GP60);
+ t = inb(sch311x_wdt_data.runtime_reg + GP60);
+ outb((t & ~0x0d) | 0x0c, sch311x_wdt_data.runtime_reg + GP60);
spin_unlock(&sch311x_wdt_data.io_lock);
@@ -162,10 +159,13 @@ static void sch311x_wdt_start(void)
static void sch311x_wdt_stop(void)
{
+ unsigned char t;
+
spin_lock(&sch311x_wdt_data.io_lock);
/* stop the watchdog */
- outb(0x01, sch311x_wdt_data.runtime_reg + GP60);
+ t = inb(sch311x_wdt_data.runtime_reg + GP60);
+ outb((t & ~0x0d) | 0x01, sch311x_wdt_data.runtime_reg + GP60);
/* disable timeout by setting it to 0 */
sch311x_wdt_set_timeout(0);
@@ -355,29 +355,19 @@ static struct miscdevice sch311x_wdt_miscdev = {
* Init & exit routines
*/
-static int __devinit sch311x_wdt_probe(struct platform_device *pdev)
+static int sch311x_wdt_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
- unsigned char val;
int err;
spin_lock_init(&sch311x_wdt_data.io_lock);
- if (!request_region(sch311x_wdt_data.runtime_reg + RESGEN, 1,
- DRV_NAME)) {
- dev_err(dev, "Failed to request region 0x%04x-0x%04x.\n",
- sch311x_wdt_data.runtime_reg + RESGEN,
- sch311x_wdt_data.runtime_reg + RESGEN);
- err = -EBUSY;
- goto exit;
- }
-
if (!request_region(sch311x_wdt_data.runtime_reg + GP60, 1, DRV_NAME)) {
dev_err(dev, "Failed to request region 0x%04x-0x%04x.\n",
sch311x_wdt_data.runtime_reg + GP60,
sch311x_wdt_data.runtime_reg + GP60);
err = -EBUSY;
- goto exit_release_region;
+ goto exit;
}
if (!request_region(sch311x_wdt_data.runtime_reg + WDT_TIME_OUT, 4,
@@ -386,7 +376,7 @@ static int __devinit sch311x_wdt_probe(struct platform_device *pdev)
sch311x_wdt_data.runtime_reg + WDT_TIME_OUT,
sch311x_wdt_data.runtime_reg + WDT_CTRL);
err = -EBUSY;
- goto exit_release_region2;
+ goto exit_release_region;
}
/* Make sure that the watchdog is not running */
@@ -414,24 +404,13 @@ static int __devinit sch311x_wdt_probe(struct platform_device *pdev)
/* Get status at boot */
sch311x_wdt_get_status(&sch311x_wdt_data.boot_status);
- /* enable watchdog */
- /* -- Reset Generator --
- * Bit 0 Enable Watchdog Timer Generation: 0* = Enabled, 1 = Disabled
- * Bit 1 Thermtrip Source Select: O* = No Source, 1 = Source
- * Bit 2 WDT2_CTL: WDT input bit
- * Bit 3-7 Reserved
- */
- outb(0, sch311x_wdt_data.runtime_reg + RESGEN);
- val = therm_trip ? 0x06 : 0x04;
- outb(val, sch311x_wdt_data.runtime_reg + RESGEN);
-
sch311x_wdt_miscdev.parent = dev;
err = misc_register(&sch311x_wdt_miscdev);
if (err != 0) {
dev_err(dev, "cannot register miscdev on minor=%d (err=%d)\n",
WATCHDOG_MINOR, err);
- goto exit_release_region3;
+ goto exit_release_region2;
}
dev_info(dev,
@@ -440,18 +419,16 @@ static int __devinit sch311x_wdt_probe(struct platform_device *pdev)
return 0;
-exit_release_region3:
- release_region(sch311x_wdt_data.runtime_reg + WDT_TIME_OUT, 4);
exit_release_region2:
- release_region(sch311x_wdt_data.runtime_reg + GP60, 1);
+ release_region(sch311x_wdt_data.runtime_reg + WDT_TIME_OUT, 4);
exit_release_region:
- release_region(sch311x_wdt_data.runtime_reg + RESGEN, 1);
+ release_region(sch311x_wdt_data.runtime_reg + GP60, 1);
sch311x_wdt_data.runtime_reg = 0;
exit:
return err;
}
-static int __devexit sch311x_wdt_remove(struct platform_device *pdev)
+static int sch311x_wdt_remove(struct platform_device *pdev)
{
/* Stop the timer before we leave */
if (!nowayout)
@@ -461,7 +438,6 @@ static int __devexit sch311x_wdt_remove(struct platform_device *pdev)
misc_deregister(&sch311x_wdt_miscdev);
release_region(sch311x_wdt_data.runtime_reg + WDT_TIME_OUT, 4);
release_region(sch311x_wdt_data.runtime_reg + GP60, 1);
- release_region(sch311x_wdt_data.runtime_reg + RESGEN, 1);
sch311x_wdt_data.runtime_reg = 0;
return 0;
}
@@ -474,7 +450,7 @@ static void sch311x_wdt_shutdown(struct platform_device *dev)
static struct platform_driver sch311x_wdt_driver = {
.probe = sch311x_wdt_probe,
- .remove = __devexit_p(sch311x_wdt_remove),
+ .remove = sch311x_wdt_remove,
.shutdown = sch311x_wdt_shutdown,
.driver = {
.owner = THIS_MODULE,
@@ -568,5 +544,3 @@ module_exit(sch311x_wdt_exit);
MODULE_AUTHOR("Wim Van Sebroeck <wim@iguana.be>");
MODULE_DESCRIPTION("SMSC SCH311x WatchDog Timer Driver");
MODULE_LICENSE("GPL");
-MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
-
diff --git a/drivers/watchdog/scx200_wdt.c b/drivers/watchdog/scx200_wdt.c
index 8ae7c282d46..836377cf927 100644
--- a/drivers/watchdog/scx200_wdt.c
+++ b/drivers/watchdog/scx200_wdt.c
@@ -37,7 +37,6 @@
MODULE_AUTHOR("Christer Weinigel <wingel@nano-system.com>");
MODULE_DESCRIPTION("NatSemi SCx200 Watchdog Driver");
MODULE_LICENSE("GPL");
-MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
static int margin = 60; /* in seconds */
module_param(margin, int, 0);
diff --git a/drivers/watchdog/shwdt.c b/drivers/watchdog/shwdt.c
index 93958a7763e..061756e36cf 100644
--- a/drivers/watchdog/shwdt.c
+++ b/drivers/watchdog/shwdt.c
@@ -3,7 +3,7 @@
*
* Watchdog driver for integrated watchdog in the SuperH processors.
*
- * Copyright (C) 2001 - 2010 Paul Mundt <lethal@linux-sh.org>
+ * Copyright (C) 2001 - 2012 Paul Mundt <lethal@linux-sh.org>
*
* 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
@@ -25,16 +25,15 @@
#include <linux/platform_device.h>
#include <linux/init.h>
#include <linux/types.h>
-#include <linux/miscdevice.h>
+#include <linux/spinlock.h>
#include <linux/watchdog.h>
-#include <linux/reboot.h>
-#include <linux/notifier.h>
-#include <linux/ioport.h>
+#include <linux/pm_runtime.h>
#include <linux/fs.h>
#include <linux/mm.h>
#include <linux/slab.h>
#include <linux/io.h>
-#include <linux/uaccess.h>
+#include <linux/clk.h>
+#include <linux/err.h>
#include <asm/watchdog.h>
#define DRV_NAME "sh-wdt"
@@ -69,10 +68,6 @@
static int clock_division_ratio = WTCSR_CKS_4096;
#define next_ping_period(cks) (jiffies + msecs_to_jiffies(cks - 4))
-static const struct watchdog_info sh_wdt_info;
-static struct platform_device *sh_wdt_dev;
-static DEFINE_SPINLOCK(shwdt_lock);
-
#define WATCHDOG_HEARTBEAT 30 /* 30 sec default heartbeat */
static int heartbeat = WATCHDOG_HEARTBEAT; /* in seconds */
static bool nowayout = WATCHDOG_NOWAYOUT;
@@ -81,19 +76,22 @@ static unsigned long next_heartbeat;
struct sh_wdt {
void __iomem *base;
struct device *dev;
+ struct clk *clk;
+ spinlock_t lock;
struct timer_list timer;
-
- unsigned long enabled;
- char expect_close;
};
-static void sh_wdt_start(struct sh_wdt *wdt)
+static int sh_wdt_start(struct watchdog_device *wdt_dev)
{
+ struct sh_wdt *wdt = watchdog_get_drvdata(wdt_dev);
unsigned long flags;
u8 csr;
- spin_lock_irqsave(&shwdt_lock, flags);
+ pm_runtime_get_sync(wdt->dev);
+ clk_enable(wdt->clk);
+
+ spin_lock_irqsave(&wdt->lock, flags);
next_heartbeat = jiffies + (heartbeat * HZ);
mod_timer(&wdt->timer, next_ping_period(clock_division_ratio));
@@ -122,15 +120,18 @@ static void sh_wdt_start(struct sh_wdt *wdt)
csr &= ~RSTCSR_RSTS;
sh_wdt_write_rstcsr(csr);
#endif
- spin_unlock_irqrestore(&shwdt_lock, flags);
+ spin_unlock_irqrestore(&wdt->lock, flags);
+
+ return 0;
}
-static void sh_wdt_stop(struct sh_wdt *wdt)
+static int sh_wdt_stop(struct watchdog_device *wdt_dev)
{
+ struct sh_wdt *wdt = watchdog_get_drvdata(wdt_dev);
unsigned long flags;
u8 csr;
- spin_lock_irqsave(&shwdt_lock, flags);
+ spin_lock_irqsave(&wdt->lock, flags);
del_timer(&wdt->timer);
@@ -138,28 +139,39 @@ static void sh_wdt_stop(struct sh_wdt *wdt)
csr &= ~WTCSR_TME;
sh_wdt_write_csr(csr);
- spin_unlock_irqrestore(&shwdt_lock, flags);
+ spin_unlock_irqrestore(&wdt->lock, flags);
+
+ clk_disable(wdt->clk);
+ pm_runtime_put_sync(wdt->dev);
+
+ return 0;
}
-static inline void sh_wdt_keepalive(struct sh_wdt *wdt)
+static int sh_wdt_keepalive(struct watchdog_device *wdt_dev)
{
+ struct sh_wdt *wdt = watchdog_get_drvdata(wdt_dev);
unsigned long flags;
- spin_lock_irqsave(&shwdt_lock, flags);
+ spin_lock_irqsave(&wdt->lock, flags);
next_heartbeat = jiffies + (heartbeat * HZ);
- spin_unlock_irqrestore(&shwdt_lock, flags);
+ spin_unlock_irqrestore(&wdt->lock, flags);
+
+ return 0;
}
-static int sh_wdt_set_heartbeat(int t)
+static int sh_wdt_set_heartbeat(struct watchdog_device *wdt_dev, unsigned t)
{
+ struct sh_wdt *wdt = watchdog_get_drvdata(wdt_dev);
unsigned long flags;
if (unlikely(t < 1 || t > 3600)) /* arbitrary upper limit */
return -EINVAL;
- spin_lock_irqsave(&shwdt_lock, flags);
+ spin_lock_irqsave(&wdt->lock, flags);
heartbeat = t;
- spin_unlock_irqrestore(&shwdt_lock, flags);
+ wdt_dev->timeout = t;
+ spin_unlock_irqrestore(&wdt->lock, flags);
+
return 0;
}
@@ -168,7 +180,7 @@ static void sh_wdt_ping(unsigned long data)
struct sh_wdt *wdt = (struct sh_wdt *)data;
unsigned long flags;
- spin_lock_irqsave(&shwdt_lock, flags);
+ spin_lock_irqsave(&wdt->lock, flags);
if (time_before(jiffies, next_heartbeat)) {
u8 csr;
@@ -182,137 +194,9 @@ static void sh_wdt_ping(unsigned long data)
} else
dev_warn(wdt->dev, "Heartbeat lost! Will not ping "
"the watchdog\n");
- spin_unlock_irqrestore(&shwdt_lock, flags);
-}
-
-static int sh_wdt_open(struct inode *inode, struct file *file)
-{
- struct sh_wdt *wdt = platform_get_drvdata(sh_wdt_dev);
-
- if (test_and_set_bit(0, &wdt->enabled))
- return -EBUSY;
- if (nowayout)
- __module_get(THIS_MODULE);
-
- file->private_data = wdt;
-
- sh_wdt_start(wdt);
-
- return nonseekable_open(inode, file);
+ spin_unlock_irqrestore(&wdt->lock, flags);
}
-static int sh_wdt_close(struct inode *inode, struct file *file)
-{
- struct sh_wdt *wdt = file->private_data;
-
- if (wdt->expect_close == 42) {
- sh_wdt_stop(wdt);
- } else {
- dev_crit(wdt->dev, "Unexpected close, not "
- "stopping watchdog!\n");
- sh_wdt_keepalive(wdt);
- }
-
- clear_bit(0, &wdt->enabled);
- wdt->expect_close = 0;
-
- return 0;
-}
-
-static ssize_t sh_wdt_write(struct file *file, const char *buf,
- size_t count, loff_t *ppos)
-{
- struct sh_wdt *wdt = file->private_data;
-
- if (count) {
- if (!nowayout) {
- size_t i;
-
- wdt->expect_close = 0;
-
- for (i = 0; i != count; i++) {
- char c;
- if (get_user(c, buf + i))
- return -EFAULT;
- if (c == 'V')
- wdt->expect_close = 42;
- }
- }
- sh_wdt_keepalive(wdt);
- }
-
- return count;
-}
-
-static long sh_wdt_ioctl(struct file *file, unsigned int cmd,
- unsigned long arg)
-{
- struct sh_wdt *wdt = file->private_data;
- int new_heartbeat;
- int options, retval = -EINVAL;
-
- switch (cmd) {
- case WDIOC_GETSUPPORT:
- return copy_to_user((struct watchdog_info *)arg,
- &sh_wdt_info, sizeof(sh_wdt_info)) ? -EFAULT : 0;
- case WDIOC_GETSTATUS:
- case WDIOC_GETBOOTSTATUS:
- return put_user(0, (int *)arg);
- case WDIOC_SETOPTIONS:
- if (get_user(options, (int *)arg))
- return -EFAULT;
-
- if (options & WDIOS_DISABLECARD) {
- sh_wdt_stop(wdt);
- retval = 0;
- }
-
- if (options & WDIOS_ENABLECARD) {
- sh_wdt_start(wdt);
- retval = 0;
- }
-
- return retval;
- case WDIOC_KEEPALIVE:
- sh_wdt_keepalive(wdt);
- return 0;
- case WDIOC_SETTIMEOUT:
- if (get_user(new_heartbeat, (int *)arg))
- return -EFAULT;
-
- if (sh_wdt_set_heartbeat(new_heartbeat))
- return -EINVAL;
-
- sh_wdt_keepalive(wdt);
- /* Fall */
- case WDIOC_GETTIMEOUT:
- return put_user(heartbeat, (int *)arg);
- default:
- return -ENOTTY;
- }
- return 0;
-}
-
-static int sh_wdt_notify_sys(struct notifier_block *this,
- unsigned long code, void *unused)
-{
- struct sh_wdt *wdt = platform_get_drvdata(sh_wdt_dev);
-
- if (code == SYS_DOWN || code == SYS_HALT)
- sh_wdt_stop(wdt);
-
- return NOTIFY_DONE;
-}
-
-static const struct file_operations sh_wdt_fops = {
- .owner = THIS_MODULE,
- .llseek = no_llseek,
- .write = sh_wdt_write,
- .unlocked_ioctl = sh_wdt_ioctl,
- .open = sh_wdt_open,
- .release = sh_wdt_close,
-};
-
static const struct watchdog_info sh_wdt_info = {
.options = WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT |
WDIOF_MAGICCLOSE,
@@ -320,17 +204,20 @@ static const struct watchdog_info sh_wdt_info = {
.identity = "SH WDT",
};
-static struct notifier_block sh_wdt_notifier = {
- .notifier_call = sh_wdt_notify_sys,
+static const struct watchdog_ops sh_wdt_ops = {
+ .owner = THIS_MODULE,
+ .start = sh_wdt_start,
+ .stop = sh_wdt_stop,
+ .ping = sh_wdt_keepalive,
+ .set_timeout = sh_wdt_set_heartbeat,
};
-static struct miscdevice sh_wdt_miscdev = {
- .minor = WATCHDOG_MINOR,
- .name = "watchdog",
- .fops = &sh_wdt_fops,
+static struct watchdog_device sh_wdt_dev = {
+ .info = &sh_wdt_info,
+ .ops = &sh_wdt_ops,
};
-static int __devinit sh_wdt_probe(struct platform_device *pdev)
+static int sh_wdt_probe(struct platform_device *pdev)
{
struct sh_wdt *wdt;
struct resource *res;
@@ -347,39 +234,47 @@ static int __devinit sh_wdt_probe(struct platform_device *pdev)
if (unlikely(!res))
return -EINVAL;
- if (!devm_request_mem_region(&pdev->dev, res->start,
- resource_size(res), DRV_NAME))
- return -EBUSY;
-
wdt = devm_kzalloc(&pdev->dev, sizeof(struct sh_wdt), GFP_KERNEL);
- if (unlikely(!wdt)) {
- rc = -ENOMEM;
- goto out_release;
- }
+ if (unlikely(!wdt))
+ return -ENOMEM;
wdt->dev = &pdev->dev;
- wdt->base = devm_ioremap(&pdev->dev, res->start, resource_size(res));
- if (unlikely(!wdt->base)) {
- rc = -ENXIO;
- goto out_err;
+ wdt->clk = devm_clk_get(&pdev->dev, NULL);
+ if (IS_ERR(wdt->clk)) {
+ /*
+ * Clock framework support is optional, continue on
+ * anyways if we don't find a matching clock.
+ */
+ wdt->clk = NULL;
}
- rc = register_reboot_notifier(&sh_wdt_notifier);
+ wdt->base = devm_ioremap_resource(wdt->dev, res);
+ if (IS_ERR(wdt->base))
+ return PTR_ERR(wdt->base);
+
+ watchdog_set_nowayout(&sh_wdt_dev, nowayout);
+ watchdog_set_drvdata(&sh_wdt_dev, wdt);
+
+ spin_lock_init(&wdt->lock);
+
+ rc = sh_wdt_set_heartbeat(&sh_wdt_dev, heartbeat);
if (unlikely(rc)) {
- dev_err(&pdev->dev,
- "Can't register reboot notifier (err=%d)\n", rc);
- goto out_unmap;
+ /* Default timeout if invalid */
+ sh_wdt_set_heartbeat(&sh_wdt_dev, WATCHDOG_HEARTBEAT);
+
+ dev_warn(&pdev->dev,
+ "heartbeat value must be 1<=x<=3600, using %d\n",
+ sh_wdt_dev.timeout);
}
- sh_wdt_miscdev.parent = wdt->dev;
+ dev_info(&pdev->dev, "configured with heartbeat=%d sec (nowayout=%d)\n",
+ sh_wdt_dev.timeout, nowayout);
- rc = misc_register(&sh_wdt_miscdev);
+ rc = watchdog_register_device(&sh_wdt_dev);
if (unlikely(rc)) {
- dev_err(&pdev->dev,
- "Can't register miscdev on minor=%d (err=%d)\n",
- sh_wdt_miscdev.minor, rc);
- goto out_unreg;
+ dev_err(&pdev->dev, "Can't register watchdog (err=%d)\n", rc);
+ return rc;
}
init_timer(&wdt->timer);
@@ -387,58 +282,40 @@ static int __devinit sh_wdt_probe(struct platform_device *pdev)
wdt->timer.data = (unsigned long)wdt;
wdt->timer.expires = next_ping_period(clock_division_ratio);
- platform_set_drvdata(pdev, wdt);
- sh_wdt_dev = pdev;
-
dev_info(&pdev->dev, "initialized.\n");
- return 0;
-
-out_unreg:
- unregister_reboot_notifier(&sh_wdt_notifier);
-out_unmap:
- devm_iounmap(&pdev->dev, wdt->base);
-out_err:
- devm_kfree(&pdev->dev, wdt);
-out_release:
- devm_release_mem_region(&pdev->dev, res->start, resource_size(res));
+ pm_runtime_enable(&pdev->dev);
- return rc;
+ return 0;
}
-static int __devexit sh_wdt_remove(struct platform_device *pdev)
+static int sh_wdt_remove(struct platform_device *pdev)
{
- struct sh_wdt *wdt = platform_get_drvdata(pdev);
- struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-
- platform_set_drvdata(pdev, NULL);
+ watchdog_unregister_device(&sh_wdt_dev);
- misc_deregister(&sh_wdt_miscdev);
-
- sh_wdt_dev = NULL;
-
- unregister_reboot_notifier(&sh_wdt_notifier);
- devm_release_mem_region(&pdev->dev, res->start, resource_size(res));
- devm_iounmap(&pdev->dev, wdt->base);
- devm_kfree(&pdev->dev, wdt);
+ pm_runtime_disable(&pdev->dev);
return 0;
}
+static void sh_wdt_shutdown(struct platform_device *pdev)
+{
+ sh_wdt_stop(&sh_wdt_dev);
+}
+
static struct platform_driver sh_wdt_driver = {
.driver = {
.name = DRV_NAME,
.owner = THIS_MODULE,
},
- .probe = sh_wdt_probe,
- .remove = __devexit_p(sh_wdt_remove),
+ .probe = sh_wdt_probe,
+ .remove = sh_wdt_remove,
+ .shutdown = sh_wdt_shutdown,
};
static int __init sh_wdt_init(void)
{
- int rc;
-
if (unlikely(clock_division_ratio < 0x5 ||
clock_division_ratio > 0x7)) {
clock_division_ratio = WTCSR_CKS_4096;
@@ -447,17 +324,6 @@ static int __init sh_wdt_init(void)
clock_division_ratio);
}
- rc = sh_wdt_set_heartbeat(heartbeat);
- if (unlikely(rc)) {
- heartbeat = WATCHDOG_HEARTBEAT;
-
- pr_info("heartbeat value must be 1<=x<=3600, using %d\n",
- heartbeat);
- }
-
- pr_info("configured with heartbeat=%d sec (nowayout=%d)\n",
- heartbeat, nowayout);
-
return platform_driver_register(&sh_wdt_driver);
}
@@ -472,7 +338,6 @@ MODULE_AUTHOR("Paul Mundt <lethal@linux-sh.org>");
MODULE_DESCRIPTION("SuperH watchdog driver");
MODULE_LICENSE("GPL");
MODULE_ALIAS("platform:" DRV_NAME);
-MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
module_param(clock_division_ratio, int, 0);
MODULE_PARM_DESC(clock_division_ratio,
diff --git a/drivers/watchdog/sirfsoc_wdt.c b/drivers/watchdog/sirfsoc_wdt.c
new file mode 100644
index 00000000000..702d0787080
--- /dev/null
+++ b/drivers/watchdog/sirfsoc_wdt.c
@@ -0,0 +1,226 @@
+/*
+ * Watchdog driver for CSR SiRFprimaII and SiRFatlasVI
+ *
+ * Copyright (c) 2013 Cambridge Silicon Radio Limited, a CSR plc group company.
+ *
+ * Licensed under GPLv2 or later.
+ */
+
+#include <linux/module.h>
+#include <linux/watchdog.h>
+#include <linux/platform_device.h>
+#include <linux/moduleparam.h>
+#include <linux/of.h>
+#include <linux/io.h>
+#include <linux/uaccess.h>
+
+#define CLOCK_FREQ 1000000
+
+#define SIRFSOC_TIMER_COUNTER_LO 0x0000
+#define SIRFSOC_TIMER_MATCH_0 0x0008
+#define SIRFSOC_TIMER_INT_EN 0x0024
+#define SIRFSOC_TIMER_WATCHDOG_EN 0x0028
+#define SIRFSOC_TIMER_LATCH 0x0030
+#define SIRFSOC_TIMER_LATCHED_LO 0x0034
+
+#define SIRFSOC_TIMER_WDT_INDEX 5
+
+#define SIRFSOC_WDT_MIN_TIMEOUT 30 /* 30 secs */
+#define SIRFSOC_WDT_MAX_TIMEOUT (10 * 60) /* 10 mins */
+#define SIRFSOC_WDT_DEFAULT_TIMEOUT 30 /* 30 secs */
+
+static unsigned int timeout = SIRFSOC_WDT_DEFAULT_TIMEOUT;
+static bool nowayout = WATCHDOG_NOWAYOUT;
+
+module_param(timeout, uint, 0);
+module_param(nowayout, bool, 0);
+
+MODULE_PARM_DESC(timeout, "Default watchdog timeout (in seconds)");
+MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default="
+ __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
+
+static unsigned int sirfsoc_wdt_gettimeleft(struct watchdog_device *wdd)
+{
+ u32 counter, match;
+ void __iomem *wdt_base;
+ int time_left;
+
+ wdt_base = watchdog_get_drvdata(wdd);
+ counter = readl(wdt_base + SIRFSOC_TIMER_COUNTER_LO);
+ match = readl(wdt_base +
+ SIRFSOC_TIMER_MATCH_0 + (SIRFSOC_TIMER_WDT_INDEX << 2));
+
+ time_left = match - counter;
+
+ return time_left / CLOCK_FREQ;
+}
+
+static int sirfsoc_wdt_updatetimeout(struct watchdog_device *wdd)
+{
+ u32 counter, timeout_ticks;
+ void __iomem *wdt_base;
+
+ timeout_ticks = wdd->timeout * CLOCK_FREQ;
+ wdt_base = watchdog_get_drvdata(wdd);
+
+ /* Enable the latch before reading the LATCH_LO register */
+ writel(1, wdt_base + SIRFSOC_TIMER_LATCH);
+
+ /* Set the TO value */
+ counter = readl(wdt_base + SIRFSOC_TIMER_LATCHED_LO);
+
+ counter += timeout_ticks;
+
+ writel(counter, wdt_base +
+ SIRFSOC_TIMER_MATCH_0 + (SIRFSOC_TIMER_WDT_INDEX << 2));
+
+ return 0;
+}
+
+static int sirfsoc_wdt_enable(struct watchdog_device *wdd)
+{
+ void __iomem *wdt_base = watchdog_get_drvdata(wdd);
+ sirfsoc_wdt_updatetimeout(wdd);
+
+ /*
+ * NOTE: If interrupt is not enabled
+ * then WD-Reset doesn't get generated at all.
+ */
+ writel(readl(wdt_base + SIRFSOC_TIMER_INT_EN)
+ | (1 << SIRFSOC_TIMER_WDT_INDEX),
+ wdt_base + SIRFSOC_TIMER_INT_EN);
+ writel(1, wdt_base + SIRFSOC_TIMER_WATCHDOG_EN);
+
+ return 0;
+}
+
+static int sirfsoc_wdt_disable(struct watchdog_device *wdd)
+{
+ void __iomem *wdt_base = watchdog_get_drvdata(wdd);
+
+ writel(0, wdt_base + SIRFSOC_TIMER_WATCHDOG_EN);
+ writel(readl(wdt_base + SIRFSOC_TIMER_INT_EN)
+ & (~(1 << SIRFSOC_TIMER_WDT_INDEX)),
+ wdt_base + SIRFSOC_TIMER_INT_EN);
+
+ return 0;
+}
+
+static int sirfsoc_wdt_settimeout(struct watchdog_device *wdd, unsigned int to)
+{
+ wdd->timeout = to;
+ sirfsoc_wdt_updatetimeout(wdd);
+
+ return 0;
+}
+
+#define OPTIONS (WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE)
+
+static const struct watchdog_info sirfsoc_wdt_ident = {
+ .options = OPTIONS,
+ .firmware_version = 0,
+ .identity = "SiRFSOC Watchdog",
+};
+
+static struct watchdog_ops sirfsoc_wdt_ops = {
+ .owner = THIS_MODULE,
+ .start = sirfsoc_wdt_enable,
+ .stop = sirfsoc_wdt_disable,
+ .get_timeleft = sirfsoc_wdt_gettimeleft,
+ .ping = sirfsoc_wdt_updatetimeout,
+ .set_timeout = sirfsoc_wdt_settimeout,
+};
+
+static struct watchdog_device sirfsoc_wdd = {
+ .info = &sirfsoc_wdt_ident,
+ .ops = &sirfsoc_wdt_ops,
+ .timeout = SIRFSOC_WDT_DEFAULT_TIMEOUT,
+ .min_timeout = SIRFSOC_WDT_MIN_TIMEOUT,
+ .max_timeout = SIRFSOC_WDT_MAX_TIMEOUT,
+};
+
+static int sirfsoc_wdt_probe(struct platform_device *pdev)
+{
+ struct resource *res;
+ int ret;
+ void __iomem *base;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ base = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(base))
+ return PTR_ERR(base);
+
+ watchdog_set_drvdata(&sirfsoc_wdd, base);
+
+ watchdog_init_timeout(&sirfsoc_wdd, timeout, &pdev->dev);
+ watchdog_set_nowayout(&sirfsoc_wdd, nowayout);
+
+ ret = watchdog_register_device(&sirfsoc_wdd);
+ if (ret)
+ return ret;
+
+ platform_set_drvdata(pdev, &sirfsoc_wdd);
+
+ return 0;
+}
+
+static void sirfsoc_wdt_shutdown(struct platform_device *pdev)
+{
+ struct watchdog_device *wdd = platform_get_drvdata(pdev);
+
+ sirfsoc_wdt_disable(wdd);
+}
+
+static int sirfsoc_wdt_remove(struct platform_device *pdev)
+{
+ sirfsoc_wdt_shutdown(pdev);
+ return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int sirfsoc_wdt_suspend(struct device *dev)
+{
+ return 0;
+}
+
+static int sirfsoc_wdt_resume(struct device *dev)
+{
+ struct watchdog_device *wdd = dev_get_drvdata(dev);
+
+ /*
+ * NOTE: Since timer controller registers settings are saved
+ * and restored back by the timer-prima2.c, so we need not
+ * update WD settings except refreshing timeout.
+ */
+ sirfsoc_wdt_updatetimeout(wdd);
+
+ return 0;
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(sirfsoc_wdt_pm_ops,
+ sirfsoc_wdt_suspend, sirfsoc_wdt_resume);
+
+static const struct of_device_id sirfsoc_wdt_of_match[] = {
+ { .compatible = "sirf,prima2-tick"},
+ {},
+};
+MODULE_DEVICE_TABLE(of, sirfsoc_wdt_of_match);
+
+static struct platform_driver sirfsoc_wdt_driver = {
+ .driver = {
+ .name = "sirfsoc-wdt",
+ .owner = THIS_MODULE,
+ .pm = &sirfsoc_wdt_pm_ops,
+ .of_match_table = sirfsoc_wdt_of_match,
+ },
+ .probe = sirfsoc_wdt_probe,
+ .remove = sirfsoc_wdt_remove,
+ .shutdown = sirfsoc_wdt_shutdown,
+};
+module_platform_driver(sirfsoc_wdt_driver);
+
+MODULE_DESCRIPTION("SiRF SoC watchdog driver");
+MODULE_AUTHOR("Xianglong Du <Xianglong.Du@csr.com>");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:sirfsoc-wdt");
diff --git a/drivers/watchdog/smsc37b787_wdt.c b/drivers/watchdog/smsc37b787_wdt.c
index 6d665f9c1d5..445ea1ad1fa 100644
--- a/drivers/watchdog/smsc37b787_wdt.c
+++ b/drivers/watchdog/smsc37b787_wdt.c
@@ -603,8 +603,6 @@ MODULE_DESCRIPTION("Driver for SMsC 37B787 watchdog component (Version "
VERSION ")");
MODULE_LICENSE("GPL");
-MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
-
#ifdef SMSC_SUPPORT_MINUTES
module_param(unit, int, 0);
MODULE_PARM_DESC(unit,
diff --git a/drivers/watchdog/softdog.c b/drivers/watchdog/softdog.c
index fe83beb8f1b..0dc5e323d59 100644
--- a/drivers/watchdog/softdog.c
+++ b/drivers/watchdog/softdog.c
@@ -42,7 +42,6 @@
#include <linux/moduleparam.h>
#include <linux/types.h>
#include <linux/timer.h>
-#include <linux/miscdevice.h>
#include <linux/watchdog.h>
#include <linux/notifier.h>
#include <linux/reboot.h>
@@ -63,7 +62,7 @@ MODULE_PARM_DESC(nowayout,
"Watchdog cannot be stopped once started (default="
__MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
-static int soft_noboot = 0;
+static int soft_noboot;
module_param(soft_noboot, int, 0);
MODULE_PARM_DESC(soft_noboot,
"Softdog action, set to 1 to ignore reboots, 0 to reboot (default=0)");
@@ -152,7 +151,6 @@ static struct watchdog_ops softdog_ops = {
.owner = THIS_MODULE,
.start = softdog_ping,
.stop = softdog_stop,
- .ping = softdog_ping,
.set_timeout = softdog_set_timeout,
};
@@ -208,4 +206,3 @@ module_exit(watchdog_exit);
MODULE_AUTHOR("Alan Cox");
MODULE_DESCRIPTION("Software Watchdog Device Driver");
MODULE_LICENSE("GPL");
-MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
diff --git a/drivers/watchdog/sp5100_tco.c b/drivers/watchdog/sp5100_tco.c
index 59108e48ada..5cca9cddb87 100644
--- a/drivers/watchdog/sp5100_tco.c
+++ b/drivers/watchdog/sp5100_tco.c
@@ -13,7 +13,9 @@
* as published by the Free Software Foundation; either version
* 2 of the License, or (at your option) any later version.
*
- * See AMD Publication 43009 "AMD SB700/710/750 Register Reference Guide"
+ * See AMD Publication 43009 "AMD SB700/710/750 Register Reference Guide",
+ * AMD Publication 45482 "AMD SB800-Series Southbridges Register
+ * Reference Guide"
*/
/*
@@ -38,12 +40,13 @@
#include "sp5100_tco.h"
/* Module and version information */
-#define TCO_VERSION "0.01"
+#define TCO_VERSION "0.05"
#define TCO_MODULE_NAME "SP5100 TCO timer"
#define TCO_DRIVER_NAME TCO_MODULE_NAME ", v" TCO_VERSION
/* internal variables */
static u32 tcobase_phys;
+static u32 tco_wdt_fired;
static void __iomem *tcobase;
static unsigned int pm_iobase;
static DEFINE_SPINLOCK(tco_lock); /* Guards the hardware */
@@ -64,7 +67,7 @@ MODULE_PARM_DESC(heartbeat, "Watchdog heartbeat in seconds. (default="
static bool nowayout = WATCHDOG_NOWAYOUT;
module_param(nowayout, bool, 0);
-MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started"
+MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started."
" (default=" __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
/*
@@ -122,6 +125,46 @@ static int tco_timer_set_heartbeat(int t)
return 0;
}
+static void tco_timer_enable(void)
+{
+ int val;
+
+ if (sp5100_tco_pci->revision >= 0x40) {
+ /* For SB800 or later */
+ /* Set the Watchdog timer resolution to 1 sec */
+ outb(SB800_PM_WATCHDOG_CONFIG, SB800_IO_PM_INDEX_REG);
+ val = inb(SB800_IO_PM_DATA_REG);
+ val |= SB800_PM_WATCHDOG_SECOND_RES;
+ outb(val, SB800_IO_PM_DATA_REG);
+
+ /* Enable watchdog decode bit and watchdog timer */
+ outb(SB800_PM_WATCHDOG_CONTROL, SB800_IO_PM_INDEX_REG);
+ val = inb(SB800_IO_PM_DATA_REG);
+ val |= SB800_PCI_WATCHDOG_DECODE_EN;
+ val &= ~SB800_PM_WATCHDOG_DISABLE;
+ outb(val, SB800_IO_PM_DATA_REG);
+ } else {
+ /* For SP5100 or SB7x0 */
+ /* Enable watchdog decode bit */
+ pci_read_config_dword(sp5100_tco_pci,
+ SP5100_PCI_WATCHDOG_MISC_REG,
+ &val);
+
+ val |= SP5100_PCI_WATCHDOG_DECODE_EN;
+
+ pci_write_config_dword(sp5100_tco_pci,
+ SP5100_PCI_WATCHDOG_MISC_REG,
+ val);
+
+ /* Enable Watchdog timer and set the resolution to 1 sec */
+ outb(SP5100_PM_WATCHDOG_CONTROL, SP5100_IO_PM_INDEX_REG);
+ val = inb(SP5100_IO_PM_DATA_REG);
+ val |= SP5100_PM_WATCHDOG_SECOND_RES;
+ val &= ~SP5100_PM_WATCHDOG_DISABLE;
+ outb(val, SP5100_IO_PM_DATA_REG);
+ }
+}
+
/*
* /dev/watchdog handling
*/
@@ -260,7 +303,7 @@ static struct miscdevice sp5100_tco_miscdev = {
* register a pci_driver, because someone else might
* want to register another driver on the same PCI id.
*/
-static DEFINE_PCI_DEVICE_TABLE(sp5100_tco_pci_tbl) = {
+static const struct pci_device_id sp5100_tco_pci_tbl[] = {
{ PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_SBX00_SMBUS, PCI_ANY_ID,
PCI_ANY_ID, },
{ 0, }, /* End of list */
@@ -270,11 +313,12 @@ MODULE_DEVICE_TABLE(pci, sp5100_tco_pci_tbl);
/*
* Init & exit routines
*/
-
-static unsigned char __devinit sp5100_tco_setupdevice(void)
+static unsigned char sp5100_tco_setupdevice(void)
{
struct pci_dev *dev = NULL;
+ const char *dev_name = NULL;
u32 val;
+ u32 index_reg, data_reg, base_addr;
/* Match the PCI device */
for_each_pci_dev(dev) {
@@ -287,57 +331,114 @@ static unsigned char __devinit sp5100_tco_setupdevice(void)
if (!sp5100_tco_pci)
return 0;
+ pr_info("PCI Revision ID: 0x%x\n", sp5100_tco_pci->revision);
+
+ /*
+ * Determine type of southbridge chipset.
+ */
+ if (sp5100_tco_pci->revision >= 0x40) {
+ dev_name = SB800_DEVNAME;
+ index_reg = SB800_IO_PM_INDEX_REG;
+ data_reg = SB800_IO_PM_DATA_REG;
+ base_addr = SB800_PM_WATCHDOG_BASE;
+ } else {
+ dev_name = SP5100_DEVNAME;
+ index_reg = SP5100_IO_PM_INDEX_REG;
+ data_reg = SP5100_IO_PM_DATA_REG;
+ base_addr = SP5100_PM_WATCHDOG_BASE;
+ }
+
/* Request the IO ports used by this driver */
pm_iobase = SP5100_IO_PM_INDEX_REG;
- if (!request_region(pm_iobase, SP5100_PM_IOPORTS_SIZE, "SP5100 TCO")) {
+ if (!request_region(pm_iobase, SP5100_PM_IOPORTS_SIZE, dev_name)) {
pr_err("I/O address 0x%04x already in use\n", pm_iobase);
goto exit;
}
- /* Find the watchdog base address. */
- outb(SP5100_PM_WATCHDOG_BASE3, SP5100_IO_PM_INDEX_REG);
- val = inb(SP5100_IO_PM_DATA_REG);
- outb(SP5100_PM_WATCHDOG_BASE2, SP5100_IO_PM_INDEX_REG);
- val = val << 8 | inb(SP5100_IO_PM_DATA_REG);
- outb(SP5100_PM_WATCHDOG_BASE1, SP5100_IO_PM_INDEX_REG);
- val = val << 8 | inb(SP5100_IO_PM_DATA_REG);
- outb(SP5100_PM_WATCHDOG_BASE0, SP5100_IO_PM_INDEX_REG);
- /* Low three bits of BASE0 are reserved. */
- val = val << 8 | (inb(SP5100_IO_PM_DATA_REG) & 0xf8);
-
- if (!request_mem_region_exclusive(val, SP5100_WDT_MEM_MAP_SIZE,
- "SP5100 TCO")) {
- pr_err("mmio address 0x%04x already in use\n", val);
- goto unreg_region;
+ /*
+ * First, Find the watchdog timer MMIO address from indirect I/O.
+ */
+ outb(base_addr+3, index_reg);
+ val = inb(data_reg);
+ outb(base_addr+2, index_reg);
+ val = val << 8 | inb(data_reg);
+ outb(base_addr+1, index_reg);
+ val = val << 8 | inb(data_reg);
+ outb(base_addr+0, index_reg);
+ /* Low three bits of BASE are reserved */
+ val = val << 8 | (inb(data_reg) & 0xf8);
+
+ pr_debug("Got 0x%04x from indirect I/O\n", val);
+
+ /* Check MMIO address conflict */
+ if (request_mem_region_exclusive(val, SP5100_WDT_MEM_MAP_SIZE,
+ dev_name))
+ goto setup_wdt;
+ else
+ pr_debug("MMIO address 0x%04x already in use\n", val);
+
+ /*
+ * Secondly, Find the watchdog timer MMIO address
+ * from SBResource_MMIO register.
+ */
+ if (sp5100_tco_pci->revision >= 0x40) {
+ /* Read SBResource_MMIO from AcpiMmioEn(PM_Reg: 24h) */
+ outb(SB800_PM_ACPI_MMIO_EN+3, SB800_IO_PM_INDEX_REG);
+ val = inb(SB800_IO_PM_DATA_REG);
+ outb(SB800_PM_ACPI_MMIO_EN+2, SB800_IO_PM_INDEX_REG);
+ val = val << 8 | inb(SB800_IO_PM_DATA_REG);
+ outb(SB800_PM_ACPI_MMIO_EN+1, SB800_IO_PM_INDEX_REG);
+ val = val << 8 | inb(SB800_IO_PM_DATA_REG);
+ outb(SB800_PM_ACPI_MMIO_EN+0, SB800_IO_PM_INDEX_REG);
+ val = val << 8 | inb(SB800_IO_PM_DATA_REG);
+ } else {
+ /* Read SBResource_MMIO from PCI config(PCI_Reg: 9Ch) */
+ pci_read_config_dword(sp5100_tco_pci,
+ SP5100_SB_RESOURCE_MMIO_BASE, &val);
}
+
+ /* The SBResource_MMIO is enabled and mapped memory space? */
+ if ((val & (SB800_ACPI_MMIO_DECODE_EN | SB800_ACPI_MMIO_SEL)) ==
+ SB800_ACPI_MMIO_DECODE_EN) {
+ /* Clear unnecessary the low twelve bits */
+ val &= ~0xFFF;
+ /* Add the Watchdog Timer offset to base address. */
+ val += SB800_PM_WDT_MMIO_OFFSET;
+ /* Check MMIO address conflict */
+ if (request_mem_region_exclusive(val, SP5100_WDT_MEM_MAP_SIZE,
+ dev_name)) {
+ pr_debug("Got 0x%04x from SBResource_MMIO register\n",
+ val);
+ goto setup_wdt;
+ } else
+ pr_debug("MMIO address 0x%04x already in use\n", val);
+ } else
+ pr_debug("SBResource_MMIO is disabled(0x%04x)\n", val);
+
+ pr_notice("failed to find MMIO address, giving up.\n");
+ goto unreg_region;
+
+setup_wdt:
tcobase_phys = val;
tcobase = ioremap(val, SP5100_WDT_MEM_MAP_SIZE);
- if (tcobase == 0) {
+ if (!tcobase) {
pr_err("failed to get tcobase address\n");
goto unreg_mem_region;
}
- /* Enable watchdog decode bit */
- pci_read_config_dword(sp5100_tco_pci,
- SP5100_PCI_WATCHDOG_MISC_REG,
- &val);
-
- val |= SP5100_PCI_WATCHDOG_DECODE_EN;
-
- pci_write_config_dword(sp5100_tco_pci,
- SP5100_PCI_WATCHDOG_MISC_REG,
- val);
+ pr_info("Using 0x%04x for watchdog MMIO address\n", val);
- /* Enable Watchdog timer and set the resolution to 1 sec. */
- outb(SP5100_PM_WATCHDOG_CONTROL, SP5100_IO_PM_INDEX_REG);
- val = inb(SP5100_IO_PM_DATA_REG);
- val |= SP5100_PM_WATCHDOG_SECOND_RES;
- val &= ~SP5100_PM_WATCHDOG_DISABLE;
- outb(val, SP5100_IO_PM_DATA_REG);
+ /* Setup the watchdog timer */
+ tco_timer_enable();
- /* Check that the watchdog action is set to reset the system. */
+ /* Check that the watchdog action is set to reset the system */
val = readl(SP5100_WDT_CONTROL(tcobase));
+ /*
+ * Save WatchDogFired status, because WatchDogFired flag is
+ * cleared here.
+ */
+ tco_wdt_fired = val & SP5100_PM_WATCHDOG_FIRED;
val &= ~SP5100_PM_WATCHDOG_ACTION_RESET;
writel(val, SP5100_WDT_CONTROL(tcobase));
@@ -361,26 +462,20 @@ exit:
return 0;
}
-static int __devinit sp5100_tco_init(struct platform_device *dev)
+static int sp5100_tco_init(struct platform_device *dev)
{
int ret;
- u32 val;
- /* Check whether or not the hardware watchdog is there. If found, then
+ /*
+ * Check whether or not the hardware watchdog is there. If found, then
* set it up.
*/
if (!sp5100_tco_setupdevice())
return -ENODEV;
/* Check to see if last reboot was due to watchdog timeout */
- pr_info("Watchdog reboot %sdetected\n",
- readl(SP5100_WDT_CONTROL(tcobase)) & SP5100_PM_WATCHDOG_FIRED ?
- "" : "not ");
-
- /* Clear out the old status */
- val = readl(SP5100_WDT_CONTROL(tcobase));
- val &= ~SP5100_PM_WATCHDOG_FIRED;
- writel(val, SP5100_WDT_CONTROL(tcobase));
+ pr_info("Last reboot was %striggered by watchdog.\n",
+ tco_wdt_fired ? "" : "not ");
/*
* Check that the heartbeat value is within it's range.
@@ -400,6 +495,7 @@ static int __devinit sp5100_tco_init(struct platform_device *dev)
clear_bit(0, &timer_alive);
+ /* Show module parameters */
pr_info("initialized (0x%p). heartbeat=%d sec (nowayout=%d)\n",
tcobase, heartbeat, nowayout);
@@ -412,7 +508,7 @@ exit:
return ret;
}
-static void __devexit sp5100_tco_cleanup(void)
+static void sp5100_tco_cleanup(void)
{
/* Stop the timer before we leave */
if (!nowayout)
@@ -425,7 +521,7 @@ static void __devexit sp5100_tco_cleanup(void)
release_region(pm_iobase, SP5100_PM_IOPORTS_SIZE);
}
-static int __devexit sp5100_tco_remove(struct platform_device *dev)
+static int sp5100_tco_remove(struct platform_device *dev)
{
if (tcobase)
sp5100_tco_cleanup();
@@ -439,7 +535,7 @@ static void sp5100_tco_shutdown(struct platform_device *dev)
static struct platform_driver sp5100_tco_driver = {
.probe = sp5100_tco_init,
- .remove = __devexit_p(sp5100_tco_remove),
+ .remove = sp5100_tco_remove,
.shutdown = sp5100_tco_shutdown,
.driver = {
.owner = THIS_MODULE,
@@ -451,7 +547,7 @@ static int __init sp5100_tco_init_module(void)
{
int err;
- pr_info("SP5100 TCO WatchDog Timer Driver v%s\n", TCO_VERSION);
+ pr_info("SP5100/SB800 TCO WatchDog Timer Driver v%s\n", TCO_VERSION);
err = platform_driver_register(&sp5100_tco_driver);
if (err)
@@ -475,13 +571,12 @@ static void __exit sp5100_tco_cleanup_module(void)
{
platform_device_unregister(sp5100_tco_platform_device);
platform_driver_unregister(&sp5100_tco_driver);
- pr_info("SP5100 TCO Watchdog Module Unloaded\n");
+ pr_info("SP5100/SB800 TCO Watchdog Module Unloaded\n");
}
module_init(sp5100_tco_init_module);
module_exit(sp5100_tco_cleanup_module);
MODULE_AUTHOR("Priyanka Gupta");
-MODULE_DESCRIPTION("TCO timer driver for SP5100 chipset");
+MODULE_DESCRIPTION("TCO timer driver for SP5100/SB800 chipset");
MODULE_LICENSE("GPL");
-MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
diff --git a/drivers/watchdog/sp5100_tco.h b/drivers/watchdog/sp5100_tco.h
index a5a16cc90a3..2b28c00da0d 100644
--- a/drivers/watchdog/sp5100_tco.h
+++ b/drivers/watchdog/sp5100_tco.h
@@ -9,33 +9,57 @@
/*
* Some address definitions for the Watchdog
*/
-
#define SP5100_WDT_MEM_MAP_SIZE 0x08
#define SP5100_WDT_CONTROL(base) ((base) + 0x00) /* Watchdog Control */
#define SP5100_WDT_COUNT(base) ((base) + 0x04) /* Watchdog Count */
-#define SP5100_WDT_START_STOP_BIT 1
+#define SP5100_WDT_START_STOP_BIT (1 << 0)
#define SP5100_WDT_TRIGGER_BIT (1 << 7)
-#define SP5100_PCI_WATCHDOG_MISC_REG 0x41
-#define SP5100_PCI_WATCHDOG_DECODE_EN (1 << 3)
-
#define SP5100_PM_IOPORTS_SIZE 0x02
-/* These two IO registers are hardcoded and there doesn't seem to be a way to
+/*
+ * These two IO registers are hardcoded and there doesn't seem to be a way to
* read them from a register.
*/
+
+/* For SP5100/SB7x0 chipset */
#define SP5100_IO_PM_INDEX_REG 0xCD6
#define SP5100_IO_PM_DATA_REG 0xCD7
+#define SP5100_SB_RESOURCE_MMIO_BASE 0x9C
+
#define SP5100_PM_WATCHDOG_CONTROL 0x69
-#define SP5100_PM_WATCHDOG_BASE0 0x6C
-#define SP5100_PM_WATCHDOG_BASE1 0x6D
-#define SP5100_PM_WATCHDOG_BASE2 0x6E
-#define SP5100_PM_WATCHDOG_BASE3 0x6F
+#define SP5100_PM_WATCHDOG_BASE 0x6C
#define SP5100_PM_WATCHDOG_FIRED (1 << 1)
#define SP5100_PM_WATCHDOG_ACTION_RESET (1 << 2)
-#define SP5100_PM_WATCHDOG_DISABLE 1
+#define SP5100_PCI_WATCHDOG_MISC_REG 0x41
+#define SP5100_PCI_WATCHDOG_DECODE_EN (1 << 3)
+
+#define SP5100_PM_WATCHDOG_DISABLE (1 << 0)
#define SP5100_PM_WATCHDOG_SECOND_RES (3 << 1)
+
+#define SP5100_DEVNAME "SP5100 TCO"
+
+
+/* For SB8x0(or later) chipset */
+#define SB800_IO_PM_INDEX_REG 0xCD6
+#define SB800_IO_PM_DATA_REG 0xCD7
+
+#define SB800_PM_ACPI_MMIO_EN 0x24
+#define SB800_PM_WATCHDOG_CONTROL 0x48
+#define SB800_PM_WATCHDOG_BASE 0x48
+#define SB800_PM_WATCHDOG_CONFIG 0x4C
+
+#define SB800_PCI_WATCHDOG_DECODE_EN (1 << 0)
+#define SB800_PM_WATCHDOG_DISABLE (1 << 2)
+#define SB800_PM_WATCHDOG_SECOND_RES (3 << 0)
+#define SB800_ACPI_MMIO_DECODE_EN (1 << 0)
+#define SB800_ACPI_MMIO_SEL (1 << 1)
+
+
+#define SB800_PM_WDT_MMIO_OFFSET 0xB00
+
+#define SB800_DEVNAME "SB800 TCO"
diff --git a/drivers/watchdog/sp805_wdt.c b/drivers/watchdog/sp805_wdt.c
index bbb170e5005..c1b03f4235b 100644
--- a/drivers/watchdog/sp805_wdt.c
+++ b/drivers/watchdog/sp805_wdt.c
@@ -4,7 +4,7 @@
* Watchdog driver for ARM SP805 watchdog module
*
* Copyright (C) 2010 ST Microelectronics
- * Viresh Kumar<viresh.kumar@st.com>
+ * Viresh Kumar <viresh.linux@gmail.com>
*
* This file is licensed under the terms of the GNU General Public
* License version 2 or later. This program is licensed "as is" without any
@@ -16,20 +16,16 @@
#include <linux/amba/bus.h>
#include <linux/bitops.h>
#include <linux/clk.h>
-#include <linux/fs.h>
-#include <linux/init.h>
#include <linux/io.h>
#include <linux/ioport.h>
#include <linux/kernel.h>
#include <linux/math64.h>
-#include <linux/miscdevice.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/pm.h>
#include <linux/slab.h>
#include <linux/spinlock.h>
#include <linux/types.h>
-#include <linux/uaccess.h>
#include <linux/watchdog.h>
/* default timeout in seconds */
@@ -56,33 +52,32 @@
/**
* struct sp805_wdt: sp805 wdt device structure
+ * @wdd: instance of struct watchdog_device
* @lock: spin lock protecting dev structure and io access
* @base: base address of wdt
* @clk: clock structure of wdt
* @adev: amba device structure of wdt
* @status: current status of wdt
* @load_val: load value to be set for current timeout
- * @timeout: current programmed timeout
*/
struct sp805_wdt {
+ struct watchdog_device wdd;
spinlock_t lock;
void __iomem *base;
struct clk *clk;
struct amba_device *adev;
- unsigned long status;
- #define WDT_BUSY 0
- #define WDT_CAN_BE_CLOSED 1
unsigned int load_val;
- unsigned int timeout;
};
-/* local variables */
-static struct sp805_wdt *wdt;
static bool nowayout = WATCHDOG_NOWAYOUT;
+module_param(nowayout, bool, 0);
+MODULE_PARM_DESC(nowayout,
+ "Set to 1 to keep watchdog running after device release");
/* This routine finds load value that will reset system in required timout */
-static void wdt_setload(unsigned int timeout)
+static int wdt_setload(struct watchdog_device *wdd, unsigned int timeout)
{
+ struct sp805_wdt *wdt = watchdog_get_drvdata(wdd);
u64 load, rate;
rate = clk_get_rate(wdt->clk);
@@ -101,13 +96,16 @@ static void wdt_setload(unsigned int timeout)
spin_lock(&wdt->lock);
wdt->load_val = load;
/* roundup timeout to closest positive integer value */
- wdt->timeout = div_u64((load + 1) * 2 + (rate / 2), rate);
+ wdd->timeout = div_u64((load + 1) * 2 + (rate / 2), rate);
spin_unlock(&wdt->lock);
+
+ return 0;
}
/* returns number of seconds left for reset to occur */
-static u32 wdt_timeleft(void)
+static unsigned int wdt_timeleft(struct watchdog_device *wdd)
{
+ struct sp805_wdt *wdt = watchdog_get_drvdata(wdd);
u64 load, rate;
rate = clk_get_rate(wdt->clk);
@@ -123,191 +121,102 @@ static u32 wdt_timeleft(void)
return div_u64(load, rate);
}
-/* enables watchdog timers reset */
-static void wdt_enable(void)
+static int wdt_config(struct watchdog_device *wdd, bool ping)
{
- spin_lock(&wdt->lock);
+ struct sp805_wdt *wdt = watchdog_get_drvdata(wdd);
+ int ret;
- writel_relaxed(UNLOCK, wdt->base + WDTLOCK);
- writel_relaxed(wdt->load_val, wdt->base + WDTLOAD);
- writel_relaxed(INT_MASK, wdt->base + WDTINTCLR);
- writel_relaxed(INT_ENABLE | RESET_ENABLE, wdt->base + WDTCONTROL);
- writel_relaxed(LOCK, wdt->base + WDTLOCK);
+ if (!ping) {
- /* Flush posted writes. */
- readl_relaxed(wdt->base + WDTLOCK);
- spin_unlock(&wdt->lock);
-}
+ ret = clk_prepare_enable(wdt->clk);
+ if (ret) {
+ dev_err(&wdt->adev->dev, "clock enable fail");
+ return ret;
+ }
+ }
-/* disables watchdog timers reset */
-static void wdt_disable(void)
-{
spin_lock(&wdt->lock);
writel_relaxed(UNLOCK, wdt->base + WDTLOCK);
- writel_relaxed(0, wdt->base + WDTCONTROL);
+ writel_relaxed(wdt->load_val, wdt->base + WDTLOAD);
+
+ if (!ping) {
+ writel_relaxed(INT_MASK, wdt->base + WDTINTCLR);
+ writel_relaxed(INT_ENABLE | RESET_ENABLE, wdt->base +
+ WDTCONTROL);
+ }
+
writel_relaxed(LOCK, wdt->base + WDTLOCK);
/* Flush posted writes. */
readl_relaxed(wdt->base + WDTLOCK);
spin_unlock(&wdt->lock);
+
+ return 0;
}
-static ssize_t sp805_wdt_write(struct file *file, const char *data,
- size_t len, loff_t *ppos)
+static int wdt_ping(struct watchdog_device *wdd)
{
- if (len) {
- if (!nowayout) {
- size_t i;
-
- clear_bit(WDT_CAN_BE_CLOSED, &wdt->status);
-
- for (i = 0; i != len; i++) {
- char c;
-
- if (get_user(c, data + i))
- return -EFAULT;
- /* Check for Magic Close character */
- if (c == 'V') {
- set_bit(WDT_CAN_BE_CLOSED,
- &wdt->status);
- break;
- }
- }
- }
- wdt_enable();
- }
- return len;
+ return wdt_config(wdd, true);
}
-static const struct watchdog_info ident = {
- .options = WDIOF_MAGICCLOSE | WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING,
- .identity = MODULE_NAME,
-};
-
-static long sp805_wdt_ioctl(struct file *file, unsigned int cmd,
- unsigned long arg)
+/* enables watchdog timers reset */
+static int wdt_enable(struct watchdog_device *wdd)
{
- int ret = -ENOTTY;
- unsigned int timeout;
-
- switch (cmd) {
- case WDIOC_GETSUPPORT:
- ret = copy_to_user((struct watchdog_info *)arg, &ident,
- sizeof(ident)) ? -EFAULT : 0;
- break;
-
- case WDIOC_GETSTATUS:
- ret = put_user(0, (int *)arg);
- break;
-
- case WDIOC_KEEPALIVE:
- wdt_enable();
- ret = 0;
- break;
-
- case WDIOC_SETTIMEOUT:
- ret = get_user(timeout, (unsigned int *)arg);
- if (ret)
- break;
-
- wdt_setload(timeout);
-
- wdt_enable();
- /* Fall through */
-
- case WDIOC_GETTIMEOUT:
- ret = put_user(wdt->timeout, (unsigned int *)arg);
- break;
- case WDIOC_GETTIMELEFT:
- ret = put_user(wdt_timeleft(), (unsigned int *)arg);
- break;
- }
- return ret;
+ return wdt_config(wdd, false);
}
-static int sp805_wdt_open(struct inode *inode, struct file *file)
+/* disables watchdog timers reset */
+static int wdt_disable(struct watchdog_device *wdd)
{
- int ret = 0;
-
- if (test_and_set_bit(WDT_BUSY, &wdt->status))
- return -EBUSY;
+ struct sp805_wdt *wdt = watchdog_get_drvdata(wdd);
- ret = clk_enable(wdt->clk);
- if (ret) {
- dev_err(&wdt->adev->dev, "clock enable fail");
- goto err;
- }
-
- wdt_enable();
-
- /* can not be closed, once enabled */
- clear_bit(WDT_CAN_BE_CLOSED, &wdt->status);
- return nonseekable_open(inode, file);
+ spin_lock(&wdt->lock);
-err:
- clear_bit(WDT_BUSY, &wdt->status);
- return ret;
-}
+ writel_relaxed(UNLOCK, wdt->base + WDTLOCK);
+ writel_relaxed(0, wdt->base + WDTCONTROL);
+ writel_relaxed(LOCK, wdt->base + WDTLOCK);
-static int sp805_wdt_release(struct inode *inode, struct file *file)
-{
- if (!test_bit(WDT_CAN_BE_CLOSED, &wdt->status)) {
- clear_bit(WDT_BUSY, &wdt->status);
- dev_warn(&wdt->adev->dev, "Device closed unexpectedly\n");
- return 0;
- }
+ /* Flush posted writes. */
+ readl_relaxed(wdt->base + WDTLOCK);
+ spin_unlock(&wdt->lock);
- wdt_disable();
- clk_disable(wdt->clk);
- clear_bit(WDT_BUSY, &wdt->status);
+ clk_disable_unprepare(wdt->clk);
return 0;
}
-static const struct file_operations sp805_wdt_fops = {
- .owner = THIS_MODULE,
- .llseek = no_llseek,
- .write = sp805_wdt_write,
- .unlocked_ioctl = sp805_wdt_ioctl,
- .open = sp805_wdt_open,
- .release = sp805_wdt_release,
+static const struct watchdog_info wdt_info = {
+ .options = WDIOF_MAGICCLOSE | WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING,
+ .identity = MODULE_NAME,
};
-static struct miscdevice sp805_wdt_miscdev = {
- .minor = WATCHDOG_MINOR,
- .name = "watchdog",
- .fops = &sp805_wdt_fops,
+static const struct watchdog_ops wdt_ops = {
+ .owner = THIS_MODULE,
+ .start = wdt_enable,
+ .stop = wdt_disable,
+ .ping = wdt_ping,
+ .set_timeout = wdt_setload,
+ .get_timeleft = wdt_timeleft,
};
-static int __devinit
+static int
sp805_wdt_probe(struct amba_device *adev, const struct amba_id *id)
{
+ struct sp805_wdt *wdt;
int ret = 0;
- if (!devm_request_mem_region(&adev->dev, adev->res.start,
- resource_size(&adev->res), "sp805_wdt")) {
- dev_warn(&adev->dev, "Failed to get memory region resource\n");
- ret = -ENOENT;
- goto err;
- }
-
wdt = devm_kzalloc(&adev->dev, sizeof(*wdt), GFP_KERNEL);
if (!wdt) {
- dev_warn(&adev->dev, "Kzalloc failed\n");
ret = -ENOMEM;
goto err;
}
- wdt->base = devm_ioremap(&adev->dev, adev->res.start,
- resource_size(&adev->res));
- if (!wdt->base) {
- ret = -ENOMEM;
- dev_warn(&adev->dev, "ioremap fail\n");
- goto err;
- }
+ wdt->base = devm_ioremap_resource(&adev->dev, &adev->res);
+ if (IS_ERR(wdt->base))
+ return PTR_ERR(wdt->base);
- wdt->clk = clk_get(&adev->dev, NULL);
+ wdt->clk = devm_clk_get(&adev->dev, NULL);
if (IS_ERR(wdt->clk)) {
dev_warn(&adev->dev, "Clock not found\n");
ret = PTR_ERR(wdt->clk);
@@ -315,60 +224,59 @@ sp805_wdt_probe(struct amba_device *adev, const struct amba_id *id)
}
wdt->adev = adev;
+ wdt->wdd.info = &wdt_info;
+ wdt->wdd.ops = &wdt_ops;
+
spin_lock_init(&wdt->lock);
- wdt_setload(DEFAULT_TIMEOUT);
+ watchdog_set_nowayout(&wdt->wdd, nowayout);
+ watchdog_set_drvdata(&wdt->wdd, wdt);
+ wdt_setload(&wdt->wdd, DEFAULT_TIMEOUT);
- ret = misc_register(&sp805_wdt_miscdev);
- if (ret < 0) {
- dev_warn(&adev->dev, "cannot register misc device\n");
- goto err_misc_register;
+ ret = watchdog_register_device(&wdt->wdd);
+ if (ret) {
+ dev_err(&adev->dev, "watchdog_register_device() failed: %d\n",
+ ret);
+ goto err;
}
+ amba_set_drvdata(adev, wdt);
dev_info(&adev->dev, "registration successful\n");
return 0;
-err_misc_register:
- clk_put(wdt->clk);
err:
dev_err(&adev->dev, "Probe Failed!!!\n");
return ret;
}
-static int __devexit sp805_wdt_remove(struct amba_device *adev)
+static int sp805_wdt_remove(struct amba_device *adev)
{
- misc_deregister(&sp805_wdt_miscdev);
- clk_put(wdt->clk);
+ struct sp805_wdt *wdt = amba_get_drvdata(adev);
+
+ watchdog_unregister_device(&wdt->wdd);
+ watchdog_set_drvdata(&wdt->wdd, NULL);
return 0;
}
-#ifdef CONFIG_PM
-static int sp805_wdt_suspend(struct device *dev)
+static int __maybe_unused sp805_wdt_suspend(struct device *dev)
{
- if (test_bit(WDT_BUSY, &wdt->status)) {
- wdt_disable();
- clk_disable(wdt->clk);
- }
+ struct sp805_wdt *wdt = dev_get_drvdata(dev);
+
+ if (watchdog_active(&wdt->wdd))
+ return wdt_disable(&wdt->wdd);
return 0;
}
-static int sp805_wdt_resume(struct device *dev)
+static int __maybe_unused sp805_wdt_resume(struct device *dev)
{
- int ret = 0;
+ struct sp805_wdt *wdt = dev_get_drvdata(dev);
- if (test_bit(WDT_BUSY, &wdt->status)) {
- ret = clk_enable(wdt->clk);
- if (ret) {
- dev_err(dev, "clock enable fail");
- return ret;
- }
- wdt_enable();
- }
+ if (watchdog_active(&wdt->wdd))
+ return wdt_enable(&wdt->wdd);
- return ret;
+ return 0;
}
-#endif /* CONFIG_PM */
static SIMPLE_DEV_PM_OPS(sp805_wdt_dev_pm_ops, sp805_wdt_suspend,
sp805_wdt_resume);
@@ -390,16 +298,11 @@ static struct amba_driver sp805_wdt_driver = {
},
.id_table = sp805_wdt_ids,
.probe = sp805_wdt_probe,
- .remove = __devexit_p(sp805_wdt_remove),
+ .remove = sp805_wdt_remove,
};
module_amba_driver(sp805_wdt_driver);
-module_param(nowayout, bool, 0);
-MODULE_PARM_DESC(nowayout,
- "Set to 1 to keep watchdog running after device release");
-
-MODULE_AUTHOR("Viresh Kumar <viresh.kumar@st.com>");
+MODULE_AUTHOR("Viresh Kumar <viresh.linux@gmail.com>");
MODULE_DESCRIPTION("ARM SP805 Watchdog Driver");
MODULE_LICENSE("GPL");
-MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
diff --git a/drivers/watchdog/stmp3xxx_rtc_wdt.c b/drivers/watchdog/stmp3xxx_rtc_wdt.c
new file mode 100644
index 00000000000..3804d5e9bae
--- /dev/null
+++ b/drivers/watchdog/stmp3xxx_rtc_wdt.c
@@ -0,0 +1,108 @@
+/*
+ * Watchdog driver for the RTC based watchdog in STMP3xxx and i.MX23/28
+ *
+ * Author: Wolfram Sang <w.sang@pengutronix.de>
+ *
+ * Copyright (C) 2011-12 Wolfram Sang, Pengutronix
+ *
+ * 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/kernel.h>
+#include <linux/module.h>
+#include <linux/watchdog.h>
+#include <linux/platform_device.h>
+#include <linux/stmp3xxx_rtc_wdt.h>
+
+#define WDOG_TICK_RATE 1000 /* 1 kHz clock */
+#define STMP3XXX_DEFAULT_TIMEOUT 19
+#define STMP3XXX_MAX_TIMEOUT (UINT_MAX / WDOG_TICK_RATE)
+
+static int heartbeat = STMP3XXX_DEFAULT_TIMEOUT;
+module_param(heartbeat, uint, 0);
+MODULE_PARM_DESC(heartbeat, "Watchdog heartbeat period in seconds from 1 to "
+ __MODULE_STRING(STMP3XXX_MAX_TIMEOUT) ", default "
+ __MODULE_STRING(STMP3XXX_DEFAULT_TIMEOUT));
+
+static int wdt_start(struct watchdog_device *wdd)
+{
+ struct device *dev = watchdog_get_drvdata(wdd);
+ struct stmp3xxx_wdt_pdata *pdata = dev_get_platdata(dev);
+
+ pdata->wdt_set_timeout(dev->parent, wdd->timeout * WDOG_TICK_RATE);
+ return 0;
+}
+
+static int wdt_stop(struct watchdog_device *wdd)
+{
+ struct device *dev = watchdog_get_drvdata(wdd);
+ struct stmp3xxx_wdt_pdata *pdata = dev_get_platdata(dev);
+
+ pdata->wdt_set_timeout(dev->parent, 0);
+ return 0;
+}
+
+static int wdt_set_timeout(struct watchdog_device *wdd, unsigned new_timeout)
+{
+ wdd->timeout = new_timeout;
+ return wdt_start(wdd);
+}
+
+static const struct watchdog_info stmp3xxx_wdt_ident = {
+ .options = WDIOF_MAGICCLOSE | WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING,
+ .identity = "STMP3XXX RTC Watchdog",
+};
+
+static const struct watchdog_ops stmp3xxx_wdt_ops = {
+ .owner = THIS_MODULE,
+ .start = wdt_start,
+ .stop = wdt_stop,
+ .set_timeout = wdt_set_timeout,
+};
+
+static struct watchdog_device stmp3xxx_wdd = {
+ .info = &stmp3xxx_wdt_ident,
+ .ops = &stmp3xxx_wdt_ops,
+ .min_timeout = 1,
+ .max_timeout = STMP3XXX_MAX_TIMEOUT,
+ .status = WATCHDOG_NOWAYOUT_INIT_STATUS,
+};
+
+static int stmp3xxx_wdt_probe(struct platform_device *pdev)
+{
+ int ret;
+
+ watchdog_set_drvdata(&stmp3xxx_wdd, &pdev->dev);
+
+ stmp3xxx_wdd.timeout = clamp_t(unsigned, heartbeat, 1, STMP3XXX_MAX_TIMEOUT);
+
+ ret = watchdog_register_device(&stmp3xxx_wdd);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "cannot register watchdog device\n");
+ return ret;
+ }
+
+ dev_info(&pdev->dev, "initialized watchdog with heartbeat %ds\n",
+ stmp3xxx_wdd.timeout);
+ return 0;
+}
+
+static int stmp3xxx_wdt_remove(struct platform_device *pdev)
+{
+ watchdog_unregister_device(&stmp3xxx_wdd);
+ return 0;
+}
+
+static struct platform_driver stmp3xxx_wdt_driver = {
+ .driver = {
+ .name = "stmp3xxx_rtc_wdt",
+ },
+ .probe = stmp3xxx_wdt_probe,
+ .remove = stmp3xxx_wdt_remove,
+};
+module_platform_driver(stmp3xxx_wdt_driver);
+
+MODULE_DESCRIPTION("STMP3XXX RTC Watchdog Driver");
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Wolfram Sang <w.sang@pengutronix.de>");
diff --git a/drivers/watchdog/stmp3xxx_wdt.c b/drivers/watchdog/stmp3xxx_wdt.c
deleted file mode 100644
index 21d96b92bfd..00000000000
--- a/drivers/watchdog/stmp3xxx_wdt.c
+++ /dev/null
@@ -1,288 +0,0 @@
-/*
- * Watchdog driver for Freescale STMP37XX/STMP378X
- *
- * Author: Vitaly Wool <vital@embeddedalley.com>
- *
- * Copyright 2008 Freescale Semiconductor, Inc. All Rights Reserved.
- * Copyright 2008 Embedded Alley Solutions, Inc All Rights Reserved.
- */
-
-#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
-
-#include <linux/init.h>
-#include <linux/kernel.h>
-#include <linux/fs.h>
-#include <linux/miscdevice.h>
-#include <linux/watchdog.h>
-#include <linux/platform_device.h>
-#include <linux/spinlock.h>
-#include <linux/uaccess.h>
-#include <linux/module.h>
-
-#include <mach/platform.h>
-#include <mach/regs-rtc.h>
-
-#define DEFAULT_HEARTBEAT 19
-#define MAX_HEARTBEAT (0x10000000 >> 6)
-
-/* missing bitmask in headers */
-#define BV_RTC_PERSISTENT1_GENERAL__RTC_FORCE_UPDATER 0x80000000
-
-#define WDT_IN_USE 0
-#define WDT_OK_TO_CLOSE 1
-
-#define WDOG_COUNTER_RATE 1000 /* 1 kHz clock */
-
-static DEFINE_SPINLOCK(stmp3xxx_wdt_io_lock);
-static unsigned long wdt_status;
-static const bool nowayout = WATCHDOG_NOWAYOUT;
-static int heartbeat = DEFAULT_HEARTBEAT;
-static unsigned long boot_status;
-
-static void wdt_enable(u32 value)
-{
- spin_lock(&stmp3xxx_wdt_io_lock);
- __raw_writel(value, REGS_RTC_BASE + HW_RTC_WATCHDOG);
- stmp3xxx_setl(BM_RTC_CTRL_WATCHDOGEN, REGS_RTC_BASE + HW_RTC_CTRL);
- stmp3xxx_setl(BV_RTC_PERSISTENT1_GENERAL__RTC_FORCE_UPDATER,
- REGS_RTC_BASE + HW_RTC_PERSISTENT1);
- spin_unlock(&stmp3xxx_wdt_io_lock);
-}
-
-static void wdt_disable(void)
-{
- spin_lock(&stmp3xxx_wdt_io_lock);
- stmp3xxx_clearl(BV_RTC_PERSISTENT1_GENERAL__RTC_FORCE_UPDATER,
- REGS_RTC_BASE + HW_RTC_PERSISTENT1);
- stmp3xxx_clearl(BM_RTC_CTRL_WATCHDOGEN, REGS_RTC_BASE + HW_RTC_CTRL);
- spin_unlock(&stmp3xxx_wdt_io_lock);
-}
-
-static void wdt_ping(void)
-{
- wdt_enable(heartbeat * WDOG_COUNTER_RATE);
-}
-
-static int stmp3xxx_wdt_open(struct inode *inode, struct file *file)
-{
- if (test_and_set_bit(WDT_IN_USE, &wdt_status))
- return -EBUSY;
-
- clear_bit(WDT_OK_TO_CLOSE, &wdt_status);
- wdt_ping();
-
- return nonseekable_open(inode, file);
-}
-
-static ssize_t stmp3xxx_wdt_write(struct file *file, const char __user *data,
- size_t len, loff_t *ppos)
-{
- if (len) {
- if (!nowayout) {
- size_t i;
-
- clear_bit(WDT_OK_TO_CLOSE, &wdt_status);
-
- for (i = 0; i != len; i++) {
- char c;
-
- if (get_user(c, data + i))
- return -EFAULT;
- if (c == 'V')
- set_bit(WDT_OK_TO_CLOSE, &wdt_status);
- }
- }
- wdt_ping();
- }
-
- return len;
-}
-
-static const struct watchdog_info ident = {
- .options = WDIOF_CARDRESET |
- WDIOF_MAGICCLOSE |
- WDIOF_SETTIMEOUT |
- WDIOF_KEEPALIVEPING,
- .identity = "STMP3XXX Watchdog",
-};
-
-static long stmp3xxx_wdt_ioctl(struct file *file, unsigned int cmd,
- unsigned long arg)
-{
- void __user *argp = (void __user *)arg;
- int __user *p = argp;
- int new_heartbeat, opts;
- int ret = -ENOTTY;
-
- switch (cmd) {
- case WDIOC_GETSUPPORT:
- ret = copy_to_user(argp, &ident, sizeof(ident)) ? -EFAULT : 0;
- break;
-
- case WDIOC_GETSTATUS:
- ret = put_user(0, p);
- break;
-
- case WDIOC_GETBOOTSTATUS:
- ret = put_user(boot_status, p);
- break;
-
- case WDIOC_SETOPTIONS:
- if (get_user(opts, p)) {
- ret = -EFAULT;
- break;
- }
- if (opts & WDIOS_DISABLECARD)
- wdt_disable();
- else if (opts & WDIOS_ENABLECARD)
- wdt_ping();
- else {
- pr_debug("%s: unknown option 0x%x\n", __func__, opts);
- ret = -EINVAL;
- break;
- }
- ret = 0;
- break;
-
- case WDIOC_KEEPALIVE:
- wdt_ping();
- ret = 0;
- break;
-
- case WDIOC_SETTIMEOUT:
- if (get_user(new_heartbeat, p)) {
- ret = -EFAULT;
- break;
- }
- if (new_heartbeat <= 0 || new_heartbeat > MAX_HEARTBEAT) {
- ret = -EINVAL;
- break;
- }
-
- heartbeat = new_heartbeat;
- wdt_ping();
- /* Fall through */
-
- case WDIOC_GETTIMEOUT:
- ret = put_user(heartbeat, p);
- break;
- }
- return ret;
-}
-
-static int stmp3xxx_wdt_release(struct inode *inode, struct file *file)
-{
- int ret = 0;
-
- if (!nowayout) {
- if (!test_bit(WDT_OK_TO_CLOSE, &wdt_status)) {
- wdt_ping();
- pr_debug("%s: Device closed unexpectedly\n", __func__);
- ret = -EINVAL;
- } else {
- wdt_disable();
- clear_bit(WDT_OK_TO_CLOSE, &wdt_status);
- }
- }
- clear_bit(WDT_IN_USE, &wdt_status);
-
- return ret;
-}
-
-static const struct file_operations stmp3xxx_wdt_fops = {
- .owner = THIS_MODULE,
- .llseek = no_llseek,
- .write = stmp3xxx_wdt_write,
- .unlocked_ioctl = stmp3xxx_wdt_ioctl,
- .open = stmp3xxx_wdt_open,
- .release = stmp3xxx_wdt_release,
-};
-
-static struct miscdevice stmp3xxx_wdt_miscdev = {
- .minor = WATCHDOG_MINOR,
- .name = "watchdog",
- .fops = &stmp3xxx_wdt_fops,
-};
-
-static int __devinit stmp3xxx_wdt_probe(struct platform_device *pdev)
-{
- int ret = 0;
-
- if (heartbeat < 1 || heartbeat > MAX_HEARTBEAT)
- heartbeat = DEFAULT_HEARTBEAT;
-
- boot_status = __raw_readl(REGS_RTC_BASE + HW_RTC_PERSISTENT1) &
- BV_RTC_PERSISTENT1_GENERAL__RTC_FORCE_UPDATER;
- boot_status = !!boot_status;
- stmp3xxx_clearl(BV_RTC_PERSISTENT1_GENERAL__RTC_FORCE_UPDATER,
- REGS_RTC_BASE + HW_RTC_PERSISTENT1);
- wdt_disable(); /* disable for now */
-
- ret = misc_register(&stmp3xxx_wdt_miscdev);
- if (ret < 0) {
- dev_err(&pdev->dev, "cannot register misc device\n");
- return ret;
- }
-
- pr_info("initialized, heartbeat %d sec\n", heartbeat);
-
- return ret;
-}
-
-static int __devexit stmp3xxx_wdt_remove(struct platform_device *pdev)
-{
- misc_deregister(&stmp3xxx_wdt_miscdev);
- return 0;
-}
-
-#ifdef CONFIG_PM
-static int wdt_suspended;
-static u32 wdt_saved_time;
-
-static int stmp3xxx_wdt_suspend(struct platform_device *pdev,
- pm_message_t state)
-{
- if (__raw_readl(REGS_RTC_BASE + HW_RTC_CTRL) &
- BM_RTC_CTRL_WATCHDOGEN) {
- wdt_suspended = 1;
- wdt_saved_time = __raw_readl(REGS_RTC_BASE + HW_RTC_WATCHDOG);
- wdt_disable();
- }
- return 0;
-}
-
-static int stmp3xxx_wdt_resume(struct platform_device *pdev)
-{
- if (wdt_suspended) {
- wdt_enable(wdt_saved_time);
- wdt_suspended = 0;
- }
- return 0;
-}
-#else
-#define stmp3xxx_wdt_suspend NULL
-#define stmp3xxx_wdt_resume NULL
-#endif
-
-static struct platform_driver platform_wdt_driver = {
- .driver = {
- .name = "stmp3xxx_wdt",
- },
- .probe = stmp3xxx_wdt_probe,
- .remove = __devexit_p(stmp3xxx_wdt_remove),
- .suspend = stmp3xxx_wdt_suspend,
- .resume = stmp3xxx_wdt_resume,
-};
-
-module_platform_driver(platform_wdt_driver);
-
-MODULE_DESCRIPTION("STMP3XXX Watchdog Driver");
-MODULE_LICENSE("GPL");
-
-module_param(heartbeat, int, 0);
-MODULE_PARM_DESC(heartbeat,
- "Watchdog heartbeat period in seconds from 1 to "
- __MODULE_STRING(MAX_HEARTBEAT) ", default "
- __MODULE_STRING(DEFAULT_HEARTBEAT));
-
-MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
diff --git a/drivers/watchdog/sunxi_wdt.c b/drivers/watchdog/sunxi_wdt.c
new file mode 100644
index 00000000000..693b9d2c6e3
--- /dev/null
+++ b/drivers/watchdog/sunxi_wdt.c
@@ -0,0 +1,237 @@
+/*
+ * sunxi Watchdog Driver
+ *
+ * Copyright (c) 2013 Carlo Caione
+ * 2012 Henrik Nordstrom
+ *
+ * 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.
+ *
+ * Based on xen_wdt.c
+ * (c) Copyright 2010 Novell, Inc.
+ */
+
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/types.h>
+#include <linux/watchdog.h>
+
+#define WDT_MAX_TIMEOUT 16
+#define WDT_MIN_TIMEOUT 1
+#define WDT_MODE_TIMEOUT(n) ((n) << 3)
+#define WDT_TIMEOUT_MASK WDT_MODE_TIMEOUT(0x0F)
+
+#define WDT_CTRL 0x00
+#define WDT_CTRL_RELOAD ((1 << 0) | (0x0a57 << 1))
+
+#define WDT_MODE 0x04
+#define WDT_MODE_EN (1 << 0)
+#define WDT_MODE_RST_EN (1 << 1)
+
+#define DRV_NAME "sunxi-wdt"
+#define DRV_VERSION "1.0"
+
+static bool nowayout = WATCHDOG_NOWAYOUT;
+static unsigned int timeout = WDT_MAX_TIMEOUT;
+
+struct sunxi_wdt_dev {
+ struct watchdog_device wdt_dev;
+ void __iomem *wdt_base;
+};
+
+/*
+ * wdt_timeout_map maps the watchdog timer interval value in seconds to
+ * the value of the register WDT_MODE bit 3:6
+ *
+ * [timeout seconds] = register value
+ *
+ */
+
+static const int wdt_timeout_map[] = {
+ [1] = 0x1, /* 1s */
+ [2] = 0x2, /* 2s */
+ [3] = 0x3, /* 3s */
+ [4] = 0x4, /* 4s */
+ [5] = 0x5, /* 5s */
+ [6] = 0x6, /* 6s */
+ [8] = 0x7, /* 8s */
+ [10] = 0x8, /* 10s */
+ [12] = 0x9, /* 12s */
+ [14] = 0xA, /* 14s */
+ [16] = 0xB, /* 16s */
+};
+
+static int sunxi_wdt_ping(struct watchdog_device *wdt_dev)
+{
+ struct sunxi_wdt_dev *sunxi_wdt = watchdog_get_drvdata(wdt_dev);
+ void __iomem *wdt_base = sunxi_wdt->wdt_base;
+
+ iowrite32(WDT_CTRL_RELOAD, wdt_base + WDT_CTRL);
+
+ return 0;
+}
+
+static int sunxi_wdt_set_timeout(struct watchdog_device *wdt_dev,
+ unsigned int timeout)
+{
+ struct sunxi_wdt_dev *sunxi_wdt = watchdog_get_drvdata(wdt_dev);
+ void __iomem *wdt_base = sunxi_wdt->wdt_base;
+ u32 reg;
+
+ if (wdt_timeout_map[timeout] == 0)
+ timeout++;
+
+ sunxi_wdt->wdt_dev.timeout = timeout;
+
+ reg = ioread32(wdt_base + WDT_MODE);
+ reg &= ~WDT_TIMEOUT_MASK;
+ reg |= WDT_MODE_TIMEOUT(wdt_timeout_map[timeout]);
+ iowrite32(reg, wdt_base + WDT_MODE);
+
+ sunxi_wdt_ping(wdt_dev);
+
+ return 0;
+}
+
+static int sunxi_wdt_stop(struct watchdog_device *wdt_dev)
+{
+ struct sunxi_wdt_dev *sunxi_wdt = watchdog_get_drvdata(wdt_dev);
+ void __iomem *wdt_base = sunxi_wdt->wdt_base;
+
+ iowrite32(0, wdt_base + WDT_MODE);
+
+ return 0;
+}
+
+static int sunxi_wdt_start(struct watchdog_device *wdt_dev)
+{
+ u32 reg;
+ struct sunxi_wdt_dev *sunxi_wdt = watchdog_get_drvdata(wdt_dev);
+ void __iomem *wdt_base = sunxi_wdt->wdt_base;
+ int ret;
+
+ ret = sunxi_wdt_set_timeout(&sunxi_wdt->wdt_dev,
+ sunxi_wdt->wdt_dev.timeout);
+ if (ret < 0)
+ return ret;
+
+ reg = ioread32(wdt_base + WDT_MODE);
+ reg |= (WDT_MODE_RST_EN | WDT_MODE_EN);
+ iowrite32(reg, wdt_base + WDT_MODE);
+
+ return 0;
+}
+
+static const struct watchdog_info sunxi_wdt_info = {
+ .identity = DRV_NAME,
+ .options = WDIOF_SETTIMEOUT |
+ WDIOF_KEEPALIVEPING |
+ WDIOF_MAGICCLOSE,
+};
+
+static const struct watchdog_ops sunxi_wdt_ops = {
+ .owner = THIS_MODULE,
+ .start = sunxi_wdt_start,
+ .stop = sunxi_wdt_stop,
+ .ping = sunxi_wdt_ping,
+ .set_timeout = sunxi_wdt_set_timeout,
+};
+
+static int sunxi_wdt_probe(struct platform_device *pdev)
+{
+ struct sunxi_wdt_dev *sunxi_wdt;
+ struct resource *res;
+ int err;
+
+ sunxi_wdt = devm_kzalloc(&pdev->dev, sizeof(*sunxi_wdt), GFP_KERNEL);
+ if (!sunxi_wdt)
+ return -EINVAL;
+
+ platform_set_drvdata(pdev, sunxi_wdt);
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ sunxi_wdt->wdt_base = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(sunxi_wdt->wdt_base))
+ return PTR_ERR(sunxi_wdt->wdt_base);
+
+ sunxi_wdt->wdt_dev.info = &sunxi_wdt_info;
+ sunxi_wdt->wdt_dev.ops = &sunxi_wdt_ops;
+ sunxi_wdt->wdt_dev.timeout = WDT_MAX_TIMEOUT;
+ sunxi_wdt->wdt_dev.max_timeout = WDT_MAX_TIMEOUT;
+ sunxi_wdt->wdt_dev.min_timeout = WDT_MIN_TIMEOUT;
+ sunxi_wdt->wdt_dev.parent = &pdev->dev;
+
+ watchdog_init_timeout(&sunxi_wdt->wdt_dev, timeout, &pdev->dev);
+ watchdog_set_nowayout(&sunxi_wdt->wdt_dev, nowayout);
+
+ watchdog_set_drvdata(&sunxi_wdt->wdt_dev, sunxi_wdt);
+
+ sunxi_wdt_stop(&sunxi_wdt->wdt_dev);
+
+ err = watchdog_register_device(&sunxi_wdt->wdt_dev);
+ if (unlikely(err))
+ return err;
+
+ dev_info(&pdev->dev, "Watchdog enabled (timeout=%d sec, nowayout=%d)",
+ sunxi_wdt->wdt_dev.timeout, nowayout);
+
+ 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 const struct of_device_id sunxi_wdt_dt_ids[] = {
+ { .compatible = "allwinner,sun4i-a10-wdt" },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, sunxi_wdt_dt_ids);
+
+static struct platform_driver sunxi_wdt_driver = {
+ .probe = sunxi_wdt_probe,
+ .remove = sunxi_wdt_remove,
+ .shutdown = sunxi_wdt_shutdown,
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = DRV_NAME,
+ .of_match_table = sunxi_wdt_dt_ids,
+ },
+};
+
+module_platform_driver(sunxi_wdt_driver);
+
+module_param(timeout, uint, 0);
+MODULE_PARM_DESC(timeout, "Watchdog heartbeat in seconds");
+
+module_param(nowayout, bool, 0);
+MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started "
+ "(default=" __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Carlo Caione <carlo.caione@gmail.com>");
+MODULE_AUTHOR("Henrik Nordstrom <henrik@henriknordstrom.net>");
+MODULE_DESCRIPTION("sunxi WatchDog Timer Driver");
+MODULE_VERSION(DRV_VERSION);
diff --git a/drivers/watchdog/tegra_wdt.c b/drivers/watchdog/tegra_wdt.c
new file mode 100644
index 00000000000..750e2a26cb1
--- /dev/null
+++ b/drivers/watchdog/tegra_wdt.c
@@ -0,0 +1,302 @@
+/*
+ * Copyright (c) 2014, NVIDIA CORPORATION. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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/kernel.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/watchdog.h>
+
+/* minimum and maximum watchdog trigger timeout, in seconds */
+#define MIN_WDT_TIMEOUT 1
+#define MAX_WDT_TIMEOUT 255
+
+/*
+ * Base of the WDT registers, from the timer base address. There are
+ * actually 5 watchdogs that can be configured (by pairing with an available
+ * timer), at bases 0x100 + (WDT ID) * 0x20, where WDT ID is 0 through 4.
+ * This driver only configures the first watchdog (WDT ID 0).
+ */
+#define WDT_BASE 0x100
+#define WDT_ID 0
+
+/*
+ * Register base of the timer that's selected for pairing with the watchdog.
+ * This driver arbitrarily uses timer 5, which is currently unused by
+ * other drivers (in particular, the Tegra clocksource driver). If this
+ * needs to change, take care that the new timer is not used by the
+ * clocksource driver.
+ */
+#define WDT_TIMER_BASE 0x60
+#define WDT_TIMER_ID 5
+
+/* WDT registers */
+#define WDT_CFG 0x0
+#define WDT_CFG_PERIOD_SHIFT 4
+#define WDT_CFG_PERIOD_MASK 0xff
+#define WDT_CFG_INT_EN (1 << 12)
+#define WDT_CFG_PMC2CAR_RST_EN (1 << 15)
+#define WDT_STS 0x4
+#define WDT_STS_COUNT_SHIFT 4
+#define WDT_STS_COUNT_MASK 0xff
+#define WDT_STS_EXP_SHIFT 12
+#define WDT_STS_EXP_MASK 0x3
+#define WDT_CMD 0x8
+#define WDT_CMD_START_COUNTER (1 << 0)
+#define WDT_CMD_DISABLE_COUNTER (1 << 1)
+#define WDT_UNLOCK (0xc)
+#define WDT_UNLOCK_PATTERN (0xc45a << 0)
+
+/* Timer registers */
+#define TIMER_PTV 0x0
+#define TIMER_EN (1 << 31)
+#define TIMER_PERIODIC (1 << 30)
+
+struct tegra_wdt {
+ struct watchdog_device wdd;
+ void __iomem *wdt_regs;
+ void __iomem *tmr_regs;
+};
+
+#define WDT_HEARTBEAT 120
+static int heartbeat = WDT_HEARTBEAT;
+module_param(heartbeat, int, 0);
+MODULE_PARM_DESC(heartbeat,
+ "Watchdog heartbeats in seconds. (default = "
+ __MODULE_STRING(WDT_HEARTBEAT) ")");
+
+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) ")");
+
+static int tegra_wdt_start(struct watchdog_device *wdd)
+{
+ struct tegra_wdt *wdt = watchdog_get_drvdata(wdd);
+ u32 val;
+
+ /*
+ * This thing has a fixed 1MHz clock. Normally, we would set the
+ * period to 1 second by writing 1000000ul, but the watchdog system
+ * reset actually occurs on the 4th expiration of this counter,
+ * so we set the period to 1/4 of this amount.
+ */
+ val = 1000000ul / 4;
+ val |= (TIMER_EN | TIMER_PERIODIC);
+ writel(val, wdt->tmr_regs + TIMER_PTV);
+
+ /*
+ * Set number of periods and start counter.
+ *
+ * Interrupt handler is not required for user space
+ * WDT accesses, since the caller is responsible to ping the
+ * WDT to reset the counter before expiration, through ioctls.
+ */
+ val = WDT_TIMER_ID |
+ (wdd->timeout << WDT_CFG_PERIOD_SHIFT) |
+ WDT_CFG_PMC2CAR_RST_EN;
+ writel(val, wdt->wdt_regs + WDT_CFG);
+
+ writel(WDT_CMD_START_COUNTER, wdt->wdt_regs + WDT_CMD);
+
+ return 0;
+}
+
+static int tegra_wdt_stop(struct watchdog_device *wdd)
+{
+ struct tegra_wdt *wdt = watchdog_get_drvdata(wdd);
+
+ writel(WDT_UNLOCK_PATTERN, wdt->wdt_regs + WDT_UNLOCK);
+ writel(WDT_CMD_DISABLE_COUNTER, wdt->wdt_regs + WDT_CMD);
+ writel(0, wdt->tmr_regs + TIMER_PTV);
+
+ return 0;
+}
+
+static int tegra_wdt_ping(struct watchdog_device *wdd)
+{
+ struct tegra_wdt *wdt = watchdog_get_drvdata(wdd);
+
+ writel(WDT_CMD_START_COUNTER, wdt->wdt_regs + WDT_CMD);
+
+ return 0;
+}
+
+static int tegra_wdt_set_timeout(struct watchdog_device *wdd,
+ unsigned int timeout)
+{
+ wdd->timeout = timeout;
+
+ if (watchdog_active(wdd))
+ return tegra_wdt_start(wdd);
+
+ return 0;
+}
+
+static unsigned int tegra_wdt_get_timeleft(struct watchdog_device *wdd)
+{
+ struct tegra_wdt *wdt = watchdog_get_drvdata(wdd);
+ u32 val;
+ int count;
+ int exp;
+
+ val = readl(wdt->wdt_regs + WDT_STS);
+
+ /* Current countdown (from timeout) */
+ count = (val >> WDT_STS_COUNT_SHIFT) & WDT_STS_COUNT_MASK;
+
+ /* Number of expirations (we are waiting for the 4th expiration) */
+ exp = (val >> WDT_STS_EXP_SHIFT) & WDT_STS_EXP_MASK;
+
+ /*
+ * The entire thing is divided by 4 because we are ticking down 4 times
+ * faster due to needing to wait for the 4th expiration.
+ */
+ return (((3 - exp) * wdd->timeout) + count) / 4;
+}
+
+static const struct watchdog_info tegra_wdt_info = {
+ .options = WDIOF_SETTIMEOUT |
+ WDIOF_MAGICCLOSE |
+ WDIOF_KEEPALIVEPING,
+ .firmware_version = 0,
+ .identity = "Tegra Watchdog",
+};
+
+static struct watchdog_ops tegra_wdt_ops = {
+ .owner = THIS_MODULE,
+ .start = tegra_wdt_start,
+ .stop = tegra_wdt_stop,
+ .ping = tegra_wdt_ping,
+ .set_timeout = tegra_wdt_set_timeout,
+ .get_timeleft = tegra_wdt_get_timeleft,
+};
+
+static int tegra_wdt_probe(struct platform_device *pdev)
+{
+ struct watchdog_device *wdd;
+ struct tegra_wdt *wdt;
+ struct resource *res;
+ void __iomem *regs;
+ int ret;
+
+ /* This is the timer base. */
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ regs = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(regs))
+ return PTR_ERR(regs);
+
+ /*
+ * Allocate our watchdog driver data, which has the
+ * struct watchdog_device nested within it.
+ */
+ wdt = devm_kzalloc(&pdev->dev, sizeof(*wdt), GFP_KERNEL);
+ if (!wdt)
+ return -ENOMEM;
+
+ /* Initialize struct tegra_wdt. */
+ wdt->wdt_regs = regs + WDT_BASE;
+ wdt->tmr_regs = regs + WDT_TIMER_BASE;
+
+ /* Initialize struct watchdog_device. */
+ wdd = &wdt->wdd;
+ wdd->timeout = heartbeat;
+ wdd->info = &tegra_wdt_info;
+ wdd->ops = &tegra_wdt_ops;
+ wdd->min_timeout = MIN_WDT_TIMEOUT;
+ wdd->max_timeout = MAX_WDT_TIMEOUT;
+
+ watchdog_set_drvdata(wdd, wdt);
+
+ watchdog_set_nowayout(wdd, nowayout);
+
+ ret = watchdog_register_device(wdd);
+ if (ret) {
+ dev_err(&pdev->dev,
+ "failed to register watchdog device\n");
+ return ret;
+ }
+
+ platform_set_drvdata(pdev, wdt);
+
+ dev_info(&pdev->dev,
+ "initialized (heartbeat = %d sec, nowayout = %d)\n",
+ heartbeat, nowayout);
+
+ return 0;
+}
+
+static int tegra_wdt_remove(struct platform_device *pdev)
+{
+ struct tegra_wdt *wdt = platform_get_drvdata(pdev);
+
+ tegra_wdt_stop(&wdt->wdd);
+
+ watchdog_unregister_device(&wdt->wdd);
+
+ dev_info(&pdev->dev, "removed wdt\n");
+
+ return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int tegra_wdt_runtime_suspend(struct device *dev)
+{
+ struct tegra_wdt *wdt = dev_get_drvdata(dev);
+
+ if (watchdog_active(&wdt->wdd))
+ tegra_wdt_stop(&wdt->wdd);
+
+ return 0;
+}
+
+static int tegra_wdt_runtime_resume(struct device *dev)
+{
+ struct tegra_wdt *wdt = dev_get_drvdata(dev);
+
+ if (watchdog_active(&wdt->wdd))
+ tegra_wdt_start(&wdt->wdd);
+
+ return 0;
+}
+#endif
+
+static const struct of_device_id tegra_wdt_of_match[] = {
+ { .compatible = "nvidia,tegra30-timer", },
+ { },
+};
+MODULE_DEVICE_TABLE(of, tegra_wdt_of_match);
+
+static const struct dev_pm_ops tegra_wdt_pm_ops = {
+ SET_SYSTEM_SLEEP_PM_OPS(tegra_wdt_runtime_suspend,
+ tegra_wdt_runtime_resume)
+};
+
+static struct platform_driver tegra_wdt_driver = {
+ .probe = tegra_wdt_probe,
+ .remove = tegra_wdt_remove,
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "tegra-wdt",
+ .pm = &tegra_wdt_pm_ops,
+ .of_match_table = tegra_wdt_of_match,
+ },
+};
+module_platform_driver(tegra_wdt_driver);
+
+MODULE_AUTHOR("NVIDIA Corporation");
+MODULE_DESCRIPTION("Tegra Watchdog Driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/watchdog/ts72xx_wdt.c b/drivers/watchdog/ts72xx_wdt.c
index 8df050d800e..afa9d6ef353 100644
--- a/drivers/watchdog/ts72xx_wdt.c
+++ b/drivers/watchdog/ts72xx_wdt.c
@@ -61,7 +61,7 @@ struct ts72xx_wdt {
struct platform_device *pdev;
};
-struct platform_device *ts72xx_wdt_pdev;
+static struct platform_device *ts72xx_wdt_pdev;
/*
* TS-72xx Watchdog supports following timeouts (value written
@@ -192,7 +192,7 @@ static int ts72xx_wdt_open(struct inode *inode, struct file *file)
dev_err(&wdt->pdev->dev,
"failed to convert timeout (%d) to register value\n",
timeout);
- return -EINVAL;
+ return regval;
}
if (mutex_lock_interruptible(&wdt->lock))
@@ -305,12 +305,14 @@ static long ts72xx_wdt_ioctl(struct file *file, unsigned int cmd,
switch (cmd) {
case WDIOC_GETSUPPORT:
- error = copy_to_user(argp, &winfo, sizeof(winfo));
+ if (copy_to_user(argp, &winfo, sizeof(winfo)))
+ error = -EFAULT;
break;
case WDIOC_GETSTATUS:
case WDIOC_GETBOOTSTATUS:
- return put_user(0, p);
+ error = put_user(0, p);
+ break;
case WDIOC_KEEPALIVE:
ts72xx_wdt_kick(wdt);
@@ -319,10 +321,9 @@ static long ts72xx_wdt_ioctl(struct file *file, unsigned int cmd,
case WDIOC_SETOPTIONS: {
int options;
- if (get_user(options, p)) {
- error = -EFAULT;
+ error = get_user(options, p);
+ if (error)
break;
- }
error = -EINVAL;
@@ -340,30 +341,26 @@ static long ts72xx_wdt_ioctl(struct file *file, unsigned int cmd,
case WDIOC_SETTIMEOUT: {
int new_timeout;
+ int regval;
- if (get_user(new_timeout, p)) {
- error = -EFAULT;
- } else {
- int regval;
-
- regval = timeout_to_regval(new_timeout);
- if (regval < 0) {
- error = -EINVAL;
- } else {
- ts72xx_wdt_stop(wdt);
- wdt->regval = regval;
- ts72xx_wdt_start(wdt);
- }
- }
+ 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:
- if (put_user(regval_to_timeout(wdt->regval), p))
- error = -EFAULT;
+ error = put_user(regval_to_timeout(wdt->regval), p);
break;
default:
@@ -390,59 +387,25 @@ static struct miscdevice ts72xx_wdt_miscdev = {
.fops = &ts72xx_wdt_fops,
};
-static __devinit int ts72xx_wdt_probe(struct platform_device *pdev)
+static int ts72xx_wdt_probe(struct platform_device *pdev)
{
struct ts72xx_wdt *wdt;
struct resource *r1, *r2;
int error = 0;
- wdt = kzalloc(sizeof(struct ts72xx_wdt), GFP_KERNEL);
- if (!wdt) {
- dev_err(&pdev->dev, "failed to allocate memory\n");
+ wdt = devm_kzalloc(&pdev->dev, sizeof(struct ts72xx_wdt), GFP_KERNEL);
+ if (!wdt)
return -ENOMEM;
- }
r1 = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (!r1) {
- dev_err(&pdev->dev, "failed to get memory resource\n");
- error = -ENODEV;
- goto fail;
- }
-
- r1 = request_mem_region(r1->start, resource_size(r1), pdev->name);
- if (!r1) {
- dev_err(&pdev->dev, "cannot request memory region\n");
- error = -EBUSY;
- goto fail;
- }
-
- wdt->control_reg = ioremap(r1->start, resource_size(r1));
- if (!wdt->control_reg) {
- dev_err(&pdev->dev, "failed to map memory\n");
- error = -ENODEV;
- goto fail_free_control;
- }
+ wdt->control_reg = devm_ioremap_resource(&pdev->dev, r1);
+ if (IS_ERR(wdt->control_reg))
+ return PTR_ERR(wdt->control_reg);
r2 = platform_get_resource(pdev, IORESOURCE_MEM, 1);
- if (!r2) {
- dev_err(&pdev->dev, "failed to get memory resource\n");
- error = -ENODEV;
- goto fail_unmap_control;
- }
-
- r2 = request_mem_region(r2->start, resource_size(r2), pdev->name);
- if (!r2) {
- dev_err(&pdev->dev, "cannot request memory region\n");
- error = -EBUSY;
- goto fail_unmap_control;
- }
-
- wdt->feed_reg = ioremap(r2->start, resource_size(r2));
- if (!wdt->feed_reg) {
- dev_err(&pdev->dev, "failed to map memory\n");
- error = -ENODEV;
- goto fail_free_feed;
- }
+ wdt->feed_reg = devm_ioremap_resource(&pdev->dev, r2);
+ if (IS_ERR(wdt->feed_reg))
+ return PTR_ERR(wdt->feed_reg);
platform_set_drvdata(pdev, wdt);
ts72xx_wdt_pdev = pdev;
@@ -455,51 +418,26 @@ static __devinit int ts72xx_wdt_probe(struct platform_device *pdev)
error = misc_register(&ts72xx_wdt_miscdev);
if (error) {
dev_err(&pdev->dev, "failed to register miscdev\n");
- goto fail_unmap_feed;
+ return error;
}
dev_info(&pdev->dev, "TS-72xx Watchdog driver\n");
return 0;
-
-fail_unmap_feed:
- platform_set_drvdata(pdev, NULL);
- iounmap(wdt->feed_reg);
-fail_free_feed:
- release_mem_region(r2->start, resource_size(r2));
-fail_unmap_control:
- iounmap(wdt->control_reg);
-fail_free_control:
- release_mem_region(r1->start, resource_size(r1));
-fail:
- kfree(wdt);
- return error;
}
-static __devexit int ts72xx_wdt_remove(struct platform_device *pdev)
+static int ts72xx_wdt_remove(struct platform_device *pdev)
{
- struct ts72xx_wdt *wdt = platform_get_drvdata(pdev);
- struct resource *res;
int error;
error = misc_deregister(&ts72xx_wdt_miscdev);
- platform_set_drvdata(pdev, NULL);
-
- iounmap(wdt->feed_reg);
- res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
- release_mem_region(res->start, resource_size(res));
-
- iounmap(wdt->control_reg);
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- release_mem_region(res->start, resource_size(res));
- kfree(wdt);
return error;
}
static struct platform_driver ts72xx_wdt_driver = {
.probe = ts72xx_wdt_probe,
- .remove = __devexit_p(ts72xx_wdt_remove),
+ .remove = ts72xx_wdt_remove,
.driver = {
.name = "ts72xx-wdt",
.owner = THIS_MODULE,
diff --git a/drivers/watchdog/twl4030_wdt.c b/drivers/watchdog/twl4030_wdt.c
index 249f11305d2..2d4535dc267 100644
--- a/drivers/watchdog/twl4030_wdt.c
+++ b/drivers/watchdog/twl4030_wdt.c
@@ -22,26 +22,12 @@
#include <linux/types.h>
#include <linux/slab.h>
#include <linux/kernel.h>
-#include <linux/fs.h>
#include <linux/watchdog.h>
#include <linux/platform_device.h>
-#include <linux/miscdevice.h>
-#include <linux/uaccess.h>
#include <linux/i2c/twl.h>
#define TWL4030_WATCHDOG_CFG_REG_OFFS 0x3
-#define TWL4030_WDT_STATE_OPEN 0x1
-#define TWL4030_WDT_STATE_ACTIVE 0x8
-
-static struct platform_device *twl4030_wdt_dev;
-
-struct twl4030_wdt {
- struct miscdevice miscdev;
- int timer_margin;
- unsigned long state;
-};
-
static bool nowayout = WATCHDOG_NOWAYOUT;
module_param(nowayout, bool, 0);
MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started "
@@ -49,175 +35,72 @@ MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started "
static int twl4030_wdt_write(unsigned char val)
{
- return twl_i2c_write_u8(TWL4030_MODULE_PM_RECEIVER, val,
+ return twl_i2c_write_u8(TWL_MODULE_PM_RECEIVER, val,
TWL4030_WATCHDOG_CFG_REG_OFFS);
}
-static int twl4030_wdt_enable(struct twl4030_wdt *wdt)
+static int twl4030_wdt_start(struct watchdog_device *wdt)
{
- return twl4030_wdt_write(wdt->timer_margin + 1);
+ return twl4030_wdt_write(wdt->timeout + 1);
}
-static int twl4030_wdt_disable(struct twl4030_wdt *wdt)
+static int twl4030_wdt_stop(struct watchdog_device *wdt)
{
return twl4030_wdt_write(0);
}
-static int twl4030_wdt_set_timeout(struct twl4030_wdt *wdt, int timeout)
-{
- if (timeout < 0 || timeout > 30) {
- dev_warn(wdt->miscdev.parent,
- "Timeout can only be in the range [0-30] seconds");
- return -EINVAL;
- }
- wdt->timer_margin = timeout;
- return twl4030_wdt_enable(wdt);
-}
-
-static ssize_t twl4030_wdt_write_fop(struct file *file,
- const char __user *data, size_t len, loff_t *ppos)
+static int twl4030_wdt_set_timeout(struct watchdog_device *wdt,
+ unsigned int timeout)
{
- struct twl4030_wdt *wdt = file->private_data;
-
- if (len)
- twl4030_wdt_enable(wdt);
-
- return len;
-}
-
-static long twl4030_wdt_ioctl(struct file *file,
- unsigned int cmd, unsigned long arg)
-{
- void __user *argp = (void __user *)arg;
- int __user *p = argp;
- int new_margin;
- struct twl4030_wdt *wdt = file->private_data;
-
- static const struct watchdog_info twl4030_wd_ident = {
- .identity = "TWL4030 Watchdog",
- .options = WDIOF_SETTIMEOUT,
- .firmware_version = 0,
- };
-
- switch (cmd) {
- case WDIOC_GETSUPPORT:
- return copy_to_user(argp, &twl4030_wd_ident,
- sizeof(twl4030_wd_ident)) ? -EFAULT : 0;
-
- case WDIOC_GETSTATUS:
- case WDIOC_GETBOOTSTATUS:
- return put_user(0, p);
-
- case WDIOC_KEEPALIVE:
- twl4030_wdt_enable(wdt);
- break;
-
- case WDIOC_SETTIMEOUT:
- if (get_user(new_margin, p))
- return -EFAULT;
- if (twl4030_wdt_set_timeout(wdt, new_margin))
- return -EINVAL;
- return put_user(wdt->timer_margin, p);
-
- case WDIOC_GETTIMEOUT:
- return put_user(wdt->timer_margin, p);
-
- default:
- return -ENOTTY;
- }
-
+ wdt->timeout = timeout;
return 0;
}
-static int twl4030_wdt_open(struct inode *inode, struct file *file)
-{
- struct twl4030_wdt *wdt = platform_get_drvdata(twl4030_wdt_dev);
-
- /* /dev/watchdog can only be opened once */
- if (test_and_set_bit(0, &wdt->state))
- return -EBUSY;
-
- wdt->state |= TWL4030_WDT_STATE_ACTIVE;
- file->private_data = (void *) wdt;
-
- twl4030_wdt_enable(wdt);
- return nonseekable_open(inode, file);
-}
-
-static int twl4030_wdt_release(struct inode *inode, struct file *file)
-{
- struct twl4030_wdt *wdt = file->private_data;
- if (nowayout) {
- dev_alert(wdt->miscdev.parent,
- "Unexpected close, watchdog still running!\n");
- twl4030_wdt_enable(wdt);
- } else {
- if (twl4030_wdt_disable(wdt))
- return -EFAULT;
- wdt->state &= ~TWL4030_WDT_STATE_ACTIVE;
- }
-
- clear_bit(0, &wdt->state);
- return 0;
-}
+static const struct watchdog_info twl4030_wdt_info = {
+ .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING,
+ .identity = "TWL4030 Watchdog",
+};
-static const struct file_operations twl4030_wdt_fops = {
+static const struct watchdog_ops twl4030_wdt_ops = {
.owner = THIS_MODULE,
- .llseek = no_llseek,
- .open = twl4030_wdt_open,
- .release = twl4030_wdt_release,
- .unlocked_ioctl = twl4030_wdt_ioctl,
- .write = twl4030_wdt_write_fop,
+ .start = twl4030_wdt_start,
+ .stop = twl4030_wdt_stop,
+ .set_timeout = twl4030_wdt_set_timeout,
};
-static int __devinit twl4030_wdt_probe(struct platform_device *pdev)
+static int twl4030_wdt_probe(struct platform_device *pdev)
{
int ret = 0;
- struct twl4030_wdt *wdt;
+ struct watchdog_device *wdt;
- wdt = kzalloc(sizeof(struct twl4030_wdt), GFP_KERNEL);
+ wdt = devm_kzalloc(&pdev->dev, sizeof(*wdt), GFP_KERNEL);
if (!wdt)
return -ENOMEM;
- wdt->state = 0;
- wdt->timer_margin = 30;
- wdt->miscdev.parent = &pdev->dev;
- wdt->miscdev.fops = &twl4030_wdt_fops;
- wdt->miscdev.minor = WATCHDOG_MINOR;
- wdt->miscdev.name = "watchdog";
+ wdt->info = &twl4030_wdt_info;
+ wdt->ops = &twl4030_wdt_ops;
+ wdt->status = 0;
+ wdt->timeout = 30;
+ wdt->min_timeout = 1;
+ wdt->max_timeout = 30;
+ watchdog_set_nowayout(wdt, nowayout);
platform_set_drvdata(pdev, wdt);
- twl4030_wdt_dev = pdev;
+ twl4030_wdt_stop(wdt);
- twl4030_wdt_disable(wdt);
-
- ret = misc_register(&wdt->miscdev);
- if (ret) {
- dev_err(wdt->miscdev.parent,
- "Failed to register misc device\n");
- platform_set_drvdata(pdev, NULL);
- kfree(wdt);
- twl4030_wdt_dev = NULL;
+ ret = watchdog_register_device(wdt);
+ if (ret)
return ret;
- }
+
return 0;
}
-static int __devexit twl4030_wdt_remove(struct platform_device *pdev)
+static int twl4030_wdt_remove(struct platform_device *pdev)
{
- struct twl4030_wdt *wdt = platform_get_drvdata(pdev);
+ struct watchdog_device *wdt = platform_get_drvdata(pdev);
- if (wdt->state & TWL4030_WDT_STATE_ACTIVE)
- if (twl4030_wdt_disable(wdt))
- return -EFAULT;
-
- wdt->state &= ~TWL4030_WDT_STATE_ACTIVE;
- misc_deregister(&wdt->miscdev);
-
- platform_set_drvdata(pdev, NULL);
- kfree(wdt);
- twl4030_wdt_dev = NULL;
+ watchdog_unregister_device(wdt);
return 0;
}
@@ -225,18 +108,18 @@ static int __devexit twl4030_wdt_remove(struct platform_device *pdev)
#ifdef CONFIG_PM
static int twl4030_wdt_suspend(struct platform_device *pdev, pm_message_t state)
{
- struct twl4030_wdt *wdt = platform_get_drvdata(pdev);
- if (wdt->state & TWL4030_WDT_STATE_ACTIVE)
- return twl4030_wdt_disable(wdt);
+ struct watchdog_device *wdt = platform_get_drvdata(pdev);
+ if (watchdog_active(wdt))
+ return twl4030_wdt_stop(wdt);
return 0;
}
static int twl4030_wdt_resume(struct platform_device *pdev)
{
- struct twl4030_wdt *wdt = platform_get_drvdata(pdev);
- if (wdt->state & TWL4030_WDT_STATE_ACTIVE)
- return twl4030_wdt_enable(wdt);
+ struct watchdog_device *wdt = platform_get_drvdata(pdev);
+ if (watchdog_active(wdt))
+ return twl4030_wdt_start(wdt);
return 0;
}
@@ -245,14 +128,21 @@ static int twl4030_wdt_resume(struct platform_device *pdev)
#define twl4030_wdt_resume NULL
#endif
+static const struct of_device_id twl_wdt_of_match[] = {
+ { .compatible = "ti,twl4030-wdt", },
+ { },
+};
+MODULE_DEVICE_TABLE(of, twl_wdt_of_match);
+
static struct platform_driver twl4030_wdt_driver = {
.probe = twl4030_wdt_probe,
- .remove = __devexit_p(twl4030_wdt_remove),
+ .remove = twl4030_wdt_remove,
.suspend = twl4030_wdt_suspend,
.resume = twl4030_wdt_resume,
.driver = {
- .owner = THIS_MODULE,
- .name = "twl4030_wdt",
+ .owner = THIS_MODULE,
+ .name = "twl4030_wdt",
+ .of_match_table = twl_wdt_of_match,
},
};
@@ -260,6 +150,5 @@ module_platform_driver(twl4030_wdt_driver);
MODULE_AUTHOR("Nokia Corporation");
MODULE_LICENSE("GPL");
-MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
MODULE_ALIAS("platform:twl4030_wdt");
diff --git a/drivers/watchdog/txx9wdt.c b/drivers/watchdog/txx9wdt.c
index 98e16373e64..6a447e321dd 100644
--- a/drivers/watchdog/txx9wdt.c
+++ b/drivers/watchdog/txx9wdt.c
@@ -13,7 +13,6 @@
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/types.h>
-#include <linux/miscdevice.h>
#include <linux/watchdog.h>
#include <linux/init.h>
#include <linux/platform_device.h>
@@ -121,9 +120,9 @@ static int __init txx9wdt_probe(struct platform_device *dev)
}
res = platform_get_resource(dev, IORESOURCE_MEM, 0);
- txx9wdt_reg = devm_request_and_ioremap(&dev->dev, res);
- if (!txx9wdt_reg) {
- ret = -EBUSY;
+ txx9wdt_reg = devm_ioremap_resource(&dev->dev, res);
+ if (IS_ERR(txx9wdt_reg)) {
+ ret = PTR_ERR(txx9wdt_reg);
goto exit;
}
@@ -172,20 +171,8 @@ static struct platform_driver txx9wdt_driver = {
},
};
-static int __init watchdog_init(void)
-{
- return platform_driver_probe(&txx9wdt_driver, txx9wdt_probe);
-}
-
-static void __exit watchdog_exit(void)
-{
- platform_driver_unregister(&txx9wdt_driver);
-}
-
-module_init(watchdog_init);
-module_exit(watchdog_exit);
+module_platform_driver_probe(txx9wdt_driver, txx9wdt_probe);
MODULE_DESCRIPTION("TXx9 Watchdog Driver");
MODULE_LICENSE("GPL");
-MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
MODULE_ALIAS("platform:txx9wdt");
diff --git a/drivers/watchdog/ux500_wdt.c b/drivers/watchdog/ux500_wdt.c
new file mode 100644
index 00000000000..5aed9d7ad47
--- /dev/null
+++ b/drivers/watchdog/ux500_wdt.c
@@ -0,0 +1,169 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2011-2013
+ *
+ * License Terms: GNU General Public License v2
+ *
+ * Author: Mathieu Poirier <mathieu.poirier@linaro.org> for ST-Ericsson
+ * Author: Jonas Aaberg <jonas.aberg@stericsson.com> for ST-Ericsson
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/moduleparam.h>
+#include <linux/err.h>
+#include <linux/uaccess.h>
+#include <linux/watchdog.h>
+#include <linux/platform_device.h>
+#include <linux/platform_data/ux500_wdt.h>
+
+#include <linux/mfd/dbx500-prcmu.h>
+
+#define WATCHDOG_TIMEOUT 600 /* 10 minutes */
+
+#define WATCHDOG_MIN 0
+#define WATCHDOG_MAX28 268435 /* 28 bit resolution in ms == 268435.455 s */
+#define WATCHDOG_MAX32 4294967 /* 32 bit resolution in ms == 4294967.295 s */
+
+static unsigned int timeout = WATCHDOG_TIMEOUT;
+module_param(timeout, uint, 0);
+MODULE_PARM_DESC(timeout,
+ "Watchdog timeout in seconds. default="
+ __MODULE_STRING(WATCHDOG_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) ")");
+
+static int ux500_wdt_start(struct watchdog_device *wdd)
+{
+ return prcmu_enable_a9wdog(PRCMU_WDOG_ALL);
+}
+
+static int ux500_wdt_stop(struct watchdog_device *wdd)
+{
+ return prcmu_disable_a9wdog(PRCMU_WDOG_ALL);
+}
+
+static int ux500_wdt_keepalive(struct watchdog_device *wdd)
+{
+ return prcmu_kick_a9wdog(PRCMU_WDOG_ALL);
+}
+
+static int ux500_wdt_set_timeout(struct watchdog_device *wdd,
+ unsigned int timeout)
+{
+ ux500_wdt_stop(wdd);
+ prcmu_load_a9wdog(PRCMU_WDOG_ALL, timeout * 1000);
+ ux500_wdt_start(wdd);
+
+ return 0;
+}
+
+static const struct watchdog_info ux500_wdt_info = {
+ .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE,
+ .identity = "Ux500 WDT",
+ .firmware_version = 1,
+};
+
+static const struct watchdog_ops ux500_wdt_ops = {
+ .owner = THIS_MODULE,
+ .start = ux500_wdt_start,
+ .stop = ux500_wdt_stop,
+ .ping = ux500_wdt_keepalive,
+ .set_timeout = ux500_wdt_set_timeout,
+};
+
+static struct watchdog_device ux500_wdt = {
+ .info = &ux500_wdt_info,
+ .ops = &ux500_wdt_ops,
+ .min_timeout = WATCHDOG_MIN,
+ .max_timeout = WATCHDOG_MAX32,
+};
+
+static int ux500_wdt_probe(struct platform_device *pdev)
+{
+ int ret;
+ struct ux500_wdt_data *pdata = dev_get_platdata(&pdev->dev);
+
+ if (pdata) {
+ if (pdata->timeout > 0)
+ timeout = pdata->timeout;
+ if (pdata->has_28_bits_resolution)
+ ux500_wdt.max_timeout = WATCHDOG_MAX28;
+ }
+
+ watchdog_set_nowayout(&ux500_wdt, nowayout);
+
+ /* disable auto off on sleep */
+ prcmu_config_a9wdog(PRCMU_WDOG_CPU1, false);
+
+ /* set HW initial value */
+ prcmu_load_a9wdog(PRCMU_WDOG_ALL, timeout * 1000);
+
+ ret = watchdog_register_device(&ux500_wdt);
+ if (ret)
+ return ret;
+
+ dev_info(&pdev->dev, "initialized\n");
+
+ return 0;
+}
+
+static int ux500_wdt_remove(struct platform_device *dev)
+{
+ watchdog_unregister_device(&ux500_wdt);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int ux500_wdt_suspend(struct platform_device *pdev,
+ pm_message_t state)
+{
+ if (watchdog_active(&ux500_wdt)) {
+ ux500_wdt_stop(&ux500_wdt);
+ prcmu_config_a9wdog(PRCMU_WDOG_CPU1, true);
+
+ prcmu_load_a9wdog(PRCMU_WDOG_ALL, timeout * 1000);
+ ux500_wdt_start(&ux500_wdt);
+ }
+ return 0;
+}
+
+static int ux500_wdt_resume(struct platform_device *pdev)
+{
+ if (watchdog_active(&ux500_wdt)) {
+ ux500_wdt_stop(&ux500_wdt);
+ prcmu_config_a9wdog(PRCMU_WDOG_CPU1, false);
+
+ prcmu_load_a9wdog(PRCMU_WDOG_ALL, timeout * 1000);
+ ux500_wdt_start(&ux500_wdt);
+ }
+ return 0;
+}
+#else
+#define ux500_wdt_suspend NULL
+#define ux500_wdt_resume NULL
+#endif
+
+static struct platform_driver ux500_wdt_driver = {
+ .probe = ux500_wdt_probe,
+ .remove = ux500_wdt_remove,
+ .suspend = ux500_wdt_suspend,
+ .resume = ux500_wdt_resume,
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "ux500_wdt",
+ },
+};
+
+module_platform_driver(ux500_wdt_driver);
+
+MODULE_AUTHOR("Jonas Aaberg <jonas.aberg@stericsson.com>");
+MODULE_DESCRIPTION("Ux500 Watchdog Driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:ux500_wdt");
diff --git a/drivers/watchdog/via_wdt.c b/drivers/watchdog/via_wdt.c
index 465e08273c9..56369c4f196 100644
--- a/drivers/watchdog/via_wdt.c
+++ b/drivers/watchdog/via_wdt.c
@@ -91,7 +91,7 @@ static inline void wdt_reset(void)
static void wdt_timer_tick(unsigned long data)
{
if (time_before(jiffies, next_heartbeat) ||
- (!test_bit(WDOG_ACTIVE, &wdt_dev.status))) {
+ (!watchdog_active(&wdt_dev))) {
wdt_reset();
mod_timer(&timer, jiffies + WDT_HEARTBEAT);
} else
@@ -155,7 +155,7 @@ static struct watchdog_device wdt_dev = {
.max_timeout = WDT_TIMEOUT_MAX,
};
-static int __devinit wdt_probe(struct pci_dev *pdev,
+static int wdt_probe(struct pci_dev *pdev,
const struct pci_device_id *ent)
{
unsigned char conf;
@@ -202,6 +202,9 @@ static int __devinit wdt_probe(struct pci_dev *pdev,
goto err_out_release;
}
+ if (timeout < 1 || timeout > WDT_TIMEOUT_MAX)
+ timeout = WDT_TIMEOUT;
+
wdt_dev.timeout = timeout;
watchdog_set_nowayout(&wdt_dev, nowayout);
if (readl(wdt_mem) & VIA_WDT_FIRED)
@@ -226,17 +229,17 @@ err_out_disable_device:
return ret;
}
-static void __devexit wdt_remove(struct pci_dev *pdev)
+static void wdt_remove(struct pci_dev *pdev)
{
watchdog_unregister_device(&wdt_dev);
- del_timer(&timer);
+ del_timer_sync(&timer);
iounmap(wdt_mem);
release_mem_region(mmio, VIA_WDT_MMIO_LEN);
release_resource(&wdt_res);
pci_disable_device(pdev);
}
-static DEFINE_PCI_DEVICE_TABLE(wdt_pci_table) = {
+static const struct pci_device_id wdt_pci_table[] = {
{ PCI_DEVICE(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_CX700) },
{ PCI_DEVICE(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_VX800) },
{ PCI_DEVICE(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_VX855) },
@@ -247,23 +250,10 @@ static struct pci_driver wdt_driver = {
.name = "via_wdt",
.id_table = wdt_pci_table,
.probe = wdt_probe,
- .remove = __devexit_p(wdt_remove),
+ .remove = wdt_remove,
};
-static int __init wdt_init(void)
-{
- if (timeout < 1 || timeout > WDT_TIMEOUT_MAX)
- timeout = WDT_TIMEOUT;
- return pci_register_driver(&wdt_driver);
-}
-
-static void __exit wdt_exit(void)
-{
- pci_unregister_driver(&wdt_driver);
-}
-
-module_init(wdt_init);
-module_exit(wdt_exit);
+module_pci_driver(wdt_driver);
MODULE_AUTHOR("Marc Vertes");
MODULE_DESCRIPTION("Driver for watchdog timer on VIA chipset");
diff --git a/drivers/watchdog/w83627hf_wdt.c b/drivers/watchdog/w83627hf_wdt.c
index 92f1326f0cf..7165704a3e3 100644
--- a/drivers/watchdog/w83627hf_wdt.c
+++ b/drivers/watchdog/w83627hf_wdt.c
@@ -1,6 +1,9 @@
/*
* w83627hf/thf WDT driver
*
+ * (c) Copyright 2013 Guenter Roeck
+ * converted to watchdog infrastructure
+ *
* (c) Copyright 2007 Vlad Drukker <vlad@storewiz.com>
* added support for W83627THF.
*
@@ -31,31 +34,25 @@
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/types.h>
-#include <linux/miscdevice.h>
#include <linux/watchdog.h>
-#include <linux/fs.h>
#include <linux/ioport.h>
#include <linux/notifier.h>
#include <linux/reboot.h>
#include <linux/init.h>
-#include <linux/spinlock.h>
#include <linux/io.h>
-#include <linux/uaccess.h>
-
#define WATCHDOG_NAME "w83627hf/thf/hg/dhg WDT"
#define WATCHDOG_TIMEOUT 60 /* 60 sec default timeout */
-static unsigned long wdt_is_open;
-static char expect_close;
-static DEFINE_SPINLOCK(io_lock);
+static int wdt_io;
+static int cr_wdt_timeout; /* WDT timeout register */
+static int cr_wdt_control; /* WDT control register */
-/* You must set this - there is no sane way to probe for this board. */
-static int wdt_io = 0x2E;
-module_param(wdt_io, int, 0);
-MODULE_PARM_DESC(wdt_io, "w83627hf/thf WDT io port (default 0x2E)");
+enum chips { w83627hf, w83627s, w83697hf, w83697ug, w83637hf, w83627thf,
+ w83687thf, w83627ehf, w83627dhg, w83627uhg, w83667hg, w83627dhg_p,
+ w83667hg_b, nct6775, nct6776, nct6779 };
-static int timeout = WATCHDOG_TIMEOUT; /* in seconds */
+static int timeout; /* in seconds */
module_param(timeout, int, 0);
MODULE_PARM_DESC(timeout,
"Watchdog timeout in seconds. 1 <= timeout <= 255, default="
@@ -67,6 +64,10 @@ MODULE_PARM_DESC(nowayout,
"Watchdog cannot be stopped once started (default="
__MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
+static int early_disable;
+module_param(early_disable, int, 0);
+MODULE_PARM_DESC(early_disable, "Disable watchdog at boot time (default=0)");
+
/*
* Kernel methods.
*/
@@ -76,236 +77,220 @@ MODULE_PARM_DESC(nowayout,
(same as EFER) */
#define WDT_EFDR (WDT_EFIR+1) /* Extended Function Data Register */
-static void w83627hf_select_wd_register(void)
+#define W83627HF_LD_WDT 0x08
+
+#define W83627HF_ID 0x52
+#define W83627S_ID 0x59
+#define W83697HF_ID 0x60
+#define W83697UG_ID 0x68
+#define W83637HF_ID 0x70
+#define W83627THF_ID 0x82
+#define W83687THF_ID 0x85
+#define W83627EHF_ID 0x88
+#define W83627DHG_ID 0xa0
+#define W83627UHG_ID 0xa2
+#define W83667HG_ID 0xa5
+#define W83627DHG_P_ID 0xb0
+#define W83667HG_B_ID 0xb3
+#define NCT6775_ID 0xb4
+#define NCT6776_ID 0xc3
+#define NCT6779_ID 0xc5
+
+#define W83627HF_WDT_TIMEOUT 0xf6
+#define W83697HF_WDT_TIMEOUT 0xf4
+
+#define W83627HF_WDT_CONTROL 0xf5
+#define W83697HF_WDT_CONTROL 0xf3
+
+static void superio_outb(int reg, int val)
+{
+ outb(reg, WDT_EFER);
+ outb(val, WDT_EFDR);
+}
+
+static inline int superio_inb(int reg)
+{
+ outb(reg, WDT_EFER);
+ return inb(WDT_EFDR);
+}
+
+static int superio_enter(void)
{
- unsigned char c;
+ if (!request_muxed_region(wdt_io, 2, WATCHDOG_NAME))
+ return -EBUSY;
+
outb_p(0x87, WDT_EFER); /* Enter extended function mode */
outb_p(0x87, WDT_EFER); /* Again according to manual */
- outb(0x20, WDT_EFER); /* check chip version */
- c = inb(WDT_EFDR);
- if (c == 0x82) { /* W83627THF */
- outb_p(0x2b, WDT_EFER); /* select GPIO3 */
- c = ((inb_p(WDT_EFDR) & 0xf7) | 0x04); /* select WDT0 */
- outb_p(0x2b, WDT_EFER);
- outb_p(c, WDT_EFDR); /* set GPIO3 to WDT0 */
- } else if (c == 0x88 || c == 0xa0) { /* W83627EHF / W83627DHG */
- outb_p(0x2d, WDT_EFER); /* select GPIO5 */
- c = inb_p(WDT_EFDR) & ~0x01; /* PIN77 -> WDT0# */
- outb_p(0x2d, WDT_EFER);
- outb_p(c, WDT_EFDR); /* set GPIO5 to WDT0 */
- }
+ return 0;
+}
- outb_p(0x07, WDT_EFER); /* point to logical device number reg */
- outb_p(0x08, WDT_EFDR); /* select logical device 8 (GPIO2) */
- outb_p(0x30, WDT_EFER); /* select CR30 */
- outb_p(0x01, WDT_EFDR); /* set bit 0 to activate GPIO2 */
+static void superio_select(int ld)
+{
+ superio_outb(0x07, ld);
}
-static void w83627hf_unselect_wd_register(void)
+static void superio_exit(void)
{
outb_p(0xAA, WDT_EFER); /* Leave extended function mode */
+ release_region(wdt_io, 2);
}
-/* tyan motherboards seem to set F5 to 0x4C ?
- * So explicitly init to appropriate value. */
-
-static void w83627hf_init(void)
+static int w83627hf_init(struct watchdog_device *wdog, enum chips chip)
{
+ int ret;
unsigned char t;
- w83627hf_select_wd_register();
+ ret = superio_enter();
+ if (ret)
+ return ret;
- outb_p(0xF6, WDT_EFER); /* Select CRF6 */
- t = inb_p(WDT_EFDR); /* read CRF6 */
- if (t != 0) {
- pr_info("Watchdog already running. Resetting timeout to %d sec\n",
- timeout);
- outb_p(timeout, WDT_EFDR); /* Write back to CRF6 */
- }
+ superio_select(W83627HF_LD_WDT);
- outb_p(0xF5, WDT_EFER); /* Select CRF5 */
- t = inb_p(WDT_EFDR); /* read CRF5 */
- t &= ~0x0C; /* set second mode & disable keyboard
- turning off watchdog */
- t |= 0x02; /* enable the WDTO# output low pulse
- to the KBRST# pin (PIN60) */
- outb_p(t, WDT_EFDR); /* Write back to CRF5 */
-
- outb_p(0xF7, WDT_EFER); /* Select CRF7 */
- t = inb_p(WDT_EFDR); /* read CRF7 */
- t &= ~0xC0; /* disable keyboard & mouse turning off
- watchdog */
- outb_p(t, WDT_EFDR); /* Write back to CRF7 */
-
- w83627hf_unselect_wd_register();
-}
+ /* set CR30 bit 0 to activate GPIO2 */
+ t = superio_inb(0x30);
+ if (!(t & 0x01))
+ superio_outb(0x30, t | 0x01);
-static void wdt_set_time(int timeout)
-{
- spin_lock(&io_lock);
+ switch (chip) {
+ case w83627hf:
+ case w83627s:
+ t = superio_inb(0x2B) & ~0x10;
+ superio_outb(0x2B, t); /* set GPIO24 to WDT0 */
+ break;
+ case w83697hf:
+ /* Set pin 119 to WDTO# mode (= CR29, WDT0) */
+ t = superio_inb(0x29) & ~0x60;
+ t |= 0x20;
+ superio_outb(0x29, t);
+ break;
+ case w83697ug:
+ /* Set pin 118 to WDTO# mode */
+ t = superio_inb(0x2b) & ~0x04;
+ superio_outb(0x2b, t);
+ break;
+ case w83627thf:
+ t = (superio_inb(0x2B) & ~0x08) | 0x04;
+ superio_outb(0x2B, t); /* set GPIO3 to WDT0 */
+ break;
+ case w83627dhg:
+ case w83627dhg_p:
+ t = superio_inb(0x2D) & ~0x01; /* PIN77 -> WDT0# */
+ superio_outb(0x2D, t); /* set GPIO5 to WDT0 */
+ t = superio_inb(cr_wdt_control);
+ t |= 0x02; /* enable the WDTO# output low pulse
+ * to the KBRST# pin */
+ superio_outb(cr_wdt_control, t);
+ break;
+ case w83637hf:
+ break;
+ case w83687thf:
+ t = superio_inb(0x2C) & ~0x80; /* PIN47 -> WDT0# */
+ superio_outb(0x2C, t);
+ break;
+ case w83627ehf:
+ case w83627uhg:
+ case w83667hg:
+ case w83667hg_b:
+ case nct6775:
+ case nct6776:
+ case nct6779:
+ /*
+ * These chips have a fixed WDTO# output pin (W83627UHG),
+ * or support more than one WDTO# output pin.
+ * Don't touch its configuration, and hope the BIOS
+ * does the right thing.
+ */
+ t = superio_inb(cr_wdt_control);
+ t |= 0x02; /* enable the WDTO# output low pulse
+ * to the KBRST# pin */
+ superio_outb(cr_wdt_control, t);
+ break;
+ default:
+ break;
+ }
- w83627hf_select_wd_register();
+ t = superio_inb(cr_wdt_timeout);
+ if (t != 0) {
+ if (early_disable) {
+ pr_warn("Stopping previously enabled watchdog until userland kicks in\n");
+ superio_outb(cr_wdt_timeout, 0);
+ } else {
+ pr_info("Watchdog already running. Resetting timeout to %d sec\n",
+ wdog->timeout);
+ superio_outb(cr_wdt_timeout, wdog->timeout);
+ }
+ }
- outb_p(0xF6, WDT_EFER); /* Select CRF6 */
- outb_p(timeout, WDT_EFDR); /* Write Timeout counter to CRF6 */
+ /* set second mode & disable keyboard turning off watchdog */
+ t = superio_inb(cr_wdt_control) & ~0x0C;
+ superio_outb(cr_wdt_control, t);
- w83627hf_unselect_wd_register();
+ /* reset trigger, disable keyboard & mouse turning off watchdog */
+ t = superio_inb(0xF7) & ~0xD0;
+ superio_outb(0xF7, t);
- spin_unlock(&io_lock);
-}
+ superio_exit();
-static int wdt_ping(void)
-{
- wdt_set_time(timeout);
return 0;
}
-static int wdt_disable(void)
+static int wdt_set_time(unsigned int timeout)
{
- wdt_set_time(0);
- return 0;
-}
+ int ret;
+
+ ret = superio_enter();
+ if (ret)
+ return ret;
+
+ superio_select(W83627HF_LD_WDT);
+ superio_outb(cr_wdt_timeout, timeout);
+ superio_exit();
-static int wdt_set_heartbeat(int t)
-{
- if (t < 1 || t > 255)
- return -EINVAL;
- timeout = t;
return 0;
}
-static int wdt_get_time(void)
+static int wdt_start(struct watchdog_device *wdog)
{
- int timeleft;
-
- spin_lock(&io_lock);
-
- w83627hf_select_wd_register();
-
- outb_p(0xF6, WDT_EFER); /* Select CRF6 */
- timeleft = inb_p(WDT_EFDR); /* Read Timeout counter to CRF6 */
-
- w83627hf_unselect_wd_register();
-
- spin_unlock(&io_lock);
-
- return timeleft;
+ return wdt_set_time(wdog->timeout);
}
-static ssize_t wdt_write(struct file *file, const char __user *buf,
- size_t count, loff_t *ppos)
+static int wdt_stop(struct watchdog_device *wdog)
{
- if (count) {
- if (!nowayout) {
- size_t i;
-
- expect_close = 0;
-
- for (i = 0; i != count; i++) {
- char c;
- if (get_user(c, buf + i))
- return -EFAULT;
- if (c == 'V')
- expect_close = 42;
- }
- }
- wdt_ping();
- }
- return count;
+ return wdt_set_time(0);
}
-static long wdt_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+static int wdt_set_timeout(struct watchdog_device *wdog, unsigned int timeout)
{
- void __user *argp = (void __user *)arg;
- int __user *p = argp;
- int timeval;
- static const struct watchdog_info ident = {
- .options = WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT |
- WDIOF_MAGICCLOSE,
- .firmware_version = 1,
- .identity = "W83627HF WDT",
- };
+ wdog->timeout = timeout;
- switch (cmd) {
- case WDIOC_GETSUPPORT:
- if (copy_to_user(argp, &ident, sizeof(ident)))
- return -EFAULT;
- break;
- case WDIOC_GETSTATUS:
- case WDIOC_GETBOOTSTATUS:
- return put_user(0, p);
- case WDIOC_SETOPTIONS:
- {
- int options, retval = -EINVAL;
-
- if (get_user(options, p))
- return -EFAULT;
- if (options & WDIOS_DISABLECARD) {
- wdt_disable();
- retval = 0;
- }
- if (options & WDIOS_ENABLECARD) {
- wdt_ping();
- retval = 0;
- }
- return retval;
- }
- case WDIOC_KEEPALIVE:
- wdt_ping();
- break;
- case WDIOC_SETTIMEOUT:
- if (get_user(timeval, p))
- return -EFAULT;
- if (wdt_set_heartbeat(timeval))
- return -EINVAL;
- wdt_ping();
- /* Fall */
- case WDIOC_GETTIMEOUT:
- return put_user(timeout, p);
- case WDIOC_GETTIMELEFT:
- timeval = wdt_get_time();
- return put_user(timeval, p);
- default:
- return -ENOTTY;
- }
return 0;
}
-static int wdt_open(struct inode *inode, struct file *file)
+static unsigned int wdt_get_time(struct watchdog_device *wdog)
{
- if (test_and_set_bit(0, &wdt_is_open))
- return -EBUSY;
- /*
- * Activate
- */
+ unsigned int timeleft;
+ int ret;
- wdt_ping();
- return nonseekable_open(inode, file);
-}
+ ret = superio_enter();
+ if (ret)
+ return 0;
-static int wdt_close(struct inode *inode, struct file *file)
-{
- if (expect_close == 42)
- wdt_disable();
- else {
- pr_crit("Unexpected close, not stopping watchdog!\n");
- wdt_ping();
- }
- expect_close = 0;
- clear_bit(0, &wdt_is_open);
- return 0;
+ superio_select(W83627HF_LD_WDT);
+ timeleft = superio_inb(cr_wdt_timeout);
+ superio_exit();
+
+ return timeleft;
}
/*
* Notifier for system down
*/
-
static int wdt_notify_sys(struct notifier_block *this, unsigned long code,
void *unused)
{
if (code == SYS_DOWN || code == SYS_HALT)
- wdt_disable(); /* Turn the WDT off */
+ wdt_set_time(0); /* Turn the WDT off */
return NOTIFY_DONE;
}
@@ -314,19 +299,25 @@ static int wdt_notify_sys(struct notifier_block *this, unsigned long code,
* Kernel Interfaces
*/
-static const struct file_operations wdt_fops = {
- .owner = THIS_MODULE,
- .llseek = no_llseek,
- .write = wdt_write,
- .unlocked_ioctl = wdt_ioctl,
- .open = wdt_open,
- .release = wdt_close,
+static struct watchdog_info wdt_info = {
+ .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE,
+ .identity = "W83627HF Watchdog",
+};
+
+static struct watchdog_ops wdt_ops = {
+ .owner = THIS_MODULE,
+ .start = wdt_start,
+ .stop = wdt_stop,
+ .set_timeout = wdt_set_timeout,
+ .get_timeleft = wdt_get_time,
};
-static struct miscdevice wdt_miscdev = {
- .minor = WATCHDOG_MINOR,
- .name = "watchdog",
- .fops = &wdt_fops,
+static struct watchdog_device wdt_dev = {
+ .info = &wdt_info,
+ .ops = &wdt_ops,
+ .timeout = WATCHDOG_TIMEOUT,
+ .min_timeout = 1,
+ .max_timeout = 255,
};
/*
@@ -338,56 +329,152 @@ static struct notifier_block wdt_notifier = {
.notifier_call = wdt_notify_sys,
};
-static int __init wdt_init(void)
+static int wdt_find(int addr)
{
+ u8 val;
int ret;
- pr_info("WDT driver for the Winbond(TM) W83627HF/THF/HG/DHG Super I/O chip initialising\n");
-
- if (wdt_set_heartbeat(timeout)) {
- wdt_set_heartbeat(WATCHDOG_TIMEOUT);
- pr_info("timeout value must be 1 <= timeout <= 255, using %d\n",
- WATCHDOG_TIMEOUT);
+ cr_wdt_timeout = W83627HF_WDT_TIMEOUT;
+ cr_wdt_control = W83627HF_WDT_CONTROL;
+
+ ret = superio_enter();
+ if (ret)
+ return ret;
+ superio_select(W83627HF_LD_WDT);
+ val = superio_inb(0x20);
+ switch (val) {
+ case W83627HF_ID:
+ ret = w83627hf;
+ break;
+ case W83627S_ID:
+ ret = w83627s;
+ break;
+ case W83697HF_ID:
+ ret = w83697hf;
+ cr_wdt_timeout = W83697HF_WDT_TIMEOUT;
+ cr_wdt_control = W83697HF_WDT_CONTROL;
+ break;
+ case W83697UG_ID:
+ ret = w83697ug;
+ cr_wdt_timeout = W83697HF_WDT_TIMEOUT;
+ cr_wdt_control = W83697HF_WDT_CONTROL;
+ break;
+ case W83637HF_ID:
+ ret = w83637hf;
+ break;
+ case W83627THF_ID:
+ ret = w83627thf;
+ break;
+ case W83687THF_ID:
+ ret = w83687thf;
+ break;
+ case W83627EHF_ID:
+ ret = w83627ehf;
+ break;
+ case W83627DHG_ID:
+ ret = w83627dhg;
+ break;
+ case W83627DHG_P_ID:
+ ret = w83627dhg_p;
+ break;
+ case W83627UHG_ID:
+ ret = w83627uhg;
+ break;
+ case W83667HG_ID:
+ ret = w83667hg;
+ break;
+ case W83667HG_B_ID:
+ ret = w83667hg_b;
+ break;
+ case NCT6775_ID:
+ ret = nct6775;
+ break;
+ case NCT6776_ID:
+ ret = nct6776;
+ break;
+ case NCT6779_ID:
+ ret = nct6779;
+ break;
+ case 0xff:
+ ret = -ENODEV;
+ break;
+ default:
+ ret = -ENODEV;
+ pr_err("Unsupported chip ID: 0x%02x\n", val);
+ break;
}
+ superio_exit();
+ return ret;
+}
- if (!request_region(wdt_io, 1, WATCHDOG_NAME)) {
- pr_err("I/O address 0x%04x already in use\n", wdt_io);
- ret = -EIO;
- goto out;
+static int __init wdt_init(void)
+{
+ int ret;
+ int chip;
+ const char * const chip_name[] = {
+ "W83627HF",
+ "W83627S",
+ "W83697HF",
+ "W83697UG",
+ "W83637HF",
+ "W83627THF",
+ "W83687THF",
+ "W83627EHF",
+ "W83627DHG",
+ "W83627UHG",
+ "W83667HG",
+ "W83667DHG-P",
+ "W83667HG-B",
+ "NCT6775",
+ "NCT6776",
+ "NCT6779",
+ };
+
+ wdt_io = 0x2e;
+ chip = wdt_find(0x2e);
+ if (chip < 0) {
+ wdt_io = 0x4e;
+ chip = wdt_find(0x4e);
+ if (chip < 0)
+ return chip;
}
- w83627hf_init();
+ pr_info("WDT driver for %s Super I/O chip initialising\n",
+ chip_name[chip]);
+
+ watchdog_init_timeout(&wdt_dev, timeout, NULL);
+ watchdog_set_nowayout(&wdt_dev, nowayout);
+
+ ret = w83627hf_init(&wdt_dev, chip);
+ if (ret) {
+ pr_err("failed to initialize watchdog (err=%d)\n", ret);
+ return ret;
+ }
ret = register_reboot_notifier(&wdt_notifier);
if (ret != 0) {
pr_err("cannot register reboot notifier (err=%d)\n", ret);
- goto unreg_regions;
+ return ret;
}
- ret = misc_register(&wdt_miscdev);
- if (ret != 0) {
- pr_err("cannot register miscdev on minor=%d (err=%d)\n",
- WATCHDOG_MINOR, ret);
+ ret = watchdog_register_device(&wdt_dev);
+ if (ret)
goto unreg_reboot;
- }
pr_info("initialized. timeout=%d sec (nowayout=%d)\n",
- timeout, nowayout);
+ wdt_dev.timeout, nowayout);
-out:
return ret;
+
unreg_reboot:
unregister_reboot_notifier(&wdt_notifier);
-unreg_regions:
- release_region(wdt_io, 1);
- goto out;
+ return ret;
}
static void __exit wdt_exit(void)
{
- misc_deregister(&wdt_miscdev);
+ watchdog_unregister_device(&wdt_dev);
unregister_reboot_notifier(&wdt_notifier);
- release_region(wdt_io, 1);
}
module_init(wdt_init);
@@ -396,4 +483,3 @@ module_exit(wdt_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Pádraig Brady <P@draigBrady.com>");
MODULE_DESCRIPTION("w83627hf/thf WDT driver");
-MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
diff --git a/drivers/watchdog/w83697hf_wdt.c b/drivers/watchdog/w83697hf_wdt.c
deleted file mode 100644
index cd9f3c1e1af..00000000000
--- a/drivers/watchdog/w83697hf_wdt.c
+++ /dev/null
@@ -1,461 +0,0 @@
-/*
- * w83697hf/hg WDT driver
- *
- * (c) Copyright 2006 Samuel Tardieu <sam@rfc1149.net>
- * (c) Copyright 2006 Marcus Junker <junker@anduras.de>
- *
- * Based on w83627hf_wdt.c which is based on advantechwdt.c
- * which is based on wdt.c.
- * Original copyright messages:
- *
- * (c) Copyright 2003 Pádraig Brady <P@draigBrady.com>
- *
- * (c) Copyright 2000-2001 Marek Michalkiewicz <marekm@linux.org.pl>
- *
- * (c) Copyright 1996 Alan Cox <alan@lxorguk.ukuu.org.uk>,
- * All Rights Reserved.
- *
- * 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.
- *
- * Neither Marcus Junker nor ANDURAS AG admit liability nor provide
- * warranty for any of this software. This material is provided
- * "AS-IS" and at no charge.
- */
-
-#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
-
-#include <linux/module.h>
-#include <linux/moduleparam.h>
-#include <linux/types.h>
-#include <linux/miscdevice.h>
-#include <linux/watchdog.h>
-#include <linux/fs.h>
-#include <linux/ioport.h>
-#include <linux/notifier.h>
-#include <linux/reboot.h>
-#include <linux/init.h>
-#include <linux/spinlock.h>
-#include <linux/io.h>
-#include <linux/uaccess.h>
-
-
-#define WATCHDOG_NAME "w83697hf/hg WDT"
-#define WATCHDOG_TIMEOUT 60 /* 60 sec default timeout */
-#define WATCHDOG_EARLY_DISABLE 1 /* Disable until userland kicks in */
-
-static unsigned long wdt_is_open;
-static char expect_close;
-static DEFINE_SPINLOCK(io_lock);
-
-/* You must set this - there is no sane way to probe for this board. */
-static int wdt_io = 0x2e;
-module_param(wdt_io, int, 0);
-MODULE_PARM_DESC(wdt_io,
- "w83697hf/hg WDT io port (default 0x2e, 0 = autodetect)");
-
-static int timeout = WATCHDOG_TIMEOUT; /* in seconds */
-module_param(timeout, int, 0);
-MODULE_PARM_DESC(timeout,
- "Watchdog timeout in seconds. 1<= timeout <=255 (default="
- __MODULE_STRING(WATCHDOG_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) ")");
-
-static int early_disable = WATCHDOG_EARLY_DISABLE;
-module_param(early_disable, int, 0);
-MODULE_PARM_DESC(early_disable,
- "Watchdog gets disabled at boot time (default="
- __MODULE_STRING(WATCHDOG_EARLY_DISABLE) ")");
-
-/*
- * Kernel methods.
- */
-
-#define W83697HF_EFER (wdt_io + 0) /* Extended Function Enable Register */
-#define W83697HF_EFIR (wdt_io + 0) /* Extended Function Index Register
- (same as EFER) */
-#define W83697HF_EFDR (wdt_io + 1) /* Extended Function Data Register */
-
-static inline void w83697hf_unlock(void)
-{
- outb_p(0x87, W83697HF_EFER); /* Enter extended function mode */
- outb_p(0x87, W83697HF_EFER); /* Again according to manual */
-}
-
-static inline void w83697hf_lock(void)
-{
- outb_p(0xAA, W83697HF_EFER); /* Leave extended function mode */
-}
-
-/*
- * The three functions w83697hf_get_reg(), w83697hf_set_reg() and
- * w83697hf_write_timeout() must be called with the device unlocked.
- */
-
-static unsigned char w83697hf_get_reg(unsigned char reg)
-{
- outb_p(reg, W83697HF_EFIR);
- return inb_p(W83697HF_EFDR);
-}
-
-static void w83697hf_set_reg(unsigned char reg, unsigned char data)
-{
- outb_p(reg, W83697HF_EFIR);
- outb_p(data, W83697HF_EFDR);
-}
-
-static void w83697hf_write_timeout(int timeout)
-{
- /* Write Timeout counter to CRF4 */
- w83697hf_set_reg(0xF4, timeout);
-}
-
-static void w83697hf_select_wdt(void)
-{
- w83697hf_unlock();
- w83697hf_set_reg(0x07, 0x08); /* Switch to logic device 8 (GPIO2) */
-}
-
-static inline void w83697hf_deselect_wdt(void)
-{
- w83697hf_lock();
-}
-
-static void w83697hf_init(void)
-{
- unsigned char bbuf;
-
- w83697hf_select_wdt();
-
- bbuf = w83697hf_get_reg(0x29);
- bbuf &= ~0x60;
- bbuf |= 0x20;
-
- /* Set pin 119 to WDTO# mode (= CR29, WDT0) */
- w83697hf_set_reg(0x29, bbuf);
-
- bbuf = w83697hf_get_reg(0xF3);
- bbuf &= ~0x04;
- w83697hf_set_reg(0xF3, bbuf); /* Count mode is seconds */
-
- w83697hf_deselect_wdt();
-}
-
-static void wdt_ping(void)
-{
- spin_lock(&io_lock);
- w83697hf_select_wdt();
-
- w83697hf_write_timeout(timeout);
-
- w83697hf_deselect_wdt();
- spin_unlock(&io_lock);
-}
-
-static void wdt_enable(void)
-{
- spin_lock(&io_lock);
- w83697hf_select_wdt();
-
- w83697hf_write_timeout(timeout);
- w83697hf_set_reg(0x30, 1); /* Enable timer */
-
- w83697hf_deselect_wdt();
- spin_unlock(&io_lock);
-}
-
-static void wdt_disable(void)
-{
- spin_lock(&io_lock);
- w83697hf_select_wdt();
-
- w83697hf_set_reg(0x30, 0); /* Disable timer */
- w83697hf_write_timeout(0);
-
- w83697hf_deselect_wdt();
- spin_unlock(&io_lock);
-}
-
-static unsigned char wdt_running(void)
-{
- unsigned char t;
-
- spin_lock(&io_lock);
- w83697hf_select_wdt();
-
- t = w83697hf_get_reg(0xF4); /* Read timer */
-
- w83697hf_deselect_wdt();
- spin_unlock(&io_lock);
-
- return t;
-}
-
-static int wdt_set_heartbeat(int t)
-{
- if (t < 1 || t > 255)
- return -EINVAL;
-
- timeout = t;
- return 0;
-}
-
-static ssize_t wdt_write(struct file *file, const char __user *buf,
- size_t count, loff_t *ppos)
-{
- if (count) {
- if (!nowayout) {
- size_t i;
-
- expect_close = 0;
-
- for (i = 0; i != count; i++) {
- char c;
- if (get_user(c, buf + i))
- return -EFAULT;
- if (c == 'V')
- expect_close = 42;
- }
- }
- wdt_ping();
- }
- return count;
-}
-
-static long wdt_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
-{
- void __user *argp = (void __user *)arg;
- int __user *p = argp;
- int new_timeout;
- static const struct watchdog_info ident = {
- .options = WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT
- | WDIOF_MAGICCLOSE,
- .firmware_version = 1,
- .identity = "W83697HF WDT",
- };
-
- switch (cmd) {
- case WDIOC_GETSUPPORT:
- if (copy_to_user(argp, &ident, sizeof(ident)))
- return -EFAULT;
- break;
-
- case WDIOC_GETSTATUS:
- case WDIOC_GETBOOTSTATUS:
- return put_user(0, p);
-
- case WDIOC_SETOPTIONS:
- {
- int options, retval = -EINVAL;
-
- if (get_user(options, p))
- return -EFAULT;
-
- if (options & WDIOS_DISABLECARD) {
- wdt_disable();
- retval = 0;
- }
-
- if (options & WDIOS_ENABLECARD) {
- wdt_enable();
- retval = 0;
- }
-
- return retval;
- }
-
- case WDIOC_KEEPALIVE:
- wdt_ping();
- break;
-
- case WDIOC_SETTIMEOUT:
- if (get_user(new_timeout, p))
- return -EFAULT;
- if (wdt_set_heartbeat(new_timeout))
- return -EINVAL;
- wdt_ping();
- /* Fall */
-
- case WDIOC_GETTIMEOUT:
- return put_user(timeout, p);
-
- default:
- return -ENOTTY;
- }
- return 0;
-}
-
-static int wdt_open(struct inode *inode, struct file *file)
-{
- if (test_and_set_bit(0, &wdt_is_open))
- return -EBUSY;
- /*
- * Activate
- */
-
- wdt_enable();
- return nonseekable_open(inode, file);
-}
-
-static int wdt_close(struct inode *inode, struct file *file)
-{
- if (expect_close == 42)
- wdt_disable();
- else {
- pr_crit("Unexpected close, not stopping watchdog!\n");
- wdt_ping();
- }
- expect_close = 0;
- clear_bit(0, &wdt_is_open);
- return 0;
-}
-
-/*
- * Notifier for system down
- */
-
-static int wdt_notify_sys(struct notifier_block *this, unsigned long code,
- void *unused)
-{
- if (code == SYS_DOWN || code == SYS_HALT)
- wdt_disable(); /* Turn the WDT off */
-
- return NOTIFY_DONE;
-}
-
-/*
- * Kernel Interfaces
- */
-
-static const struct file_operations wdt_fops = {
- .owner = THIS_MODULE,
- .llseek = no_llseek,
- .write = wdt_write,
- .unlocked_ioctl = wdt_ioctl,
- .open = wdt_open,
- .release = wdt_close,
-};
-
-static struct miscdevice wdt_miscdev = {
- .minor = WATCHDOG_MINOR,
- .name = "watchdog",
- .fops = &wdt_fops,
-};
-
-/*
- * The WDT needs to learn about soft shutdowns in order to
- * turn the timebomb registers off.
- */
-
-static struct notifier_block wdt_notifier = {
- .notifier_call = wdt_notify_sys,
-};
-
-static int w83697hf_check_wdt(void)
-{
- if (!request_region(wdt_io, 2, WATCHDOG_NAME)) {
- pr_err("I/O address 0x%x already in use\n", wdt_io);
- return -EIO;
- }
-
- pr_debug("Looking for watchdog at address 0x%x\n", wdt_io);
- w83697hf_unlock();
- if (w83697hf_get_reg(0x20) == 0x60) {
- pr_info("watchdog found at address 0x%x\n", wdt_io);
- w83697hf_lock();
- return 0;
- }
- /* Reprotect in case it was a compatible device */
- w83697hf_lock();
-
- pr_info("watchdog not found at address 0x%x\n", wdt_io);
- release_region(wdt_io, 2);
- return -EIO;
-}
-
-static int w83697hf_ioports[] = { 0x2e, 0x4e, 0x00 };
-
-static int __init wdt_init(void)
-{
- int ret, i, found = 0;
-
- pr_info("WDT driver for W83697HF/HG initializing\n");
-
- if (wdt_io == 0) {
- /* we will autodetect the W83697HF/HG watchdog */
- for (i = 0; ((!found) && (w83697hf_ioports[i] != 0)); i++) {
- wdt_io = w83697hf_ioports[i];
- if (!w83697hf_check_wdt())
- found++;
- }
- } else {
- if (!w83697hf_check_wdt())
- found++;
- }
-
- if (!found) {
- pr_err("No W83697HF/HG could be found\n");
- ret = -EIO;
- goto out;
- }
-
- w83697hf_init();
- if (early_disable) {
- if (wdt_running())
- pr_warn("Stopping previously enabled watchdog until userland kicks in\n");
- wdt_disable();
- }
-
- if (wdt_set_heartbeat(timeout)) {
- wdt_set_heartbeat(WATCHDOG_TIMEOUT);
- pr_info("timeout value must be 1 <= timeout <= 255, using %d\n",
- WATCHDOG_TIMEOUT);
- }
-
- ret = register_reboot_notifier(&wdt_notifier);
- if (ret != 0) {
- pr_err("cannot register reboot notifier (err=%d)\n", ret);
- goto unreg_regions;
- }
-
- ret = misc_register(&wdt_miscdev);
- if (ret != 0) {
- pr_err("cannot register miscdev on minor=%d (err=%d)\n",
- WATCHDOG_MINOR, ret);
- goto unreg_reboot;
- }
-
- pr_info("initialized. timeout=%d sec (nowayout=%d)\n",
- timeout, nowayout);
-
-out:
- return ret;
-unreg_reboot:
- unregister_reboot_notifier(&wdt_notifier);
-unreg_regions:
- release_region(wdt_io, 2);
- goto out;
-}
-
-static void __exit wdt_exit(void)
-{
- misc_deregister(&wdt_miscdev);
- unregister_reboot_notifier(&wdt_notifier);
- release_region(wdt_io, 2);
-}
-
-module_init(wdt_init);
-module_exit(wdt_exit);
-
-MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Marcus Junker <junker@anduras.de>, "
- "Samuel Tardieu <sam@rfc1149.net>");
-MODULE_DESCRIPTION("w83697hf/hg WDT driver");
-MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
diff --git a/drivers/watchdog/w83697ug_wdt.c b/drivers/watchdog/w83697ug_wdt.c
deleted file mode 100644
index 274be0bfaf2..00000000000
--- a/drivers/watchdog/w83697ug_wdt.c
+++ /dev/null
@@ -1,398 +0,0 @@
-/*
- * w83697ug/uf WDT driver
- *
- * (c) Copyright 2008 Flemming Fransen <ff@nrvissing.net>
- * reused original code to support w83697ug/uf.
- *
- * Based on w83627hf_wdt.c which is based on advantechwdt.c
- * which is based on wdt.c.
- * Original copyright messages:
- *
- * (c) Copyright 2007 Vlad Drukker <vlad@storewiz.com>
- * added support for W83627THF.
- *
- * (c) Copyright 2003 Pádraig Brady <P@draigBrady.com>
- *
- * (c) Copyright 2000-2001 Marek Michalkiewicz <marekm@linux.org.pl>
- *
- * (c) Copyright 1996 Alan Cox <alan@redhat.com>, All Rights Reserved.
- * http://www.redhat.com
- *
- * 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.
- *
- * Neither Alan Cox nor CymruNet Ltd. admit liability nor provide
- * warranty for any of this software. This material is provided
- * "AS-IS" and at no charge.
- *
- * (c) Copyright 1995 Alan Cox <alan@redhat.com>
- */
-
-#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
-
-#include <linux/module.h>
-#include <linux/moduleparam.h>
-#include <linux/types.h>
-#include <linux/miscdevice.h>
-#include <linux/watchdog.h>
-#include <linux/fs.h>
-#include <linux/ioport.h>
-#include <linux/notifier.h>
-#include <linux/reboot.h>
-#include <linux/init.h>
-#include <linux/spinlock.h>
-#include <linux/io.h>
-#include <linux/uaccess.h>
-
-
-#define WATCHDOG_NAME "w83697ug/uf WDT"
-#define WATCHDOG_TIMEOUT 60 /* 60 sec default timeout */
-
-static unsigned long wdt_is_open;
-static char expect_close;
-static DEFINE_SPINLOCK(io_lock);
-
-static int wdt_io = 0x2e;
-module_param(wdt_io, int, 0);
-MODULE_PARM_DESC(wdt_io, "w83697ug/uf WDT io port (default 0x2e)");
-
-static int timeout = WATCHDOG_TIMEOUT; /* in seconds */
-module_param(timeout, int, 0);
-MODULE_PARM_DESC(timeout,
- "Watchdog timeout in seconds. 1<= timeout <=255 (default="
- __MODULE_STRING(WATCHDOG_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) ")");
-
-/*
- * Kernel methods.
- */
-
-#define WDT_EFER (wdt_io+0) /* Extended Function Enable Registers */
-#define WDT_EFIR (wdt_io+0) /* Extended Function Index Register
- (same as EFER) */
-#define WDT_EFDR (WDT_EFIR+1) /* Extended Function Data Register */
-
-static int w83697ug_select_wd_register(void)
-{
- unsigned char c;
- unsigned char version;
-
- outb_p(0x87, WDT_EFER); /* Enter extended function mode */
- outb_p(0x87, WDT_EFER); /* Again according to manual */
-
- outb(0x20, WDT_EFER); /* check chip version */
- version = inb(WDT_EFDR);
-
- if (version == 0x68) { /* W83697UG */
- pr_info("Watchdog chip version 0x%02x = W83697UG/UF found at 0x%04x\n",
- version, wdt_io);
-
- outb_p(0x2b, WDT_EFER);
- c = inb_p(WDT_EFDR); /* select WDT0 */
- c &= ~0x04;
- outb_p(0x2b, WDT_EFER);
- outb_p(c, WDT_EFDR); /* set pin118 to WDT0 */
-
- } else {
- pr_err("No W83697UG/UF could be found\n");
- return -ENODEV;
- }
-
- outb_p(0x07, WDT_EFER); /* point to logical device number reg */
- outb_p(0x08, WDT_EFDR); /* select logical device 8 (GPIO2) */
- outb_p(0x30, WDT_EFER); /* select CR30 */
- c = inb_p(WDT_EFDR);
- outb_p(c | 0x01, WDT_EFDR); /* set bit 0 to activate GPIO2 */
-
- return 0;
-}
-
-static void w83697ug_unselect_wd_register(void)
-{
- outb_p(0xAA, WDT_EFER); /* Leave extended function mode */
-}
-
-static int w83697ug_init(void)
-{
- int ret;
- unsigned char t;
-
- ret = w83697ug_select_wd_register();
- if (ret != 0)
- return ret;
-
- outb_p(0xF6, WDT_EFER); /* Select CRF6 */
- t = inb_p(WDT_EFDR); /* read CRF6 */
- if (t != 0) {
- pr_info("Watchdog already running. Resetting timeout to %d sec\n",
- timeout);
- outb_p(timeout, WDT_EFDR); /* Write back to CRF6 */
- }
- outb_p(0xF5, WDT_EFER); /* Select CRF5 */
- t = inb_p(WDT_EFDR); /* read CRF5 */
- t &= ~0x0C; /* set second mode &
- disable keyboard turning off watchdog */
- outb_p(t, WDT_EFDR); /* Write back to CRF5 */
-
- w83697ug_unselect_wd_register();
- return 0;
-}
-
-static void wdt_ctrl(int timeout)
-{
- spin_lock(&io_lock);
-
- if (w83697ug_select_wd_register() < 0) {
- spin_unlock(&io_lock);
- return;
- }
-
- outb_p(0xF4, WDT_EFER); /* Select CRF4 */
- outb_p(timeout, WDT_EFDR); /* Write Timeout counter to CRF4 */
-
- w83697ug_unselect_wd_register();
-
- spin_unlock(&io_lock);
-}
-
-static int wdt_ping(void)
-{
- wdt_ctrl(timeout);
- return 0;
-}
-
-static int wdt_disable(void)
-{
- wdt_ctrl(0);
- return 0;
-}
-
-static int wdt_set_heartbeat(int t)
-{
- if (t < 1 || t > 255)
- return -EINVAL;
-
- timeout = t;
- return 0;
-}
-
-static ssize_t wdt_write(struct file *file, const char __user *buf,
- size_t count, loff_t *ppos)
-{
- if (count) {
- if (!nowayout) {
- size_t i;
-
- expect_close = 0;
-
- for (i = 0; i != count; i++) {
- char c;
- if (get_user(c, buf + i))
- return -EFAULT;
- if (c == 'V')
- expect_close = 42;
- }
- }
- wdt_ping();
- }
- return count;
-}
-
-static long wdt_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
-{
- void __user *argp = (void __user *)arg;
- int __user *p = argp;
- int new_timeout;
- static const struct watchdog_info ident = {
- .options = WDIOF_KEEPALIVEPING |
- WDIOF_SETTIMEOUT |
- WDIOF_MAGICCLOSE,
- .firmware_version = 1,
- .identity = "W83697UG WDT",
- };
-
- switch (cmd) {
- case WDIOC_GETSUPPORT:
- if (copy_to_user(argp, &ident, sizeof(ident)))
- return -EFAULT;
- break;
-
- case WDIOC_GETSTATUS:
- case WDIOC_GETBOOTSTATUS:
- return put_user(0, p);
-
- case WDIOC_SETOPTIONS:
- {
- int options, retval = -EINVAL;
-
- if (get_user(options, p))
- return -EFAULT;
-
- if (options & WDIOS_DISABLECARD) {
- wdt_disable();
- retval = 0;
- }
-
- if (options & WDIOS_ENABLECARD) {
- wdt_ping();
- retval = 0;
- }
-
- return retval;
- }
-
- case WDIOC_KEEPALIVE:
- wdt_ping();
- break;
-
- case WDIOC_SETTIMEOUT:
- if (get_user(new_timeout, p))
- return -EFAULT;
- if (wdt_set_heartbeat(new_timeout))
- return -EINVAL;
- wdt_ping();
- /* Fall */
-
- case WDIOC_GETTIMEOUT:
- return put_user(timeout, p);
-
- default:
- return -ENOTTY;
- }
- return 0;
-}
-
-static int wdt_open(struct inode *inode, struct file *file)
-{
- if (test_and_set_bit(0, &wdt_is_open))
- return -EBUSY;
- /*
- * Activate
- */
-
- wdt_ping();
- return nonseekable_open(inode, file);
-}
-
-static int wdt_close(struct inode *inode, struct file *file)
-{
- if (expect_close == 42)
- wdt_disable();
- else {
- pr_crit("Unexpected close, not stopping watchdog!\n");
- wdt_ping();
- }
- expect_close = 0;
- clear_bit(0, &wdt_is_open);
- return 0;
-}
-
-/*
- * Notifier for system down
- */
-
-static int wdt_notify_sys(struct notifier_block *this, unsigned long code,
- void *unused)
-{
- if (code == SYS_DOWN || code == SYS_HALT)
- wdt_disable(); /* Turn the WDT off */
-
- return NOTIFY_DONE;
-}
-
-/*
- * Kernel Interfaces
- */
-
-static const struct file_operations wdt_fops = {
- .owner = THIS_MODULE,
- .llseek = no_llseek,
- .write = wdt_write,
- .unlocked_ioctl = wdt_ioctl,
- .open = wdt_open,
- .release = wdt_close,
-};
-
-static struct miscdevice wdt_miscdev = {
- .minor = WATCHDOG_MINOR,
- .name = "watchdog",
- .fops = &wdt_fops,
-};
-
-/*
- * The WDT needs to learn about soft shutdowns in order to
- * turn the timebomb registers off.
- */
-
-static struct notifier_block wdt_notifier = {
- .notifier_call = wdt_notify_sys,
-};
-
-static int __init wdt_init(void)
-{
- int ret;
-
- pr_info("WDT driver for the Winbond(TM) W83697UG/UF Super I/O chip initialising\n");
-
- if (wdt_set_heartbeat(timeout)) {
- wdt_set_heartbeat(WATCHDOG_TIMEOUT);
- pr_info("timeout value must be 1<=timeout<=255, using %d\n",
- WATCHDOG_TIMEOUT);
- }
-
- if (!request_region(wdt_io, 1, WATCHDOG_NAME)) {
- pr_err("I/O address 0x%04x already in use\n", wdt_io);
- ret = -EIO;
- goto out;
- }
-
- ret = w83697ug_init();
- if (ret != 0)
- goto unreg_regions;
-
- ret = register_reboot_notifier(&wdt_notifier);
- if (ret != 0) {
- pr_err("cannot register reboot notifier (err=%d)\n", ret);
- goto unreg_regions;
- }
-
- ret = misc_register(&wdt_miscdev);
- if (ret != 0) {
- pr_err("cannot register miscdev on minor=%d (err=%d)\n",
- WATCHDOG_MINOR, ret);
- goto unreg_reboot;
- }
-
- pr_info("initialized. timeout=%d sec (nowayout=%d)\n",
- timeout, nowayout);
-
-out:
- return ret;
-unreg_reboot:
- unregister_reboot_notifier(&wdt_notifier);
-unreg_regions:
- release_region(wdt_io, 1);
- goto out;
-}
-
-static void __exit wdt_exit(void)
-{
- misc_deregister(&wdt_miscdev);
- unregister_reboot_notifier(&wdt_notifier);
- release_region(wdt_io, 1);
-}
-
-module_init(wdt_init);
-module_exit(wdt_exit);
-
-MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Flemming Frandsen <ff@nrvissing.net>");
-MODULE_DESCRIPTION("w83697ug/uf WDT driver");
-MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
diff --git a/drivers/watchdog/w83877f_wdt.c b/drivers/watchdog/w83877f_wdt.c
index 7874ae06232..f0483c75ed3 100644
--- a/drivers/watchdog/w83877f_wdt.c
+++ b/drivers/watchdog/w83877f_wdt.c
@@ -406,4 +406,3 @@ module_exit(w83877f_wdt_unload);
MODULE_AUTHOR("Scott and Bill Jennings");
MODULE_DESCRIPTION("Driver for watchdog timer in w83877f chip");
MODULE_LICENSE("GPL");
-MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
diff --git a/drivers/watchdog/w83977f_wdt.c b/drivers/watchdog/w83977f_wdt.c
index 5d2c902825c..91bf55a2002 100644
--- a/drivers/watchdog/w83977f_wdt.c
+++ b/drivers/watchdog/w83977f_wdt.c
@@ -527,4 +527,3 @@ module_exit(w83977f_wdt_exit);
MODULE_AUTHOR("Jose Goncalves <jose.goncalves@inov.pt>");
MODULE_DESCRIPTION("Driver for watchdog timer in W83977F I/O chip");
MODULE_LICENSE("GPL");
-MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
diff --git a/drivers/watchdog/wafer5823wdt.c b/drivers/watchdog/wafer5823wdt.c
index 25aba6e00a2..db0da7ea4fd 100644
--- a/drivers/watchdog/wafer5823wdt.c
+++ b/drivers/watchdog/wafer5823wdt.c
@@ -322,6 +322,5 @@ module_exit(wafwdt_exit);
MODULE_AUTHOR("Justin Cormack");
MODULE_DESCRIPTION("ICP Wafer 5823 Single Board Computer WDT driver");
MODULE_LICENSE("GPL");
-MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
/* end of wafer5823wdt.c */
diff --git a/drivers/watchdog/watchdog_core.c b/drivers/watchdog/watchdog_core.c
index 14d768bfa26..cec9b559647 100644
--- a/drivers/watchdog/watchdog_core.c
+++ b/drivers/watchdog/watchdog_core.c
@@ -34,8 +34,69 @@
#include <linux/kernel.h> /* For printk/panic/... */
#include <linux/watchdog.h> /* For watchdog specific items */
#include <linux/init.h> /* For __init/__exit/... */
+#include <linux/idr.h> /* For ida_* macros */
+#include <linux/err.h> /* For IS_ERR macros */
+#include <linux/of.h> /* For of_get_timeout_sec */
-#include "watchdog_dev.h" /* For watchdog_dev_register/... */
+#include "watchdog_core.h" /* For watchdog_dev_register/... */
+
+static DEFINE_IDA(watchdog_ida);
+static struct class *watchdog_class;
+
+static void watchdog_check_min_max_timeout(struct watchdog_device *wdd)
+{
+ /*
+ * Check that we have valid min and max timeout values, if
+ * not reset them both to 0 (=not used or unknown)
+ */
+ if (wdd->min_timeout > wdd->max_timeout) {
+ pr_info("Invalid min and max timeout values, resetting to 0!\n");
+ wdd->min_timeout = 0;
+ wdd->max_timeout = 0;
+ }
+}
+
+/**
+ * watchdog_init_timeout() - initialize the timeout field
+ * @timeout_parm: timeout module parameter
+ * @dev: Device that stores the timeout-sec property
+ *
+ * Initialize the timeout field of the watchdog_device struct with either the
+ * timeout module parameter (if it is valid value) or the timeout-sec property
+ * (only if it is a valid value and the timeout_parm is out of bounds).
+ * If none of them are valid then we keep the old value (which should normally
+ * be the default timeout value.
+ *
+ * A zero is returned on success and -EINVAL for failure.
+ */
+int watchdog_init_timeout(struct watchdog_device *wdd,
+ unsigned int timeout_parm, struct device *dev)
+{
+ unsigned int t = 0;
+ int ret = 0;
+
+ watchdog_check_min_max_timeout(wdd);
+
+ /* try to get the timeout module parameter first */
+ if (!watchdog_timeout_invalid(wdd, timeout_parm) && timeout_parm) {
+ wdd->timeout = timeout_parm;
+ return ret;
+ }
+ if (timeout_parm)
+ ret = -EINVAL;
+
+ /* try to get the timeout_sec property */
+ if (dev == NULL || dev->of_node == NULL)
+ return ret;
+ of_property_read_u32(dev->of_node, "timeout-sec", &t);
+ if (!watchdog_timeout_invalid(wdd, t) && t)
+ wdd->timeout = t;
+ else
+ ret = -EINVAL;
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(watchdog_init_timeout);
/**
* watchdog_register_device() - register a watchdog device
@@ -49,7 +110,7 @@
*/
int watchdog_register_device(struct watchdog_device *wdd)
{
- int ret;
+ int ret, id, devno;
if (wdd == NULL || wdd->info == NULL || wdd->ops == NULL)
return -EINVAL;
@@ -58,15 +119,7 @@ int watchdog_register_device(struct watchdog_device *wdd)
if (wdd->ops->start == NULL || wdd->ops->stop == NULL)
return -EINVAL;
- /*
- * Check that we have valid min and max timeout values, if
- * not reset them both to 0 (=not used or unknown)
- */
- if (wdd->min_timeout > wdd->max_timeout) {
- pr_info("Invalid min and max timeout values, resetting to 0!\n");
- wdd->min_timeout = 0;
- wdd->max_timeout = 0;
- }
+ watchdog_check_min_max_timeout(wdd);
/*
* Note: now that all watchdog_device data has been verified, we
@@ -74,10 +127,38 @@ int watchdog_register_device(struct watchdog_device *wdd)
* corrupted in a later stage then we expect a kernel panic!
*/
- /* We only support 1 watchdog device via the /dev/watchdog interface */
+ mutex_init(&wdd->lock);
+ id = ida_simple_get(&watchdog_ida, 0, MAX_DOGS, GFP_KERNEL);
+ if (id < 0)
+ return id;
+ wdd->id = id;
+
ret = watchdog_dev_register(wdd);
if (ret) {
- pr_err("error registering /dev/watchdog (err=%d)\n", ret);
+ ida_simple_remove(&watchdog_ida, id);
+ if (!(id == 0 && ret == -EBUSY))
+ return ret;
+
+ /* Retry in case a legacy watchdog module exists */
+ id = ida_simple_get(&watchdog_ida, 1, MAX_DOGS, GFP_KERNEL);
+ if (id < 0)
+ return id;
+ wdd->id = id;
+
+ ret = watchdog_dev_register(wdd);
+ if (ret) {
+ ida_simple_remove(&watchdog_ida, id);
+ return ret;
+ }
+ }
+
+ devno = wdd->cdev.dev;
+ wdd->dev = device_create(watchdog_class, wdd->parent, devno,
+ NULL, "watchdog%d", wdd->id);
+ if (IS_ERR(wdd->dev)) {
+ watchdog_dev_unregister(wdd);
+ ida_simple_remove(&watchdog_ida, id);
+ ret = PTR_ERR(wdd->dev);
return ret;
}
@@ -95,16 +176,50 @@ EXPORT_SYMBOL_GPL(watchdog_register_device);
void watchdog_unregister_device(struct watchdog_device *wdd)
{
int ret;
+ int devno;
if (wdd == NULL)
return;
+ devno = wdd->cdev.dev;
ret = watchdog_dev_unregister(wdd);
if (ret)
pr_err("error unregistering /dev/watchdog (err=%d)\n", ret);
+ device_destroy(watchdog_class, devno);
+ ida_simple_remove(&watchdog_ida, wdd->id);
+ wdd->dev = NULL;
}
EXPORT_SYMBOL_GPL(watchdog_unregister_device);
+static int __init watchdog_init(void)
+{
+ int err;
+
+ watchdog_class = class_create(THIS_MODULE, "watchdog");
+ if (IS_ERR(watchdog_class)) {
+ pr_err("couldn't create class\n");
+ return PTR_ERR(watchdog_class);
+ }
+
+ err = watchdog_dev_init();
+ if (err < 0) {
+ class_destroy(watchdog_class);
+ return err;
+ }
+
+ return 0;
+}
+
+static void __exit watchdog_exit(void)
+{
+ watchdog_dev_exit();
+ class_destroy(watchdog_class);
+ ida_destroy(&watchdog_ida);
+}
+
+subsys_initcall(watchdog_init);
+module_exit(watchdog_exit);
+
MODULE_AUTHOR("Alan Cox <alan@lxorguk.ukuu.org.uk>");
MODULE_AUTHOR("Wim Van Sebroeck <wim@iguana.be>");
MODULE_DESCRIPTION("WatchDog Timer Driver Core");
diff --git a/drivers/watchdog/watchdog_dev.h b/drivers/watchdog/watchdog_core.h
index bc7612be25c..6c951418fca 100644
--- a/drivers/watchdog/watchdog_dev.h
+++ b/drivers/watchdog/watchdog_core.h
@@ -26,8 +26,12 @@
* This material is provided "AS-IS" and at no charge.
*/
+#define MAX_DOGS 32 /* Maximum number of watchdog devices */
+
/*
* Functions/procedures to be called by the core
*/
-int watchdog_dev_register(struct watchdog_device *);
-int watchdog_dev_unregister(struct watchdog_device *);
+extern int watchdog_dev_register(struct watchdog_device *);
+extern int watchdog_dev_unregister(struct watchdog_device *);
+extern int __init watchdog_dev_init(void);
+extern void __exit watchdog_dev_exit(void);
diff --git a/drivers/watchdog/watchdog_dev.c b/drivers/watchdog/watchdog_dev.c
index 8558da912c4..6aaefbad303 100644
--- a/drivers/watchdog/watchdog_dev.c
+++ b/drivers/watchdog/watchdog_dev.c
@@ -42,10 +42,12 @@
#include <linux/init.h> /* For __init/__exit/... */
#include <linux/uaccess.h> /* For copy_to_user/put_user/... */
-/* make sure we only register one /dev/watchdog device */
-static unsigned long watchdog_dev_busy;
+#include "watchdog_core.h"
+
+/* the dev_t structure to store the dynamically allocated watchdog devices */
+static dev_t watchdog_devt;
/* the watchdog device behind /dev/watchdog */
-static struct watchdog_device *wdd;
+static struct watchdog_device *old_wdd;
/*
* watchdog_ping: ping the watchdog.
@@ -59,13 +61,26 @@ static struct watchdog_device *wdd;
static int watchdog_ping(struct watchdog_device *wddev)
{
- if (test_bit(WDOG_ACTIVE, &wddev->status)) {
- if (wddev->ops->ping)
- return wddev->ops->ping(wddev); /* ping the watchdog */
- else
- return wddev->ops->start(wddev); /* restart watchdog */
+ int err = 0;
+
+ mutex_lock(&wddev->lock);
+
+ if (test_bit(WDOG_UNREGISTERED, &wddev->status)) {
+ err = -ENODEV;
+ goto out_ping;
}
- return 0;
+
+ if (!watchdog_active(wddev))
+ goto out_ping;
+
+ if (wddev->ops->ping)
+ err = wddev->ops->ping(wddev); /* ping the watchdog */
+ else
+ err = wddev->ops->start(wddev); /* restart watchdog */
+
+out_ping:
+ mutex_unlock(&wddev->lock);
+ return err;
}
/*
@@ -79,16 +94,25 @@ static int watchdog_ping(struct watchdog_device *wddev)
static int watchdog_start(struct watchdog_device *wddev)
{
- int err;
+ int err = 0;
- if (!test_bit(WDOG_ACTIVE, &wddev->status)) {
- err = wddev->ops->start(wddev);
- if (err < 0)
- return err;
+ mutex_lock(&wddev->lock);
- set_bit(WDOG_ACTIVE, &wddev->status);
+ if (test_bit(WDOG_UNREGISTERED, &wddev->status)) {
+ err = -ENODEV;
+ goto out_start;
}
- return 0;
+
+ if (watchdog_active(wddev))
+ goto out_start;
+
+ err = wddev->ops->start(wddev);
+ if (err == 0)
+ set_bit(WDOG_ACTIVE, &wddev->status);
+
+out_start:
+ mutex_unlock(&wddev->lock);
+ return err;
}
/*
@@ -103,22 +127,154 @@ static int watchdog_start(struct watchdog_device *wddev)
static int watchdog_stop(struct watchdog_device *wddev)
{
- int err = -EBUSY;
+ int err = 0;
- if (test_bit(WDOG_NO_WAY_OUT, &wddev->status)) {
- pr_info("%s: nowayout prevents watchdog to be stopped!\n",
- wddev->info->identity);
- return err;
+ mutex_lock(&wddev->lock);
+
+ if (test_bit(WDOG_UNREGISTERED, &wddev->status)) {
+ err = -ENODEV;
+ goto out_stop;
}
- if (test_bit(WDOG_ACTIVE, &wddev->status)) {
- err = wddev->ops->stop(wddev);
- if (err < 0)
- return err;
+ if (!watchdog_active(wddev))
+ goto out_stop;
+ if (test_bit(WDOG_NO_WAY_OUT, &wddev->status)) {
+ dev_info(wddev->dev, "nowayout prevents watchdog being stopped!\n");
+ err = -EBUSY;
+ goto out_stop;
+ }
+
+ err = wddev->ops->stop(wddev);
+ if (err == 0)
clear_bit(WDOG_ACTIVE, &wddev->status);
+
+out_stop:
+ mutex_unlock(&wddev->lock);
+ return err;
+}
+
+/*
+ * watchdog_get_status: wrapper to get the watchdog status
+ * @wddev: the watchdog device to get the status from
+ * @status: the status of the watchdog device
+ *
+ * Get the watchdog's status flags.
+ */
+
+static int watchdog_get_status(struct watchdog_device *wddev,
+ unsigned int *status)
+{
+ int err = 0;
+
+ *status = 0;
+ if (!wddev->ops->status)
+ return -EOPNOTSUPP;
+
+ mutex_lock(&wddev->lock);
+
+ if (test_bit(WDOG_UNREGISTERED, &wddev->status)) {
+ err = -ENODEV;
+ goto out_status;
}
- return 0;
+
+ *status = wddev->ops->status(wddev);
+
+out_status:
+ mutex_unlock(&wddev->lock);
+ return err;
+}
+
+/*
+ * watchdog_set_timeout: set the watchdog timer timeout
+ * @wddev: the watchdog device to set the timeout for
+ * @timeout: timeout to set in seconds
+ */
+
+static int watchdog_set_timeout(struct watchdog_device *wddev,
+ unsigned int timeout)
+{
+ int err;
+
+ if ((wddev->ops->set_timeout == NULL) ||
+ !(wddev->info->options & WDIOF_SETTIMEOUT))
+ return -EOPNOTSUPP;
+
+ if (watchdog_timeout_invalid(wddev, timeout))
+ return -EINVAL;
+
+ mutex_lock(&wddev->lock);
+
+ if (test_bit(WDOG_UNREGISTERED, &wddev->status)) {
+ err = -ENODEV;
+ goto out_timeout;
+ }
+
+ err = wddev->ops->set_timeout(wddev, timeout);
+
+out_timeout:
+ mutex_unlock(&wddev->lock);
+ return err;
+}
+
+/*
+ * watchdog_get_timeleft: wrapper to get the time left before a reboot
+ * @wddev: the watchdog device to get the remaining time from
+ * @timeleft: the time that's left
+ *
+ * Get the time before a watchdog will reboot (if not pinged).
+ */
+
+static int watchdog_get_timeleft(struct watchdog_device *wddev,
+ unsigned int *timeleft)
+{
+ int err = 0;
+
+ *timeleft = 0;
+ if (!wddev->ops->get_timeleft)
+ return -EOPNOTSUPP;
+
+ mutex_lock(&wddev->lock);
+
+ if (test_bit(WDOG_UNREGISTERED, &wddev->status)) {
+ err = -ENODEV;
+ goto out_timeleft;
+ }
+
+ *timeleft = wddev->ops->get_timeleft(wddev);
+
+out_timeleft:
+ mutex_unlock(&wddev->lock);
+ return err;
+}
+
+/*
+ * watchdog_ioctl_op: call the watchdog drivers ioctl op if defined
+ * @wddev: the watchdog device to do the ioctl on
+ * @cmd: watchdog command
+ * @arg: argument pointer
+ */
+
+static int watchdog_ioctl_op(struct watchdog_device *wddev, unsigned int cmd,
+ unsigned long arg)
+{
+ int err;
+
+ if (!wddev->ops->ioctl)
+ return -ENOIOCTLCMD;
+
+ mutex_lock(&wddev->lock);
+
+ if (test_bit(WDOG_UNREGISTERED, &wddev->status)) {
+ err = -ENODEV;
+ goto out_ioctl;
+ }
+
+ err = wddev->ops->ioctl(wddev, cmd, arg);
+
+out_ioctl:
+ mutex_unlock(&wddev->lock);
+ return err;
}
/*
@@ -136,6 +292,7 @@ static int watchdog_stop(struct watchdog_device *wddev)
static ssize_t watchdog_write(struct file *file, const char __user *data,
size_t len, loff_t *ppos)
{
+ struct watchdog_device *wdd = file->private_data;
size_t i;
char c;
@@ -175,23 +332,24 @@ static ssize_t watchdog_write(struct file *file, const char __user *data,
static long watchdog_ioctl(struct file *file, unsigned int cmd,
unsigned long arg)
{
+ struct watchdog_device *wdd = file->private_data;
void __user *argp = (void __user *)arg;
int __user *p = argp;
unsigned int val;
int err;
- if (wdd->ops->ioctl) {
- err = wdd->ops->ioctl(wdd, cmd, arg);
- if (err != -ENOIOCTLCMD)
- return err;
- }
+ err = watchdog_ioctl_op(wdd, cmd, arg);
+ if (err != -ENOIOCTLCMD)
+ return err;
switch (cmd) {
case WDIOC_GETSUPPORT:
return copy_to_user(argp, wdd->info,
sizeof(struct watchdog_info)) ? -EFAULT : 0;
case WDIOC_GETSTATUS:
- val = wdd->ops->status ? wdd->ops->status(wdd) : 0;
+ err = watchdog_get_status(wdd, &val);
+ if (err == -ENODEV)
+ return err;
return put_user(val, p);
case WDIOC_GETBOOTSTATUS:
return put_user(wdd->bootstatus, p);
@@ -215,15 +373,9 @@ static long watchdog_ioctl(struct file *file, unsigned int cmd,
watchdog_ping(wdd);
return 0;
case WDIOC_SETTIMEOUT:
- if ((wdd->ops->set_timeout == NULL) ||
- !(wdd->info->options & WDIOF_SETTIMEOUT))
- return -EOPNOTSUPP;
if (get_user(val, p))
return -EFAULT;
- if ((wdd->max_timeout != 0) &&
- (val < wdd->min_timeout || val > wdd->max_timeout))
- return -EINVAL;
- err = wdd->ops->set_timeout(wdd, val);
+ err = watchdog_set_timeout(wdd, val);
if (err < 0)
return err;
/* If the watchdog is active then we send a keepalive ping
@@ -237,21 +389,21 @@ static long watchdog_ioctl(struct file *file, unsigned int cmd,
return -EOPNOTSUPP;
return put_user(wdd->timeout, p);
case WDIOC_GETTIMELEFT:
- if (!wdd->ops->get_timeleft)
- return -EOPNOTSUPP;
-
- return put_user(wdd->ops->get_timeleft(wdd), p);
+ err = watchdog_get_timeleft(wdd, &val);
+ if (err)
+ return err;
+ return put_user(val, p);
default:
return -ENOTTY;
}
}
/*
- * watchdog_open: open the /dev/watchdog device.
+ * watchdog_open: open the /dev/watchdog* devices.
* @inode: inode of device
* @file: file handle to device
*
- * When the /dev/watchdog device gets opened, we start the watchdog.
+ * When the /dev/watchdog* device gets opened, we start the watchdog.
* Watch out: the /dev/watchdog device is single open, so we make sure
* it can only be opened once.
*/
@@ -259,6 +411,13 @@ static long watchdog_ioctl(struct file *file, unsigned int cmd,
static int watchdog_open(struct inode *inode, struct file *file)
{
int err = -EBUSY;
+ struct watchdog_device *wdd;
+
+ /* Get the corresponding watchdog device */
+ if (imajor(inode) == MISC_MAJOR)
+ wdd = old_wdd;
+ else
+ wdd = container_of(inode->i_cdev, struct watchdog_device, cdev);
/* the watchdog is single open! */
if (test_and_set_bit(WDOG_DEV_OPEN, &wdd->status))
@@ -275,6 +434,11 @@ static int watchdog_open(struct inode *inode, struct file *file)
if (err < 0)
goto out_mod;
+ file->private_data = wdd;
+
+ if (wdd->ops->ref)
+ wdd->ops->ref(wdd);
+
/* dev/watchdog is a virtual (and thus non-seekable) filesystem */
return nonseekable_open(inode, file);
@@ -286,9 +450,9 @@ out:
}
/*
- * watchdog_release: release the /dev/watchdog device.
- * @inode: inode of device
- * @file: file handle to device
+ * watchdog_release: release the watchdog device.
+ * @inode: inode of device
+ * @file: file handle to device
*
* This is the code for when /dev/watchdog gets closed. We will only
* stop the watchdog when we have received the magic char (and nowayout
@@ -297,6 +461,7 @@ out:
static int watchdog_release(struct inode *inode, struct file *file)
{
+ struct watchdog_device *wdd = file->private_data;
int err = -EBUSY;
/*
@@ -304,13 +469,18 @@ static int watchdog_release(struct inode *inode, struct file *file)
* or if WDIOF_MAGICCLOSE is not set. If nowayout was set then
* watchdog_stop will fail.
*/
- if (test_and_clear_bit(WDOG_ALLOW_RELEASE, &wdd->status) ||
- !(wdd->info->options & WDIOF_MAGICCLOSE))
+ if (!test_bit(WDOG_ACTIVE, &wdd->status))
+ err = 0;
+ else if (test_and_clear_bit(WDOG_ALLOW_RELEASE, &wdd->status) ||
+ !(wdd->info->options & WDIOF_MAGICCLOSE))
err = watchdog_stop(wdd);
/* If the watchdog was not stopped, send a keepalive ping */
if (err < 0) {
- pr_crit("%s: watchdog did not stop!\n", wdd->info->identity);
+ mutex_lock(&wdd->lock);
+ if (!test_bit(WDOG_UNREGISTERED, &wdd->status))
+ dev_crit(wdd->dev, "watchdog did not stop!\n");
+ mutex_unlock(&wdd->lock);
watchdog_ping(wdd);
}
@@ -320,6 +490,10 @@ static int watchdog_release(struct inode *inode, struct file *file)
/* make sure that /dev/watchdog can be re-opened */
clear_bit(WDOG_DEV_OPEN, &wdd->status);
+ /* Note wdd may be gone after this, do not use after this! */
+ if (wdd->ops->unref)
+ wdd->ops->unref(wdd);
+
return 0;
}
@@ -338,62 +512,93 @@ static struct miscdevice watchdog_miscdev = {
};
/*
- * watchdog_dev_register:
+ * watchdog_dev_register: register a watchdog device
* @watchdog: watchdog device
*
- * Register a watchdog device as /dev/watchdog. /dev/watchdog
- * is actually a miscdevice and thus we set it up like that.
+ * Register a watchdog device including handling the legacy
+ * /dev/watchdog node. /dev/watchdog is actually a miscdevice and
+ * thus we set it up like that.
*/
int watchdog_dev_register(struct watchdog_device *watchdog)
{
- int err;
-
- /* Only one device can register for /dev/watchdog */
- if (test_and_set_bit(0, &watchdog_dev_busy)) {
- pr_err("only one watchdog can use /dev/watchdog\n");
- return -EBUSY;
+ int err, devno;
+
+ if (watchdog->id == 0) {
+ old_wdd = watchdog;
+ watchdog_miscdev.parent = watchdog->parent;
+ err = misc_register(&watchdog_miscdev);
+ if (err != 0) {
+ pr_err("%s: cannot register miscdev on minor=%d (err=%d).\n",
+ watchdog->info->identity, WATCHDOG_MINOR, err);
+ if (err == -EBUSY)
+ pr_err("%s: a legacy watchdog module is probably present.\n",
+ watchdog->info->identity);
+ old_wdd = NULL;
+ return err;
+ }
}
- wdd = watchdog;
-
- err = misc_register(&watchdog_miscdev);
- if (err != 0) {
- pr_err("%s: cannot register miscdev on minor=%d (err=%d)\n",
- watchdog->info->identity, WATCHDOG_MINOR, err);
- goto out;
+ /* Fill in the data structures */
+ devno = MKDEV(MAJOR(watchdog_devt), watchdog->id);
+ cdev_init(&watchdog->cdev, &watchdog_fops);
+ watchdog->cdev.owner = watchdog->ops->owner;
+
+ /* Add the device */
+ err = cdev_add(&watchdog->cdev, devno, 1);
+ if (err) {
+ pr_err("watchdog%d unable to add device %d:%d\n",
+ watchdog->id, MAJOR(watchdog_devt), watchdog->id);
+ if (watchdog->id == 0) {
+ misc_deregister(&watchdog_miscdev);
+ old_wdd = NULL;
+ }
}
-
- return 0;
-
-out:
- wdd = NULL;
- clear_bit(0, &watchdog_dev_busy);
return err;
}
/*
- * watchdog_dev_unregister:
+ * watchdog_dev_unregister: unregister a watchdog device
* @watchdog: watchdog device
*
- * Deregister the /dev/watchdog device.
+ * Unregister the watchdog and if needed the legacy /dev/watchdog device.
*/
int watchdog_dev_unregister(struct watchdog_device *watchdog)
{
- /* Check that a watchdog device was registered in the past */
- if (!test_bit(0, &watchdog_dev_busy) || !wdd)
- return -ENODEV;
-
- /* We can only unregister the watchdog device that was registered */
- if (watchdog != wdd) {
- pr_err("%s: watchdog was not registered as /dev/watchdog\n",
- watchdog->info->identity);
- return -ENODEV;
+ mutex_lock(&watchdog->lock);
+ set_bit(WDOG_UNREGISTERED, &watchdog->status);
+ mutex_unlock(&watchdog->lock);
+
+ cdev_del(&watchdog->cdev);
+ if (watchdog->id == 0) {
+ misc_deregister(&watchdog_miscdev);
+ old_wdd = NULL;
}
-
- misc_deregister(&watchdog_miscdev);
- wdd = NULL;
- clear_bit(0, &watchdog_dev_busy);
return 0;
}
+
+/*
+ * watchdog_dev_init: init dev part of watchdog core
+ *
+ * Allocate a range of chardev nodes to use for watchdog devices
+ */
+
+int __init watchdog_dev_init(void)
+{
+ int err = alloc_chrdev_region(&watchdog_devt, 0, MAX_DOGS, "watchdog");
+ if (err < 0)
+ pr_err("watchdog: unable to allocate char dev region\n");
+ return err;
+}
+
+/*
+ * watchdog_dev_exit: exit dev part of watchdog core
+ *
+ * Release the range of chardev nodes used for watchdog devices
+ */
+
+void __exit watchdog_dev_exit(void)
+{
+ unregister_chrdev_region(watchdog_devt, MAX_DOGS);
+}
diff --git a/drivers/watchdog/wdrtas.c b/drivers/watchdog/wdrtas.c
index 0a77655cda6..0240c60d14e 100644
--- a/drivers/watchdog/wdrtas.c
+++ b/drivers/watchdog/wdrtas.c
@@ -48,8 +48,6 @@
MODULE_AUTHOR("Utz Bacher <utz.bacher@de.ibm.com>");
MODULE_DESCRIPTION("RTAS watchdog driver");
MODULE_LICENSE("GPL");
-MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
-MODULE_ALIAS_MISCDEV(TEMP_MINOR);
static bool wdrtas_nowayout = WATCHDOG_NOWAYOUT;
static atomic_t wdrtas_miscdev_open = ATOMIC_INIT(0);
@@ -162,31 +160,6 @@ static void wdrtas_timer_stop(void)
}
/**
- * wdrtas_log_scanned_event - logs an event we received during keepalive
- *
- * wdrtas_log_scanned_event prints a message to the log buffer dumping
- * the results of the last event-scan call
- */
-static void wdrtas_log_scanned_event(void)
-{
- int i;
-
- for (i = 0; i < WDRTAS_LOGBUFFER_LEN; i += 16)
- pr_info("dumping event (line %i/%i), data = "
- "%02x %02x %02x %02x %02x %02x %02x %02x "
- "%02x %02x %02x %02x %02x %02x %02x %02x\n",
- (i / 16) + 1, (WDRTAS_LOGBUFFER_LEN / 16),
- wdrtas_logbuffer[i + 0], wdrtas_logbuffer[i + 1],
- wdrtas_logbuffer[i + 2], wdrtas_logbuffer[i + 3],
- wdrtas_logbuffer[i + 4], wdrtas_logbuffer[i + 5],
- wdrtas_logbuffer[i + 6], wdrtas_logbuffer[i + 7],
- wdrtas_logbuffer[i + 8], wdrtas_logbuffer[i + 9],
- wdrtas_logbuffer[i + 10], wdrtas_logbuffer[i + 11],
- wdrtas_logbuffer[i + 12], wdrtas_logbuffer[i + 13],
- wdrtas_logbuffer[i + 14], wdrtas_logbuffer[i + 15]);
-}
-
-/**
* wdrtas_timer_keepalive - resets watchdog timer to keep system alive
*
* wdrtas_timer_keepalive restarts the watchdog timer by calling the
@@ -205,7 +178,9 @@ static void wdrtas_timer_keepalive(void)
if (result < 0)
pr_err("event-scan failed: %li\n", result);
if (result == 0)
- wdrtas_log_scanned_event();
+ print_hex_dump(KERN_INFO, "dumping event, data: ",
+ DUMP_PREFIX_OFFSET, 16, 1,
+ wdrtas_logbuffer, WDRTAS_LOGBUFFER_LEN, false);
} while (result == 0);
}
diff --git a/drivers/watchdog/wdt.c b/drivers/watchdog/wdt.c
index ee4333c0110..e0206b5b7d8 100644
--- a/drivers/watchdog/wdt.c
+++ b/drivers/watchdog/wdt.c
@@ -664,6 +664,4 @@ module_exit(wdt_exit);
MODULE_AUTHOR("Alan Cox");
MODULE_DESCRIPTION("Driver for ISA ICS watchdog cards (WDT500/501)");
-MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
-MODULE_ALIAS_MISCDEV(TEMP_MINOR);
MODULE_LICENSE("GPL");
diff --git a/drivers/watchdog/wdt285.c b/drivers/watchdog/wdt285.c
index 5eec7405388..ebbb183be61 100644
--- a/drivers/watchdog/wdt285.c
+++ b/drivers/watchdog/wdt285.c
@@ -139,9 +139,8 @@ static const struct watchdog_info ident = {
static long watchdog_ioctl(struct file *file, unsigned int cmd,
unsigned long arg)
{
- unsigned int new_margin;
int __user *int_arg = (int __user *)arg;
- int ret = -ENOTTY;
+ int new_margin, ret = -ENOTTY;
switch (cmd) {
case WDIOC_GETSUPPORT:
@@ -224,7 +223,6 @@ static void __exit footbridge_watchdog_exit(void)
MODULE_AUTHOR("Phil Blundell <pb@nexus.co.uk>");
MODULE_DESCRIPTION("Footbridge watchdog driver");
MODULE_LICENSE("GPL");
-MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
module_param(soft_margin, int, 0);
MODULE_PARM_DESC(soft_margin, "Watchdog timeout in seconds");
diff --git a/drivers/watchdog/wdt977.c b/drivers/watchdog/wdt977.c
index 65a40234493..a8e6f87f60c 100644
--- a/drivers/watchdog/wdt977.c
+++ b/drivers/watchdog/wdt977.c
@@ -507,4 +507,3 @@ module_exit(wd977_exit);
MODULE_AUTHOR("Woody Suwalski <woodys@xandros.com>");
MODULE_DESCRIPTION("W83977AF Watchdog driver");
MODULE_LICENSE("GPL");
-MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
diff --git a/drivers/watchdog/wdt_pci.c b/drivers/watchdog/wdt_pci.c
index 1c888c7d4cc..48b2c058b00 100644
--- a/drivers/watchdog/wdt_pci.c
+++ b/drivers/watchdog/wdt_pci.c
@@ -49,7 +49,6 @@
#include <linux/delay.h>
#include <linux/notifier.h>
#include <linux/reboot.h>
-#include <linux/init.h>
#include <linux/fs.h>
#include <linux/pci.h>
#include <linux/io.h>
@@ -605,7 +604,7 @@ static struct notifier_block wdtpci_notifier = {
};
-static int __devinit wdtpci_init_one(struct pci_dev *dev,
+static int wdtpci_init_one(struct pci_dev *dev,
const struct pci_device_id *ent)
{
int ret = -EIO;
@@ -705,7 +704,7 @@ out_pci:
}
-static void __devexit wdtpci_remove_one(struct pci_dev *pdev)
+static void wdtpci_remove_one(struct pci_dev *pdev)
{
/* here we assume only one device will ever have
* been picked up and registered by probe function */
@@ -720,7 +719,7 @@ static void __devexit wdtpci_remove_one(struct pci_dev *pdev)
}
-static DEFINE_PCI_DEVICE_TABLE(wdtpci_pci_tbl) = {
+static const struct pci_device_id wdtpci_pci_tbl[] = {
{
.vendor = PCI_VENDOR_ID_ACCESSIO,
.device = PCI_DEVICE_ID_ACCESSIO_WDG_CSM,
@@ -736,45 +735,11 @@ static struct pci_driver wdtpci_driver = {
.name = "wdt_pci",
.id_table = wdtpci_pci_tbl,
.probe = wdtpci_init_one,
- .remove = __devexit_p(wdtpci_remove_one),
+ .remove = wdtpci_remove_one,
};
-
-/**
- * wdtpci_cleanup:
- *
- * Unload the watchdog. You cannot do this with any file handles open.
- * If your watchdog is set to continue ticking on close and you unload
- * it, well it keeps ticking. We won't get the interrupt but the board
- * will not touch PC memory so all is fine. You just have to load a new
- * module in xx seconds or reboot.
- */
-
-static void __exit wdtpci_cleanup(void)
-{
- pci_unregister_driver(&wdtpci_driver);
-}
-
-
-/**
- * wdtpci_init:
- *
- * Set up the WDT watchdog board. All we have to do is grab the
- * resources we require and bitch if anyone beat us to them.
- * The open() function will actually kick the board off.
- */
-
-static int __init wdtpci_init(void)
-{
- return pci_register_driver(&wdtpci_driver);
-}
-
-
-module_init(wdtpci_init);
-module_exit(wdtpci_cleanup);
+module_pci_driver(wdtpci_driver);
MODULE_AUTHOR("JP Nollmann, Alan Cox");
MODULE_DESCRIPTION("Driver for the ICS PCI-WDT500/501 watchdog cards");
MODULE_LICENSE("GPL");
-MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
-MODULE_ALIAS_MISCDEV(TEMP_MINOR);
diff --git a/drivers/watchdog/wm831x_wdt.c b/drivers/watchdog/wm831x_wdt.c
index b1815c5ed7a..2fa17e746ff 100644
--- a/drivers/watchdog/wm831x_wdt.c
+++ b/drivers/watchdog/wm831x_wdt.c
@@ -181,10 +181,10 @@ static const struct watchdog_ops wm831x_wdt_ops = {
.set_timeout = wm831x_wdt_set_timeout,
};
-static int __devinit wm831x_wdt_probe(struct platform_device *pdev)
+static int wm831x_wdt_probe(struct platform_device *pdev)
{
struct wm831x *wm831x = dev_get_drvdata(pdev->dev.parent);
- struct wm831x_pdata *chip_pdata;
+ struct wm831x_pdata *chip_pdata = dev_get_platdata(pdev->dev.parent);
struct wm831x_watchdog_pdata *pdata;
struct wm831x_wdt_drvdata *driver_data;
struct watchdog_device *wm831x_wdt;
@@ -204,7 +204,6 @@ static int __devinit wm831x_wdt_probe(struct platform_device *pdev)
driver_data = devm_kzalloc(&pdev->dev, sizeof(*driver_data),
GFP_KERNEL);
if (!driver_data) {
- dev_err(wm831x->dev, "Unable to alloacate watchdog device\n");
ret = -ENOMEM;
goto err;
}
@@ -231,12 +230,10 @@ static int __devinit wm831x_wdt_probe(struct platform_device *pdev)
wm831x_wdt->timeout = wm831x_wdt_cfgs[i].time;
/* Apply any configuration */
- if (pdev->dev.parent->platform_data) {
- chip_pdata = pdev->dev.parent->platform_data;
+ if (chip_pdata)
pdata = chip_pdata->watchdog;
- } else {
+ else
pdata = NULL;
- }
if (pdata) {
reg &= ~(WM831X_WDOG_SECACT_MASK | WM831X_WDOG_PRIMACT_MASK |
@@ -247,8 +244,10 @@ static int __devinit wm831x_wdt_probe(struct platform_device *pdev)
reg |= pdata->software << WM831X_WDOG_RST_SRC_SHIFT;
if (pdata->update_gpio) {
- ret = gpio_request(pdata->update_gpio,
- "Watchdog update");
+ ret = devm_gpio_request_one(&pdev->dev,
+ pdata->update_gpio,
+ GPIOF_OUT_INIT_LOW,
+ "Watchdog update");
if (ret < 0) {
dev_err(wm831x->dev,
"Failed to request update GPIO: %d\n",
@@ -256,14 +255,6 @@ static int __devinit wm831x_wdt_probe(struct platform_device *pdev)
goto err;
}
- ret = gpio_direction_output(pdata->update_gpio, 0);
- if (ret != 0) {
- dev_err(wm831x->dev,
- "gpio_direction_output returned: %d\n",
- ret);
- goto err_gpio;
- }
-
driver_data->update_gpio = pdata->update_gpio;
/* Make sure the watchdog takes hardware updates */
@@ -277,7 +268,7 @@ static int __devinit wm831x_wdt_probe(struct platform_device *pdev)
} else {
dev_err(wm831x->dev,
"Failed to unlock security key: %d\n", ret);
- goto err_gpio;
+ goto err;
}
}
@@ -285,35 +276,29 @@ static int __devinit wm831x_wdt_probe(struct platform_device *pdev)
if (ret != 0) {
dev_err(wm831x->dev, "watchdog_register_device() failed: %d\n",
ret);
- goto err_gpio;
+ goto err;
}
- dev_set_drvdata(&pdev->dev, driver_data);
+ platform_set_drvdata(pdev, driver_data);
return 0;
-err_gpio:
- if (driver_data->update_gpio)
- gpio_free(driver_data->update_gpio);
err:
return ret;
}
-static int __devexit wm831x_wdt_remove(struct platform_device *pdev)
+static int wm831x_wdt_remove(struct platform_device *pdev)
{
- struct wm831x_wdt_drvdata *driver_data = dev_get_drvdata(&pdev->dev);
+ struct wm831x_wdt_drvdata *driver_data = platform_get_drvdata(pdev);
watchdog_unregister_device(&driver_data->wdt);
- if (driver_data->update_gpio)
- gpio_free(driver_data->update_gpio);
-
return 0;
}
static struct platform_driver wm831x_wdt_driver = {
.probe = wm831x_wdt_probe,
- .remove = __devexit_p(wm831x_wdt_remove),
+ .remove = wm831x_wdt_remove,
.driver = {
.name = "wm831x-watchdog",
},
diff --git a/drivers/watchdog/wm8350_wdt.c b/drivers/watchdog/wm8350_wdt.c
index 3c76693447f..34d272ada23 100644
--- a/drivers/watchdog/wm8350_wdt.c
+++ b/drivers/watchdog/wm8350_wdt.c
@@ -140,7 +140,7 @@ static struct watchdog_device wm8350_wdt = {
.max_timeout = 4,
};
-static int __devinit wm8350_wdt_probe(struct platform_device *pdev)
+static int wm8350_wdt_probe(struct platform_device *pdev)
{
struct wm8350 *wm8350 = platform_get_drvdata(pdev);
@@ -158,7 +158,7 @@ static int __devinit wm8350_wdt_probe(struct platform_device *pdev)
return watchdog_register_device(&wm8350_wdt);
}
-static int __devexit wm8350_wdt_remove(struct platform_device *pdev)
+static int wm8350_wdt_remove(struct platform_device *pdev)
{
watchdog_unregister_device(&wm8350_wdt);
return 0;
@@ -166,7 +166,7 @@ static int __devexit wm8350_wdt_remove(struct platform_device *pdev)
static struct platform_driver wm8350_wdt_driver = {
.probe = wm8350_wdt_probe,
- .remove = __devexit_p(wm8350_wdt_remove),
+ .remove = wm8350_wdt_remove,
.driver = {
.name = "wm8350-wdt",
},
diff --git a/drivers/watchdog/xen_wdt.c b/drivers/watchdog/xen_wdt.c
index e4a25b51165..7a42dffd39e 100644
--- a/drivers/watchdog/xen_wdt.c
+++ b/drivers/watchdog/xen_wdt.c
@@ -244,7 +244,7 @@ static struct miscdevice xen_wdt_miscdev = {
.fops = &xen_wdt_fops,
};
-static int __devinit xen_wdt_probe(struct platform_device *dev)
+static int xen_wdt_probe(struct platform_device *dev)
{
struct sched_watchdog wd = { .id = ~0 };
int ret = HYPERVISOR_sched_op(SCHEDOP_watchdog, &wd);
@@ -280,7 +280,7 @@ static int __devinit xen_wdt_probe(struct platform_device *dev)
return ret;
}
-static int __devexit xen_wdt_remove(struct platform_device *dev)
+static int xen_wdt_remove(struct platform_device *dev)
{
/* Stop the timer before we leave */
if (!nowayout)
@@ -315,7 +315,7 @@ static int xen_wdt_resume(struct platform_device *dev)
static struct platform_driver xen_wdt_driver = {
.probe = xen_wdt_probe,
- .remove = __devexit_p(xen_wdt_remove),
+ .remove = xen_wdt_remove,
.shutdown = xen_wdt_shutdown,
.suspend = xen_wdt_suspend,
.resume = xen_wdt_resume,
@@ -362,4 +362,3 @@ MODULE_AUTHOR("Jan Beulich <jbeulich@novell.com>");
MODULE_DESCRIPTION("Xen WatchDog Timer Driver");
MODULE_VERSION(DRV_VERSION);
MODULE_LICENSE("GPL");
-MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);