Commit 7bccb9f1 authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'linux-watchdog-5.4-rc1' of git://www.linux-watchdog.org/linux-watchdog

Pull watchdog updates from Wim Van Sebroeck:

 - addition of AST2600, i.MX7ULP and F81803 watchdog support

 - removal of the w90x900 and ks8695 drivers

 - ziirave_wdt improvements

 - small fixes and improvements

* tag 'linux-watchdog-5.4-rc1' of git://www.linux-watchdog.org/linux-watchdog: (51 commits)
  watchdog: f71808e_wdt: Add F81803 support
  watchdog: qcom: remove unnecessary variable from private storage
  watchdog: qcom: support pre-timeout when the bark irq is available
  watchdog: imx_sc: this patch just fixes whitespaces
  watchdog: apseed: Add access_cs0 option for alt-boot
  watchdog: aspeed: add support for dual boot
  watchdog: orion_wdt: use timer1 as a pretimeout
  watchdog: Add i.MX7ULP watchdog support
  dt-bindings: watchdog: Add i.MX7ULP bindings
  dt-bindings: watchdog: sun4i: Add the watchdog clock
  dt-bindings: watchdog: sun4i: Add the watchdog interrupts
  dt-bindings: watchdog: Convert Allwinner watchdog to a schema
  dt-bindings: watchdog: Add YAML schemas for the generic watchdog bindings
  watchdog: aspeed: Add support for AST2600
  dt-bindings: watchdog: Add ast2600 compatible
  watchdog: ziirave_wdt: Update checked I2C functionality mask
  watchdog: ziirave_wdt: Drop ziirave_firm_write_block_data()
  watchdog: ziirave_wdt: Fix DOWNLOAD_START payload
  watchdog: ziirave_wdt: Drop status polling code
  watchdog: ziirave_wdt: Fix RESET_PROCESSOR payload
  ...
parents 289991ce ca2fc5ef
......@@ -72,3 +72,37 @@ Description:
It is a read/write file. When read, the currently assigned
pretimeout governor is returned. When written, it sets
the pretimeout governor.
What: /sys/class/watchdog/watchdog1/access_cs0
Date: August 2019
Contact: Ivan Mikhaylov <i.mikhaylov@yadro.com>,
Alexander Amelkin <a.amelkin@yadro.com>
Description:
It is a read/write file. This attribute exists only if the
system has booted from the alternate flash chip due to
expiration of a watchdog timer of AST2400/AST2500 when
alternate boot function was enabled with 'aspeed,alt-boot'
devicetree option for that watchdog or with an appropriate
h/w strapping (for WDT2 only).
At alternate flash the 'access_cs0' sysfs node provides:
ast2400: a way to get access to the primary SPI flash
chip at CS0 after booting from the alternate
chip at CS1.
ast2500: a way to restore the normal address mapping
from (CS0->CS1, CS1->CS0) to (CS0->CS0,
CS1->CS1).
Clearing the boot code selection and timeout counter also
resets to the initial state the chip select line mapping. When
the SoC is in normal mapping state (i.e. booted from CS0),
clearing those bits does nothing for both versions of the SoC.
For alternate boot mode (booted from CS1 due to wdt2
expiration) the behavior differs as described above.
This option can be used with wdt2 (watchdog1) only.
When read, the current status of the boot code selection is
shown. When written with any non-zero value, it clears
the boot code selection and the timeout counter, which results
in chipselect reset for AST2400/AST2500.
# SPDX-License-Identifier: GPL-2.0
%YAML 1.2
---
$id: http://devicetree.org/schemas/watchdog/allwinner,sun4i-a10-wdt.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Allwinner A10 Watchdog Device Tree Bindings
allOf:
- $ref: "watchdog.yaml#"
maintainers:
- Chen-Yu Tsai <wens@csie.org>
- Maxime Ripard <maxime.ripard@bootlin.com>
properties:
compatible:
oneOf:
- const: allwinner,sun4i-a10-wdt
- const: allwinner,sun6i-a31-wdt
- items:
- const: allwinner,sun50i-a64-wdt
- const: allwinner,sun6i-a31-wdt
- items:
- const: allwinner,sun50i-h6-wdt
- const: allwinner,sun6i-a31-wdt
- items:
- const: allwinner,suniv-f1c100s-wdt
- const: allwinner,sun4i-a10-wdt
reg:
maxItems: 1
clocks:
maxItems: 1
interrupts:
maxItems: 1
required:
- compatible
- reg
- clocks
- interrupts
unevaluatedProperties: false
examples:
- |
wdt: watchdog@1c20c90 {
compatible = "allwinner,sun4i-a10-wdt";
reg = <0x01c20c90 0x10>;
interrupts = <24>;
clocks = <&osc24M>;
timeout-sec = <10>;
};
...
......@@ -4,6 +4,7 @@ Required properties:
- compatible: must be one of:
- "aspeed,ast2400-wdt"
- "aspeed,ast2500-wdt"
- "aspeed,ast2600-wdt"
- reg: physical base address of the controller and length of memory mapped
region
......
* Freescale i.MX7ULP Watchdog Timer (WDT) Controller
Required properties:
- compatible : Should be "fsl,imx7ulp-wdt"
- reg : Should contain WDT registers location and length
- interrupts : Should contain WDT interrupt
- clocks: Should contain a phandle pointing to the gated peripheral clock.
Optional properties:
- timeout-sec : Contains the watchdog timeout in seconds
Examples:
wdog1: watchdog@403d0000 {
compatible = "fsl,imx7ulp-wdt";
reg = <0x403d0000 0x10000>;
interrupts = <GIC_SPI 55 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&pcc2 IMX7ULP_CLK_WDG1>;
assigned-clocks = <&pcc2 IMX7ULP_CLK_WDG1>;
assigned-clocks-parents = <&scg1 IMX7ULP_CLK_FIRC_BUS_CLK>;
timeout-sec = <40>;
};
Allwinner SoCs Watchdog timer
Required properties:
- compatible : should be one of
"allwinner,sun4i-a10-wdt"
"allwinner,sun6i-a31-wdt"
"allwinner,sun50i-a64-wdt","allwinner,sun6i-a31-wdt"
"allwinner,sun50i-h6-wdt","allwinner,sun6i-a31-wdt"
"allwinner,suniv-f1c100s-wdt", "allwinner,sun4i-a10-wdt"
- reg : Specifies base physical address and size of the registers.
Optional properties:
- timeout-sec : Contains the watchdog timeout in seconds
Example:
wdt: watchdog@1c20c90 {
compatible = "allwinner,sun4i-a10-wdt";
reg = <0x01c20c90 0x10>;
timeout-sec = <10>;
};
# SPDX-License-Identifier: GPL-2.0
%YAML 1.2
---
$id: http://devicetree.org/schemas/watchdog/watchdog.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Watchdog Generic Bindings
maintainers:
- Guenter Roeck <linux@roeck-us.net>
- Wim Van Sebroeck <wim@linux-watchdog.org>
description: |
This document describes generic bindings which can be used to
describe watchdog devices in a device tree.
properties:
$nodename:
pattern: "^watchdog(@.*|-[0-9a-f])?$"
timeout-sec:
$ref: /schemas/types.yaml#/definitions/uint32
description:
Contains the watchdog timeout in seconds.
...
......@@ -301,15 +301,6 @@ ixp4xx_wdt:
-------------------------------------------------
ks8695_wdt:
wdt_time:
Watchdog time in seconds. (default=5)
nowayout:
Watchdog cannot be stopped once started
(default=kernel config parameter)
-------------------------------------------------
machzwd:
nowayout:
Watchdog cannot be stopped once started
......@@ -375,16 +366,6 @@ nic7018_wdt:
-------------------------------------------------
nuc900_wdt:
heartbeat:
Watchdog heartbeats in seconds.
(default = 15)
nowayout:
Watchdog cannot be stopped once started
(default=kernel config parameter)
-------------------------------------------------
omap_wdt:
timer_margin:
initial watchdog timeout (in seconds)
......
......@@ -477,13 +477,6 @@ config IXP4XX_WATCHDOG
Say N if you are unsure.
config KS8695_WATCHDOG
tristate "KS8695 watchdog"
depends on ARCH_KS8695
help
Watchdog timer embedded into KS8695 processor. This will reboot your
system when the timeout is reached.
config HAVE_S3C2410_WATCHDOG
bool
help
......@@ -662,15 +655,6 @@ config STMP3XXX_RTC_WATCHDOG
To compile this driver as a module, choose M here: the
module will be called stmp3xxx_rtc_wdt.
config NUC900_WATCHDOG
tristate "Nuvoton NUC900 watchdog"
depends on ARCH_W90X900 || COMPILE_TEST
help
Say Y here if to include support for the watchdog timer
for the Nuvoton NUC900 series SoCs.
To compile this driver as a module, choose M here: the
module will be called nuc900_wdt.
config TS4800_WATCHDOG
tristate "TS-4800 Watchdog"
depends on HAS_IOMEM && OF
......@@ -740,6 +724,19 @@ config IMX_SC_WDT
To compile this driver as a module, choose M here: the
module will be called imx_sc_wdt.
config IMX7ULP_WDT
tristate "IMX7ULP Watchdog"
depends on ARCH_MXC || COMPILE_TEST
select WATCHDOG_CORE
help
This is the driver for the hardware watchdog on the Freescale
IMX7ULP and later processors. If you have one of these
processors and wish to have watchdog support enabled,
say Y, otherwise say N.
To compile this driver as a module, choose M here: the
module will be called imx7ulp_wdt.
config UX500_WATCHDOG
tristate "ST-Ericsson Ux500 watchdog"
depends on MFD_DB8500_PRCMU
......@@ -1046,8 +1043,8 @@ config F71808E_WDT
depends on X86
help
This is the driver for the hardware watchdog on the Fintek F71808E,
F71862FG, F71868, F71869, F71882FG, F71889FG, F81865 and F81866
Super I/O controllers.
F71862FG, F71868, F71869, F71882FG, F71889FG, F81803, F81865, and
F81866 Super I/O controllers.
You can compile this driver directly into the kernel, or use
it as a module. The module will be called f71808e_wdt.
......
......@@ -49,7 +49,6 @@ obj-$(CONFIG_21285_WATCHDOG) += wdt285.o
obj-$(CONFIG_977_WATCHDOG) += wdt977.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
obj-$(CONFIG_SA1100_WATCHDOG) += sa1100_wdt.o
obj-$(CONFIG_SAMA5D4_WATCHDOG) += sama5d4_wdt.o
......@@ -64,11 +63,11 @@ obj-$(CONFIG_RN5T618_WATCHDOG) += rn5t618_wdt.o
obj-$(CONFIG_COH901327_WATCHDOG) += coh901327_wdt.o
obj-$(CONFIG_NPCM7XX_WATCHDOG) += npcm_wdt.o
obj-$(CONFIG_STMP3XXX_RTC_WATCHDOG) += stmp3xxx_rtc_wdt.o
obj-$(CONFIG_NUC900_WATCHDOG) += nuc900_wdt.o
obj-$(CONFIG_TS4800_WATCHDOG) += ts4800_wdt.o
obj-$(CONFIG_TS72XX_WATCHDOG) += ts72xx_wdt.o
obj-$(CONFIG_IMX2_WDT) += imx2_wdt.o
obj-$(CONFIG_IMX_SC_WDT) += imx_sc_wdt.o
obj-$(CONFIG_IMX7ULP_WDT) += imx7ulp_wdt.o
obj-$(CONFIG_UX500_WATCHDOG) += ux500_wdt.o
obj-$(CONFIG_RETU_WATCHDOG) += retu_wdt.o
obj-$(CONFIG_BCM2835_WDT) += bcm2835_wdt.o
......
......@@ -34,6 +34,7 @@ static const struct aspeed_wdt_config ast2500_config = {
static const struct of_device_id aspeed_wdt_of_table[] = {
{ .compatible = "aspeed,ast2400-wdt", .data = &ast2400_config },
{ .compatible = "aspeed,ast2500-wdt", .data = &ast2500_config },
{ .compatible = "aspeed,ast2600-wdt", .data = &ast2500_config },
{ },
};
MODULE_DEVICE_TABLE(of, aspeed_wdt_of_table);
......@@ -53,6 +54,8 @@ MODULE_DEVICE_TABLE(of, aspeed_wdt_of_table);
#define WDT_CTRL_ENABLE BIT(0)
#define WDT_TIMEOUT_STATUS 0x10
#define WDT_TIMEOUT_STATUS_BOOT_SECONDARY BIT(1)
#define WDT_CLEAR_TIMEOUT_STATUS 0x14
#define WDT_CLEAR_TIMEOUT_AND_BOOT_CODE_SELECTION BIT(0)
/*
* WDT_RESET_WIDTH controls the characteristics of the external pulse (if
......@@ -165,6 +168,60 @@ static int aspeed_wdt_restart(struct watchdog_device *wdd,
return 0;
}
/* access_cs0 shows if cs0 is accessible, hence the reverted bit */
static ssize_t access_cs0_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct aspeed_wdt *wdt = dev_get_drvdata(dev);
u32 status = readl(wdt->base + WDT_TIMEOUT_STATUS);
return sprintf(buf, "%u\n",
!(status & WDT_TIMEOUT_STATUS_BOOT_SECONDARY));
}
static ssize_t access_cs0_store(struct device *dev,
struct device_attribute *attr, const char *buf,
size_t size)
{
struct aspeed_wdt *wdt = dev_get_drvdata(dev);
unsigned long val;
if (kstrtoul(buf, 10, &val))
return -EINVAL;
if (val)
writel(WDT_CLEAR_TIMEOUT_AND_BOOT_CODE_SELECTION,
wdt->base + WDT_CLEAR_TIMEOUT_STATUS);
return size;
}
/*
* This attribute exists only if the system has booted from the alternate
* flash with 'alt-boot' option.
*
* At alternate flash the 'access_cs0' sysfs node provides:
* ast2400: a way to get access to the primary SPI flash chip at CS0
* after booting from the alternate chip at CS1.
* ast2500: a way to restore the normal address mapping from
* (CS0->CS1, CS1->CS0) to (CS0->CS0, CS1->CS1).
*
* Clearing the boot code selection and timeout counter also resets to the
* initial state the chip select line mapping. When the SoC is in normal
* mapping state (i.e. booted from CS0), clearing those bits does nothing for
* both versions of the SoC. For alternate boot mode (booted from CS1 due to
* wdt2 expiration) the behavior differs as described above.
*
* This option can be used with wdt2 (watchdog1) only.
*/
static DEVICE_ATTR_RW(access_cs0);
static struct attribute *bswitch_attrs[] = {
&dev_attr_access_cs0.attr,
NULL
};
ATTRIBUTE_GROUPS(bswitch);
static const struct watchdog_ops aspeed_wdt_ops = {
.start = aspeed_wdt_start,
.stop = aspeed_wdt_stop,
......@@ -259,7 +316,8 @@ static int aspeed_wdt_probe(struct platform_device *pdev)
set_bit(WDOG_HW_RUNNING, &wdt->wdd.status);
}
if (of_device_is_compatible(np, "aspeed,ast2500-wdt")) {
if ((of_device_is_compatible(np, "aspeed,ast2500-wdt")) ||
(of_device_is_compatible(np, "aspeed,ast2600-wdt"))) {
u32 reg = readl(wdt->base + WDT_RESET_WIDTH);
reg &= config->ext_pulse_width_mask;
......@@ -306,9 +364,16 @@ static int aspeed_wdt_probe(struct platform_device *pdev)
}
status = readl(wdt->base + WDT_TIMEOUT_STATUS);
if (status & WDT_TIMEOUT_STATUS_BOOT_SECONDARY)
if (status & WDT_TIMEOUT_STATUS_BOOT_SECONDARY) {
wdt->wdd.bootstatus = WDIOF_CARDRESET;
if (of_device_is_compatible(np, "aspeed,ast2400-wdt") ||
of_device_is_compatible(np, "aspeed,ast2500-wdt"))
wdt->wdd.groups = bswitch_groups;
}
dev_set_drvdata(dev, wdt);
return devm_watchdog_register_device(dev, &wdt->wdd);
}
......
......@@ -302,7 +302,7 @@ static int ath79_wdt_remove(struct platform_device *pdev)
return 0;
}
static void ath97_wdt_shutdown(struct platform_device *pdev)
static void ath79_wdt_shutdown(struct platform_device *pdev)
{
ath79_wdt_disable();
}
......@@ -318,7 +318,7 @@ MODULE_DEVICE_TABLE(of, ath79_wdt_match);
static struct platform_driver ath79_wdt_driver = {
.probe = ath79_wdt_probe,
.remove = ath79_wdt_remove,
.shutdown = ath97_wdt_shutdown,
.shutdown = ath79_wdt_shutdown,
.driver = {
.name = DRIVER_NAME,
.of_match_table = of_match_ptr(ath79_wdt_match),
......
......@@ -473,29 +473,6 @@ static long cpwd_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
return 0;
}
static long cpwd_compat_ioctl(struct file *file, unsigned int cmd,
unsigned long arg)
{
int rval = -ENOIOCTLCMD;
switch (cmd) {
/* solaris ioctls are specific to this driver */
case WIOCSTART:
case WIOCSTOP:
case WIOCGSTAT:
mutex_lock(&cpwd_mutex);
rval = cpwd_ioctl(file, cmd, arg);
mutex_unlock(&cpwd_mutex);
break;
/* everything else is handled by the generic compat layer */
default:
break;
}
return rval;
}
static ssize_t cpwd_write(struct file *file, const char __user *buf,
size_t count, loff_t *ppos)
{
......@@ -520,7 +497,7 @@ static ssize_t cpwd_read(struct file *file, char __user *buffer,
static const struct file_operations cpwd_fops = {
.owner = THIS_MODULE,
.unlocked_ioctl = cpwd_ioctl,
.compat_ioctl = cpwd_compat_ioctl,
.compat_ioctl = compat_ptr_ioctl,
.open = cpwd_open,
.write = cpwd_write,
.read = cpwd_read,
......
......@@ -26,13 +26,11 @@
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/slab.h>
#include <linux/miscdevice.h>
#include <linux/watchdog.h>
#include <linux/suspend.h>
#include <asm/ebcdic.h>
#include <asm/diag.h>
#include <linux/io.h>
#include <linux/uaccess.h>
#define MAX_CMDLEN 240
#define DEFAULT_CMD "SYSTEM RESTART"
......@@ -70,7 +68,6 @@ MODULE_PARM_DESC(conceal, "Enable the CONCEAL CP option while the watchdog is ac
module_param_named(nowayout, nowayout_info, bool, 0444);
MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default = CONFIG_WATCHDOG_NOWAYOUT)");
MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
MODULE_ALIAS("vmwatchdog");
static int __diag288(unsigned int func, unsigned int timeout,
......
......@@ -31,8 +31,10 @@
#define SIO_REG_DEVID 0x20 /* Device ID (2 bytes) */
#define SIO_REG_DEVREV 0x22 /* Device revision */
#define SIO_REG_MANID 0x23 /* Fintek ID (2 bytes) */
#define SIO_REG_CLOCK_SEL 0x26 /* Clock select */
#define SIO_REG_ROM_ADDR_SEL 0x27 /* ROM address select */
#define SIO_F81866_REG_PORT_SEL 0x27 /* F81866 Multi-Function Register */
#define SIO_REG_TSI_LEVEL_SEL 0x28 /* TSI Level select */
#define SIO_REG_MFUNCT1 0x29 /* Multi function select 1 */
#define SIO_REG_MFUNCT2 0x2a /* Multi function select 2 */
#define SIO_REG_MFUNCT3 0x2b /* Multi function select 3 */
......@@ -49,6 +51,7 @@
#define SIO_F71869A_ID 0x1007 /* Chipset ID */
#define SIO_F71882_ID 0x0541 /* Chipset ID */
#define SIO_F71889_ID 0x0723 /* Chipset ID */
#define SIO_F81803_ID 0x1210 /* Chipset ID */
#define SIO_F81865_ID 0x0704 /* Chipset ID */
#define SIO_F81866_ID 0x1010 /* Chipset ID */
......@@ -108,7 +111,7 @@ MODULE_PARM_DESC(start_withtimeout, "Start watchdog timer on module load with"
" given initial timeout. Zero (default) disables this feature.");
enum chips { f71808fg, f71858fg, f71862fg, f71868, f71869, f71882fg, f71889fg,
f81865, f81866};
f81803, f81865, f81866};
static const char *f71808e_names[] = {
"f71808fg",
......@@ -118,6 +121,7 @@ static const char *f71808e_names[] = {
"f71869",
"f71882fg",
"f71889fg",
"f81803",
"f81865",
"f81866",
};
......@@ -370,6 +374,14 @@ static int watchdog_start(void)
superio_inb(watchdog.sioaddr, SIO_REG_MFUNCT3) & 0xcf);
break;
case f81803:
/* Enable TSI Level register bank */
superio_clear_bit(watchdog.sioaddr, SIO_REG_CLOCK_SEL, 3);
/* Set pin 27 to WDTRST# */
superio_outb(watchdog.sioaddr, SIO_REG_TSI_LEVEL_SEL, 0x5f &
superio_inb(watchdog.sioaddr, SIO_REG_TSI_LEVEL_SEL));
break;
case f81865:
/* Set pin 70 to WDTRST# */
superio_clear_bit(watchdog.sioaddr, SIO_REG_MFUNCT3, 5);
......@@ -809,6 +821,9 @@ static int __init f71808e_find(int sioaddr)
/* Confirmed (by datasheet) not to have a watchdog. */
err = -ENODEV;
goto exit;
case SIO_F81803_ID:
watchdog.type = f81803;
break;
case SIO_F81865_ID:
watchdog.type = f81865;
break;
......
......@@ -55,7 +55,7 @@
#define IMX2_WDT_WMCR 0x08 /* Misc Register */
#define IMX2_WDT_MAX_TIME 128
#define IMX2_WDT_MAX_TIME 128U
#define IMX2_WDT_DEFAULT_TIME 60 /* in seconds */
#define WDOG_SEC_TO_COUNT(s) ((s * 2 - 1) << 8)
......@@ -180,7 +180,7 @@ static int imx2_wdt_set_timeout(struct watchdog_device *wdog,
{
unsigned int actual;
actual = min(new_timeout, wdog->max_hw_heartbeat_ms * 1000);
actual = min(new_timeout, IMX2_WDT_MAX_TIME);
__imx2_wdt_set_timeout(wdog, actual);
wdog->timeout = new_timeout;
return 0;
......
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright 2019 NXP.
*/
#include <linux/clk.h>
#include <linux/init.h>
#include <linux/io.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/reboot.h>
#include <linux/watchdog.h>
#define WDOG_CS 0x0
#define WDOG_CS_CMD32EN BIT(13)
#define WDOG_CS_ULK BIT(11)
#define WDOG_CS_RCS BIT(10)
#define WDOG_CS_EN BIT(7)
#define WDOG_CS_UPDATE BIT(5)
#define WDOG_CNT 0x4
#define WDOG_TOVAL 0x8
#define REFRESH_SEQ0 0xA602
#define REFRESH_SEQ1 0xB480
#define REFRESH ((REFRESH_SEQ1 << 16) | REFRESH_SEQ0)
#define UNLOCK_SEQ0 0xC520
#define UNLOCK_SEQ1 0xD928
#define UNLOCK ((UNLOCK_SEQ1 << 16) | UNLOCK_SEQ0)
#define DEFAULT_TIMEOUT 60
#define MAX_TIMEOUT 128
#define WDOG_CLOCK_RATE 1000
static bool nowayout = WATCHDOG_NOWAYOUT;
module_param(nowayout, bool, 0000);
MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default="
__MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
struct imx7ulp_wdt_device {
struct notifier_block restart_handler;
struct watchdog_device wdd;
void __iomem *base;
struct clk *clk;
};
static inline void imx7ulp_wdt_enable(void __iomem *base, bool enable)
{
u32 val = readl(base + WDOG_CS);
writel(UNLOCK, base + WDOG_CNT);
if (enable)
writel(val | WDOG_CS_EN, base + WDOG_CS);
else
writel(val & ~WDOG_CS_EN, base + WDOG_CS);
}
static inline bool imx7ulp_wdt_is_enabled(void __iomem *base)
{
u32 val = readl(base + WDOG_CS);
return val & WDOG_CS_EN;
}
static int imx7ulp_wdt_ping(struct watchdog_device *wdog)
{
struct imx7ulp_wdt_device *wdt = watchdog_get_drvdata(wdog);
writel(REFRESH, wdt->base + WDOG_CNT);
return 0;
}
static int imx7ulp_wdt_start(struct watchdog_device *wdog)
{
struct imx7ulp_wdt_device *wdt = watchdog_get_drvdata(wdog);
imx7ulp_wdt_enable(wdt->base, true);
return 0;
}
static int imx7ulp_wdt_stop(struct watchdog_device *wdog)
{
struct imx7ulp_wdt_device *wdt = watchdog_get_drvdata(wdog);
imx7ulp_wdt_enable(wdt->base, false);
return 0;
}
static int imx7ulp_wdt_set_timeout(struct watchdog_device *wdog,
unsigned int timeout)
{
struct imx7ulp_wdt_device *wdt = watchdog_get_drvdata(wdog);
u32 val = WDOG_CLOCK_RATE * timeout;
writel(UNLOCK, wdt->base + WDOG_CNT);
writel(val, wdt->base + WDOG_TOVAL);
wdog->timeout = timeout;
return 0;
}
static const struct watchdog_ops imx7ulp_wdt_ops = {
.owner = THIS_MODULE,
.start = imx7ulp_wdt_start,
.stop = imx7ulp_wdt_stop,
.ping = imx7ulp_wdt_ping,
.set_timeout = imx7ulp_wdt_set_timeout,
};
static const struct watchdog_info imx7ulp_wdt_info = {
.identity = "i.MX7ULP watchdog timer",
.options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING |
WDIOF_MAGICCLOSE,
};
static inline void imx7ulp_wdt_init(void __iomem *base, unsigned int timeout)
{
u32 val;
/* unlock the wdog for reconfiguration */
writel_relaxed(UNLOCK_SEQ0, base + WDOG_CNT);
writel_relaxed(UNLOCK_SEQ1, base + WDOG_CNT);
/* set an initial timeout value in TOVAL */
writel(timeout, base + WDOG_TOVAL);
/* enable 32bit command sequence and reconfigure */
val = BIT(13) | BIT(8) | BIT(5);
writel(val, base + WDOG_CS);
}
static void imx7ulp_wdt_action(void *data)
{
clk_disable_unprepare(data);
}
static int imx7ulp_wdt_probe(struct platform_device *pdev)
{
struct imx7ulp_wdt_device *imx7ulp_wdt;
struct device *dev = &pdev->dev;
struct watchdog_device *wdog;
int ret;
imx7ulp_wdt = devm_kzalloc(dev, sizeof(*imx7ulp_wdt), GFP_KERNEL);
if (!imx7ulp_wdt)
return -ENOMEM;
platform_set_drvdata(pdev, imx7ulp_wdt);
imx7ulp_wdt->base = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(imx7ulp_wdt->base))
return PTR_ERR(imx7ulp_wdt->base);
imx7ulp_wdt->clk = devm_clk_get(dev, NULL);
if (IS_ERR(imx7ulp_wdt->clk)) {
dev_err(dev, "Failed to get watchdog clock\n");
return PTR_ERR(imx7ulp_wdt->clk);
}
ret = clk_prepare_enable(imx7ulp_wdt->clk);
if (ret)
return ret;
ret = devm_add_action_or_reset(dev, imx7ulp_wdt_action, imx7ulp_wdt->clk);
if (ret)
return ret;
wdog = &imx7ulp_wdt->wdd;
wdog->info = &imx7ulp_wdt_info;
wdog->ops = &imx7ulp_wdt_ops;
wdog->min_timeout = 1;
wdog->max_timeout = MAX_TIMEOUT;
wdog->parent = dev;
wdog->timeout = DEFAULT_TIMEOUT;
watchdog_init_timeout(wdog, 0, dev);
watchdog_stop_on_reboot(wdog);
watchdog_stop_on_unregister(wdog);
watchdog_set_drvdata(wdog, imx7ulp_wdt);
imx7ulp_wdt_init(imx7ulp_wdt->base, wdog->timeout * WDOG_CLOCK_RATE);
return devm_watchdog_register_device(dev, wdog);
}
static int __maybe_unused imx7ulp_wdt_suspend(struct device *dev)
{
struct imx7ulp_wdt_device *imx7ulp_wdt = dev_get_drvdata(dev);
if (watchdog_active(&imx7ulp_wdt->wdd))
imx7ulp_wdt_stop(&imx7ulp_wdt->wdd);
clk_disable_unprepare(imx7ulp_wdt->clk);
return 0;
}
static int __maybe_unused imx7ulp_wdt_resume(struct device *dev)
{
struct imx7ulp_wdt_device *imx7ulp_wdt = dev_get_drvdata(dev);
u32 timeout = imx7ulp_wdt->wdd.timeout * WDOG_CLOCK_RATE;
int ret;
ret = clk_prepare_enable(imx7ulp_wdt->clk);
if (ret)
return ret;
if (imx7ulp_wdt_is_enabled(imx7ulp_wdt->base))
imx7ulp_wdt_init(imx7ulp_wdt->base, timeout);
if (watchdog_active(&imx7ulp_wdt->wdd))
imx7ulp_wdt_start(&imx7ulp_wdt->wdd);
return 0;
}
static SIMPLE_DEV_PM_OPS(imx7ulp_wdt_pm_ops, imx7ulp_wdt_suspend,
imx7ulp_wdt_resume);
static const struct of_device_id imx7ulp_wdt_dt_ids[] = {
{ .compatible = "fsl,imx7ulp-wdt", },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, imx7ulp_wdt_dt_ids);
static struct platform_driver imx7ulp_wdt_driver = {
.probe = imx7ulp_wdt_probe,
.driver = {
.name = "imx7ulp-wdt",
.pm = &imx7ulp_wdt_pm_ops,
.of_match_table = imx7ulp_wdt_dt_ids,
},
};
module_platform_driver(imx7ulp_wdt_driver);
MODULE_AUTHOR("Anson Huang <Anson.Huang@nxp.com>");
MODULE_DESCRIPTION("Freescale i.MX7ULP watchdog driver");
MODULE_LICENSE("GPL v2");
......@@ -175,12 +175,9 @@ static int imx_sc_wdt_probe(struct platform_device *pdev)
watchdog_stop_on_unregister(wdog);
ret = devm_watchdog_register_device(dev, wdog);
if (ret) {
dev_err(dev, "Failed to register watchdog device\n");
return ret;
}
if (ret)
return ret;
ret = imx_scu_irq_group_enable(SC_IRQ_GROUP_WDOG,
SC_IRQ_WDOG,
true);
......
......@@ -162,7 +162,6 @@ static int jz4740_wdt_probe(struct platform_device *pdev)
struct device *dev = &pdev->dev;
struct jz4740_wdt_drvdata *drvdata;
struct watchdog_device *jz4740_wdt;
int ret;
drvdata = devm_kzalloc(dev, sizeof(struct jz4740_wdt_drvdata),
GFP_KERNEL);
......
// SPDX-License-Identifier: GPL-2.0-only
/*
* Watchdog driver for Kendin/Micrel KS8695.
*
* (C) 2007 Andrew Victor
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/bitops.h>
#include <linux/errno.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/miscdevice.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/platform_device.h>
#include <linux/types.h>
#include <linux/watchdog.h>
#include <linux/io.h>
#include <linux/uaccess.h>
#include <mach/hardware.h>
#define KS8695_TMR_OFFSET (0xF0000 + 0xE400)
#define KS8695_TMR_VA (KS8695_IO_VA + KS8695_TMR_OFFSET)
/*
* Timer registers
*/
#define KS8695_TMCON (0x00) /* Timer Control Register */
#define KS8695_T0TC (0x08) /* Timer 0 Timeout Count Register */
#define TMCON_T0EN (1 << 0) /* Timer 0 Enable */
/* Timer0 Timeout Counter Register */
#define T0TC_WATCHDOG (0xff) /* Enable watchdog mode */
#define WDT_DEFAULT_TIME 5 /* seconds */
#define WDT_MAX_TIME 171 /* seconds */
static int wdt_time = WDT_DEFAULT_TIME;
static bool nowayout = WATCHDOG_NOWAYOUT;
module_param(wdt_time, int, 0);
MODULE_PARM_DESC(wdt_time, "Watchdog time in seconds. (default="
__MODULE_STRING(WDT_DEFAULT_TIME) ")");
#ifdef CONFIG_WATCHDOG_NOWAYOUT
module_param(nowayout, bool, 0);
MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default="
__MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
#endif
static unsigned long ks8695wdt_busy;
static DEFINE_SPINLOCK(ks8695_lock);
/* ......................................................................... */
/*
* Disable the watchdog.
*/
static inline void ks8695_wdt_stop(void)
{
unsigned long tmcon;
spin_lock(&ks8695_lock);
/* disable timer0 */
tmcon = __raw_readl(KS8695_TMR_VA + KS8695_TMCON);
__raw_writel(tmcon & ~TMCON_T0EN, KS8695_TMR_VA + KS8695_TMCON);
spin_unlock(&ks8695_lock);
}
/*
* Enable and reset the watchdog.
*/
static inline void ks8695_wdt_start(void)
{
unsigned long tmcon;
unsigned long tval = wdt_time * KS8695_CLOCK_RATE;
spin_lock(&ks8695_lock);
/* disable timer0 */
tmcon = __raw_readl(KS8695_TMR_VA + KS8695_TMCON);
__raw_writel(tmcon & ~TMCON_T0EN, KS8695_TMR_VA + KS8695_TMCON);
/* program timer0 */
__raw_writel(tval | T0TC_WATCHDOG, KS8695_TMR_VA + KS8695_T0TC);
/* re-enable timer0 */
tmcon = __raw_readl(KS8695_TMR_VA + KS8695_TMCON);
__raw_writel(tmcon | TMCON_T0EN, KS8695_TMR_VA + KS8695_TMCON);
spin_unlock(&ks8695_lock);
}
/*
* Reload the watchdog timer. (ie, pat the watchdog)
*/
static inline void ks8695_wdt_reload(void)
{
unsigned long tmcon;
spin_lock(&ks8695_lock);
/* disable, then re-enable timer0 */
tmcon = __raw_readl(KS8695_TMR_VA + KS8695_TMCON);
__raw_writel(tmcon & ~TMCON_T0EN, KS8695_TMR_VA + KS8695_TMCON);
__raw_writel(tmcon | TMCON_T0EN, KS8695_TMR_VA + KS8695_TMCON);
spin_unlock(&ks8695_lock);
}
/*
* Change the watchdog time interval.
*/
static int ks8695_wdt_settimeout(int new_time)
{
/*
* All counting occurs at KS8695_CLOCK_RATE / 128 = 0.256 Hz
*
* Since WDV is a 16-bit counter, the maximum period is
* 65536 / 0.256 = 256 seconds.
*/
if ((new_time <= 0) || (new_time > WDT_MAX_TIME))
return -EINVAL;
/* Set new watchdog time. It will be used when
ks8695_wdt_start() is called. */
wdt_time = new_time;
return 0;
}
/* ......................................................................... */
/*
* Watchdog device is opened, and watchdog starts running.
*/
static int ks8695_wdt_open(struct inode *inode, struct file *file)
{
if (test_and_set_bit(0, &ks8695wdt_busy))
return -EBUSY;
ks8695_wdt_start();
return stream_open(inode, file);
}
/*
* Close the watchdog device.
* If CONFIG_WATCHDOG_NOWAYOUT is NOT defined then the watchdog is also
* disabled.
*/
static int ks8695_wdt_close(struct inode *inode, struct file *file)
{
/* Disable the watchdog when file is closed */
if (!nowayout)
ks8695_wdt_stop();
clear_bit(0, &ks8695wdt_busy);
return 0;
}
static const struct watchdog_info ks8695_wdt_info = {
.identity = "ks8695 watchdog",
.options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING,
};
/*
* Handle commands from user-space.
*/
static long ks8695_wdt_ioctl(struct file *file, unsigned int cmd,
unsigned long arg)
{
void __user *argp = (void __user *)arg;
int __user *p = argp;
int new_value;
switch (cmd) {
case WDIOC_GETSUPPORT:
return copy_to_user(argp, &ks8695_wdt_info,
sizeof(ks8695_wdt_info)) ? -EFAULT : 0;
case WDIOC_GETSTATUS:
case WDIOC_GETBOOTSTATUS:
return put_user(0, p);
case WDIOC_SETOPTIONS:
if (get_user(new_value, p))
return -EFAULT;
if (new_value & WDIOS_DISABLECARD)
ks8695_wdt_stop();
if (new_value & WDIOS_ENABLECARD)
ks8695_wdt_start();
return 0;
case WDIOC_KEEPALIVE:
ks8695_wdt_reload(); /* pat the watchdog */
return 0;
case WDIOC_SETTIMEOUT:
if (get_user(new_value, p))
return -EFAULT;
if (ks8695_wdt_settimeout(new_value))
return -EINVAL;
/* Enable new time value */
ks8695_wdt_start();
/* Return current value */
return put_user(wdt_time, p);
case WDIOC_GETTIMEOUT:
return put_user(wdt_time, p);
default:
return -ENOTTY;
}
}
/*
* Pat the watchdog whenever device is written to.
*/
static ssize_t ks8695_wdt_write(struct file *file, const char *data,
size_t len, loff_t *ppos)
{
ks8695_wdt_reload(); /* pat the watchdog */
return len;
}
/* ......................................................................... */
static const struct file_operations ks8695wdt_fops = {
.owner = THIS_MODULE,
.llseek = no_llseek,
.unlocked_ioctl = ks8695_wdt_ioctl,
.open = ks8695_wdt_open,
.release = ks8695_wdt_close,
.write = ks8695_wdt_write,
};
static struct miscdevice ks8695wdt_miscdev = {
.minor = WATCHDOG_MINOR,
.name = "watchdog",
.fops = &ks8695wdt_fops,
};
static int ks8695wdt_probe(struct platform_device *pdev)
{
int res;
if (ks8695wdt_miscdev.parent)
return -EBUSY;
ks8695wdt_miscdev.parent = &pdev->dev;
res = misc_register(&ks8695wdt_miscdev);
if (res)
return res;
pr_info("KS8695 Watchdog Timer enabled (%d seconds%s)\n",
wdt_time, nowayout ? ", nowayout" : "");
return 0;
}
static int ks8695wdt_remove(struct platform_device *pdev)
{
misc_deregister(&ks8695wdt_miscdev);
ks8695wdt_miscdev.parent = NULL;
return 0;
}
static void ks8695wdt_shutdown(struct platform_device *pdev)
{
ks8695_wdt_stop();
}
#ifdef CONFIG_PM
static int ks8695wdt_suspend(struct platform_device *pdev, pm_message_t message)
{
ks8695_wdt_stop();
return 0;
}
static int ks8695wdt_resume(struct platform_device *pdev)
{
if (ks8695wdt_busy)
ks8695_wdt_start();
return 0;
}
#else
#define ks8695wdt_suspend NULL
#define ks8695wdt_resume NULL
#endif
static struct platform_driver ks8695wdt_driver = {
.probe = ks8695wdt_probe,
.remove = ks8695wdt_remove,
.shutdown = ks8695wdt_shutdown,
.suspend = ks8695wdt_suspend,
.resume = ks8695wdt_resume,
.driver = {
.name = "ks8695_wdt",
},
};
static int __init ks8695_wdt_init(void)
{
/* Check that the heartbeat value is within range;
if not reset to the default */
if (ks8695_wdt_settimeout(wdt_time)) {
ks8695_wdt_settimeout(WDT_DEFAULT_TIME);
pr_info("ks8695_wdt: wdt_time value must be 1 <= wdt_time <= %i"
", using %d\n", wdt_time, WDT_MAX_TIME);
}
return platform_driver_register(&ks8695wdt_driver);
}
static void __exit ks8695_wdt_exit(void)
{
platform_driver_unregister(&ks8695wdt_driver);
}
module_init(ks8695_wdt_init);
module_exit(ks8695_wdt_exit);
MODULE_AUTHOR("Andrew Victor");
MODULE_DESCRIPTION("Watchdog driver for KS8695");
MODULE_LICENSE("GPL");
MODULE_ALIAS("platform:ks8695_wdt");
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2009 Nuvoton technology corporation.
*
* Wan ZongShun <mcuos.com@gmail.com>
*/
#include <linux/bitops.h>
#include <linux/errno.h>
#include <linux/fs.h>
#include <linux/io.h>
#include <linux/clk.h>
#include <linux/kernel.h>
#include <linux/miscdevice.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
#include <linux/interrupt.h>
#include <linux/types.h>
#include <linux/watchdog.h>
#include <linux/uaccess.h>
#define REG_WTCR 0x1c
#define WTCLK (0x01 << 10)
#define WTE (0x01 << 7) /*wdt enable*/
#define WTIS (0x03 << 4)
#define WTIF (0x01 << 3)
#define WTRF (0x01 << 2)
#define WTRE (0x01 << 1)
#define WTR (0x01 << 0)
/*
* The watchdog time interval can be calculated via following formula:
* WTIS real time interval (formula)
* 0x00 ((2^ 14 ) * ((external crystal freq) / 256))seconds
* 0x01 ((2^ 16 ) * ((external crystal freq) / 256))seconds
* 0x02 ((2^ 18 ) * ((external crystal freq) / 256))seconds
* 0x03 ((2^ 20 ) * ((external crystal freq) / 256))seconds
*
* The external crystal freq is 15Mhz in the nuc900 evaluation board.
* So 0x00 = +-0.28 seconds, 0x01 = +-1.12 seconds, 0x02 = +-4.48 seconds,
* 0x03 = +- 16.92 seconds..
*/
#define WDT_HW_TIMEOUT 0x02
#define WDT_TIMEOUT (HZ/2)
#define WDT_HEARTBEAT 15
static int heartbeat = WDT_HEARTBEAT;
module_param(heartbeat, int, 0);
MODULE_PARM_DESC(heartbeat, "Watchdog heartbeats in seconds. "
"(default = " __MODULE_STRING(WDT_HEARTBEAT) ")");
static bool nowayout = WATCHDOG_NOWAYOUT;
module_param(nowayout, bool, 0);
MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started "
"(default=" __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
struct nuc900_wdt {
struct clk *wdt_clock;
struct platform_device *pdev;
void __iomem *wdt_base;
char expect_close;
struct timer_list timer;
spinlock_t wdt_lock;
unsigned long next_heartbeat;
};
static unsigned long nuc900wdt_busy;
static struct nuc900_wdt *nuc900_wdt;
static inline void nuc900_wdt_keepalive(void)
{
unsigned int val;
spin_lock(&nuc900_wdt->wdt_lock);
val = __raw_readl(nuc900_wdt->wdt_base + REG_WTCR);
val |= (WTR | WTIF);
__raw_writel(val, nuc900_wdt->wdt_base + REG_WTCR);
spin_unlock(&nuc900_wdt->wdt_lock);
}
static inline void nuc900_wdt_start(void)
{
unsigned int val;
spin_lock(&nuc900_wdt->wdt_lock);
val = __raw_readl(nuc900_wdt->wdt_base + REG_WTCR);
val |= (WTRE | WTE | WTR | WTCLK | WTIF);
val &= ~WTIS;
val |= (WDT_HW_TIMEOUT << 0x04);
__raw_writel(val, nuc900_wdt->wdt_base + REG_WTCR);
spin_unlock(&nuc900_wdt->wdt_lock);
nuc900_wdt->next_heartbeat = jiffies + heartbeat * HZ;
mod_timer(&nuc900_wdt->timer, jiffies + WDT_TIMEOUT);
}
static inline void nuc900_wdt_stop(void)
{
unsigned int val;
del_timer(&nuc900_wdt->timer);
spin_lock(&nuc900_wdt->wdt_lock);
val = __raw_readl(nuc900_wdt->wdt_base + REG_WTCR);
val &= ~WTE;
__raw_writel(val, nuc900_wdt->wdt_base + REG_WTCR);
spin_unlock(&nuc900_wdt->wdt_lock);
}
static inline void nuc900_wdt_ping(void)
{
nuc900_wdt->next_heartbeat = jiffies + heartbeat * HZ;
}
static int nuc900_wdt_open(struct inode *inode, struct file *file)
{
if (test_and_set_bit(0, &nuc900wdt_busy))
return -EBUSY;
nuc900_wdt_start();
return stream_open(inode, file);
}
static int nuc900_wdt_close(struct inode *inode, struct file *file)
{
if (nuc900_wdt->expect_close == 42)
nuc900_wdt_stop();
else {
dev_crit(&nuc900_wdt->pdev->dev,
"Unexpected close, not stopping watchdog!\n");
nuc900_wdt_ping();
}
nuc900_wdt->expect_close = 0;
clear_bit(0, &nuc900wdt_busy);
return 0;
}
static const struct watchdog_info nuc900_wdt_info = {
.identity = "nuc900 watchdog",
.options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING |
WDIOF_MAGICCLOSE,
};
static long nuc900_wdt_ioctl(struct file *file,
unsigned int cmd, unsigned long arg)
{
void __user *argp = (void __user *)arg;
int __user *p = argp;
int new_value;
switch (cmd) {
case WDIOC_GETSUPPORT:
return copy_to_user(argp, &nuc900_wdt_info,
sizeof(nuc900_wdt_info)) ? -EFAULT : 0;
case WDIOC_GETSTATUS:
case WDIOC_GETBOOTSTATUS:
return put_user(0, p);
case WDIOC_KEEPALIVE:
nuc900_wdt_ping();
return 0;
case WDIOC_SETTIMEOUT:
if (get_user(new_value, p))
return -EFAULT;
heartbeat = new_value;
nuc900_wdt_ping();
return put_user(new_value, p);
case WDIOC_GETTIMEOUT:
return put_user(heartbeat, p);
default:
return -ENOTTY;
}
}
static ssize_t nuc900_wdt_write(struct file *file, const char __user *data,
size_t len, loff_t *ppos)
{
if (!len)
return 0;
/* Scan for magic character */
if (!nowayout) {
size_t i;
nuc900_wdt->expect_close = 0;
for (i = 0; i < len; i++) {
char c;
if (get_user(c, data + i))
return -EFAULT;
if (c == 'V') {
nuc900_wdt->expect_close = 42;
break;
}
}
}
nuc900_wdt_ping();
return len;
}
static void nuc900_wdt_timer_ping(struct timer_list *unused)
{
if (time_before(jiffies, nuc900_wdt->next_heartbeat)) {
nuc900_wdt_keepalive();
mod_timer(&nuc900_wdt->timer, jiffies + WDT_TIMEOUT);
} else
dev_warn(&nuc900_wdt->pdev->dev, "Will reset the machine !\n");
}
static const struct file_operations nuc900wdt_fops = {
.owner = THIS_MODULE,
.llseek = no_llseek,
.unlocked_ioctl = nuc900_wdt_ioctl,
.open = nuc900_wdt_open,
.release = nuc900_wdt_close,
.write = nuc900_wdt_write,
};
static struct miscdevice nuc900wdt_miscdev = {
.minor = WATCHDOG_MINOR,
.name = "watchdog",
.fops = &nuc900wdt_fops,
};
static int nuc900wdt_probe(struct platform_device *pdev)
{
int ret = 0;
nuc900_wdt = devm_kzalloc(&pdev->dev, sizeof(*nuc900_wdt),
GFP_KERNEL);
if (!nuc900_wdt)
return -ENOMEM;
nuc900_wdt->pdev = pdev;
spin_lock_init(&nuc900_wdt->wdt_lock);
nuc900_wdt->wdt_base = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(nuc900_wdt->wdt_base))
return PTR_ERR(nuc900_wdt->wdt_base);
nuc900_wdt->wdt_clock = devm_clk_get(&pdev->dev, NULL);
if (IS_ERR(nuc900_wdt->wdt_clock)) {
dev_err(&pdev->dev, "failed to find watchdog clock source\n");
return PTR_ERR(nuc900_wdt->wdt_clock);
}
clk_enable(nuc900_wdt->wdt_clock);
timer_setup(&nuc900_wdt->timer, nuc900_wdt_timer_ping, 0);
ret = misc_register(&nuc900wdt_miscdev);
if (ret) {
dev_err(&pdev->dev, "err register miscdev on minor=%d (%d)\n",
WATCHDOG_MINOR, ret);
goto err_clk;
}
return 0;
err_clk:
clk_disable(nuc900_wdt->wdt_clock);
return ret;
}
static int nuc900wdt_remove(struct platform_device *pdev)
{
misc_deregister(&nuc900wdt_miscdev);
clk_disable(nuc900_wdt->wdt_clock);
return 0;
}
static struct platform_driver nuc900wdt_driver = {
.probe = nuc900wdt_probe,
.remove = nuc900wdt_remove,
.driver = {
.name = "nuc900-wdt",
},
};
module_platform_driver(nuc900wdt_driver);
MODULE_AUTHOR("Wan ZongShun <mcuos.com@gmail.com>");
MODULE_DESCRIPTION("Watchdog driver for NUC900");
MODULE_LICENSE("GPL");
MODULE_ALIAS("platform:nuc900-wdt");
......@@ -35,7 +35,15 @@
* Watchdog timer block registers.
*/
#define TIMER_CTRL 0x0000
#define TIMER_A370_STATUS 0x04
#define TIMER1_FIXED_ENABLE_BIT BIT(12)
#define WDT_AXP_FIXED_ENABLE_BIT BIT(10)
#define TIMER1_ENABLE_BIT BIT(2)
#define TIMER_A370_STATUS 0x0004
#define WDT_A370_EXPIRED BIT(31)
#define TIMER1_STATUS_BIT BIT(8)
#define TIMER1_VAL_OFF 0x001c
#define WDT_MAX_CYCLE_COUNT 0xffffffff
......@@ -43,9 +51,6 @@
#define WDT_A370_RATIO_SHIFT 5
#define WDT_A370_RATIO (1 << WDT_A370_RATIO_SHIFT)
#define WDT_AXP_FIXED_ENABLE_BIT BIT(10)
#define WDT_A370_EXPIRED BIT(31)
static bool nowayout = WATCHDOG_NOWAYOUT;
static int heartbeat = -1; /* module parameter (seconds) */
......@@ -158,6 +163,7 @@ static int armadaxp_wdt_clock_init(struct platform_device *pdev,
struct orion_watchdog *dev)
{
int ret;
u32 val;
dev->clk = of_clk_get_by_name(pdev->dev.of_node, "fixed");
if (IS_ERR(dev->clk))
......@@ -168,10 +174,9 @@ static int armadaxp_wdt_clock_init(struct platform_device *pdev,
return ret;
}
/* Enable the fixed watchdog clock input */
atomic_io_modify(dev->reg + TIMER_CTRL,
WDT_AXP_FIXED_ENABLE_BIT,
WDT_AXP_FIXED_ENABLE_BIT);
/* Fix the wdt and timer1 clock freqency to 25MHz */
val = WDT_AXP_FIXED_ENABLE_BIT | TIMER1_FIXED_ENABLE_BIT;
atomic_io_modify(dev->reg + TIMER_CTRL, val, val);
dev->clk_rate = clk_get_rate(dev->clk);
return 0;
......@@ -183,6 +188,10 @@ static int orion_wdt_ping(struct watchdog_device *wdt_dev)
/* Reload watchdog duration */
writel(dev->clk_rate * wdt_dev->timeout,
dev->reg + dev->data->wdt_counter_offset);
if (dev->wdt.info->options & WDIOF_PRETIMEOUT)
writel(dev->clk_rate * (wdt_dev->timeout - wdt_dev->pretimeout),
dev->reg + TIMER1_VAL_OFF);
return 0;
}
......@@ -194,13 +203,18 @@ static int armada375_start(struct watchdog_device *wdt_dev)
/* Set watchdog duration */
writel(dev->clk_rate * wdt_dev->timeout,
dev->reg + dev->data->wdt_counter_offset);
if (dev->wdt.info->options & WDIOF_PRETIMEOUT)
writel(dev->clk_rate * (wdt_dev->timeout - wdt_dev->pretimeout),
dev->reg + TIMER1_VAL_OFF);
/* Clear the watchdog expiration bit */
atomic_io_modify(dev->reg + TIMER_A370_STATUS, WDT_A370_EXPIRED, 0);
/* Enable watchdog timer */
atomic_io_modify(dev->reg + TIMER_CTRL, dev->data->wdt_enable_bit,
dev->data->wdt_enable_bit);
reg = dev->data->wdt_enable_bit;
if (dev->wdt.info->options & WDIOF_PRETIMEOUT)
reg |= TIMER1_ENABLE_BIT;
atomic_io_modify(dev->reg + TIMER_CTRL, reg, reg);
/* Enable reset on watchdog */
reg = readl(dev->rstout);
......@@ -277,7 +291,7 @@ static int orion_stop(struct watchdog_device *wdt_dev)
static int armada375_stop(struct watchdog_device *wdt_dev)
{
struct orion_watchdog *dev = watchdog_get_drvdata(wdt_dev);
u32 reg;
u32 reg, mask;
/* Disable reset on watchdog */
atomic_io_modify(dev->rstout_mask, dev->data->rstout_mask_bit,
......@@ -287,7 +301,10 @@ static int armada375_stop(struct watchdog_device *wdt_dev)
writel(reg, dev->rstout);
/* Disable watchdog timer */
atomic_io_modify(dev->reg + TIMER_CTRL, dev->data->wdt_enable_bit, 0);
mask = dev->data->wdt_enable_bit;
if (wdt_dev->info->options & WDIOF_PRETIMEOUT)
mask |= TIMER1_ENABLE_BIT;
atomic_io_modify(dev->reg + TIMER_CTRL, mask, 0);
return 0;
}
......@@ -349,7 +366,7 @@ static unsigned int orion_wdt_get_timeleft(struct watchdog_device *wdt_dev)
return readl(dev->reg + dev->data->wdt_counter_offset) / dev->clk_rate;
}
static const struct watchdog_info orion_wdt_info = {
static struct watchdog_info orion_wdt_info = {
.options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE,
.identity = "Orion Watchdog",
};
......@@ -368,6 +385,16 @@ static irqreturn_t orion_wdt_irq(int irq, void *devid)
return IRQ_HANDLED;
}
static irqreturn_t orion_wdt_pre_irq(int irq, void *devid)
{
struct orion_watchdog *dev = devid;
atomic_io_modify(dev->reg + TIMER_A370_STATUS,
TIMER1_STATUS_BIT, 0);
watchdog_notify_pretimeout(&dev->wdt);
return IRQ_HANDLED;
}
/*
* The original devicetree binding for this driver specified only
* one memory resource, so in order to keep DT backwards compatibility
......@@ -589,6 +616,19 @@ static int orion_wdt_probe(struct platform_device *pdev)
}
}
/* Optional 2nd interrupt for pretimeout */
irq = platform_get_irq(pdev, 1);
if (irq > 0) {
orion_wdt_info.options |= WDIOF_PRETIMEOUT;
ret = devm_request_irq(&pdev->dev, irq, orion_wdt_pre_irq,
0, pdev->name, dev);
if (ret < 0) {
dev_err(&pdev->dev, "failed to request IRQ\n");
goto disable_clk;
}
}
watchdog_set_nowayout(&dev->wdt, nowayout);
ret = watchdog_register_device(&dev->wdt);
if (ret)
......
// SPDX-License-Identifier: GPL-2.0-only
/* Copyright (c) 2014, The Linux Foundation. All rights reserved.
*/
#include <linux/bits.h>
#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/kernel.h>
#include <linux/module.h>
......@@ -19,6 +21,9 @@ enum wdt_reg {
WDT_BITE_TIME,
};
#define QCOM_WDT_ENABLE BIT(0)
#define QCOM_WDT_ENABLE_IRQ BIT(1)
static const u32 reg_offset_data_apcs_tmr[] = {
[WDT_RST] = 0x38,
[WDT_EN] = 0x40,
......@@ -37,7 +42,6 @@ static const u32 reg_offset_data_kpss[] = {
struct qcom_wdt {
struct watchdog_device wdd;
struct clk *clk;
unsigned long rate;
void __iomem *base;
const u32 *layout;
......@@ -54,15 +58,35 @@ struct qcom_wdt *to_qcom_wdt(struct watchdog_device *wdd)
return container_of(wdd, struct qcom_wdt, wdd);
}
static inline int qcom_get_enable(struct watchdog_device *wdd)
{
int enable = QCOM_WDT_ENABLE;
if (wdd->pretimeout)
enable |= QCOM_WDT_ENABLE_IRQ;
return enable;
}
static irqreturn_t qcom_wdt_isr(int irq, void *arg)
{
struct watchdog_device *wdd = arg;
watchdog_notify_pretimeout(wdd);
return IRQ_HANDLED;
}
static int qcom_wdt_start(struct watchdog_device *wdd)
{
struct qcom_wdt *wdt = to_qcom_wdt(wdd);
unsigned int bark = wdd->timeout - wdd->pretimeout;
writel(0, wdt_addr(wdt, WDT_EN));
writel(1, wdt_addr(wdt, WDT_RST));
writel(wdd->timeout * wdt->rate, wdt_addr(wdt, WDT_BARK_TIME));
writel(bark * wdt->rate, wdt_addr(wdt, WDT_BARK_TIME));
writel(wdd->timeout * wdt->rate, wdt_addr(wdt, WDT_BITE_TIME));
writel(1, wdt_addr(wdt, WDT_EN));
writel(qcom_get_enable(wdd), wdt_addr(wdt, WDT_EN));
return 0;
}
......@@ -89,6 +113,13 @@ static int qcom_wdt_set_timeout(struct watchdog_device *wdd,
return qcom_wdt_start(wdd);
}
static int qcom_wdt_set_pretimeout(struct watchdog_device *wdd,
unsigned int timeout)
{
wdd->pretimeout = timeout;
return qcom_wdt_start(wdd);
}
static int qcom_wdt_restart(struct watchdog_device *wdd, unsigned long action,
void *data)
{
......@@ -105,7 +136,7 @@ static int qcom_wdt_restart(struct watchdog_device *wdd, unsigned long action,
writel(1, wdt_addr(wdt, WDT_RST));
writel(timeout, wdt_addr(wdt, WDT_BARK_TIME));
writel(timeout, wdt_addr(wdt, WDT_BITE_TIME));
writel(1, wdt_addr(wdt, WDT_EN));
writel(QCOM_WDT_ENABLE, wdt_addr(wdt, WDT_EN));
/*
* Actually make sure the above sequence hits hardware before sleeping.
......@@ -121,6 +152,7 @@ static const struct watchdog_ops qcom_wdt_ops = {
.stop = qcom_wdt_stop,
.ping = qcom_wdt_ping,
.set_timeout = qcom_wdt_set_timeout,
.set_pretimeout = qcom_wdt_set_pretimeout,
.restart = qcom_wdt_restart,
.owner = THIS_MODULE,
};
......@@ -133,6 +165,15 @@ static const struct watchdog_info qcom_wdt_info = {
.identity = KBUILD_MODNAME,
};
static const struct watchdog_info qcom_wdt_pt_info = {
.options = WDIOF_KEEPALIVEPING
| WDIOF_MAGICCLOSE
| WDIOF_SETTIMEOUT
| WDIOF_PRETIMEOUT
| WDIOF_CARDRESET,
.identity = KBUILD_MODNAME,
};
static void qcom_clk_disable_unprepare(void *data)
{
clk_disable_unprepare(data);
......@@ -146,7 +187,8 @@ static int qcom_wdt_probe(struct platform_device *pdev)
struct device_node *np = dev->of_node;
const u32 *regs;
u32 percpu_offset;
int ret;
int irq, ret;
struct clk *clk;
regs = of_device_get_match_data(dev);
if (!regs) {
......@@ -173,19 +215,18 @@ static int qcom_wdt_probe(struct platform_device *pdev)
if (IS_ERR(wdt->base))
return PTR_ERR(wdt->base);
wdt->clk = devm_clk_get(dev, NULL);
if (IS_ERR(wdt->clk)) {
clk = devm_clk_get(dev, NULL);
if (IS_ERR(clk)) {
dev_err(dev, "failed to get input clock\n");
return PTR_ERR(wdt->clk);
return PTR_ERR(clk);
}
ret = clk_prepare_enable(wdt->clk);
ret = clk_prepare_enable(clk);
if (ret) {
dev_err(dev, "failed to setup clock\n");
return ret;
}
ret = devm_add_action_or_reset(dev, qcom_clk_disable_unprepare,
wdt->clk);
ret = devm_add_action_or_reset(dev, qcom_clk_disable_unprepare, clk);
if (ret)
return ret;
......@@ -197,14 +238,31 @@ static int qcom_wdt_probe(struct platform_device *pdev)
* that it would bite before a second elapses it's usefulness is
* limited. Bail if this is the case.
*/
wdt->rate = clk_get_rate(wdt->clk);
wdt->rate = clk_get_rate(clk);
if (wdt->rate == 0 ||
wdt->rate > 0x10000000U) {
dev_err(dev, "invalid clock rate\n");
return -EINVAL;
}
wdt->wdd.info = &qcom_wdt_info;
/* check if there is pretimeout support */
irq = platform_get_irq(pdev, 0);
if (irq > 0) {
ret = devm_request_irq(dev, irq, qcom_wdt_isr,
IRQF_TRIGGER_RISING,
"wdt_bark", &wdt->wdd);
if (ret)
return ret;
wdt->wdd.info = &qcom_wdt_pt_info;
wdt->wdd.pretimeout = 1;
} else {
if (irq == -EPROBE_DEFER)
return -EPROBE_DEFER;
wdt->wdd.info = &qcom_wdt_info;
}
wdt->wdd.ops = &qcom_wdt_ops;
wdt->wdd.min_timeout = 1;
wdt->wdd.max_timeout = 0x10000000U / wdt->rate;
......
......@@ -284,10 +284,8 @@ static int sprd_wdt_probe(struct platform_device *pdev)
}
wdt->irq = platform_get_irq(pdev, 0);
if (wdt->irq < 0) {
dev_err(dev, "failed to get IRQ resource\n");
if (wdt->irq < 0)
return wdt->irq;
}
ret = devm_request_irq(dev, wdt->irq, sprd_wdt_isr, IRQF_NO_SUSPEND,
"sprd-wdt", (void *)wdt);
......
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment