linux-watchdog 4.21-rc1 tag
-----BEGIN PGP SIGNATURE----- Version: GnuPG v2.0.14 (GNU/Linux) iEYEABECAAYFAlwopYUACgkQ+iyteGJfRsqOZwCfXM0u0XQJS6TZFtEOZp5gKq+d f/4AnRXcblmb1wJiEVItizB03leOWHqk =jry0 -----END PGP SIGNATURE----- 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 ...
This commit is contained in:
commit
115502a6f3
|
@ -8,6 +8,7 @@ Required properties:
|
||||||
"mediatek,mt6797-wdt", "mediatek,mt6589-wdt": for MT6797
|
"mediatek,mt6797-wdt", "mediatek,mt6589-wdt": for MT6797
|
||||||
"mediatek,mt7622-wdt", "mediatek,mt6589-wdt": for MT7622
|
"mediatek,mt7622-wdt", "mediatek,mt6589-wdt": for MT7622
|
||||||
"mediatek,mt7623-wdt", "mediatek,mt6589-wdt": for MT7623
|
"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.
|
- reg : Specifies base physical address and size of the registers.
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,28 @@
|
||||||
|
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,r8a7744-wdt" (RZ/G1N)
|
||||||
- "renesas,r8a7745-wdt" (RZ/G1E)
|
- "renesas,r8a7745-wdt" (RZ/G1E)
|
||||||
- "renesas,r8a774a1-wdt" (RZ/G2M)
|
- "renesas,r8a774a1-wdt" (RZ/G2M)
|
||||||
|
- "renesas,r8a774c0-wdt" (RZ/G2E)
|
||||||
- "renesas,r8a7790-wdt" (R-Car H2)
|
- "renesas,r8a7790-wdt" (R-Car H2)
|
||||||
- "renesas,r8a7791-wdt" (R-Car M2-W)
|
- "renesas,r8a7791-wdt" (R-Car M2-W)
|
||||||
- "renesas,r8a7792-wdt" (R-Car V2H)
|
- "renesas,r8a7792-wdt" (R-Car V2H)
|
||||||
|
|
|
@ -128,8 +128,6 @@ struct watchdog_ops {
|
||||||
int (*set_pretimeout)(struct watchdog_device *, unsigned int);
|
int (*set_pretimeout)(struct watchdog_device *, unsigned int);
|
||||||
unsigned int (*get_timeleft)(struct watchdog_device *);
|
unsigned int (*get_timeleft)(struct watchdog_device *);
|
||||||
int (*restart)(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);
|
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
|
if a command is not supported. The parameters that are passed to the ioctl
|
||||||
call are: watchdog_device, cmd and arg.
|
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
|
The status bits should (preferably) be set with the set_bit and clear_bit alike
|
||||||
bit-operations. The status bits that are defined are:
|
bit-operations. The status bits that are defined are:
|
||||||
* WDOG_ACTIVE: this status bit indicates whether or not a watchdog timer device
|
* WDOG_ACTIVE: this status bit indicates whether or not a watchdog timer device
|
||||||
|
|
|
@ -0,0 +1,19 @@
|
||||||
|
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/platform_device.h>
|
||||||
#include <linux/leds.h>
|
#include <linux/leds.h>
|
||||||
#include <linux/gpio.h>
|
#include <linux/gpio.h>
|
||||||
|
#include <linux/gpio/machine.h>
|
||||||
#include <linux/gpio_keys.h>
|
#include <linux/gpio_keys.h>
|
||||||
#include <linux/input.h>
|
#include <linux/input.h>
|
||||||
#include <linux/mtd/partitions.h>
|
#include <linux/mtd/partitions.h>
|
||||||
|
@ -130,20 +131,18 @@ static struct platform_device mtx1_button = {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
static struct resource mtx1_wdt_res[] = {
|
static struct gpiod_lookup_table mtx1_wdt_gpio_table = {
|
||||||
[0] = {
|
.dev_id = "mtx1-wdt.0",
|
||||||
.start = 215,
|
.table = {
|
||||||
.end = 215,
|
/* Global number 215 is offset 15 on Alchemy GPIO 2 */
|
||||||
.name = "mtx1-wdt-gpio",
|
GPIO_LOOKUP("alchemy-gpio2", 15, NULL, GPIO_ACTIVE_HIGH),
|
||||||
.flags = IORESOURCE_IRQ,
|
{ },
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
static struct platform_device mtx1_wdt = {
|
static struct platform_device mtx1_wdt = {
|
||||||
.name = "mtx1-wdt",
|
.name = "mtx1-wdt",
|
||||||
.id = 0,
|
.id = 0,
|
||||||
.num_resources = ARRAY_SIZE(mtx1_wdt_res),
|
|
||||||
.resource = mtx1_wdt_res,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
static const struct gpio_led default_leds[] = {
|
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);
|
gpio_direction_input(mtx1_gpio_button[0].gpio);
|
||||||
out:
|
out:
|
||||||
|
gpiod_add_lookup_table(&mtx1_wdt_gpio_table);
|
||||||
return platform_add_devices(mtx1_devs, ARRAY_SIZE(mtx1_devs));
|
return platform_add_devices(mtx1_devs, ARRAY_SIZE(mtx1_devs));
|
||||||
}
|
}
|
||||||
arch_initcall(mtx1_register_devices);
|
arch_initcall(mtx1_register_devices);
|
||||||
|
|
|
@ -538,7 +538,7 @@ config COH901327_WATCHDOG
|
||||||
config NPCM7XX_WATCHDOG
|
config NPCM7XX_WATCHDOG
|
||||||
bool "Nuvoton NPCM750 watchdog"
|
bool "Nuvoton NPCM750 watchdog"
|
||||||
depends on ARCH_NPCM || COMPILE_TEST
|
depends on ARCH_NPCM || COMPILE_TEST
|
||||||
default y if ARCH_NPCM750
|
default y if ARCH_NPCM7XX
|
||||||
select WATCHDOG_CORE
|
select WATCHDOG_CORE
|
||||||
help
|
help
|
||||||
Say Y here to include Watchdog timer support for the
|
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
|
Say Y here to include watchdog timer supported
|
||||||
by Spreadtrum system.
|
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
|
# X86 (i386 + ia64 + x86_64) Architecture
|
||||||
|
|
||||||
config ACQUIRE_WDT
|
config ACQUIRE_WDT
|
||||||
|
@ -1308,6 +1316,18 @@ config SMSC37B787_WDT
|
||||||
|
|
||||||
Most people will say N.
|
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
|
config VIA_WDT
|
||||||
tristate "VIA Watchdog Timer"
|
tristate "VIA Watchdog Timer"
|
||||||
depends on X86 && PCI
|
depends on X86 && PCI
|
||||||
|
|
|
@ -92,6 +92,7 @@ obj-$(CONFIG_STM32_WATCHDOG) += stm32_iwdg.o
|
||||||
obj-$(CONFIG_UNIPHIER_WATCHDOG) += uniphier_wdt.o
|
obj-$(CONFIG_UNIPHIER_WATCHDOG) += uniphier_wdt.o
|
||||||
obj-$(CONFIG_RTD119X_WATCHDOG) += rtd119x_wdt.o
|
obj-$(CONFIG_RTD119X_WATCHDOG) += rtd119x_wdt.o
|
||||||
obj-$(CONFIG_SPRD_WATCHDOG) += sprd_wdt.o
|
obj-$(CONFIG_SPRD_WATCHDOG) += sprd_wdt.o
|
||||||
|
obj-$(CONFIG_PM8916_WATCHDOG) += pm8916_wdt.o
|
||||||
|
|
||||||
# X86 (i386 + ia64 + x86_64) Architecture
|
# X86 (i386 + ia64 + x86_64) Architecture
|
||||||
obj-$(CONFIG_ACQUIRE_WDT) += acquirewdt.o
|
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_CPU5_WDT) += cpu5wdt.o
|
||||||
obj-$(CONFIG_SMSC_SCH311X_WDT) += sch311x_wdt.o
|
obj-$(CONFIG_SMSC_SCH311X_WDT) += sch311x_wdt.o
|
||||||
obj-$(CONFIG_SMSC37B787_WDT) += smsc37b787_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_VIA_WDT) += via_wdt.o
|
||||||
obj-$(CONFIG_W83627HF_WDT) += w83627hf_wdt.o
|
obj-$(CONFIG_W83627HF_WDT) += w83627hf_wdt.o
|
||||||
obj-$(CONFIG_W83877F_WDT) += w83877f_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 watchdog_device *wdd;
|
||||||
struct resource *res;
|
struct resource *res;
|
||||||
int ret;
|
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),
|
priv = devm_kzalloc(&pdev->dev, sizeof(struct asm9260_wdt_priv),
|
||||||
GFP_KERNEL);
|
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
|
#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;
|
int ctl_val, cur_val;
|
||||||
unsigned long flags;
|
unsigned long flags;
|
||||||
|
@ -130,17 +130,7 @@ static int bcm_kona_wdt_dbg_show(struct seq_file *s, void *data)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int bcm_kona_dbg_open(struct inode *inode, struct file *file)
|
DEFINE_SHOW_ATTRIBUTE(bcm_kona);
|
||||||
{
|
|
||||||
return single_open(file, bcm_kona_wdt_dbg_show, inode->i_private);
|
|
||||||
}
|
|
||||||
|
|
||||||
static const struct file_operations bcm_kona_dbg_operations = {
|
|
||||||
.open = bcm_kona_dbg_open,
|
|
||||||
.read = seq_read,
|
|
||||||
.llseek = seq_lseek,
|
|
||||||
.release = single_release,
|
|
||||||
};
|
|
||||||
|
|
||||||
static void bcm_kona_wdt_debug_init(struct platform_device *pdev)
|
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;
|
return;
|
||||||
|
|
||||||
if (debugfs_create_file("info", S_IFREG | S_IRUGO, dir, wdt,
|
if (debugfs_create_file("info", S_IFREG | S_IRUGO, dir, wdt,
|
||||||
&bcm_kona_dbg_operations))
|
&bcm_kona_fops))
|
||||||
wdt->debugfs = dir;
|
wdt->debugfs = dir;
|
||||||
else
|
else
|
||||||
debugfs_remove_recursive(dir);
|
debugfs_remove_recursive(dir);
|
||||||
|
|
|
@ -570,6 +570,8 @@ static int cpwd_probe(struct platform_device *op)
|
||||||
if (str_prop)
|
if (str_prop)
|
||||||
p->timeout = simple_strtoul(str_prop, NULL, 10);
|
p->timeout = simple_strtoul(str_prop, NULL, 10);
|
||||||
|
|
||||||
|
of_node_put(options);
|
||||||
|
|
||||||
/* CP1400s seem to have broken PLD implementations-- the
|
/* CP1400s seem to have broken PLD implementations-- the
|
||||||
* interrupt_mask register cannot be written, so no timer
|
* interrupt_mask register cannot be written, so no timer
|
||||||
* interrupts can be masked within the PLD.
|
* interrupts can be masked within the PLD.
|
||||||
|
|
|
@ -26,7 +26,7 @@
|
||||||
#include <linux/watchdog.h>
|
#include <linux/watchdog.h>
|
||||||
#include <asm/nmi.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 SECS_TO_TICKS(secs) ((secs) * 1000 / 128)
|
||||||
#define TICKS_TO_SECS(ticks) ((ticks) * 128 / 1000)
|
#define TICKS_TO_SECS(ticks) ((ticks) * 128 / 1000)
|
||||||
#define HPWDT_MAX_TIMER TICKS_TO_SECS(65535)
|
#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);
|
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
|
* Watchdog operations
|
||||||
|
@ -274,12 +279,10 @@ static int hpwdt_init_one(struct pci_dev *dev,
|
||||||
return -ENODEV;
|
return -ENODEV;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
if (pci_match_id(hpwdt_blacklist, dev)) {
|
||||||
* Ignore all auxilary iLO devices with the following PCI ID
|
dev_dbg(&dev->dev, "Not supported on this device\n");
|
||||||
*/
|
|
||||||
if (dev->subsystem_vendor == PCI_VENDOR_ID_HP &&
|
|
||||||
dev->subsystem_device == 0x1979)
|
|
||||||
return -ENODEV;
|
return -ENODEV;
|
||||||
|
}
|
||||||
|
|
||||||
if (pci_enable_device(dev)) {
|
if (pci_enable_device(dev)) {
|
||||||
dev_warn(&dev->dev,
|
dev_warn(&dev->dev,
|
||||||
|
|
|
@ -193,7 +193,7 @@ static struct watchdog_device ie6xx_wdt_dev = {
|
||||||
|
|
||||||
#ifdef CONFIG_DEBUG_FS
|
#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",
|
seq_printf(s, "PV1 = 0x%08x\n",
|
||||||
inl(ie6xx_wdt_data.sch_wdtba + PV1));
|
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;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int ie6xx_wdt_dbg_open(struct inode *inode, struct file *file)
|
DEFINE_SHOW_ATTRIBUTE(ie6xx_wdt);
|
||||||
{
|
|
||||||
return single_open(file, ie6xx_wdt_dbg_show, NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
static const struct file_operations ie6xx_wdt_dbg_operations = {
|
|
||||||
.open = ie6xx_wdt_dbg_open,
|
|
||||||
.read = seq_read,
|
|
||||||
.llseek = seq_lseek,
|
|
||||||
.release = single_release,
|
|
||||||
};
|
|
||||||
|
|
||||||
static void ie6xx_wdt_debugfs_init(void)
|
static void ie6xx_wdt_debugfs_init(void)
|
||||||
{
|
{
|
||||||
/* /sys/kernel/debug/ie6xx_wdt */
|
/* /sys/kernel/debug/ie6xx_wdt */
|
||||||
ie6xx_wdt_data.debugfs = debugfs_create_file("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)
|
static void ie6xx_wdt_debugfs_exit(void)
|
||||||
|
|
|
@ -13,10 +13,10 @@
|
||||||
#include <linux/platform_device.h>
|
#include <linux/platform_device.h>
|
||||||
#include <linux/watchdog.h>
|
#include <linux/watchdog.h>
|
||||||
#include <linux/uaccess.h>
|
#include <linux/uaccess.h>
|
||||||
#include <linux/gpio.h>
|
#include <linux/gpio/consumer.h>
|
||||||
#include <linux/of_gpio.h>
|
|
||||||
#include <linux/delay.h>
|
#include <linux/delay.h>
|
||||||
#include <linux/bitops.h>
|
#include <linux/bitops.h>
|
||||||
|
#include <linux/of.h>
|
||||||
|
|
||||||
#define NUM_GPIOS 6
|
#define NUM_GPIOS 6
|
||||||
|
|
||||||
|
@ -31,7 +31,7 @@ enum a21_wdt_gpios {
|
||||||
|
|
||||||
struct a21_wdt_drv {
|
struct a21_wdt_drv {
|
||||||
struct watchdog_device wdt;
|
struct watchdog_device wdt;
|
||||||
unsigned gpios[NUM_GPIOS];
|
struct gpio_desc *gpios[NUM_GPIOS];
|
||||||
};
|
};
|
||||||
|
|
||||||
static bool nowayout = WATCHDOG_NOWAYOUT;
|
static bool nowayout = WATCHDOG_NOWAYOUT;
|
||||||
|
@ -43,9 +43,9 @@ static unsigned int a21_wdt_get_bootstatus(struct a21_wdt_drv *drv)
|
||||||
{
|
{
|
||||||
int reset = 0;
|
int reset = 0;
|
||||||
|
|
||||||
reset |= gpio_get_value(drv->gpios[GPIO_WD_RST0]) ? (1 << 0) : 0;
|
reset |= gpiod_get_value(drv->gpios[GPIO_WD_RST0]) ? (1 << 0) : 0;
|
||||||
reset |= gpio_get_value(drv->gpios[GPIO_WD_RST1]) ? (1 << 1) : 0;
|
reset |= gpiod_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_RST2]) ? (1 << 2) : 0;
|
||||||
|
|
||||||
return reset;
|
return reset;
|
||||||
}
|
}
|
||||||
|
@ -54,7 +54,7 @@ static int a21_wdt_start(struct watchdog_device *wdt)
|
||||||
{
|
{
|
||||||
struct a21_wdt_drv *drv = watchdog_get_drvdata(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;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -63,7 +63,7 @@ static int a21_wdt_stop(struct watchdog_device *wdt)
|
||||||
{
|
{
|
||||||
struct a21_wdt_drv *drv = watchdog_get_drvdata(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;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -72,9 +72,9 @@ static int a21_wdt_ping(struct watchdog_device *wdt)
|
||||||
{
|
{
|
||||||
struct a21_wdt_drv *drv = watchdog_get_drvdata(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);
|
ndelay(10);
|
||||||
gpio_set_value(drv->gpios[GPIO_WD_TRIG], 1);
|
gpiod_set_value(drv->gpios[GPIO_WD_TRIG], 1);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -96,9 +96,9 @@ static int a21_wdt_set_timeout(struct watchdog_device *wdt,
|
||||||
}
|
}
|
||||||
|
|
||||||
if (timeout == 1)
|
if (timeout == 1)
|
||||||
gpio_set_value(drv->gpios[GPIO_WD_FAST], 1);
|
gpiod_set_value(drv->gpios[GPIO_WD_FAST], 1);
|
||||||
else
|
else
|
||||||
gpio_set_value(drv->gpios[GPIO_WD_FAST], 0);
|
gpiod_set_value(drv->gpios[GPIO_WD_FAST], 0);
|
||||||
|
|
||||||
wdt->timeout = timeout;
|
wdt->timeout = timeout;
|
||||||
|
|
||||||
|
@ -127,7 +127,6 @@ static struct watchdog_device a21_wdt = {
|
||||||
|
|
||||||
static int a21_wdt_probe(struct platform_device *pdev)
|
static int a21_wdt_probe(struct platform_device *pdev)
|
||||||
{
|
{
|
||||||
struct device_node *node;
|
|
||||||
struct a21_wdt_drv *drv;
|
struct a21_wdt_drv *drv;
|
||||||
unsigned int reset = 0;
|
unsigned int reset = 0;
|
||||||
int num_gpios;
|
int num_gpios;
|
||||||
|
@ -138,42 +137,42 @@ static int a21_wdt_probe(struct platform_device *pdev)
|
||||||
if (!drv)
|
if (!drv)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
/* Fill GPIO pin array */
|
num_gpios = gpiod_count(&pdev->dev, NULL);
|
||||||
node = pdev->dev.of_node;
|
|
||||||
|
|
||||||
num_gpios = of_gpio_count(node);
|
|
||||||
if (num_gpios != NUM_GPIOS) {
|
if (num_gpios != NUM_GPIOS) {
|
||||||
dev_err(&pdev->dev, "gpios DT property wrong, got %d want %d",
|
dev_err(&pdev->dev, "gpios DT property wrong, got %d want %d",
|
||||||
num_gpios, NUM_GPIOS);
|
num_gpios, NUM_GPIOS);
|
||||||
return -ENODEV;
|
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 */
|
/* Request the used GPIOs */
|
||||||
for (i = 0; i < num_gpios; i++) {
|
for (i = 0; i < num_gpios; i++) {
|
||||||
ret = devm_gpio_request(&pdev->dev, drv->gpios[i],
|
enum gpiod_flags gflags;
|
||||||
"MEN A21 Watchdog");
|
|
||||||
if (ret)
|
|
||||||
return ret;
|
|
||||||
|
|
||||||
if (i < GPIO_WD_RST0)
|
if (i < GPIO_WD_RST0)
|
||||||
ret = gpio_direction_output(drv->gpios[i],
|
gflags = GPIOD_ASIS;
|
||||||
gpio_get_value(drv->gpios[i]));
|
else
|
||||||
else /* GPIO_WD_RST[0..2] are inputs */
|
gflags = GPIOD_IN;
|
||||||
ret = gpio_direction_input(drv->gpios[i]);
|
drv->gpios[i] = devm_gpiod_get_index(&pdev->dev, NULL, i,
|
||||||
if (ret)
|
gflags);
|
||||||
|
if (IS_ERR(drv->gpios[i])) {
|
||||||
|
ret = PTR_ERR(drv->gpios[i]);
|
||||||
return ret;
|
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_init_timeout(&a21_wdt, 30, &pdev->dev);
|
||||||
watchdog_set_nowayout(&a21_wdt, nowayout);
|
watchdog_set_nowayout(&a21_wdt, nowayout);
|
||||||
watchdog_set_drvdata(&a21_wdt, drv);
|
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);
|
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[] = {
|
static const struct of_device_id a21_wdt_ids[] = {
|
||||||
|
|
|
@ -39,7 +39,7 @@
|
||||||
#include <linux/platform_device.h>
|
#include <linux/platform_device.h>
|
||||||
#include <linux/io.h>
|
#include <linux/io.h>
|
||||||
#include <linux/uaccess.h>
|
#include <linux/uaccess.h>
|
||||||
#include <linux/gpio.h>
|
#include <linux/gpio/consumer.h>
|
||||||
|
|
||||||
#include <asm/mach-au1x00/au1000.h>
|
#include <asm/mach-au1x00/au1000.h>
|
||||||
|
|
||||||
|
@ -55,7 +55,7 @@ static struct {
|
||||||
int queue;
|
int queue;
|
||||||
int default_ticks;
|
int default_ticks;
|
||||||
unsigned long inuse;
|
unsigned long inuse;
|
||||||
unsigned gpio;
|
struct gpio_desc *gpiod;
|
||||||
unsigned int gstate;
|
unsigned int gstate;
|
||||||
} mtx1_wdt_device;
|
} mtx1_wdt_device;
|
||||||
|
|
||||||
|
@ -67,7 +67,7 @@ static void mtx1_wdt_trigger(struct timer_list *unused)
|
||||||
|
|
||||||
/* toggle wdt gpio */
|
/* toggle wdt gpio */
|
||||||
mtx1_wdt_device.gstate = !mtx1_wdt_device.gstate;
|
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)
|
if (mtx1_wdt_device.queue && ticks)
|
||||||
mod_timer(&mtx1_wdt_device.timer, jiffies + MTX1_WDT_INTERVAL);
|
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) {
|
if (!mtx1_wdt_device.queue) {
|
||||||
mtx1_wdt_device.queue = 1;
|
mtx1_wdt_device.queue = 1;
|
||||||
mtx1_wdt_device.gstate = 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);
|
mod_timer(&mtx1_wdt_device.timer, jiffies + MTX1_WDT_INTERVAL);
|
||||||
}
|
}
|
||||||
mtx1_wdt_device.running++;
|
mtx1_wdt_device.running++;
|
||||||
|
@ -105,7 +105,7 @@ static int mtx1_wdt_stop(void)
|
||||||
if (mtx1_wdt_device.queue) {
|
if (mtx1_wdt_device.queue) {
|
||||||
mtx1_wdt_device.queue = 0;
|
mtx1_wdt_device.queue = 0;
|
||||||
mtx1_wdt_device.gstate = 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;
|
ticks = mtx1_wdt_device.default_ticks;
|
||||||
spin_unlock_irqrestore(&mtx1_wdt_device.lock, flags);
|
spin_unlock_irqrestore(&mtx1_wdt_device.lock, flags);
|
||||||
|
@ -198,12 +198,11 @@ static int mtx1_wdt_probe(struct platform_device *pdev)
|
||||||
{
|
{
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
mtx1_wdt_device.gpio = pdev->resource[0].start;
|
mtx1_wdt_device.gpiod = devm_gpiod_get(&pdev->dev,
|
||||||
ret = devm_gpio_request_one(&pdev->dev, mtx1_wdt_device.gpio,
|
NULL, GPIOD_OUT_HIGH);
|
||||||
GPIOF_OUT_INIT_HIGH, "mtx1-wdt");
|
if (IS_ERR(mtx1_wdt_device.gpiod)) {
|
||||||
if (ret < 0) {
|
|
||||||
dev_err(&pdev->dev, "failed to request gpio");
|
dev_err(&pdev->dev, "failed to request gpio");
|
||||||
return ret;
|
return PTR_ERR(mtx1_wdt_device.gpiod);
|
||||||
}
|
}
|
||||||
|
|
||||||
spin_lock_init(&mtx1_wdt_device.lock);
|
spin_lock_init(&mtx1_wdt_device.lock);
|
||||||
|
|
|
@ -0,0 +1,211 @@
|
||||||
|
// 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;
|
void __iomem *base;
|
||||||
struct watchdog_device wdev;
|
struct watchdog_device wdev;
|
||||||
unsigned long clk_rate;
|
unsigned long clk_rate;
|
||||||
u16 time_left;
|
|
||||||
u8 cks;
|
u8 cks;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -74,12 +73,17 @@ static int rwdt_init_timeout(struct watchdog_device *wdev)
|
||||||
static int rwdt_start(struct watchdog_device *wdev)
|
static int rwdt_start(struct watchdog_device *wdev)
|
||||||
{
|
{
|
||||||
struct rwdt_priv *priv = watchdog_get_drvdata(wdev);
|
struct rwdt_priv *priv = watchdog_get_drvdata(wdev);
|
||||||
|
u8 val;
|
||||||
|
|
||||||
pm_runtime_get_sync(wdev->parent);
|
pm_runtime_get_sync(wdev->parent);
|
||||||
|
|
||||||
rwdt_write(priv, 0, RWTCSRB);
|
/* Stop the timer before we modify any register */
|
||||||
rwdt_write(priv, priv->cks, RWTCSRA);
|
val = readb_relaxed(priv->base + RWTCSRA) & ~RWTCSRA_TME;
|
||||||
|
rwdt_write(priv, val, RWTCSRA);
|
||||||
|
|
||||||
rwdt_init_timeout(wdev);
|
rwdt_init_timeout(wdev);
|
||||||
|
rwdt_write(priv, priv->cks, RWTCSRA);
|
||||||
|
rwdt_write(priv, 0, RWTCSRB);
|
||||||
|
|
||||||
while (readb_relaxed(priv->base + RWTCSRA) & RWTCSRA_WRFLG)
|
while (readb_relaxed(priv->base + RWTCSRA) & RWTCSRA_WRFLG)
|
||||||
cpu_relax();
|
cpu_relax();
|
||||||
|
@ -220,8 +224,8 @@ static int rwdt_probe(struct platform_device *pdev)
|
||||||
goto out_pm_disable;
|
goto out_pm_disable;
|
||||||
}
|
}
|
||||||
|
|
||||||
priv->wdev.info = &rwdt_ident,
|
priv->wdev.info = &rwdt_ident;
|
||||||
priv->wdev.ops = &rwdt_ops,
|
priv->wdev.ops = &rwdt_ops;
|
||||||
priv->wdev.parent = &pdev->dev;
|
priv->wdev.parent = &pdev->dev;
|
||||||
priv->wdev.min_timeout = 1;
|
priv->wdev.min_timeout = 1;
|
||||||
priv->wdev.max_timeout = DIV_BY_CLKS_PER_SEC(priv, 65536);
|
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);
|
struct rwdt_priv *priv = dev_get_drvdata(dev);
|
||||||
|
|
||||||
if (watchdog_active(&priv->wdev)) {
|
if (watchdog_active(&priv->wdev))
|
||||||
priv->time_left = readw(priv->base + RWTCNT);
|
|
||||||
rwdt_stop(&priv->wdev);
|
rwdt_stop(&priv->wdev);
|
||||||
}
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -274,10 +277,9 @@ static int __maybe_unused rwdt_resume(struct device *dev)
|
||||||
{
|
{
|
||||||
struct rwdt_priv *priv = dev_get_drvdata(dev);
|
struct rwdt_priv *priv = dev_get_drvdata(dev);
|
||||||
|
|
||||||
if (watchdog_active(&priv->wdev)) {
|
if (watchdog_active(&priv->wdev))
|
||||||
rwdt_start(&priv->wdev);
|
rwdt_start(&priv->wdev);
|
||||||
rwdt_write(priv, priv->time_left, RWTCNT);
|
|
||||||
}
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,126 @@
|
||||||
|
// 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/ioport.h>
|
||||||
#include <linux/init.h>
|
#include <linux/init.h>
|
||||||
#include <linux/io.h>
|
#include <linux/io.h>
|
||||||
|
#include <linux/dmi.h>
|
||||||
|
|
||||||
#define WATCHDOG_NAME "w83627hf/thf/hg/dhg WDT"
|
#define WATCHDOG_NAME "w83627hf/thf/hg/dhg WDT"
|
||||||
#define WATCHDOG_TIMEOUT 60 /* 60 sec default timeout */
|
#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_timeout; /* WDT timeout register */
|
||||||
static int cr_wdt_control; /* WDT control register */
|
static int cr_wdt_control; /* WDT control register */
|
||||||
static int cr_wdt_csr; /* WDT control & status 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,
|
enum chips { w83627hf, w83627s, w83697hf, w83697ug, w83637hf, w83627thf,
|
||||||
w83687thf, w83627ehf, w83627dhg, w83627uhg, w83667hg, w83627dhg_p,
|
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))
|
if (!request_muxed_region(wdt_io, 2, WATCHDOG_NAME))
|
||||||
return -EBUSY;
|
return -EBUSY;
|
||||||
|
|
||||||
outb_p(0x87, WDT_EFER); /* Enter extended function mode */
|
outb_p(wdt_cfg_enter, WDT_EFER); /* Enter extended function mode */
|
||||||
outb_p(0x87, WDT_EFER); /* Again according to manual */
|
outb_p(wdt_cfg_enter, WDT_EFER); /* Again according to manual */
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -143,7 +146,7 @@ static void superio_select(int ld)
|
||||||
|
|
||||||
static void superio_exit(void)
|
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);
|
release_region(wdt_io, 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -430,6 +433,32 @@ static int wdt_find(int addr)
|
||||||
return ret;
|
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)
|
static int __init wdt_init(void)
|
||||||
{
|
{
|
||||||
int ret;
|
int ret;
|
||||||
|
@ -459,6 +488,9 @@ static int __init wdt_init(void)
|
||||||
"NCT6102",
|
"NCT6102",
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/* Apply system-specific quirks */
|
||||||
|
dmi_check_system(wdt_dmi_table);
|
||||||
|
|
||||||
wdt_io = 0x2e;
|
wdt_io = 0x2e;
|
||||||
chip = wdt_find(0x2e);
|
chip = wdt_find(0x2e);
|
||||||
if (chip < 0) {
|
if (chip < 0) {
|
||||||
|
|
|
@ -90,9 +90,6 @@ struct watchdog_ops {
|
||||||
*
|
*
|
||||||
* The driver-data field may not be accessed directly. It must be accessed
|
* The driver-data field may not be accessed directly. It must be accessed
|
||||||
* via the watchdog_set_drvdata and watchdog_get_drvdata helpers.
|
* 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 {
|
struct watchdog_device {
|
||||||
int id;
|
int id;
|
||||||
|
|
Loading…
Reference in New Issue