Commit 115502a6 authored by Linus Torvalds's avatar Linus Torvalds

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

Pull watchdog updates from Wim Van Sebroeck:
 - add TQ-Systems TQMX86 watchdog driver
 - add Qualcomm PM8916 watchdog driver
 - w83627hf_wdt: add quirk for Inves system
 - renesas_wdt: several improvements and document r8a774c0 support
 - mena21_wdt, mtx-1: Convert to use GPIO descriptor
 - bcm281xx, ie6xx_wdt: convert to DEFINE_SHOW_ATTRIBUTE
 - documentation: add PM usage and kernel-api: don't reference removed functions
 - update bindings for MT7629 SoC
 - several small fixes

* tag 'linux-watchdog-4.21-rc1' of git://www.linux-watchdog.org/linux-watchdog: (22 commits)
  watchdog: tqmx86: Add watchdog driver for the IO controller
  dt-bindings: watchdog: renesas-wdt: Document r8a774c0 support
  watchdog: docs: kernel-api: don't reference removed functions
  watchdog: add documentation for PM usage
  watchdog: mtx-1: Convert to use GPIO descriptor
  watchdog: mena21_wdt: Convert to GPIO descriptors
  dt-bindings: watchdog: Add Qualcomm PM8916 watchdog
  watchdog: Add pm8916 watchdog driver
  dt-bindings: watchdog: update bindings for MT7629 SoC
  watchdog: renesas_wdt: don't keep timer value during suspend/resume
  watchdog: ie6xx_wdt: convert to DEFINE_SHOW_ATTRIBUTE
  watchdog: bcm281xx: convert to DEFINE_SHOW_ATTRIBUTE
  watchdog: asm9260_wdt: make array mode_name static, shrinks object size
  watchdog/hpwdt: Update driver version.
  watchdog/hpwdt: Do not claim unsupported hardware
  watchdog/hpwdt: Exclude via blacklist
  Watchdog: remove outdated comment
  watchdog: w83627hf_wdt: Add quirk for Inves system
  watchdog: cpwd: add of_node_put()
  watchdog: renesas_wdt: don't set divider while watchdog is running
  ...
parents 57b0e314 e3c21e08
......@@ -8,6 +8,7 @@ Required properties:
"mediatek,mt6797-wdt", "mediatek,mt6589-wdt": for MT6797
"mediatek,mt7622-wdt", "mediatek,mt6589-wdt": for MT7622
"mediatek,mt7623-wdt", "mediatek,mt6589-wdt": for MT7623
"mediatek,mt7629-wdt", "mediatek,mt6589-wdt": for MT7629
- reg : Specifies base physical address and size of the registers.
......
QCOM PM8916 watchdog timer controller
This pm8916 watchdog timer controller must be under pm8916-pon node.
Required properties:
- compatible: should be "qcom,pm8916-wdt"
Optional properties :
- interrupts : Watchdog pre-timeout (bark) interrupt.
- timeout-sec : Watchdog timeout value in seconds.
Example:
pm8916_0: pm8916@0 {
compatible = "qcom,pm8916", "qcom,spmi-pmic";
reg = <0x0 SPMI_USID>;
pon@800 {
compatible = "qcom,pm8916-pon";
reg = <0x800>;
watchdog {
compatible = "qcom,pm8916-wdt";
interrupts = <0x0 0x8 6 IRQ_TYPE_EDGE_RISING>;
timeout-sec = <10>;
};
};
};
......@@ -9,6 +9,7 @@ Required properties:
- "renesas,r8a7744-wdt" (RZ/G1N)
- "renesas,r8a7745-wdt" (RZ/G1E)
- "renesas,r8a774a1-wdt" (RZ/G2M)
- "renesas,r8a774c0-wdt" (RZ/G2E)
- "renesas,r8a7790-wdt" (R-Car H2)
- "renesas,r8a7791-wdt" (R-Car M2-W)
- "renesas,r8a7792-wdt" (R-Car V2H)
......
......@@ -128,8 +128,6 @@ struct watchdog_ops {
int (*set_pretimeout)(struct watchdog_device *, unsigned int);
unsigned int (*get_timeleft)(struct watchdog_device *);
int (*restart)(struct watchdog_device *);
void (*ref)(struct watchdog_device *) __deprecated;
void (*unref)(struct watchdog_device *) __deprecated;
long (*ioctl)(struct watchdog_device *, unsigned int, unsigned long);
};
......@@ -218,8 +216,6 @@ they are supported. These optional routines/operations are:
if a command is not supported. The parameters that are passed to the ioctl
call are: watchdog_device, cmd and arg.
The 'ref' and 'unref' operations are no longer used and deprecated.
The status bits should (preferably) be set with the set_bit and clear_bit alike
bit-operations. The status bits that are defined are:
* WDOG_ACTIVE: this status bit indicates whether or not a watchdog timer device
......
The Linux WatchDog Timer Power Management Guide
===============================================
Last reviewed: 17-Dec-2018
Wolfram Sang <wsa+renesas@sang-engineering.com>
Introduction
------------
This document states rules about watchdog devices and their power management
handling to ensure a uniform behaviour for Linux systems.
Ping on resume
--------------
On resume, a watchdog timer shall be reset to its selected value to give
userspace enough time to resume. [1] [2]
[1] https://patchwork.kernel.org/patch/10252209/
[2] https://patchwork.kernel.org/patch/10711625/
......@@ -24,6 +24,7 @@
#include <linux/platform_device.h>
#include <linux/leds.h>
#include <linux/gpio.h>
#include <linux/gpio/machine.h>
#include <linux/gpio_keys.h>
#include <linux/input.h>
#include <linux/mtd/partitions.h>
......@@ -130,20 +131,18 @@ static struct platform_device mtx1_button = {
}
};
static struct resource mtx1_wdt_res[] = {
[0] = {
.start = 215,
.end = 215,
.name = "mtx1-wdt-gpio",
.flags = IORESOURCE_IRQ,
}
static struct gpiod_lookup_table mtx1_wdt_gpio_table = {
.dev_id = "mtx1-wdt.0",
.table = {
/* Global number 215 is offset 15 on Alchemy GPIO 2 */
GPIO_LOOKUP("alchemy-gpio2", 15, NULL, GPIO_ACTIVE_HIGH),
{ },
},
};
static struct platform_device mtx1_wdt = {
.name = "mtx1-wdt",
.id = 0,
.num_resources = ARRAY_SIZE(mtx1_wdt_res),
.resource = mtx1_wdt_res,
};
static const struct gpio_led default_leds[] = {
......@@ -310,6 +309,7 @@ static int __init mtx1_register_devices(void)
}
gpio_direction_input(mtx1_gpio_button[0].gpio);
out:
gpiod_add_lookup_table(&mtx1_wdt_gpio_table);
return platform_add_devices(mtx1_devs, ARRAY_SIZE(mtx1_devs));
}
arch_initcall(mtx1_register_devices);
......@@ -538,7 +538,7 @@ config COH901327_WATCHDOG
config NPCM7XX_WATCHDOG
bool "Nuvoton NPCM750 watchdog"
depends on ARCH_NPCM || COMPILE_TEST
default y if ARCH_NPCM750
default y if ARCH_NPCM7XX
select WATCHDOG_CORE
help
Say Y here to include Watchdog timer support for the
......@@ -847,6 +847,14 @@ config SPRD_WATCHDOG
Say Y here to include watchdog timer supported
by Spreadtrum system.
config PM8916_WATCHDOG
tristate "QCOM PM8916 pmic watchdog"
depends on OF && MFD_SPMI_PMIC
select WATCHDOG_CORE
help
Say Y here to include support watchdog timer embedded into the
pm8916 module.
# X86 (i386 + ia64 + x86_64) Architecture
config ACQUIRE_WDT
......@@ -1308,6 +1316,18 @@ config SMSC37B787_WDT
Most people will say N.
config TQMX86_WDT
tristate "TQ-Systems TQMX86 Watchdog Timer"
depends on X86
help
This is the driver for the hardware watchdog timer in the TQMX86 IO
controller found on some of their ComExpress Modules.
To compile this driver as a module, choose M here; the module
will be called tqmx86_wdt.
Most people will say N.
config VIA_WDT
tristate "VIA Watchdog Timer"
depends on X86 && PCI
......
......@@ -92,6 +92,7 @@ obj-$(CONFIG_STM32_WATCHDOG) += stm32_iwdg.o
obj-$(CONFIG_UNIPHIER_WATCHDOG) += uniphier_wdt.o
obj-$(CONFIG_RTD119X_WATCHDOG) += rtd119x_wdt.o
obj-$(CONFIG_SPRD_WATCHDOG) += sprd_wdt.o
obj-$(CONFIG_PM8916_WATCHDOG) += pm8916_wdt.o
# X86 (i386 + ia64 + x86_64) Architecture
obj-$(CONFIG_ACQUIRE_WDT) += acquirewdt.o
......@@ -129,6 +130,7 @@ obj-$(CONFIG_SBC7240_WDT) += sbc7240_wdt.o
obj-$(CONFIG_CPU5_WDT) += cpu5wdt.o
obj-$(CONFIG_SMSC_SCH311X_WDT) += sch311x_wdt.o
obj-$(CONFIG_SMSC37B787_WDT) += smsc37b787_wdt.o
obj-$(CONFIG_TQMX86_WDT) += tqmx86_wdt.o
obj-$(CONFIG_VIA_WDT) += via_wdt.o
obj-$(CONFIG_W83627HF_WDT) += w83627hf_wdt.o
obj-$(CONFIG_W83877F_WDT) += w83877f_wdt.o
......
......@@ -278,7 +278,7 @@ static int asm9260_wdt_probe(struct platform_device *pdev)
struct watchdog_device *wdd;
struct resource *res;
int ret;
const char * const mode_name[] = { "hw", "sw", "debug", };
static const char * const mode_name[] = { "hw", "sw", "debug", };
priv = devm_kzalloc(&pdev->dev, sizeof(struct asm9260_wdt_priv),
GFP_KERNEL);
......
......@@ -90,7 +90,7 @@ static int secure_register_read(struct bcm_kona_wdt *wdt, uint32_t offset)
#ifdef CONFIG_BCM_KONA_WDT_DEBUG
static int bcm_kona_wdt_dbg_show(struct seq_file *s, void *data)
static int bcm_kona_show(struct seq_file *s, void *data)
{
int ctl_val, cur_val;
unsigned long flags;
......@@ -130,17 +130,7 @@ static int bcm_kona_wdt_dbg_show(struct seq_file *s, void *data)
return 0;
}
static int bcm_kona_dbg_open(struct inode *inode, struct file *file)
{
return single_open(file, bcm_kona_wdt_dbg_show, inode->i_private);
}
static const struct file_operations bcm_kona_dbg_operations = {
.open = bcm_kona_dbg_open,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
};
DEFINE_SHOW_ATTRIBUTE(bcm_kona);
static void bcm_kona_wdt_debug_init(struct platform_device *pdev)
{
......@@ -157,7 +147,7 @@ static void bcm_kona_wdt_debug_init(struct platform_device *pdev)
return;
if (debugfs_create_file("info", S_IFREG | S_IRUGO, dir, wdt,
&bcm_kona_dbg_operations))
&bcm_kona_fops))
wdt->debugfs = dir;
else
debugfs_remove_recursive(dir);
......
......@@ -570,6 +570,8 @@ static int cpwd_probe(struct platform_device *op)
if (str_prop)
p->timeout = simple_strtoul(str_prop, NULL, 10);
of_node_put(options);
/* CP1400s seem to have broken PLD implementations-- the
* interrupt_mask register cannot be written, so no timer
* interrupts can be masked within the PLD.
......
......@@ -26,7 +26,7 @@
#include <linux/watchdog.h>
#include <asm/nmi.h>
#define HPWDT_VERSION "2.0.1"
#define HPWDT_VERSION "2.0.2"
#define SECS_TO_TICKS(secs) ((secs) * 1000 / 128)
#define TICKS_TO_SECS(ticks) ((ticks) * 128 / 1000)
#define HPWDT_MAX_TIMER TICKS_TO_SECS(65535)
......@@ -50,6 +50,11 @@ static const struct pci_device_id hpwdt_devices[] = {
};
MODULE_DEVICE_TABLE(pci, hpwdt_devices);
static const struct pci_device_id hpwdt_blacklist[] = {
{ PCI_DEVICE_SUB(PCI_VENDOR_ID_HP, 0x3306, PCI_VENDOR_ID_HP, 0x1979) }, /* auxilary iLO */
{ PCI_DEVICE_SUB(PCI_VENDOR_ID_HP, 0x3306, PCI_VENDOR_ID_HP_3PAR, 0x0289) }, /* CL */
{0}, /* terminate list */
};
/*
* Watchdog operations
......@@ -274,12 +279,10 @@ static int hpwdt_init_one(struct pci_dev *dev,
return -ENODEV;
}
/*
* Ignore all auxilary iLO devices with the following PCI ID
*/
if (dev->subsystem_vendor == PCI_VENDOR_ID_HP &&
dev->subsystem_device == 0x1979)
if (pci_match_id(hpwdt_blacklist, dev)) {
dev_dbg(&dev->dev, "Not supported on this device\n");
return -ENODEV;
}
if (pci_enable_device(dev)) {
dev_warn(&dev->dev,
......
......@@ -193,7 +193,7 @@ static struct watchdog_device ie6xx_wdt_dev = {
#ifdef CONFIG_DEBUG_FS
static int ie6xx_wdt_dbg_show(struct seq_file *s, void *unused)
static int ie6xx_wdt_show(struct seq_file *s, void *unused)
{
seq_printf(s, "PV1 = 0x%08x\n",
inl(ie6xx_wdt_data.sch_wdtba + PV1));
......@@ -212,23 +212,13 @@ static int ie6xx_wdt_dbg_show(struct seq_file *s, void *unused)
return 0;
}
static int ie6xx_wdt_dbg_open(struct inode *inode, struct file *file)
{
return single_open(file, ie6xx_wdt_dbg_show, NULL);
}
static const struct file_operations ie6xx_wdt_dbg_operations = {
.open = ie6xx_wdt_dbg_open,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
};
DEFINE_SHOW_ATTRIBUTE(ie6xx_wdt);
static void ie6xx_wdt_debugfs_init(void)
{
/* /sys/kernel/debug/ie6xx_wdt */
ie6xx_wdt_data.debugfs = debugfs_create_file("ie6xx_wdt",
S_IFREG | S_IRUGO, NULL, NULL, &ie6xx_wdt_dbg_operations);
S_IFREG | S_IRUGO, NULL, NULL, &ie6xx_wdt_fops);
}
static void ie6xx_wdt_debugfs_exit(void)
......
......@@ -13,10 +13,10 @@
#include <linux/platform_device.h>
#include <linux/watchdog.h>
#include <linux/uaccess.h>
#include <linux/gpio.h>
#include <linux/of_gpio.h>
#include <linux/gpio/consumer.h>
#include <linux/delay.h>
#include <linux/bitops.h>
#include <linux/of.h>
#define NUM_GPIOS 6
......@@ -31,7 +31,7 @@ enum a21_wdt_gpios {
struct a21_wdt_drv {
struct watchdog_device wdt;
unsigned gpios[NUM_GPIOS];
struct gpio_desc *gpios[NUM_GPIOS];
};
static bool nowayout = WATCHDOG_NOWAYOUT;
......@@ -43,9 +43,9 @@ static unsigned int a21_wdt_get_bootstatus(struct a21_wdt_drv *drv)
{
int reset = 0;
reset |= gpio_get_value(drv->gpios[GPIO_WD_RST0]) ? (1 << 0) : 0;
reset |= gpio_get_value(drv->gpios[GPIO_WD_RST1]) ? (1 << 1) : 0;
reset |= gpio_get_value(drv->gpios[GPIO_WD_RST2]) ? (1 << 2) : 0;
reset |= gpiod_get_value(drv->gpios[GPIO_WD_RST0]) ? (1 << 0) : 0;
reset |= gpiod_get_value(drv->gpios[GPIO_WD_RST1]) ? (1 << 1) : 0;
reset |= gpiod_get_value(drv->gpios[GPIO_WD_RST2]) ? (1 << 2) : 0;
return reset;
}
......@@ -54,7 +54,7 @@ static int a21_wdt_start(struct watchdog_device *wdt)
{
struct a21_wdt_drv *drv = watchdog_get_drvdata(wdt);
gpio_set_value(drv->gpios[GPIO_WD_ENAB], 1);
gpiod_set_value(drv->gpios[GPIO_WD_ENAB], 1);
return 0;
}
......@@ -63,7 +63,7 @@ static int a21_wdt_stop(struct watchdog_device *wdt)
{
struct a21_wdt_drv *drv = watchdog_get_drvdata(wdt);
gpio_set_value(drv->gpios[GPIO_WD_ENAB], 0);
gpiod_set_value(drv->gpios[GPIO_WD_ENAB], 0);
return 0;
}
......@@ -72,9 +72,9 @@ static int a21_wdt_ping(struct watchdog_device *wdt)
{
struct a21_wdt_drv *drv = watchdog_get_drvdata(wdt);
gpio_set_value(drv->gpios[GPIO_WD_TRIG], 0);
gpiod_set_value(drv->gpios[GPIO_WD_TRIG], 0);
ndelay(10);
gpio_set_value(drv->gpios[GPIO_WD_TRIG], 1);
gpiod_set_value(drv->gpios[GPIO_WD_TRIG], 1);
return 0;
}
......@@ -96,9 +96,9 @@ static int a21_wdt_set_timeout(struct watchdog_device *wdt,
}
if (timeout == 1)
gpio_set_value(drv->gpios[GPIO_WD_FAST], 1);
gpiod_set_value(drv->gpios[GPIO_WD_FAST], 1);
else
gpio_set_value(drv->gpios[GPIO_WD_FAST], 0);
gpiod_set_value(drv->gpios[GPIO_WD_FAST], 0);
wdt->timeout = timeout;
......@@ -127,7 +127,6 @@ static struct watchdog_device a21_wdt = {
static int a21_wdt_probe(struct platform_device *pdev)
{
struct device_node *node;
struct a21_wdt_drv *drv;
unsigned int reset = 0;
int num_gpios;
......@@ -138,42 +137,42 @@ static int a21_wdt_probe(struct platform_device *pdev)
if (!drv)
return -ENOMEM;
/* Fill GPIO pin array */
node = pdev->dev.of_node;
num_gpios = of_gpio_count(node);
num_gpios = gpiod_count(&pdev->dev, NULL);
if (num_gpios != NUM_GPIOS) {
dev_err(&pdev->dev, "gpios DT property wrong, got %d want %d",
num_gpios, NUM_GPIOS);
return -ENODEV;
}
for (i = 0; i < num_gpios; i++) {
int val;
val = of_get_gpio(node, i);
if (val < 0)
return val;
drv->gpios[i] = val;
}
/* Request the used GPIOs */
for (i = 0; i < num_gpios; i++) {
ret = devm_gpio_request(&pdev->dev, drv->gpios[i],
"MEN A21 Watchdog");
if (ret)
return ret;
enum gpiod_flags gflags;
if (i < GPIO_WD_RST0)
ret = gpio_direction_output(drv->gpios[i],
gpio_get_value(drv->gpios[i]));
else /* GPIO_WD_RST[0..2] are inputs */
ret = gpio_direction_input(drv->gpios[i]);
if (ret)
gflags = GPIOD_ASIS;
else
gflags = GPIOD_IN;
drv->gpios[i] = devm_gpiod_get_index(&pdev->dev, NULL, i,
gflags);
if (IS_ERR(drv->gpios[i])) {
ret = PTR_ERR(drv->gpios[i]);
return ret;
}
gpiod_set_consumer_name(drv->gpios[i], "MEN A21 Watchdog");
/*
* Retrieve the initial value from the GPIOs that should be
* output, then set up the line as output with that value.
*/
if (i < GPIO_WD_RST0) {
int val;
val = gpiod_get_value(drv->gpios[i]);
gpiod_direction_output(drv->gpios[i], val);
}
}
watchdog_init_timeout(&a21_wdt, 30, &pdev->dev);
watchdog_set_nowayout(&a21_wdt, nowayout);
watchdog_set_drvdata(&a21_wdt, drv);
......@@ -207,7 +206,7 @@ static void a21_wdt_shutdown(struct platform_device *pdev)
{
struct a21_wdt_drv *drv = dev_get_drvdata(&pdev->dev);
gpio_set_value(drv->gpios[GPIO_WD_ENAB], 0);
gpiod_set_value(drv->gpios[GPIO_WD_ENAB], 0);
}
static const struct of_device_id a21_wdt_ids[] = {
......
......@@ -39,7 +39,7 @@
#include <linux/platform_device.h>
#include <linux/io.h>
#include <linux/uaccess.h>
#include <linux/gpio.h>
#include <linux/gpio/consumer.h>
#include <asm/mach-au1x00/au1000.h>
......@@ -55,7 +55,7 @@ static struct {
int queue;
int default_ticks;
unsigned long inuse;
unsigned gpio;
struct gpio_desc *gpiod;
unsigned int gstate;
} mtx1_wdt_device;
......@@ -67,7 +67,7 @@ static void mtx1_wdt_trigger(struct timer_list *unused)
/* toggle wdt gpio */
mtx1_wdt_device.gstate = !mtx1_wdt_device.gstate;
gpio_set_value(mtx1_wdt_device.gpio, mtx1_wdt_device.gstate);
gpiod_set_value(mtx1_wdt_device.gpiod, mtx1_wdt_device.gstate);
if (mtx1_wdt_device.queue && ticks)
mod_timer(&mtx1_wdt_device.timer, jiffies + MTX1_WDT_INTERVAL);
......@@ -90,7 +90,7 @@ static void mtx1_wdt_start(void)
if (!mtx1_wdt_device.queue) {
mtx1_wdt_device.queue = 1;
mtx1_wdt_device.gstate = 1;
gpio_set_value(mtx1_wdt_device.gpio, 1);
gpiod_set_value(mtx1_wdt_device.gpiod, 1);
mod_timer(&mtx1_wdt_device.timer, jiffies + MTX1_WDT_INTERVAL);
}
mtx1_wdt_device.running++;
......@@ -105,7 +105,7 @@ static int mtx1_wdt_stop(void)
if (mtx1_wdt_device.queue) {
mtx1_wdt_device.queue = 0;
mtx1_wdt_device.gstate = 0;
gpio_set_value(mtx1_wdt_device.gpio, 0);
gpiod_set_value(mtx1_wdt_device.gpiod, 0);
}
ticks = mtx1_wdt_device.default_ticks;
spin_unlock_irqrestore(&mtx1_wdt_device.lock, flags);
......@@ -198,12 +198,11 @@ static int mtx1_wdt_probe(struct platform_device *pdev)
{
int ret;
mtx1_wdt_device.gpio = pdev->resource[0].start;
ret = devm_gpio_request_one(&pdev->dev, mtx1_wdt_device.gpio,
GPIOF_OUT_INIT_HIGH, "mtx1-wdt");
if (ret < 0) {
mtx1_wdt_device.gpiod = devm_gpiod_get(&pdev->dev,
NULL, GPIOD_OUT_HIGH);
if (IS_ERR(mtx1_wdt_device.gpiod)) {
dev_err(&pdev->dev, "failed to request gpio");
return ret;
return PTR_ERR(mtx1_wdt_device.gpiod);
}
spin_lock_init(&mtx1_wdt_device.lock);
......
// SPDX-License-Identifier: GPL-2.0
#include <linux/bitops.h>
#include <linux/interrupt.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/property.h>
#include <linux/platform_device.h>
#include <linux/regmap.h>
#include <linux/watchdog.h>
#define PON_INT_RT_STS 0x10
#define PMIC_WD_BARK_STS_BIT BIT(6)
#define PON_PMIC_WD_RESET_S1_TIMER 0x54
#define PON_PMIC_WD_RESET_S2_TIMER 0x55
#define PON_PMIC_WD_RESET_S2_CTL 0x56
#define RESET_TYPE_WARM 0x01
#define RESET_TYPE_SHUTDOWN 0x04
#define RESET_TYPE_HARD 0x07
#define PON_PMIC_WD_RESET_S2_CTL2 0x57
#define S2_RESET_EN_BIT BIT(7)
#define PON_PMIC_WD_RESET_PET 0x58
#define WATCHDOG_PET_BIT BIT(0)
#define PM8916_WDT_DEFAULT_TIMEOUT 32
#define PM8916_WDT_MIN_TIMEOUT 1
#define PM8916_WDT_MAX_TIMEOUT 127
struct pm8916_wdt {
struct regmap *regmap;
struct watchdog_device wdev;
u32 baseaddr;
};
static int pm8916_wdt_start(struct watchdog_device *wdev)
{
struct pm8916_wdt *wdt = watchdog_get_drvdata(wdev);
return regmap_update_bits(wdt->regmap,
wdt->baseaddr + PON_PMIC_WD_RESET_S2_CTL2,
S2_RESET_EN_BIT, S2_RESET_EN_BIT);
}
static int pm8916_wdt_stop(struct watchdog_device *wdev)
{
struct pm8916_wdt *wdt = watchdog_get_drvdata(wdev);
return regmap_update_bits(wdt->regmap,
wdt->baseaddr + PON_PMIC_WD_RESET_S2_CTL2,
S2_RESET_EN_BIT, 0);
}
static int pm8916_wdt_ping(struct watchdog_device *wdev)
{
struct pm8916_wdt *wdt = watchdog_get_drvdata(wdev);
return regmap_update_bits(wdt->regmap,
wdt->baseaddr + PON_PMIC_WD_RESET_PET,
WATCHDOG_PET_BIT, WATCHDOG_PET_BIT);
}
static int pm8916_wdt_configure_timers(struct watchdog_device *wdev)
{
struct pm8916_wdt *wdt = watchdog_get_drvdata(wdev);
int err;
err = regmap_write(wdt->regmap,
wdt->baseaddr + PON_PMIC_WD_RESET_S1_TIMER,
wdev->timeout - wdev->pretimeout);
if (err)
return err;
return regmap_write(wdt->regmap,
wdt->baseaddr + PON_PMIC_WD_RESET_S2_TIMER,
wdev->pretimeout);
}
static int pm8916_wdt_set_timeout(struct watchdog_device *wdev,
unsigned int timeout)
{
wdev->timeout = timeout;
return pm8916_wdt_configure_timers(wdev);
}
static int pm8916_wdt_set_pretimeout(struct watchdog_device *wdev,
unsigned int pretimeout)
{
wdev->pretimeout = pretimeout;
return pm8916_wdt_configure_timers(wdev);
}
static irqreturn_t pm8916_wdt_isr(int irq, void *arg)
{
struct pm8916_wdt *wdt = arg;
int err, sts;
err = regmap_read(wdt->regmap, wdt->baseaddr + PON_INT_RT_STS, &sts);
if (err)
return IRQ_HANDLED;
if (sts & PMIC_WD_BARK_STS_BIT)
watchdog_notify_pretimeout(&wdt->wdev);
return IRQ_HANDLED;
}
static const struct watchdog_info pm8916_wdt_ident = {
.options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE,
.identity = "QCOM PM8916 PON WDT",
};
static const struct watchdog_info pm8916_wdt_pt_ident = {
.options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE |
WDIOF_PRETIMEOUT,
.identity = "QCOM PM8916 PON WDT",
};
static const struct watchdog_ops pm8916_wdt_ops = {
.owner = THIS_MODULE,
.start = pm8916_wdt_start,
.stop = pm8916_wdt_stop,
.ping = pm8916_wdt_ping,
.set_timeout = pm8916_wdt_set_timeout,
.set_pretimeout = pm8916_wdt_set_pretimeout,
};
static int pm8916_wdt_probe(struct platform_device *pdev)
{
struct pm8916_wdt *wdt;
struct device *parent;
int err, irq;
wdt = devm_kzalloc(&pdev->dev, sizeof(*wdt), GFP_KERNEL);
if (!wdt)
return -ENOMEM;
parent = pdev->dev.parent;
/*
* The pm8916-pon-wdt is a child of the pon device, which is a child
* of the pm8916 mfd device. We want access to the pm8916 registers.
* Retrieve regmap from pm8916 (parent->parent) and base address
* from pm8916-pon (pon).
*/
wdt->regmap = dev_get_regmap(parent->parent, NULL);
if (!wdt->regmap) {
dev_err(&pdev->dev, "failed to locate regmap\n");
return -ENODEV;
}
err = device_property_read_u32(parent, "reg", &wdt->baseaddr);
if (err) {
dev_err(&pdev->dev, "failed to get pm8916-pon address\n");
return err;
}
irq = platform_get_irq(pdev, 0);
if (irq > 0) {
if (devm_request_irq(&pdev->dev, irq, pm8916_wdt_isr, 0,
"pm8916_wdt", wdt))
irq = 0;
}
/* Configure watchdog to hard-reset mode */
err = regmap_write(wdt->regmap,
wdt->baseaddr + PON_PMIC_WD_RESET_S2_CTL,
RESET_TYPE_HARD);
if (err) {
dev_err(&pdev->dev, "failed configure watchdog\n");
return err;
}
wdt->wdev.info = (irq > 0) ? &pm8916_wdt_pt_ident : &pm8916_wdt_ident,
wdt->wdev.ops = &pm8916_wdt_ops,
wdt->wdev.parent = &pdev->dev;
wdt->wdev.min_timeout = PM8916_WDT_MIN_TIMEOUT;
wdt->wdev.max_timeout = PM8916_WDT_MAX_TIMEOUT;
wdt->wdev.timeout = PM8916_WDT_DEFAULT_TIMEOUT;
wdt->wdev.pretimeout = 0;
watchdog_set_drvdata(&wdt->wdev, wdt);
watchdog_init_timeout(&wdt->wdev, 0, &pdev->dev);
pm8916_wdt_configure_timers(&wdt->wdev);
return devm_watchdog_register_device(&pdev->dev, &wdt->wdev);
}
static const struct of_device_id pm8916_wdt_id_table[] = {
{ .compatible = "qcom,pm8916-wdt" },
{ }
};
MODULE_DEVICE_TABLE(of, pm8916_wdt_id_table);
static struct platform_driver pm8916_wdt_driver = {
.probe = pm8916_wdt_probe,
.driver = {
.name = "pm8916-wdt",
.of_match_table = of_match_ptr(pm8916_wdt_id_table),
},
};
module_platform_driver(pm8916_wdt_driver);
MODULE_AUTHOR("Loic Poulain <loic.poulain@linaro.org>");
MODULE_DESCRIPTION("Qualcomm pm8916 watchdog driver");
MODULE_LICENSE("GPL v2");
......@@ -48,7 +48,6 @@ struct rwdt_priv {
void __iomem *base;
struct watchdog_device wdev;
unsigned long clk_rate;
u16 time_left;
u8 cks;
};
......@@ -74,12 +73,17 @@ static int rwdt_init_timeout(struct watchdog_device *wdev)
static int rwdt_start(struct watchdog_device *wdev)
{
struct rwdt_priv *priv = watchdog_get_drvdata(wdev);
u8 val;
pm_runtime_get_sync(wdev->parent);
rwdt_write(priv, 0, RWTCSRB);
rwdt_write(priv, priv->cks, RWTCSRA);
/* Stop the timer before we modify any register */
val = readb_relaxed(priv->base + RWTCSRA) & ~RWTCSRA_TME;
rwdt_write(priv, val, RWTCSRA);
rwdt_init_timeout(wdev);
rwdt_write(priv, priv->cks, RWTCSRA);
rwdt_write(priv, 0, RWTCSRB);
while (readb_relaxed(priv->base + RWTCSRA) & RWTCSRA_WRFLG)
cpu_relax();
......@@ -220,8 +224,8 @@ static int rwdt_probe(struct platform_device *pdev)
goto out_pm_disable;
}
priv->wdev.info = &rwdt_ident,
priv->wdev.ops = &rwdt_ops,
priv->wdev.info = &rwdt_ident;
priv->wdev.ops = &rwdt_ops;
priv->wdev.parent = &pdev->dev;
priv->wdev.min_timeout = 1;
priv->wdev.max_timeout = DIV_BY_CLKS_PER_SEC(priv, 65536);
......@@ -263,10 +267,9 @@ static int __maybe_unused rwdt_suspend(struct device *dev)
{
struct rwdt_priv *priv = dev_get_drvdata(dev);
if (watchdog_active(&priv->wdev)) {
priv->time_left = readw(priv->base + RWTCNT);
if (watchdog_active(&priv->wdev))
rwdt_stop(&priv->wdev);
}
return 0;
}
......@@ -274,10 +277,9 @@ static int __maybe_unused rwdt_resume(struct device *dev)
{
struct rwdt_priv *priv = dev_get_drvdata(dev);
if (watchdog_active(&priv->wdev)) {
if (watchdog_active(&priv->wdev))
rwdt_start(&priv->wdev);
rwdt_write(priv, priv->time_left, RWTCNT);
}
return 0;
}
......
// SPDX-License-Identifier: GPL-2.0+
/*
* Watchdog driver for TQMx86 PLD.
*
* The watchdog supports power of 2 timeouts from 1 to 4096sec.
* Once started, it cannot be stopped.
*
* Based on the vendor code written by Vadim V.Vlasov
* <vvlasov@dev.rtsoft.ru>
*/
#include <linux/io.h>
#include <linux/log2.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/timer.h>
#include <linux/watchdog.h>
/* default timeout (secs) */
#define WDT_TIMEOUT 32
static unsigned int timeout;
module_param(timeout, uint, 0);
MODULE_PARM_DESC(timeout,
"Watchdog timeout in seconds. (1<=timeout<=4096, default="
__MODULE_STRING(WDT_TIMEOUT) ")");
struct tqmx86_wdt {
struct watchdog_device wdd;
void __iomem *io_base;
};
#define TQMX86_WDCFG 0x00 /* Watchdog Configuration Register */
#define TQMX86_WDCS 0x01 /* Watchdog Config/Status Register */
static int tqmx86_wdt_start(struct watchdog_device *wdd)
{
struct tqmx86_wdt *priv = watchdog_get_drvdata(wdd);
iowrite8(0x81, priv->io_base + TQMX86_WDCS);
return 0;
}
static int tqmx86_wdt_set_timeout(struct watchdog_device *wdd, unsigned int t)
{
struct tqmx86_wdt *priv = watchdog_get_drvdata(wdd);
u8 val;
t = roundup_pow_of_two(t);
val = ilog2(t) | 0x90;
val += 3; /* values 0,1,2 correspond to 0.125,0.25,0.5s timeouts */
iowrite8(val, priv->io_base + TQMX86_WDCFG);
wdd->timeout = t;
return 0;
}
static const struct watchdog_info tqmx86_wdt_info = {
.options = WDIOF_SETTIMEOUT |
WDIOF_KEEPALIVEPING,
.identity = "TQMx86 Watchdog",
};
static struct watchdog_ops tqmx86_wdt_ops = {
.owner = THIS_MODULE,
.start = tqmx86_wdt_start,
.set_timeout = tqmx86_wdt_set_timeout,
};
static int tqmx86_wdt_probe(struct platform_device *pdev)
{
struct tqmx86_wdt *priv;
struct resource *res;
int err;
priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
if (!priv)
return -ENOMEM;
res = platform_get_resource(pdev, IORESOURCE_IO, 0);
if (IS_ERR(res))
return PTR_ERR(res);
priv->io_base = devm_ioport_map(&pdev->dev, res->start,
resource_size(res));
if (IS_ERR(priv->io_base))
return PTR_ERR(priv->io_base);
watchdog_set_drvdata(&priv->wdd, priv);
priv->wdd.parent = &pdev->dev;
priv->wdd.info = &tqmx86_wdt_info;
priv->wdd.ops = &tqmx86_wdt_ops;
priv->wdd.min_timeout = 1;
priv->wdd.max_timeout = 4096;
priv->wdd.max_hw_heartbeat_ms = 4096*1000;
priv->wdd.timeout = WDT_TIMEOUT;
watchdog_init_timeout(&priv->wdd, timeout, &pdev->dev);
watchdog_set_nowayout(&priv->wdd, WATCHDOG_NOWAYOUT);
tqmx86_wdt_set_timeout(&priv->wdd, priv->wdd.timeout);
err = devm_watchdog_register_device(&pdev->dev, &priv->wdd);
if (err)
return err;
dev_info(&pdev->dev, "TQMx86 watchdog\n");
return 0;
}
static struct platform_driver tqmx86_wdt_driver = {
.driver = {
.name = "tqmx86-wdt",
},
.probe = tqmx86_wdt_probe,
};
module_platform_driver(tqmx86_wdt_driver);
MODULE_AUTHOR("Andrew Lunn <andrew@lunn.ch>");
MODULE_DESCRIPTION("TQMx86 Watchdog");
MODULE_ALIAS("platform:tqmx86-wdt");
MODULE_LICENSE("GPL");
......@@ -38,6 +38,7 @@
#include <linux/ioport.h>
#include <linux/init.h>
#include <linux/io.h>
#include <linux/dmi.h>
#define WATCHDOG_NAME "w83627hf/thf/hg/dhg WDT"
#define WATCHDOG_TIMEOUT 60 /* 60 sec default timeout */
......@@ -46,6 +47,8 @@ static int wdt_io;
static int cr_wdt_timeout; /* WDT timeout register */
static int cr_wdt_control; /* WDT control register */
static int cr_wdt_csr; /* WDT control & status register */
static int wdt_cfg_enter = 0x87;/* key to unlock configuration space */
static int wdt_cfg_leave = 0xAA;/* key to lock configuration space */
enum chips { w83627hf, w83627s, w83697hf, w83697ug, w83637hf, w83627thf,
w83687thf, w83627ehf, w83627dhg, w83627uhg, w83667hg, w83627dhg_p,
......@@ -130,8 +133,8 @@ static int superio_enter(void)
if (!request_muxed_region(wdt_io, 2, WATCHDOG_NAME))
return -EBUSY;
outb_p(0x87, WDT_EFER); /* Enter extended function mode */
outb_p(0x87, WDT_EFER); /* Again according to manual */
outb_p(wdt_cfg_enter, WDT_EFER); /* Enter extended function mode */
outb_p(wdt_cfg_enter, WDT_EFER); /* Again according to manual */
return 0;
}
......@@ -143,7 +146,7 @@ static void superio_select(int ld)
static void superio_exit(void)
{
outb_p(0xAA, WDT_EFER); /* Leave extended function mode */
outb_p(wdt_cfg_leave, WDT_EFER); /* Leave extended function mode */
release_region(wdt_io, 2);
}
......@@ -430,6 +433,32 @@ static int wdt_find(int addr)
return ret;
}
/*
* On some systems, the NCT6791D comes with a companion chip and the
* watchdog function is in this companion chip. We must use a different
* unlocking sequence to access the companion chip.
*/
static int __init wdt_use_alt_key(const struct dmi_system_id *d)
{
wdt_cfg_enter = 0x88;
wdt_cfg_leave = 0xBB;
return 0;
}
static const struct dmi_system_id wdt_dmi_table[] __initconst = {
{
.matches = {
DMI_EXACT_MATCH(DMI_SYS_VENDOR, "INVES"),
DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "CTS"),
DMI_EXACT_MATCH(DMI_BOARD_VENDOR, "INVES"),
DMI_EXACT_MATCH(DMI_BOARD_NAME, "SHARKBAY"),
},
.callback = wdt_use_alt_key,
},
{}
};
static int __init wdt_init(void)
{
int ret;
......@@ -459,6 +488,9 @@ static int __init wdt_init(void)
"NCT6102",
};
/* Apply system-specific quirks */
dmi_check_system(wdt_dmi_table);
wdt_io = 0x2e;
chip = wdt_find(0x2e);
if (chip < 0) {
......
......@@ -90,9 +90,6 @@ struct watchdog_ops {
*
* The driver-data field may not be accessed directly. It must be accessed
* via the watchdog_set_drvdata and watchdog_get_drvdata helpers.
*
* The lock field is for watchdog core internal use only and should not be
* touched.
*/
struct watchdog_device {
int id;
......
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