You've already forked linux-apfs
mirror of
https://github.com/linux-apfs/linux-apfs.git
synced 2026-05-01 15:00:59 -07:00
Merge tag 'linux-watchdog-4.16-rc1' of git://www.linux-watchdog.org/linux-watchdog
Pull watchdog updates from Wim Van Sebroeck:
- new watchdog device drivers for Realtek RTD1295 and Spreadtrum SC9860
platform
- add support for the following devices: jz4780 SoC, AST25xx series SoC
and r8a77970 SoC
- convert to watchdog framework: i6300esb_wdt, xen_wdt and sp5100_tco
- several fixes for watchdog core
- remove at32ap700x and obsolete documentation
- gpio: Convert to use GPIO descriptors
- rename gemini into FTWDT010 as this IP block is generc from Faraday
Technology
- various clean-ups and small bugfixes
- add Guenter Roeck as co-maintainer
- change maintainers e-mail address
* tag 'linux-watchdog-4.16-rc1' of git://www.linux-watchdog.org/linux-watchdog: (74 commits)
documentation: watchdog: remove documentation of w83697hf_wdt/w83697ug_wdt
documentation: watchdog: remove documentation for ixp2000
documentation: watchdog: remove documentation of at32ap700x_wdt
watchdog: remove at32ap700x_wdt
watchdog: sp5100_tco: Add support for recent FCH versions
watchdog: sp5100-tco: Abort if watchdog is disabled by hardware
watchdog: sp5100_tco: Use bit operations
watchdog: sp5100_tco: Convert to use watchdog subsystem
watchdog: sp5100_tco: Clean up function and variable names
watchdog: sp5100_tco: Use dev_ print functions where possible
watchdog: sp5100_tco: Match PCI device early
watchdog: sp5100_tco: Clean up sp5100_tco_setupdevice
watchdog: sp5100_tco: Use standard error codes
watchdog: sp5100_tco: Use request_muxed_region where possible
watchdog: sp5100_tco: Fix watchdog disable bit
watchdog: sp5100_tco: Always use SP5100_IO_PM_{INDEX_REG,DATA_REG}
watchdog: core: make sure the watchdog_worker is not deferred
watchdog: mt7621: switch to using managed devm_watchdog_register_device()
watchdog: mt7621: set WDOG_HW_RUNNING bit when appropriate
watchdog: imx2_wdt: restore previous timeout after suspend+resume
...
This commit is contained in:
@@ -1,17 +0,0 @@
|
||||
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>;
|
||||
};
|
||||
+8
-3
@@ -1,7 +1,12 @@
|
||||
Cortina Systems Gemini SoC Watchdog
|
||||
Faraday Technology FTWDT010 watchdog
|
||||
|
||||
This is an IP part from Faraday Technology found in the Gemini
|
||||
SoCs and others.
|
||||
|
||||
Required properties:
|
||||
- compatible : must be "cortina,gemini-watchdog"
|
||||
- compatible : must be one of
|
||||
"faraday,ftwdt010"
|
||||
"cortina,gemini-watchdog", "faraday,ftwdt010"
|
||||
- reg : shall contain base register location and length
|
||||
- interrupts : shall contain the interrupt for the watchdog
|
||||
|
||||
@@ -11,7 +16,7 @@ Optional properties:
|
||||
Example:
|
||||
|
||||
watchdog@41000000 {
|
||||
compatible = "cortina,gemini-watchdog";
|
||||
compatible = "faraday,ftwdt010";
|
||||
reg = <0x41000000 0x1000>;
|
||||
interrupts = <3 IRQ_TYPE_LEVEL_HIGH>;
|
||||
};
|
||||
@@ -1,7 +1,7 @@
|
||||
Ingenic Watchdog Timer (WDT) Controller for JZ4740
|
||||
Ingenic Watchdog Timer (WDT) Controller for JZ4740 & JZ4780
|
||||
|
||||
Required properties:
|
||||
compatible: "ingenic,jz4740-watchdog"
|
||||
compatible: "ingenic,jz4740-watchdog" or "ingenic,jz4780-watchdog"
|
||||
reg: Register address and length for watchdog registers
|
||||
|
||||
Example:
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
Realtek RTD1295 Watchdog
|
||||
========================
|
||||
|
||||
Required properties:
|
||||
|
||||
- compatible : Should be "realtek,rtd1295-watchdog"
|
||||
- reg : Specifies the physical base address and size of registers
|
||||
- clocks : Specifies one clock input
|
||||
|
||||
|
||||
Example:
|
||||
|
||||
watchdog@98007680 {
|
||||
compatible = "realtek,rtd1295-watchdog";
|
||||
reg = <0x98007680 0x100>;
|
||||
clocks = <&osc27M>;
|
||||
};
|
||||
@@ -4,10 +4,11 @@ Required properties:
|
||||
- compatible : Should be "renesas,<soctype>-wdt", and
|
||||
"renesas,rcar-gen3-wdt" or "renesas,rza-wdt" as fallback.
|
||||
Examples with soctypes are:
|
||||
- "renesas,r7s72100-wdt" (RZ/A1)
|
||||
- "renesas,r8a7795-wdt" (R-Car H3)
|
||||
- "renesas,r8a7796-wdt" (R-Car M3-W)
|
||||
- "renesas,r8a77970-wdt" (R-Car V3M)
|
||||
- "renesas,r8a77995-wdt" (R-Car D3)
|
||||
- "renesas,r7s72100-wdt" (RZ/A1)
|
||||
|
||||
When compatible with the generic version, nodes must list the SoC-specific
|
||||
version corresponding to the platform first, followed by the generic
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
Spreadtrum SoCs Watchdog timer
|
||||
|
||||
Required properties:
|
||||
- compatible : Should be "sprd,sp9860-wdt".
|
||||
- reg : Specifies base physical address and size of the registers.
|
||||
- interrupts : Exactly one interrupt specifier.
|
||||
- timeout-sec : Contain the default watchdog timeout in seconds.
|
||||
- clock-names : Contain the input clock names.
|
||||
- clocks : Phandles to input clocks.
|
||||
|
||||
Example:
|
||||
watchdog: watchdog@40310000 {
|
||||
compatible = "sprd,sp9860-wdt";
|
||||
reg = <0 0x40310000 0 0x1000>;
|
||||
interrupts = <GIC_SPI 61 IRQ_TYPE_LEVEL_HIGH>;
|
||||
timeout-sec = <12>;
|
||||
clock-names = "enable", "rtc_enable";
|
||||
clocks = <&clk_aon_apb_gates1 8>, <&clk_aon_apb_rtc_gates 9>;
|
||||
};
|
||||
@@ -40,11 +40,6 @@ margin: Watchdog margin in seconds (default=60)
|
||||
nowayout: Disable watchdog shutdown on close
|
||||
(default=kernel config parameter)
|
||||
-------------------------------------------------
|
||||
at32ap700x_wdt:
|
||||
timeout: Timeout value. Limited to be 1 or 2 seconds. (default=2)
|
||||
nowayout: Watchdog cannot be stopped once started
|
||||
(default=kernel config parameter)
|
||||
-------------------------------------------------
|
||||
at91rm9200_wdt:
|
||||
wdt_time: Watchdog time in seconds. (default=5)
|
||||
nowayout: Watchdog cannot be stopped once started
|
||||
@@ -162,11 +157,6 @@ testmode: Watchdog test mode (1 = no reboot), default=0
|
||||
nowayout: Watchdog cannot be stopped once started
|
||||
(default=kernel config parameter)
|
||||
-------------------------------------------------
|
||||
ixp2000_wdt:
|
||||
heartbeat: Watchdog heartbeat in seconds (default 60s)
|
||||
nowayout: Watchdog cannot be stopped once started
|
||||
(default=kernel config parameter)
|
||||
-------------------------------------------------
|
||||
ixp4xx_wdt:
|
||||
heartbeat: Watchdog heartbeat in seconds (default 60s)
|
||||
nowayout: Watchdog cannot be stopped once started
|
||||
@@ -381,19 +371,6 @@ timeout: Watchdog timeout in seconds. 1 <= timeout <= 255, default=60.
|
||||
nowayout: Watchdog cannot be stopped once started
|
||||
(default=kernel config parameter)
|
||||
-------------------------------------------------
|
||||
w83697hf_wdt:
|
||||
wdt_io: w83697hf/hg WDT io port (default 0x2e, 0 = autodetect)
|
||||
timeout: Watchdog timeout in seconds. 1<= timeout <=255 (default=60)
|
||||
nowayout: Watchdog cannot be stopped once started
|
||||
(default=kernel config parameter)
|
||||
early_disable: Watchdog gets disabled at boot time (default=1)
|
||||
-------------------------------------------------
|
||||
w83697ug_wdt:
|
||||
wdt_io: w83697ug/uf WDT io port (default 0x2e)
|
||||
timeout: Watchdog timeout in seconds. 1<= timeout <=255 (default=60)
|
||||
nowayout: Watchdog cannot be stopped once started
|
||||
(default=kernel config parameter)
|
||||
-------------------------------------------------
|
||||
w83877f_wdt:
|
||||
timeout: Watchdog timeout in seconds. (1<=timeout<=3600, default=30)
|
||||
nowayout: Watchdog cannot be stopped once started
|
||||
|
||||
+2
-2
@@ -14991,8 +14991,8 @@ S: Maintained
|
||||
F: drivers/input/tablet/wacom_serial4.c
|
||||
|
||||
WATCHDOG DEVICE DRIVERS
|
||||
M: Wim Van Sebroeck <wim@iguana.be>
|
||||
R: Guenter Roeck <linux@roeck-us.net>
|
||||
M: Wim Van Sebroeck <wim@linux-watchdog.org>
|
||||
M: Guenter Roeck <linux@roeck-us.net>
|
||||
L: linux-watchdog@vger.kernel.org
|
||||
W: http://www.linux-watchdog.org/
|
||||
T: git git://www.linux-watchdog.org/linux-watchdog.git
|
||||
|
||||
+27
-16
@@ -328,16 +328,18 @@ config 977_WATCHDOG
|
||||
|
||||
Not sure? It's safe to say N.
|
||||
|
||||
config GEMINI_WATCHDOG
|
||||
tristate "Gemini watchdog"
|
||||
depends on ARCH_GEMINI
|
||||
config FTWDT010_WATCHDOG
|
||||
tristate "Faraday Technology FTWDT010 watchdog"
|
||||
depends on ARM || COMPILE_TEST
|
||||
select WATCHDOG_CORE
|
||||
default ARCH_GEMINI
|
||||
help
|
||||
Say Y here if to include support for the watchdog timer
|
||||
embedded in the Cortina Systems Gemini family of devices.
|
||||
Say Y here if to include support for the Faraday Technology
|
||||
FTWDT010 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.
|
||||
module will be called ftwdt010_wdt.
|
||||
|
||||
config IXP4XX_WATCHDOG
|
||||
tristate "IXP4xx Watchdog"
|
||||
@@ -748,12 +750,12 @@ config RENESAS_RZAWDT
|
||||
Renesas RZ/A SoCs. These watchdogs can be used to reset a system.
|
||||
|
||||
config ASPEED_WATCHDOG
|
||||
tristate "Aspeed 2400 watchdog support"
|
||||
tristate "Aspeed BMC watchdog support"
|
||||
depends on ARCH_ASPEED || COMPILE_TEST
|
||||
select WATCHDOG_CORE
|
||||
help
|
||||
Say Y here to include support for the watchdog timer
|
||||
in Apseed BMC SoCs.
|
||||
in Aspeed BMC SoCs.
|
||||
|
||||
This driver is required to reboot the SoC.
|
||||
|
||||
@@ -794,14 +796,23 @@ config UNIPHIER_WATCHDOG
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called uniphier_wdt.
|
||||
|
||||
# AVR32 Architecture
|
||||
|
||||
config AT32AP700X_WDT
|
||||
tristate "AT32AP700x watchdog"
|
||||
depends on CPU_AT32AP700X || COMPILE_TEST
|
||||
config RTD119X_WATCHDOG
|
||||
bool "Realtek RTD119x/RTD129x watchdog support"
|
||||
depends on ARCH_REALTEK || COMPILE_TEST
|
||||
depends on OF
|
||||
select WATCHDOG_CORE
|
||||
default ARCH_REALTEK
|
||||
help
|
||||
Watchdog timer embedded into AT32AP700x devices. This will reboot
|
||||
your system when the timeout is reached.
|
||||
Say Y here to include support for the watchdog timer in
|
||||
Realtek RTD1295 SoCs.
|
||||
|
||||
config SPRD_WATCHDOG
|
||||
tristate "Spreadtrum watchdog support"
|
||||
depends on ARCH_SPRD || COMPILE_TEST
|
||||
select WATCHDOG_CORE
|
||||
help
|
||||
Say Y here to include watchdog timer supported
|
||||
by Spreadtrum system.
|
||||
|
||||
# BLACKFIN Architecture
|
||||
|
||||
@@ -1458,7 +1469,7 @@ config RC32434_WDT
|
||||
|
||||
config INDYDOG
|
||||
tristate "Indy/I2 Hardware Watchdog"
|
||||
depends on SGI_HAS_INDYDOG || (MIPS && COMPILE_TEST)
|
||||
depends on SGI_HAS_INDYDOG
|
||||
help
|
||||
Hardware driver for the Indy's/I2's watchdog. This is a
|
||||
watchdog timer that will reboot the machine after a 60 second
|
||||
|
||||
@@ -46,7 +46,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_FTWDT010_WATCHDOG) += ftwdt010_wdt.o
|
||||
obj-$(CONFIG_IXP4XX_WATCHDOG) += ixp4xx_wdt.o
|
||||
obj-$(CONFIG_KS8695_WATCHDOG) += ks8695_wdt.o
|
||||
obj-$(CONFIG_S3C2410_WATCHDOG) += s3c2410_wdt.o
|
||||
@@ -88,9 +88,8 @@ obj-$(CONFIG_ASPEED_WATCHDOG) += aspeed_wdt.o
|
||||
obj-$(CONFIG_ZX2967_WATCHDOG) += zx2967_wdt.o
|
||||
obj-$(CONFIG_STM32_WATCHDOG) += stm32_iwdg.o
|
||||
obj-$(CONFIG_UNIPHIER_WATCHDOG) += uniphier_wdt.o
|
||||
|
||||
# AVR32 Architecture
|
||||
obj-$(CONFIG_AT32AP700X_WDT) += at32ap700x_wdt.o
|
||||
obj-$(CONFIG_RTD119X_WATCHDOG) += rtd119x_wdt.o
|
||||
obj-$(CONFIG_SPRD_WATCHDOG) += sprd_wdt.o
|
||||
|
||||
# BLACKFIN Architecture
|
||||
obj-$(CONFIG_BFIN_WDT) += bfin_wdt.o
|
||||
|
||||
@@ -181,7 +181,7 @@ static long advwdt_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
|
||||
if (advwdt_set_heartbeat(new_timeout))
|
||||
return -EINVAL;
|
||||
advwdt_ping();
|
||||
/* Fall */
|
||||
/* fall through */
|
||||
case WDIOC_GETTIMEOUT:
|
||||
return put_user(timeout, p);
|
||||
default:
|
||||
|
||||
@@ -223,8 +223,8 @@ static long ali_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
|
||||
if (ali_settimer(new_timeout))
|
||||
return -EINVAL;
|
||||
ali_keepalive();
|
||||
/* Fall */
|
||||
}
|
||||
/* fall through */
|
||||
case WDIOC_GETTIMEOUT:
|
||||
return put_user(timeout, p);
|
||||
default:
|
||||
|
||||
@@ -243,9 +243,13 @@ static int aspeed_wdt_probe(struct platform_device *pdev)
|
||||
if (of_property_read_bool(np, "aspeed,external-signal"))
|
||||
wdt->ctrl |= WDT_CTRL_WDT_EXT;
|
||||
|
||||
writel(wdt->ctrl, wdt->base + WDT_CTRL);
|
||||
|
||||
if (readl(wdt->base + WDT_CTRL) & WDT_CTRL_ENABLE) {
|
||||
/*
|
||||
* The watchdog is running, but invoke aspeed_wdt_start() to
|
||||
* write wdt->ctrl to WDT_CTRL to ensure the watchdog's
|
||||
* configuration conforms to the driver's expectations.
|
||||
* Primarily, ensure we're using the 1MHz clock source.
|
||||
*/
|
||||
aspeed_wdt_start(&wdt->wdd);
|
||||
set_bit(WDOG_HW_RUNNING, &wdt->wdd.status);
|
||||
}
|
||||
@@ -312,7 +316,18 @@ static struct platform_driver aspeed_watchdog_driver = {
|
||||
.of_match_table = of_match_ptr(aspeed_wdt_of_table),
|
||||
},
|
||||
};
|
||||
module_platform_driver(aspeed_watchdog_driver);
|
||||
|
||||
static int __init aspeed_wdt_init(void)
|
||||
{
|
||||
return platform_driver_register(&aspeed_watchdog_driver);
|
||||
}
|
||||
arch_initcall(aspeed_wdt_init);
|
||||
|
||||
static void __exit aspeed_wdt_exit(void)
|
||||
{
|
||||
platform_driver_unregister(&aspeed_watchdog_driver);
|
||||
}
|
||||
module_exit(aspeed_wdt_exit);
|
||||
|
||||
MODULE_DESCRIPTION("Aspeed Watchdog Driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
@@ -1,433 +0,0 @@
|
||||
/*
|
||||
* Watchdog driver for Atmel AT32AP700X devices
|
||||
*
|
||||
* Copyright (C) 2005-2006 Atmel Corporation
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*
|
||||
* Errata: WDT Clear is blocked after WDT Reset
|
||||
*
|
||||
* A watchdog timer event will, after reset, block writes to the WDT_CLEAR
|
||||
* register, preventing the program to clear the next Watchdog Timer Reset.
|
||||
*
|
||||
* If you still want to use the WDT after a WDT reset a small code can be
|
||||
* insterted at the startup checking the AVR32_PM.rcause register for WDT reset
|
||||
* and use a GPIO pin to reset the system. This method requires that one of the
|
||||
* GPIO pins are available and connected externally to the RESET_N pin. After
|
||||
* the GPIO pin has pulled down the reset line the GPIO will be reset and leave
|
||||
* the pin tristated with pullup.
|
||||
*/
|
||||
|
||||
#include <linux/init.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/moduleparam.h>
|
||||
#include <linux/miscdevice.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/watchdog.h>
|
||||
#include <linux/uaccess.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
#define TIMEOUT_MIN 1
|
||||
#define TIMEOUT_MAX 2
|
||||
#define TIMEOUT_DEFAULT TIMEOUT_MAX
|
||||
|
||||
/* module parameters */
|
||||
static int timeout = TIMEOUT_DEFAULT;
|
||||
module_param(timeout, int, 0);
|
||||
MODULE_PARM_DESC(timeout,
|
||||
"Timeout value. Limited to be 1 or 2 seconds. (default="
|
||||
__MODULE_STRING(TIMEOUT_DEFAULT) ")");
|
||||
|
||||
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) ")");
|
||||
|
||||
/* Watchdog registers and write/read macro */
|
||||
#define WDT_CTRL 0x00
|
||||
#define WDT_CTRL_EN 0
|
||||
#define WDT_CTRL_PSEL 8
|
||||
#define WDT_CTRL_KEY 24
|
||||
|
||||
#define WDT_CLR 0x04
|
||||
|
||||
#define WDT_RCAUSE 0x10
|
||||
#define WDT_RCAUSE_POR 0
|
||||
#define WDT_RCAUSE_EXT 2
|
||||
#define WDT_RCAUSE_WDT 3
|
||||
#define WDT_RCAUSE_JTAG 4
|
||||
#define WDT_RCAUSE_SERP 5
|
||||
|
||||
#define WDT_BIT(name) (1 << WDT_##name)
|
||||
#define WDT_BF(name, value) ((value) << WDT_##name)
|
||||
|
||||
#define wdt_readl(dev, reg) \
|
||||
__raw_readl((dev)->regs + WDT_##reg)
|
||||
#define wdt_writel(dev, reg, value) \
|
||||
__raw_writel((value), (dev)->regs + WDT_##reg)
|
||||
|
||||
struct wdt_at32ap700x {
|
||||
void __iomem *regs;
|
||||
spinlock_t io_lock;
|
||||
int timeout;
|
||||
int boot_status;
|
||||
unsigned long users;
|
||||
struct miscdevice miscdev;
|
||||
};
|
||||
|
||||
static struct wdt_at32ap700x *wdt;
|
||||
static char expect_release;
|
||||
|
||||
/*
|
||||
* Disable the watchdog.
|
||||
*/
|
||||
static inline void at32_wdt_stop(void)
|
||||
{
|
||||
unsigned long psel;
|
||||
|
||||
spin_lock(&wdt->io_lock);
|
||||
psel = wdt_readl(wdt, CTRL) & WDT_BF(CTRL_PSEL, 0x0f);
|
||||
wdt_writel(wdt, CTRL, psel | WDT_BF(CTRL_KEY, 0x55));
|
||||
wdt_writel(wdt, CTRL, psel | WDT_BF(CTRL_KEY, 0xaa));
|
||||
spin_unlock(&wdt->io_lock);
|
||||
}
|
||||
|
||||
/*
|
||||
* Enable and reset the watchdog.
|
||||
*/
|
||||
static inline void at32_wdt_start(void)
|
||||
{
|
||||
/* 0xf is 2^16 divider = 2 sec, 0xe is 2^15 divider = 1 sec */
|
||||
unsigned long psel = (wdt->timeout > 1) ? 0xf : 0xe;
|
||||
|
||||
spin_lock(&wdt->io_lock);
|
||||
wdt_writel(wdt, CTRL, WDT_BIT(CTRL_EN)
|
||||
| WDT_BF(CTRL_PSEL, psel)
|
||||
| WDT_BF(CTRL_KEY, 0x55));
|
||||
wdt_writel(wdt, CTRL, WDT_BIT(CTRL_EN)
|
||||
| WDT_BF(CTRL_PSEL, psel)
|
||||
| WDT_BF(CTRL_KEY, 0xaa));
|
||||
spin_unlock(&wdt->io_lock);
|
||||
}
|
||||
|
||||
/*
|
||||
* Pat the watchdog timer.
|
||||
*/
|
||||
static inline void at32_wdt_pat(void)
|
||||
{
|
||||
spin_lock(&wdt->io_lock);
|
||||
wdt_writel(wdt, CLR, 0x42);
|
||||
spin_unlock(&wdt->io_lock);
|
||||
}
|
||||
|
||||
/*
|
||||
* Watchdog device is opened, and watchdog starts running.
|
||||
*/
|
||||
static int at32_wdt_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
if (test_and_set_bit(1, &wdt->users))
|
||||
return -EBUSY;
|
||||
|
||||
at32_wdt_start();
|
||||
return nonseekable_open(inode, file);
|
||||
}
|
||||
|
||||
/*
|
||||
* Close the watchdog device.
|
||||
*/
|
||||
static int at32_wdt_close(struct inode *inode, struct file *file)
|
||||
{
|
||||
if (expect_release == 42) {
|
||||
at32_wdt_stop();
|
||||
} else {
|
||||
dev_dbg(wdt->miscdev.parent,
|
||||
"unexpected close, not stopping watchdog!\n");
|
||||
at32_wdt_pat();
|
||||
}
|
||||
clear_bit(1, &wdt->users);
|
||||
expect_release = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Change the watchdog time interval.
|
||||
*/
|
||||
static int at32_wdt_settimeout(int time)
|
||||
{
|
||||
/*
|
||||
* All counting occurs at 1 / SLOW_CLOCK (32 kHz) and max prescaler is
|
||||
* 2 ^ 16 allowing up to 2 seconds timeout.
|
||||
*/
|
||||
if ((time < TIMEOUT_MIN) || (time > TIMEOUT_MAX))
|
||||
return -EINVAL;
|
||||
|
||||
/*
|
||||
* Set new watchdog time. It will be used when at32_wdt_start() is
|
||||
* called.
|
||||
*/
|
||||
wdt->timeout = time;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Get the watchdog status.
|
||||
*/
|
||||
static int at32_wdt_get_status(void)
|
||||
{
|
||||
int rcause;
|
||||
int status = 0;
|
||||
|
||||
rcause = wdt_readl(wdt, RCAUSE);
|
||||
|
||||
switch (rcause) {
|
||||
case WDT_BIT(RCAUSE_EXT):
|
||||
status = WDIOF_EXTERN1;
|
||||
break;
|
||||
case WDT_BIT(RCAUSE_WDT):
|
||||
status = WDIOF_CARDRESET;
|
||||
break;
|
||||
case WDT_BIT(RCAUSE_POR): /* fall through */
|
||||
case WDT_BIT(RCAUSE_JTAG): /* fall through */
|
||||
case WDT_BIT(RCAUSE_SERP): /* fall through */
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
static const struct watchdog_info at32_wdt_info = {
|
||||
.identity = "at32ap700x watchdog",
|
||||
.options = WDIOF_SETTIMEOUT |
|
||||
WDIOF_KEEPALIVEPING |
|
||||
WDIOF_MAGICCLOSE,
|
||||
};
|
||||
|
||||
/*
|
||||
* Handle commands from user-space.
|
||||
*/
|
||||
static long at32_wdt_ioctl(struct file *file,
|
||||
unsigned int cmd, unsigned long arg)
|
||||
{
|
||||
int ret = -ENOTTY;
|
||||
int time;
|
||||
void __user *argp = (void __user *)arg;
|
||||
int __user *p = argp;
|
||||
|
||||
switch (cmd) {
|
||||
case WDIOC_GETSUPPORT:
|
||||
ret = copy_to_user(argp, &at32_wdt_info,
|
||||
sizeof(at32_wdt_info)) ? -EFAULT : 0;
|
||||
break;
|
||||
case WDIOC_GETSTATUS:
|
||||
ret = put_user(0, p);
|
||||
break;
|
||||
case WDIOC_GETBOOTSTATUS:
|
||||
ret = put_user(wdt->boot_status, p);
|
||||
break;
|
||||
case WDIOC_SETOPTIONS:
|
||||
ret = get_user(time, p);
|
||||
if (ret)
|
||||
break;
|
||||
if (time & WDIOS_DISABLECARD)
|
||||
at32_wdt_stop();
|
||||
if (time & WDIOS_ENABLECARD)
|
||||
at32_wdt_start();
|
||||
ret = 0;
|
||||
break;
|
||||
case WDIOC_KEEPALIVE:
|
||||
at32_wdt_pat();
|
||||
ret = 0;
|
||||
break;
|
||||
case WDIOC_SETTIMEOUT:
|
||||
ret = get_user(time, p);
|
||||
if (ret)
|
||||
break;
|
||||
ret = at32_wdt_settimeout(time);
|
||||
if (ret)
|
||||
break;
|
||||
/* Enable new time value */
|
||||
at32_wdt_start();
|
||||
/* fall through */
|
||||
case WDIOC_GETTIMEOUT:
|
||||
ret = put_user(wdt->timeout, p);
|
||||
break;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static ssize_t at32_wdt_write(struct file *file, const char __user *data,
|
||||
size_t len, loff_t *ppos)
|
||||
{
|
||||
/* See if we got the magic character 'V' and reload the timer */
|
||||
if (len) {
|
||||
if (!nowayout) {
|
||||
size_t i;
|
||||
|
||||
/*
|
||||
* note: just in case someone wrote the magic
|
||||
* character five months ago...
|
||||
*/
|
||||
expect_release = 0;
|
||||
|
||||
/*
|
||||
* scan to see whether or not we got the magic
|
||||
* character
|
||||
*/
|
||||
for (i = 0; i != len; i++) {
|
||||
char c;
|
||||
if (get_user(c, data + i))
|
||||
return -EFAULT;
|
||||
if (c == 'V')
|
||||
expect_release = 42;
|
||||
}
|
||||
}
|
||||
/* someone wrote to us, we should pat the watchdog */
|
||||
at32_wdt_pat();
|
||||
}
|
||||
return len;
|
||||
}
|
||||
|
||||
static const struct file_operations at32_wdt_fops = {
|
||||
.owner = THIS_MODULE,
|
||||
.llseek = no_llseek,
|
||||
.unlocked_ioctl = at32_wdt_ioctl,
|
||||
.open = at32_wdt_open,
|
||||
.release = at32_wdt_close,
|
||||
.write = at32_wdt_write,
|
||||
};
|
||||
|
||||
static int __init at32_wdt_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct resource *regs;
|
||||
int ret;
|
||||
|
||||
if (wdt) {
|
||||
dev_dbg(&pdev->dev, "only 1 wdt instance supported.\n");
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
if (!regs) {
|
||||
dev_dbg(&pdev->dev, "missing mmio resource\n");
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
wdt = devm_kzalloc(&pdev->dev, sizeof(struct wdt_at32ap700x),
|
||||
GFP_KERNEL);
|
||||
if (!wdt)
|
||||
return -ENOMEM;
|
||||
|
||||
wdt->regs = devm_ioremap(&pdev->dev, regs->start, resource_size(regs));
|
||||
if (!wdt->regs) {
|
||||
ret = -ENOMEM;
|
||||
dev_dbg(&pdev->dev, "could not map I/O memory\n");
|
||||
goto err_free;
|
||||
}
|
||||
|
||||
spin_lock_init(&wdt->io_lock);
|
||||
wdt->boot_status = at32_wdt_get_status();
|
||||
|
||||
/* Work-around for watchdog silicon errata. */
|
||||
if (wdt->boot_status & WDIOF_CARDRESET) {
|
||||
dev_info(&pdev->dev, "CPU must be reset with external "
|
||||
"reset or POR due to silicon errata.\n");
|
||||
ret = -EIO;
|
||||
goto err_free;
|
||||
} else {
|
||||
wdt->users = 0;
|
||||
}
|
||||
|
||||
wdt->miscdev.minor = WATCHDOG_MINOR;
|
||||
wdt->miscdev.name = "watchdog";
|
||||
wdt->miscdev.fops = &at32_wdt_fops;
|
||||
wdt->miscdev.parent = &pdev->dev;
|
||||
|
||||
platform_set_drvdata(pdev, wdt);
|
||||
|
||||
if (at32_wdt_settimeout(timeout)) {
|
||||
at32_wdt_settimeout(TIMEOUT_DEFAULT);
|
||||
dev_dbg(&pdev->dev,
|
||||
"default timeout invalid, set to %d sec.\n",
|
||||
TIMEOUT_DEFAULT);
|
||||
}
|
||||
|
||||
ret = misc_register(&wdt->miscdev);
|
||||
if (ret) {
|
||||
dev_dbg(&pdev->dev, "failed to register wdt miscdev\n");
|
||||
goto err_free;
|
||||
}
|
||||
|
||||
dev_info(&pdev->dev,
|
||||
"AT32AP700X WDT at 0x%p, timeout %d sec (nowayout=%d)\n",
|
||||
wdt->regs, wdt->timeout, nowayout);
|
||||
|
||||
return 0;
|
||||
|
||||
err_free:
|
||||
wdt = NULL;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int __exit at32_wdt_remove(struct platform_device *pdev)
|
||||
{
|
||||
if (wdt && platform_get_drvdata(pdev) == wdt) {
|
||||
/* Stop the timer before we leave */
|
||||
if (!nowayout)
|
||||
at32_wdt_stop();
|
||||
|
||||
misc_deregister(&wdt->miscdev);
|
||||
wdt = NULL;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void at32_wdt_shutdown(struct platform_device *pdev)
|
||||
{
|
||||
at32_wdt_stop();
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
static int at32_wdt_suspend(struct platform_device *pdev, pm_message_t message)
|
||||
{
|
||||
at32_wdt_stop();
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int at32_wdt_resume(struct platform_device *pdev)
|
||||
{
|
||||
if (wdt->users)
|
||||
at32_wdt_start();
|
||||
return 0;
|
||||
}
|
||||
#else
|
||||
#define at32_wdt_suspend NULL
|
||||
#define at32_wdt_resume NULL
|
||||
#endif
|
||||
|
||||
/* work with hotplug and coldplug */
|
||||
MODULE_ALIAS("platform:at32_wdt");
|
||||
|
||||
static struct platform_driver at32_wdt_driver = {
|
||||
.remove = __exit_p(at32_wdt_remove),
|
||||
.suspend = at32_wdt_suspend,
|
||||
.resume = at32_wdt_resume,
|
||||
.driver = {
|
||||
.name = "at32_wdt",
|
||||
},
|
||||
.shutdown = at32_wdt_shutdown,
|
||||
};
|
||||
|
||||
module_platform_driver_probe(at32_wdt_driver, at32_wdt_probe);
|
||||
|
||||
MODULE_AUTHOR("Hans-Christian Egtvedt <egtvedt@samfundet.no>");
|
||||
MODULE_DESCRIPTION("Watchdog driver for Atmel AT32AP700X");
|
||||
MODULE_LICENSE("GPL");
|
||||
@@ -46,22 +46,6 @@ static void da9062_set_window_start(struct da9062_watchdog *wdt)
|
||||
wdt->j_time_stamp = jiffies;
|
||||
}
|
||||
|
||||
static void da9062_apply_window_protection(struct da9062_watchdog *wdt)
|
||||
{
|
||||
unsigned long delay = msecs_to_jiffies(DA9062_RESET_PROTECTION_MS);
|
||||
unsigned long timeout = wdt->j_time_stamp + delay;
|
||||
unsigned long now = jiffies;
|
||||
unsigned int diff_ms;
|
||||
|
||||
/* if time-limit has not elapsed then wait for remainder */
|
||||
if (time_before(now, timeout)) {
|
||||
diff_ms = jiffies_to_msecs(timeout-now);
|
||||
dev_dbg(wdt->hw->dev,
|
||||
"Kicked too quickly. Delaying %u msecs\n", diff_ms);
|
||||
msleep(diff_ms);
|
||||
}
|
||||
}
|
||||
|
||||
static unsigned int da9062_wdt_timeout_to_sel(unsigned int secs)
|
||||
{
|
||||
unsigned int i;
|
||||
@@ -78,8 +62,6 @@ static int da9062_reset_watchdog_timer(struct da9062_watchdog *wdt)
|
||||
{
|
||||
int ret;
|
||||
|
||||
da9062_apply_window_protection(wdt);
|
||||
|
||||
ret = regmap_update_bits(wdt->hw->regmap,
|
||||
DA9062AA_CONTROL_F,
|
||||
DA9062AA_WATCHDOG_MASK,
|
||||
@@ -100,6 +82,13 @@ static int da9062_wdt_update_timeout_register(struct da9062_watchdog *wdt,
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
regmap_update_bits(chip->regmap,
|
||||
DA9062AA_CONTROL_D,
|
||||
DA9062AA_TWDSCALE_MASK,
|
||||
DA9062_TWDSCALE_DISABLE);
|
||||
|
||||
usleep_range(150, 300);
|
||||
|
||||
return regmap_update_bits(chip->regmap,
|
||||
DA9062AA_CONTROL_D,
|
||||
DA9062AA_TWDSCALE_MASK,
|
||||
@@ -175,6 +164,25 @@ static int da9062_wdt_set_timeout(struct watchdog_device *wdd,
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int da9062_wdt_restart(struct watchdog_device *wdd, unsigned long action,
|
||||
void *data)
|
||||
{
|
||||
struct da9062_watchdog *wdt = watchdog_get_drvdata(wdd);
|
||||
int ret;
|
||||
|
||||
ret = regmap_write(wdt->hw->regmap,
|
||||
DA9062AA_CONTROL_F,
|
||||
DA9062AA_SHUTDOWN_MASK);
|
||||
if (ret)
|
||||
dev_alert(wdt->hw->dev, "Failed to shutdown (err = %d)\n",
|
||||
ret);
|
||||
|
||||
/* wait for reset to assert... */
|
||||
mdelay(500);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct watchdog_info da9062_watchdog_info = {
|
||||
.options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING,
|
||||
.identity = "DA9062 WDT",
|
||||
@@ -186,6 +194,7 @@ static const struct watchdog_ops da9062_watchdog_ops = {
|
||||
.stop = da9062_wdt_stop,
|
||||
.ping = da9062_wdt_ping,
|
||||
.set_timeout = da9062_wdt_set_timeout,
|
||||
.restart = da9062_wdt_restart,
|
||||
};
|
||||
|
||||
static const struct of_device_id da9062_compatible_id_table[] = {
|
||||
@@ -215,10 +224,13 @@ static int da9062_wdt_probe(struct platform_device *pdev)
|
||||
wdt->wdtdev.ops = &da9062_watchdog_ops;
|
||||
wdt->wdtdev.min_timeout = DA9062_WDT_MIN_TIMEOUT;
|
||||
wdt->wdtdev.max_timeout = DA9062_WDT_MAX_TIMEOUT;
|
||||
wdt->wdtdev.min_hw_heartbeat_ms = DA9062_RESET_PROTECTION_MS;
|
||||
wdt->wdtdev.timeout = DA9062_WDG_DEFAULT_TIMEOUT;
|
||||
wdt->wdtdev.status = WATCHDOG_NOWAYOUT_INIT_STATUS;
|
||||
wdt->wdtdev.parent = &pdev->dev;
|
||||
|
||||
watchdog_set_restart_priority(&wdt->wdtdev, 128);
|
||||
|
||||
watchdog_set_drvdata(&wdt->wdtdev, wdt);
|
||||
|
||||
ret = devm_watchdog_register_device(&pdev->dev, &wdt->wdtdev);
|
||||
|
||||
@@ -140,6 +140,42 @@ static unsigned int davinci_wdt_get_timeleft(struct watchdog_device *wdd)
|
||||
return wdd->timeout - timer_counter;
|
||||
}
|
||||
|
||||
static int davinci_wdt_restart(struct watchdog_device *wdd,
|
||||
unsigned long action, void *data)
|
||||
{
|
||||
struct davinci_wdt_device *davinci_wdt = watchdog_get_drvdata(wdd);
|
||||
u32 tgcr, wdtcr;
|
||||
|
||||
/* disable, internal clock source */
|
||||
iowrite32(0, davinci_wdt->base + TCR);
|
||||
|
||||
/* reset timer, set mode to 64-bit watchdog, and unreset */
|
||||
tgcr = 0;
|
||||
iowrite32(tgcr, davinci_wdt->base + TGCR);
|
||||
tgcr = TIMMODE_64BIT_WDOG | TIM12RS_UNRESET | TIM34RS_UNRESET;
|
||||
iowrite32(tgcr, davinci_wdt->base + TGCR);
|
||||
|
||||
/* clear counter and period regs */
|
||||
iowrite32(0, davinci_wdt->base + TIM12);
|
||||
iowrite32(0, davinci_wdt->base + TIM34);
|
||||
iowrite32(0, davinci_wdt->base + PRD12);
|
||||
iowrite32(0, davinci_wdt->base + PRD34);
|
||||
|
||||
/* put watchdog in pre-active state */
|
||||
wdtcr = WDKEY_SEQ0 | WDEN;
|
||||
iowrite32(wdtcr, davinci_wdt->base + WDTCR);
|
||||
|
||||
/* put watchdog in active state */
|
||||
wdtcr = WDKEY_SEQ1 | WDEN;
|
||||
iowrite32(wdtcr, davinci_wdt->base + WDTCR);
|
||||
|
||||
/* write an invalid value to the WDKEY field to trigger a restart */
|
||||
wdtcr = 0x00004000;
|
||||
iowrite32(wdtcr, davinci_wdt->base + WDTCR);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct watchdog_info davinci_wdt_info = {
|
||||
.options = WDIOF_KEEPALIVEPING,
|
||||
.identity = "DaVinci/Keystone Watchdog",
|
||||
@@ -151,6 +187,7 @@ static const struct watchdog_ops davinci_wdt_ops = {
|
||||
.stop = davinci_wdt_ping,
|
||||
.ping = davinci_wdt_ping,
|
||||
.get_timeleft = davinci_wdt_get_timeleft,
|
||||
.restart = davinci_wdt_restart,
|
||||
};
|
||||
|
||||
static int davinci_wdt_probe(struct platform_device *pdev)
|
||||
@@ -195,6 +232,7 @@ static int davinci_wdt_probe(struct platform_device *pdev)
|
||||
|
||||
watchdog_set_drvdata(wdd, davinci_wdt);
|
||||
watchdog_set_nowayout(wdd, 1);
|
||||
watchdog_set_restart_priority(wdd, 128);
|
||||
|
||||
wdt_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
davinci_wdt->base = devm_ioremap_resource(dev, wdt_mem);
|
||||
|
||||
@@ -127,14 +127,27 @@ static int dw_wdt_start(struct watchdog_device *wdd)
|
||||
|
||||
dw_wdt_set_timeout(wdd, wdd->timeout);
|
||||
|
||||
set_bit(WDOG_HW_RUNNING, &wdd->status);
|
||||
|
||||
writel(WDOG_CONTROL_REG_WDT_EN_MASK,
|
||||
dw_wdt->regs + WDOG_CONTROL_REG_OFFSET);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dw_wdt_stop(struct watchdog_device *wdd)
|
||||
{
|
||||
struct dw_wdt *dw_wdt = to_dw_wdt(wdd);
|
||||
|
||||
if (!dw_wdt->rst) {
|
||||
set_bit(WDOG_HW_RUNNING, &wdd->status);
|
||||
return 0;
|
||||
}
|
||||
|
||||
reset_control_assert(dw_wdt->rst);
|
||||
reset_control_deassert(dw_wdt->rst);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dw_wdt_restart(struct watchdog_device *wdd,
|
||||
unsigned long action, void *data)
|
||||
{
|
||||
@@ -173,6 +186,7 @@ static const struct watchdog_info dw_wdt_ident = {
|
||||
static const struct watchdog_ops dw_wdt_ops = {
|
||||
.owner = THIS_MODULE,
|
||||
.start = dw_wdt_start,
|
||||
.stop = dw_wdt_stop,
|
||||
.ping = dw_wdt_ping,
|
||||
.set_timeout = dw_wdt_set_timeout,
|
||||
.get_timeleft = dw_wdt_get_timeleft,
|
||||
|
||||
@@ -290,7 +290,7 @@ static long eurwdt_ioctl(struct file *file,
|
||||
eurwdt_timeout = time;
|
||||
eurwdt_set_timeout(time);
|
||||
spin_unlock(&eurwdt_lock);
|
||||
/* Fall */
|
||||
/* fall through */
|
||||
|
||||
case WDIOC_GETTIMEOUT:
|
||||
return put_user(eurwdt_timeout, p);
|
||||
|
||||
@@ -627,7 +627,7 @@ static long watchdog_ioctl(struct file *file, unsigned int cmd,
|
||||
|
||||
if (new_options & WDIOS_ENABLECARD)
|
||||
return watchdog_start();
|
||||
|
||||
/* fall through */
|
||||
|
||||
case WDIOC_KEEPALIVE:
|
||||
watchdog_keepalive();
|
||||
@@ -641,7 +641,7 @@ static long watchdog_ioctl(struct file *file, unsigned int cmd,
|
||||
return -EINVAL;
|
||||
|
||||
watchdog_keepalive();
|
||||
/* Fall */
|
||||
/* fall through */
|
||||
|
||||
case WDIOC_GETTIMEOUT:
|
||||
return put_user(watchdog.timeout, uarg.i);
|
||||
|
||||
@@ -0,0 +1,236 @@
|
||||
/*
|
||||
* Watchdog driver for Faraday Technology FTWDT010
|
||||
*
|
||||
* 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 FTWDT010_WDCOUNTER 0x0
|
||||
#define FTWDT010_WDLOAD 0x4
|
||||
#define FTWDT010_WDRESTART 0x8
|
||||
#define FTWDT010_WDCR 0xC
|
||||
|
||||
#define WDRESTART_MAGIC 0x5AB9
|
||||
|
||||
#define WDCR_CLOCK_5MHZ BIT(4)
|
||||
#define WDCR_WDEXT BIT(3)
|
||||
#define WDCR_WDINTR BIT(2)
|
||||
#define WDCR_SYS_RST BIT(1)
|
||||
#define WDCR_ENABLE BIT(0)
|
||||
|
||||
#define WDT_CLOCK 5000000 /* 5 MHz */
|
||||
|
||||
struct ftwdt010_wdt {
|
||||
struct watchdog_device wdd;
|
||||
struct device *dev;
|
||||
void __iomem *base;
|
||||
bool has_irq;
|
||||
};
|
||||
|
||||
static inline
|
||||
struct ftwdt010_wdt *to_ftwdt010_wdt(struct watchdog_device *wdd)
|
||||
{
|
||||
return container_of(wdd, struct ftwdt010_wdt, wdd);
|
||||
}
|
||||
|
||||
static int ftwdt010_wdt_start(struct watchdog_device *wdd)
|
||||
{
|
||||
struct ftwdt010_wdt *gwdt = to_ftwdt010_wdt(wdd);
|
||||
u32 enable;
|
||||
|
||||
writel(wdd->timeout * WDT_CLOCK, gwdt->base + FTWDT010_WDLOAD);
|
||||
writel(WDRESTART_MAGIC, gwdt->base + FTWDT010_WDRESTART);
|
||||
/* set clock before enabling */
|
||||
enable = WDCR_CLOCK_5MHZ | WDCR_SYS_RST;
|
||||
writel(enable, gwdt->base + FTWDT010_WDCR);
|
||||
if (gwdt->has_irq)
|
||||
enable |= WDCR_WDINTR;
|
||||
enable |= WDCR_ENABLE;
|
||||
writel(enable, gwdt->base + FTWDT010_WDCR);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ftwdt010_wdt_stop(struct watchdog_device *wdd)
|
||||
{
|
||||
struct ftwdt010_wdt *gwdt = to_ftwdt010_wdt(wdd);
|
||||
|
||||
writel(0, gwdt->base + FTWDT010_WDCR);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ftwdt010_wdt_ping(struct watchdog_device *wdd)
|
||||
{
|
||||
struct ftwdt010_wdt *gwdt = to_ftwdt010_wdt(wdd);
|
||||
|
||||
writel(WDRESTART_MAGIC, gwdt->base + FTWDT010_WDRESTART);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ftwdt010_wdt_set_timeout(struct watchdog_device *wdd,
|
||||
unsigned int timeout)
|
||||
{
|
||||
wdd->timeout = timeout;
|
||||
if (watchdog_active(wdd))
|
||||
ftwdt010_wdt_start(wdd);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static irqreturn_t ftwdt010_wdt_interrupt(int irq, void *data)
|
||||
{
|
||||
struct ftwdt010_wdt *gwdt = data;
|
||||
|
||||
watchdog_notify_pretimeout(&gwdt->wdd);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static const struct watchdog_ops ftwdt010_wdt_ops = {
|
||||
.start = ftwdt010_wdt_start,
|
||||
.stop = ftwdt010_wdt_stop,
|
||||
.ping = ftwdt010_wdt_ping,
|
||||
.set_timeout = ftwdt010_wdt_set_timeout,
|
||||
.owner = THIS_MODULE,
|
||||
};
|
||||
|
||||
static const struct watchdog_info ftwdt010_wdt_info = {
|
||||
.options = WDIOF_KEEPALIVEPING
|
||||
| WDIOF_MAGICCLOSE
|
||||
| WDIOF_SETTIMEOUT,
|
||||
.identity = KBUILD_MODNAME,
|
||||
};
|
||||
|
||||
|
||||
static int ftwdt010_wdt_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct resource *res;
|
||||
struct ftwdt010_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);
|
||||
|
||||
gwdt->dev = dev;
|
||||
gwdt->wdd.info = &ftwdt010_wdt_info;
|
||||
gwdt->wdd.ops = &ftwdt010_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 + FTWDT010_WDCR);
|
||||
if (reg & WDCR_ENABLE) {
|
||||
/* Watchdog was enabled by the bootloader, disable it. */
|
||||
reg &= ~WDCR_ENABLE;
|
||||
writel(reg, gwdt->base + FTWDT010_WDCR);
|
||||
}
|
||||
|
||||
irq = platform_get_irq(pdev, 0);
|
||||
if (irq) {
|
||||
ret = devm_request_irq(dev, irq, ftwdt010_wdt_interrupt, 0,
|
||||
"watchdog bark", gwdt);
|
||||
if (ret)
|
||||
return ret;
|
||||
gwdt->has_irq = true;
|
||||
}
|
||||
|
||||
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, "FTWDT010 watchdog driver enabled\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __maybe_unused ftwdt010_wdt_suspend(struct device *dev)
|
||||
{
|
||||
struct ftwdt010_wdt *gwdt = dev_get_drvdata(dev);
|
||||
unsigned int reg;
|
||||
|
||||
reg = readw(gwdt->base + FTWDT010_WDCR);
|
||||
reg &= ~WDCR_ENABLE;
|
||||
writel(reg, gwdt->base + FTWDT010_WDCR);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __maybe_unused ftwdt010_wdt_resume(struct device *dev)
|
||||
{
|
||||
struct ftwdt010_wdt *gwdt = dev_get_drvdata(dev);
|
||||
unsigned int reg;
|
||||
|
||||
if (watchdog_active(&gwdt->wdd)) {
|
||||
reg = readw(gwdt->base + FTWDT010_WDCR);
|
||||
reg |= WDCR_ENABLE;
|
||||
writel(reg, gwdt->base + FTWDT010_WDCR);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct dev_pm_ops ftwdt010_wdt_dev_pm_ops = {
|
||||
SET_SYSTEM_SLEEP_PM_OPS(ftwdt010_wdt_suspend,
|
||||
ftwdt010_wdt_resume)
|
||||
};
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
static const struct of_device_id ftwdt010_wdt_match[] = {
|
||||
{ .compatible = "faraday,ftwdt010" },
|
||||
{ .compatible = "cortina,gemini-watchdog" },
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, ftwdt010_wdt_match);
|
||||
#endif
|
||||
|
||||
static struct platform_driver ftwdt010_wdt_driver = {
|
||||
.probe = ftwdt010_wdt_probe,
|
||||
.driver = {
|
||||
.name = "ftwdt010-wdt",
|
||||
.of_match_table = of_match_ptr(ftwdt010_wdt_match),
|
||||
.pm = &ftwdt010_wdt_dev_pm_ops,
|
||||
},
|
||||
};
|
||||
module_platform_driver(ftwdt010_wdt_driver);
|
||||
MODULE_AUTHOR("Linus Walleij");
|
||||
MODULE_DESCRIPTION("Watchdog driver for Faraday Technology FTWDT010");
|
||||
MODULE_LICENSE("GPL");
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user