mirror of
https://github.com/AuxXxilium/linux_dsm_epyc7002.git
synced 2024-11-24 02:30:52 +07:00
linux-watchdog 5.10-rc1 tag
-----BEGIN PGP SIGNATURE----- Version: GnuPG v2.0.14 (GNU/Linux) iEYEABECAAYFAl+QLMUACgkQ+iyteGJfRsreMACcDuI5w/3sJ5kmjasDm0e7dLte 20gAoL4kLcLAD2v8HiV8RPR+HYyvuic5 =MLjG -----END PGP SIGNATURE----- Merge tag 'linux-watchdog-5.10-rc1' of git://www.linux-watchdog.org/linux-watchdog Pull watchdog updates from Wim Van Sebroeck: - Add Toshiba Visconti watchdog driver - it87_wdt: add IT8772 + IT8784 - several fixes and improvements * tag 'linux-watchdog-5.10-rc1' of git://www.linux-watchdog.org/linux-watchdog: watchdog: Add Toshiba Visconti watchdog driver watchdog: bindings: Add binding documentation for Toshiba Visconti watchdog device watchdog: it87_wdt: add IT8784 ID watchdog: sp5100_tco: Enable watchdog on Family 17h devices if disabled watchdog: sp5100: Fix definition of EFCH_PM_DECODEEN3 watchdog: renesas_wdt: support handover from bootloader watchdog: imx7ulp: Watchdog should continue running for wait/stop mode watchdog: rti: Simplify with dev_err_probe() watchdog: davinci: Simplify with dev_err_probe() watchdog: cadence: Simplify with dev_err_probe() watchdog: remove unneeded inclusion of <uapi/linux/sched/types.h> watchdog: Use put_device on error watchdog: Fix memleak in watchdog_cdev_register watchdog: imx7ulp: Strictly follow the sequence for wdog operations watchdog: it87_wdt: add IT8772 ID watchdog: pcwd_usb: Avoid GFP_ATOMIC where it is not needed drivers: watchdog: rdc321x_wdt: Fix race condition bugs
This commit is contained in:
commit
f804b31594
@ -0,0 +1,54 @@
|
||||
# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
|
||||
# Copyright 2020 Toshiba Electronic Devices & Storage Corporation
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: "http://devicetree.org/schemas/watchdog/toshiba,visconti-wdt.yaml#"
|
||||
$schema: "http://devicetree.org/meta-schemas/core.yaml#"
|
||||
|
||||
title: Toshiba Visconti SoCs PIUWDT Watchdog timer
|
||||
|
||||
maintainers:
|
||||
- Nobuhiro Iwamatsu <nobuhiro1.iwamatsu@toshiba.co.jp>
|
||||
|
||||
allOf:
|
||||
- $ref: watchdog.yaml#
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
enum:
|
||||
- toshiba,visconti-wdt
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
clocks:
|
||||
maxItems: 1
|
||||
|
||||
timeout-sec: true
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- clocks
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
soc {
|
||||
#address-cells = <2>;
|
||||
#size-cells = <2>;
|
||||
|
||||
wdt_clk: wdt-clk {
|
||||
compatible = "fixed-clock";
|
||||
clock-frequency = <150000000>;
|
||||
#clock-cells = <0>;
|
||||
};
|
||||
|
||||
watchdog@28330000 {
|
||||
compatible = "toshiba,visconti-wdt";
|
||||
reg = <0 0x28330000 0 0x1000>;
|
||||
clocks = <&wdt_clk>;
|
||||
timeout-sec = <20>;
|
||||
};
|
||||
};
|
@ -1015,6 +1015,14 @@ config PM8916_WATCHDOG
|
||||
Say Y here to include support watchdog timer embedded into the
|
||||
pm8916 module.
|
||||
|
||||
config VISCONTI_WATCHDOG
|
||||
tristate "Toshiba Visconti series watchdog support"
|
||||
depends on ARCH_VISCONTI || COMPILE_TEST
|
||||
select WATCHDOG_CORE
|
||||
help
|
||||
Say Y here to include support for the watchdog timer in Toshiba
|
||||
Visconti SoCs.
|
||||
|
||||
# X86 (i386 + ia64 + x86_64) Architecture
|
||||
|
||||
config ACQUIRE_WDT
|
||||
|
@ -95,6 +95,7 @@ obj-$(CONFIG_RTD119X_WATCHDOG) += rtd119x_wdt.o
|
||||
obj-$(CONFIG_SPRD_WATCHDOG) += sprd_wdt.o
|
||||
obj-$(CONFIG_PM8916_WATCHDOG) += pm8916_wdt.o
|
||||
obj-$(CONFIG_ARM_SMC_WATCHDOG) += arm_smc_wdt.o
|
||||
obj-$(CONFIG_VISCONTI_WATCHDOG) += visconti_wdt.o
|
||||
|
||||
# X86 (i386 + ia64 + x86_64) Architecture
|
||||
obj-$(CONFIG_ACQUIRE_WDT) += acquirewdt.o
|
||||
|
@ -334,12 +334,9 @@ static int cdns_wdt_probe(struct platform_device *pdev)
|
||||
watchdog_set_drvdata(cdns_wdt_device, wdt);
|
||||
|
||||
wdt->clk = devm_clk_get(dev, NULL);
|
||||
if (IS_ERR(wdt->clk)) {
|
||||
ret = PTR_ERR(wdt->clk);
|
||||
if (ret != -EPROBE_DEFER)
|
||||
dev_err(dev, "input clock not found\n");
|
||||
return ret;
|
||||
}
|
||||
if (IS_ERR(wdt->clk))
|
||||
return dev_err_probe(dev, PTR_ERR(wdt->clk),
|
||||
"input clock not found\n");
|
||||
|
||||
ret = clk_prepare_enable(wdt->clk);
|
||||
if (ret) {
|
||||
|
@ -206,12 +206,9 @@ static int davinci_wdt_probe(struct platform_device *pdev)
|
||||
return -ENOMEM;
|
||||
|
||||
davinci_wdt->clk = devm_clk_get(dev, NULL);
|
||||
|
||||
if (IS_ERR(davinci_wdt->clk)) {
|
||||
if (PTR_ERR(davinci_wdt->clk) != -EPROBE_DEFER)
|
||||
dev_err(dev, "failed to get clock node\n");
|
||||
return PTR_ERR(davinci_wdt->clk);
|
||||
}
|
||||
if (IS_ERR(davinci_wdt->clk))
|
||||
return dev_err_probe(dev, PTR_ERR(davinci_wdt->clk),
|
||||
"failed to get clock node\n");
|
||||
|
||||
ret = clk_prepare_enable(davinci_wdt->clk);
|
||||
if (ret) {
|
||||
|
@ -5,6 +5,7 @@
|
||||
|
||||
#include <linux/clk.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/iopoll.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
@ -21,6 +22,8 @@
|
||||
#define WDOG_CS_CLK (LPO_CLK << LPO_CLK_SHIFT)
|
||||
#define WDOG_CS_EN BIT(7)
|
||||
#define WDOG_CS_UPDATE BIT(5)
|
||||
#define WDOG_CS_WAIT BIT(1)
|
||||
#define WDOG_CS_STOP BIT(0)
|
||||
|
||||
#define WDOG_CNT 0x4
|
||||
#define WDOG_TOVAL 0x8
|
||||
@ -36,6 +39,7 @@
|
||||
#define DEFAULT_TIMEOUT 60
|
||||
#define MAX_TIMEOUT 128
|
||||
#define WDOG_CLOCK_RATE 1000
|
||||
#define WDOG_WAIT_TIMEOUT 20
|
||||
|
||||
static bool nowayout = WATCHDOG_NOWAYOUT;
|
||||
module_param(nowayout, bool, 0000);
|
||||
@ -48,17 +52,40 @@ struct imx7ulp_wdt_device {
|
||||
struct clk *clk;
|
||||
};
|
||||
|
||||
static void imx7ulp_wdt_enable(struct watchdog_device *wdog, bool enable)
|
||||
static int imx7ulp_wdt_wait(void __iomem *base, u32 mask)
|
||||
{
|
||||
u32 val = readl(base + WDOG_CS);
|
||||
|
||||
if (!(val & mask) && readl_poll_timeout_atomic(base + WDOG_CS, val,
|
||||
val & mask, 0,
|
||||
WDOG_WAIT_TIMEOUT))
|
||||
return -ETIMEDOUT;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int imx7ulp_wdt_enable(struct watchdog_device *wdog, bool enable)
|
||||
{
|
||||
struct imx7ulp_wdt_device *wdt = watchdog_get_drvdata(wdog);
|
||||
|
||||
u32 val = readl(wdt->base + WDOG_CS);
|
||||
int ret;
|
||||
|
||||
local_irq_disable();
|
||||
writel(UNLOCK, wdt->base + WDOG_CNT);
|
||||
ret = imx7ulp_wdt_wait(wdt->base, WDOG_CS_ULK);
|
||||
if (ret)
|
||||
goto enable_out;
|
||||
if (enable)
|
||||
writel(val | WDOG_CS_EN, wdt->base + WDOG_CS);
|
||||
else
|
||||
writel(val & ~WDOG_CS_EN, wdt->base + WDOG_CS);
|
||||
imx7ulp_wdt_wait(wdt->base, WDOG_CS_RCS);
|
||||
|
||||
enable_out:
|
||||
local_irq_enable();
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static bool imx7ulp_wdt_is_enabled(void __iomem *base)
|
||||
@ -79,17 +106,12 @@ static int imx7ulp_wdt_ping(struct watchdog_device *wdog)
|
||||
|
||||
static int imx7ulp_wdt_start(struct watchdog_device *wdog)
|
||||
{
|
||||
|
||||
imx7ulp_wdt_enable(wdog, true);
|
||||
|
||||
return 0;
|
||||
return imx7ulp_wdt_enable(wdog, true);
|
||||
}
|
||||
|
||||
static int imx7ulp_wdt_stop(struct watchdog_device *wdog)
|
||||
{
|
||||
imx7ulp_wdt_enable(wdog, false);
|
||||
|
||||
return 0;
|
||||
return imx7ulp_wdt_enable(wdog, false);
|
||||
}
|
||||
|
||||
static int imx7ulp_wdt_set_timeout(struct watchdog_device *wdog,
|
||||
@ -97,22 +119,37 @@ static int imx7ulp_wdt_set_timeout(struct watchdog_device *wdog,
|
||||
{
|
||||
struct imx7ulp_wdt_device *wdt = watchdog_get_drvdata(wdog);
|
||||
u32 val = WDOG_CLOCK_RATE * timeout;
|
||||
int ret;
|
||||
|
||||
local_irq_disable();
|
||||
writel(UNLOCK, wdt->base + WDOG_CNT);
|
||||
ret = imx7ulp_wdt_wait(wdt->base, WDOG_CS_ULK);
|
||||
if (ret)
|
||||
goto timeout_out;
|
||||
writel(val, wdt->base + WDOG_TOVAL);
|
||||
imx7ulp_wdt_wait(wdt->base, WDOG_CS_RCS);
|
||||
|
||||
wdog->timeout = timeout;
|
||||
|
||||
return 0;
|
||||
timeout_out:
|
||||
local_irq_enable();
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int imx7ulp_wdt_restart(struct watchdog_device *wdog,
|
||||
unsigned long action, void *data)
|
||||
{
|
||||
struct imx7ulp_wdt_device *wdt = watchdog_get_drvdata(wdog);
|
||||
int ret;
|
||||
|
||||
imx7ulp_wdt_enable(wdog, true);
|
||||
imx7ulp_wdt_set_timeout(&wdt->wdd, 1);
|
||||
ret = imx7ulp_wdt_enable(wdog, true);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = imx7ulp_wdt_set_timeout(&wdt->wdd, 1);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* wait for wdog to fire */
|
||||
while (true)
|
||||
@ -136,19 +173,31 @@ static const struct watchdog_info imx7ulp_wdt_info = {
|
||||
WDIOF_MAGICCLOSE,
|
||||
};
|
||||
|
||||
static void imx7ulp_wdt_init(void __iomem *base, unsigned int timeout)
|
||||
static int imx7ulp_wdt_init(void __iomem *base, unsigned int timeout)
|
||||
{
|
||||
u32 val;
|
||||
int ret;
|
||||
|
||||
local_irq_disable();
|
||||
/* unlock the wdog for reconfiguration */
|
||||
writel_relaxed(UNLOCK_SEQ0, base + WDOG_CNT);
|
||||
writel_relaxed(UNLOCK_SEQ1, base + WDOG_CNT);
|
||||
ret = imx7ulp_wdt_wait(base, WDOG_CS_ULK);
|
||||
if (ret)
|
||||
goto init_out;
|
||||
|
||||
/* set an initial timeout value in TOVAL */
|
||||
writel(timeout, base + WDOG_TOVAL);
|
||||
/* enable 32bit command sequence and reconfigure */
|
||||
val = WDOG_CS_CMD32EN | WDOG_CS_CLK | WDOG_CS_UPDATE;
|
||||
val = WDOG_CS_CMD32EN | WDOG_CS_CLK | WDOG_CS_UPDATE |
|
||||
WDOG_CS_WAIT | WDOG_CS_STOP;
|
||||
writel(val, base + WDOG_CS);
|
||||
imx7ulp_wdt_wait(base, WDOG_CS_RCS);
|
||||
|
||||
init_out:
|
||||
local_irq_enable();
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void imx7ulp_wdt_action(void *data)
|
||||
@ -199,7 +248,9 @@ static int imx7ulp_wdt_probe(struct platform_device *pdev)
|
||||
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);
|
||||
ret = imx7ulp_wdt_init(imx7ulp_wdt->base, wdog->timeout * WDOG_CLOCK_RATE);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return devm_watchdog_register_device(dev, wdog);
|
||||
}
|
||||
|
@ -15,7 +15,7 @@
|
||||
* Support of the watchdog timers, which are available on
|
||||
* IT8607, IT8620, IT8622, IT8625, IT8628, IT8655, IT8665, IT8686,
|
||||
* IT8702, IT8712, IT8716, IT8718, IT8720, IT8721, IT8726, IT8728,
|
||||
* and IT8783.
|
||||
* IT8772, IT8783 and IT8784.
|
||||
*/
|
||||
|
||||
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
||||
@ -66,7 +66,9 @@
|
||||
#define IT8721_ID 0x8721
|
||||
#define IT8726_ID 0x8726 /* the data sheet suggest wrongly 0x8716 */
|
||||
#define IT8728_ID 0x8728
|
||||
#define IT8772_ID 0x8772
|
||||
#define IT8783_ID 0x8783
|
||||
#define IT8784_ID 0x8784
|
||||
#define IT8786_ID 0x8786
|
||||
|
||||
/* GPIO Configuration Registers LDN=0x07 */
|
||||
@ -294,7 +296,9 @@ static int __init it87_wdt_init(void)
|
||||
case IT8720_ID:
|
||||
case IT8721_ID:
|
||||
case IT8728_ID:
|
||||
case IT8772_ID:
|
||||
case IT8783_ID:
|
||||
case IT8784_ID:
|
||||
case IT8786_ID:
|
||||
max_units = 65535;
|
||||
break;
|
||||
|
@ -656,7 +656,7 @@ static int usb_pcwd_probe(struct usb_interface *interface,
|
||||
|
||||
/* set up the memory buffer's */
|
||||
usb_pcwd->intr_buffer = usb_alloc_coherent(udev, usb_pcwd->intr_size,
|
||||
GFP_ATOMIC, &usb_pcwd->intr_dma);
|
||||
GFP_KERNEL, &usb_pcwd->intr_dma);
|
||||
if (!usb_pcwd->intr_buffer) {
|
||||
pr_err("Out of memory\n");
|
||||
goto error;
|
||||
|
@ -231,6 +231,8 @@ static int rdc321x_wdt_probe(struct platform_device *pdev)
|
||||
|
||||
rdc321x_wdt_device.sb_pdev = pdata->sb_pdev;
|
||||
rdc321x_wdt_device.base_reg = r->start;
|
||||
rdc321x_wdt_device.queue = 0;
|
||||
rdc321x_wdt_device.default_ticks = ticks;
|
||||
|
||||
err = misc_register(&rdc321x_wdt_misc);
|
||||
if (err < 0) {
|
||||
@ -245,14 +247,11 @@ static int rdc321x_wdt_probe(struct platform_device *pdev)
|
||||
rdc321x_wdt_device.base_reg, RDC_WDT_RST);
|
||||
|
||||
init_completion(&rdc321x_wdt_device.stop);
|
||||
rdc321x_wdt_device.queue = 0;
|
||||
|
||||
clear_bit(0, &rdc321x_wdt_device.inuse);
|
||||
|
||||
timer_setup(&rdc321x_wdt_device.timer, rdc321x_wdt_trigger, 0);
|
||||
|
||||
rdc321x_wdt_device.default_ticks = ticks;
|
||||
|
||||
dev_info(&pdev->dev, "watchdog init success\n");
|
||||
|
||||
return 0;
|
||||
|
@ -194,6 +194,7 @@ static int rwdt_probe(struct platform_device *pdev)
|
||||
struct clk *clk;
|
||||
unsigned long clks_per_sec;
|
||||
int ret, i;
|
||||
u8 csra;
|
||||
|
||||
if (rwdt_blacklisted(dev))
|
||||
return -ENODEV;
|
||||
@ -213,8 +214,8 @@ static int rwdt_probe(struct platform_device *pdev)
|
||||
pm_runtime_enable(dev);
|
||||
pm_runtime_get_sync(dev);
|
||||
priv->clk_rate = clk_get_rate(clk);
|
||||
priv->wdev.bootstatus = (readb_relaxed(priv->base + RWTCSRA) &
|
||||
RWTCSRA_WOVF) ? WDIOF_CARDRESET : 0;
|
||||
csra = readb_relaxed(priv->base + RWTCSRA);
|
||||
priv->wdev.bootstatus = csra & RWTCSRA_WOVF ? WDIOF_CARDRESET : 0;
|
||||
pm_runtime_put(dev);
|
||||
|
||||
if (!priv->clk_rate) {
|
||||
@ -252,6 +253,13 @@ static int rwdt_probe(struct platform_device *pdev)
|
||||
/* This overrides the default timeout only if DT configuration was found */
|
||||
watchdog_init_timeout(&priv->wdev, 0, dev);
|
||||
|
||||
/* Check if FW enabled the watchdog */
|
||||
if (csra & RWTCSRA_TME) {
|
||||
/* Ensure properly initialized dividers */
|
||||
rwdt_start(&priv->wdev);
|
||||
set_bit(WDOG_HW_RUNNING, &priv->wdev.status);
|
||||
}
|
||||
|
||||
ret = watchdog_register_device(&priv->wdev);
|
||||
if (ret < 0)
|
||||
goto out_pm_disable;
|
||||
|
@ -205,11 +205,8 @@ static int rti_wdt_probe(struct platform_device *pdev)
|
||||
return -ENOMEM;
|
||||
|
||||
clk = clk_get(dev, NULL);
|
||||
if (IS_ERR(clk)) {
|
||||
if (PTR_ERR(clk) != -EPROBE_DEFER)
|
||||
dev_err(dev, "failed to get clock\n");
|
||||
return PTR_ERR(clk);
|
||||
}
|
||||
if (IS_ERR(clk))
|
||||
return dev_err_probe(dev, PTR_ERR(clk), "failed to get clock\n");
|
||||
|
||||
wdt->freq = clk_get_rate(clk);
|
||||
|
||||
@ -230,11 +227,8 @@ static int rti_wdt_probe(struct platform_device *pdev)
|
||||
|
||||
pm_runtime_enable(dev);
|
||||
ret = pm_runtime_get_sync(dev);
|
||||
if (ret) {
|
||||
if (ret != -EPROBE_DEFER)
|
||||
dev_err(&pdev->dev, "runtime pm failed\n");
|
||||
return ret;
|
||||
}
|
||||
if (ret)
|
||||
return dev_err_probe(dev, ret, "runtime pm failed\n");
|
||||
|
||||
platform_set_drvdata(pdev, wdt);
|
||||
|
||||
|
@ -17,6 +17,12 @@
|
||||
* AMD Publication 51192 "AMD Bolton FCH Register Reference Guide"
|
||||
* AMD Publication 52740 "BIOS and Kernel Developer’s Guide (BKDG)
|
||||
* for AMD Family 16h Models 30h-3Fh Processors"
|
||||
* AMD Publication 55570-B1-PUB "Processor Programming Reference (PPR)
|
||||
* for AMD Family 17h Model 18h, Revision B1
|
||||
* Processors (PUB)
|
||||
* AMD Publication 55772-A1-PUB "Processor Programming Reference (PPR)
|
||||
* for AMD Family 17h Model 20h, Revision A1
|
||||
* Processors (PUB)
|
||||
*/
|
||||
|
||||
/*
|
||||
@ -241,6 +247,18 @@ static int sp5100_tco_setupdevice(struct device *dev,
|
||||
break;
|
||||
case efch:
|
||||
dev_name = SB800_DEVNAME;
|
||||
/*
|
||||
* On Family 17h devices, the EFCH_PM_DECODEEN_WDT_TMREN bit of
|
||||
* EFCH_PM_DECODEEN not only enables the EFCH_PM_WDT_ADDR memory
|
||||
* region, it also enables the watchdog itself.
|
||||
*/
|
||||
if (boot_cpu_data.x86 == 0x17) {
|
||||
val = sp5100_tco_read_pm_reg8(EFCH_PM_DECODEEN);
|
||||
if (!(val & EFCH_PM_DECODEEN_WDT_TMREN)) {
|
||||
sp5100_tco_update_pm_reg8(EFCH_PM_DECODEEN, 0xff,
|
||||
EFCH_PM_DECODEEN_WDT_TMREN);
|
||||
}
|
||||
}
|
||||
val = sp5100_tco_read_pm_reg8(EFCH_PM_DECODEEN);
|
||||
if (val & EFCH_PM_DECODEEN_WDT_TMREN)
|
||||
mmio_addr = EFCH_PM_WDT_ADDR;
|
||||
|
@ -70,7 +70,7 @@
|
||||
#define EFCH_PM_DECODEEN_WDT_TMREN BIT(7)
|
||||
|
||||
|
||||
#define EFCH_PM_DECODEEN3 0x00
|
||||
#define EFCH_PM_DECODEEN3 0x03
|
||||
#define EFCH_PM_DECODEEN_SECOND_RES GENMASK(1, 0)
|
||||
#define EFCH_PM_WATCHDOG_DISABLE ((u8)GENMASK(3, 2))
|
||||
|
||||
|
195
drivers/watchdog/visconti_wdt.c
Normal file
195
drivers/watchdog/visconti_wdt.c
Normal file
@ -0,0 +1,195 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Copyright (c) 2020 TOSHIBA CORPORATION
|
||||
* Copyright (c) 2020 Toshiba Electronic Devices & Storage Corporation
|
||||
* Copyright (c) 2020 Nobuhiro Iwamatsu <nobuhiro1.iwamatsu@toshiba.co.jp>
|
||||
*/
|
||||
|
||||
#include <linux/clk.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/watchdog.h>
|
||||
|
||||
#define WDT_CNT 0x00
|
||||
#define WDT_MIN 0x04
|
||||
#define WDT_MAX 0x08
|
||||
#define WDT_CTL 0x0c
|
||||
#define WDT_CMD 0x10
|
||||
#define WDT_CMD_CLEAR 0x4352
|
||||
#define WDT_CMD_START_STOP 0x5354
|
||||
#define WDT_DIV 0x30
|
||||
|
||||
#define VISCONTI_WDT_FREQ 2000000 /* 2MHz */
|
||||
#define WDT_DEFAULT_TIMEOUT 10U /* in seconds */
|
||||
|
||||
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 visconti_wdt_priv {
|
||||
struct watchdog_device wdev;
|
||||
void __iomem *base;
|
||||
u32 div;
|
||||
};
|
||||
|
||||
static int visconti_wdt_start(struct watchdog_device *wdev)
|
||||
{
|
||||
struct visconti_wdt_priv *priv = watchdog_get_drvdata(wdev);
|
||||
u32 timeout = wdev->timeout * VISCONTI_WDT_FREQ;
|
||||
|
||||
writel(priv->div, priv->base + WDT_DIV);
|
||||
writel(0, priv->base + WDT_MIN);
|
||||
writel(timeout, priv->base + WDT_MAX);
|
||||
writel(0, priv->base + WDT_CTL);
|
||||
writel(WDT_CMD_START_STOP, priv->base + WDT_CMD);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int visconti_wdt_stop(struct watchdog_device *wdev)
|
||||
{
|
||||
struct visconti_wdt_priv *priv = watchdog_get_drvdata(wdev);
|
||||
|
||||
writel(1, priv->base + WDT_CTL);
|
||||
writel(WDT_CMD_START_STOP, priv->base + WDT_CMD);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int visconti_wdt_ping(struct watchdog_device *wdd)
|
||||
{
|
||||
struct visconti_wdt_priv *priv = watchdog_get_drvdata(wdd);
|
||||
|
||||
writel(WDT_CMD_CLEAR, priv->base + WDT_CMD);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static unsigned int visconti_wdt_get_timeleft(struct watchdog_device *wdev)
|
||||
{
|
||||
struct visconti_wdt_priv *priv = watchdog_get_drvdata(wdev);
|
||||
u32 timeout = wdev->timeout * VISCONTI_WDT_FREQ;
|
||||
u32 cnt = readl(priv->base + WDT_CNT);
|
||||
|
||||
if (timeout <= cnt)
|
||||
return 0;
|
||||
timeout -= cnt;
|
||||
|
||||
return timeout / VISCONTI_WDT_FREQ;
|
||||
}
|
||||
|
||||
static int visconti_wdt_set_timeout(struct watchdog_device *wdev, unsigned int timeout)
|
||||
{
|
||||
u32 val;
|
||||
struct visconti_wdt_priv *priv = watchdog_get_drvdata(wdev);
|
||||
|
||||
wdev->timeout = timeout;
|
||||
val = wdev->timeout * VISCONTI_WDT_FREQ;
|
||||
|
||||
/* Clear counter before setting timeout because WDT expires */
|
||||
writel(WDT_CMD_CLEAR, priv->base + WDT_CMD);
|
||||
writel(val, priv->base + WDT_MAX);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct watchdog_info visconti_wdt_info = {
|
||||
.options = WDIOF_SETTIMEOUT | WDIOF_MAGICCLOSE | WDIOF_KEEPALIVEPING,
|
||||
.identity = "Visconti Watchdog",
|
||||
};
|
||||
|
||||
static const struct watchdog_ops visconti_wdt_ops = {
|
||||
.owner = THIS_MODULE,
|
||||
.start = visconti_wdt_start,
|
||||
.stop = visconti_wdt_stop,
|
||||
.ping = visconti_wdt_ping,
|
||||
.get_timeleft = visconti_wdt_get_timeleft,
|
||||
.set_timeout = visconti_wdt_set_timeout,
|
||||
};
|
||||
|
||||
static void visconti_clk_disable_unprepare(void *data)
|
||||
{
|
||||
clk_disable_unprepare(data);
|
||||
}
|
||||
|
||||
static int visconti_wdt_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct watchdog_device *wdev;
|
||||
struct visconti_wdt_priv *priv;
|
||||
struct device *dev = &pdev->dev;
|
||||
struct clk *clk;
|
||||
int ret;
|
||||
unsigned long clk_freq;
|
||||
|
||||
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
|
||||
if (!priv)
|
||||
return -ENOMEM;
|
||||
|
||||
priv->base = devm_platform_ioremap_resource(pdev, 0);
|
||||
if (IS_ERR(priv->base))
|
||||
return PTR_ERR(priv->base);
|
||||
|
||||
clk = devm_clk_get(dev, NULL);
|
||||
if (IS_ERR(clk))
|
||||
return dev_err_probe(dev, PTR_ERR(clk), "Could not get clock\n");
|
||||
|
||||
ret = clk_prepare_enable(clk);
|
||||
if (ret) {
|
||||
dev_err(dev, "Could not enable clock\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = devm_add_action_or_reset(dev, visconti_clk_disable_unprepare, clk);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
clk_freq = clk_get_rate(clk);
|
||||
if (!clk_freq)
|
||||
return -EINVAL;
|
||||
|
||||
priv->div = clk_freq / VISCONTI_WDT_FREQ;
|
||||
|
||||
/* Initialize struct watchdog_device. */
|
||||
wdev = &priv->wdev;
|
||||
wdev->info = &visconti_wdt_info;
|
||||
wdev->ops = &visconti_wdt_ops;
|
||||
wdev->parent = dev;
|
||||
wdev->min_timeout = 1;
|
||||
wdev->max_timeout = 0xffffffff / VISCONTI_WDT_FREQ;
|
||||
wdev->timeout = min(wdev->max_timeout, WDT_DEFAULT_TIMEOUT);
|
||||
|
||||
watchdog_set_drvdata(wdev, priv);
|
||||
watchdog_set_nowayout(wdev, nowayout);
|
||||
watchdog_stop_on_unregister(wdev);
|
||||
|
||||
/* This overrides the default timeout only if DT configuration was found */
|
||||
ret = watchdog_init_timeout(wdev, 0, dev);
|
||||
if (ret)
|
||||
dev_warn(dev, "Specified timeout value invalid, using default\n");
|
||||
|
||||
return devm_watchdog_register_device(dev, wdev);
|
||||
}
|
||||
|
||||
static const struct of_device_id visconti_wdt_of_match[] = {
|
||||
{ .compatible = "toshiba,visconti-wdt", },
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, visconti_wdt_of_match);
|
||||
|
||||
static struct platform_driver visconti_wdt_driver = {
|
||||
.driver = {
|
||||
.name = "visconti_wdt",
|
||||
.of_match_table = visconti_wdt_of_match,
|
||||
},
|
||||
.probe = visconti_wdt_probe,
|
||||
};
|
||||
module_platform_driver(visconti_wdt_driver);
|
||||
|
||||
MODULE_DESCRIPTION("TOSHIBA Visconti Watchdog Driver");
|
||||
MODULE_AUTHOR("Nobuhiro Iwamatsu <nobuhiro1.iwamatsu@toshiba.co.jp");
|
||||
MODULE_LICENSE("GPL v2");
|
@ -43,8 +43,6 @@
|
||||
#include <linux/watchdog.h> /* For watchdog specific items */
|
||||
#include <linux/uaccess.h> /* For copy_to_user/put_user/... */
|
||||
|
||||
#include <uapi/linux/sched/types.h> /* For struct sched_param */
|
||||
|
||||
#include "watchdog_core.h"
|
||||
#include "watchdog_pretimeout.h"
|
||||
|
||||
@ -994,8 +992,10 @@ static int watchdog_cdev_register(struct watchdog_device *wdd)
|
||||
wd_data->wdd = wdd;
|
||||
wdd->wd_data = wd_data;
|
||||
|
||||
if (IS_ERR_OR_NULL(watchdog_kworker))
|
||||
if (IS_ERR_OR_NULL(watchdog_kworker)) {
|
||||
kfree(wd_data);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
device_initialize(&wd_data->dev);
|
||||
wd_data->dev.devt = MKDEV(MAJOR(watchdog_devt), wdd->id);
|
||||
@ -1021,7 +1021,7 @@ static int watchdog_cdev_register(struct watchdog_device *wdd)
|
||||
pr_err("%s: a legacy watchdog module is probably present.\n",
|
||||
wdd->info->identity);
|
||||
old_wd_data = NULL;
|
||||
kfree(wd_data);
|
||||
put_device(&wd_data->dev);
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user