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