Commit f804b315 authored by Linus Torvalds's avatar Linus Torvalds

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
parents b7769c45 c5b8e464
# 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 ...@@ -1015,6 +1015,14 @@ config PM8916_WATCHDOG
Say Y here to include support watchdog timer embedded into the Say Y here to include support watchdog timer embedded into the
pm8916 module. 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 # X86 (i386 + ia64 + x86_64) Architecture
config ACQUIRE_WDT config ACQUIRE_WDT
......
...@@ -95,6 +95,7 @@ obj-$(CONFIG_RTD119X_WATCHDOG) += rtd119x_wdt.o ...@@ -95,6 +95,7 @@ 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 obj-$(CONFIG_PM8916_WATCHDOG) += pm8916_wdt.o
obj-$(CONFIG_ARM_SMC_WATCHDOG) += arm_smc_wdt.o obj-$(CONFIG_ARM_SMC_WATCHDOG) += arm_smc_wdt.o
obj-$(CONFIG_VISCONTI_WATCHDOG) += visconti_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
......
...@@ -334,12 +334,9 @@ static int cdns_wdt_probe(struct platform_device *pdev) ...@@ -334,12 +334,9 @@ static int cdns_wdt_probe(struct platform_device *pdev)
watchdog_set_drvdata(cdns_wdt_device, wdt); watchdog_set_drvdata(cdns_wdt_device, wdt);
wdt->clk = devm_clk_get(dev, NULL); wdt->clk = devm_clk_get(dev, NULL);
if (IS_ERR(wdt->clk)) { if (IS_ERR(wdt->clk))
ret = PTR_ERR(wdt->clk); return dev_err_probe(dev, PTR_ERR(wdt->clk),
if (ret != -EPROBE_DEFER) "input clock not found\n");
dev_err(dev, "input clock not found\n");
return ret;
}
ret = clk_prepare_enable(wdt->clk); ret = clk_prepare_enable(wdt->clk);
if (ret) { if (ret) {
......
...@@ -206,12 +206,9 @@ static int davinci_wdt_probe(struct platform_device *pdev) ...@@ -206,12 +206,9 @@ static int davinci_wdt_probe(struct platform_device *pdev)
return -ENOMEM; return -ENOMEM;
davinci_wdt->clk = devm_clk_get(dev, NULL); davinci_wdt->clk = devm_clk_get(dev, NULL);
if (IS_ERR(davinci_wdt->clk))
if (IS_ERR(davinci_wdt->clk)) { return dev_err_probe(dev, PTR_ERR(davinci_wdt->clk),
if (PTR_ERR(davinci_wdt->clk) != -EPROBE_DEFER) "failed to get clock node\n");
dev_err(dev, "failed to get clock node\n");
return PTR_ERR(davinci_wdt->clk);
}
ret = clk_prepare_enable(davinci_wdt->clk); ret = clk_prepare_enable(davinci_wdt->clk);
if (ret) { if (ret) {
......
...@@ -5,6 +5,7 @@ ...@@ -5,6 +5,7 @@
#include <linux/clk.h> #include <linux/clk.h>
#include <linux/io.h> #include <linux/io.h>
#include <linux/iopoll.h>
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/of.h> #include <linux/of.h>
...@@ -21,6 +22,8 @@ ...@@ -21,6 +22,8 @@
#define WDOG_CS_CLK (LPO_CLK << LPO_CLK_SHIFT) #define WDOG_CS_CLK (LPO_CLK << LPO_CLK_SHIFT)
#define WDOG_CS_EN BIT(7) #define WDOG_CS_EN BIT(7)
#define WDOG_CS_UPDATE BIT(5) #define WDOG_CS_UPDATE BIT(5)
#define WDOG_CS_WAIT BIT(1)
#define WDOG_CS_STOP BIT(0)
#define WDOG_CNT 0x4 #define WDOG_CNT 0x4
#define WDOG_TOVAL 0x8 #define WDOG_TOVAL 0x8
...@@ -36,6 +39,7 @@ ...@@ -36,6 +39,7 @@
#define DEFAULT_TIMEOUT 60 #define DEFAULT_TIMEOUT 60
#define MAX_TIMEOUT 128 #define MAX_TIMEOUT 128
#define WDOG_CLOCK_RATE 1000 #define WDOG_CLOCK_RATE 1000
#define WDOG_WAIT_TIMEOUT 20
static bool nowayout = WATCHDOG_NOWAYOUT; static bool nowayout = WATCHDOG_NOWAYOUT;
module_param(nowayout, bool, 0000); module_param(nowayout, bool, 0000);
...@@ -48,17 +52,40 @@ struct imx7ulp_wdt_device { ...@@ -48,17 +52,40 @@ struct imx7ulp_wdt_device {
struct clk *clk; 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); struct imx7ulp_wdt_device *wdt = watchdog_get_drvdata(wdog);
u32 val = readl(wdt->base + WDOG_CS); u32 val = readl(wdt->base + WDOG_CS);
int ret;
local_irq_disable();
writel(UNLOCK, wdt->base + WDOG_CNT); writel(UNLOCK, wdt->base + WDOG_CNT);
ret = imx7ulp_wdt_wait(wdt->base, WDOG_CS_ULK);
if (ret)
goto enable_out;
if (enable) if (enable)
writel(val | WDOG_CS_EN, wdt->base + WDOG_CS); writel(val | WDOG_CS_EN, wdt->base + WDOG_CS);
else else
writel(val & ~WDOG_CS_EN, wdt->base + WDOG_CS); 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) static bool imx7ulp_wdt_is_enabled(void __iomem *base)
...@@ -79,17 +106,12 @@ static int imx7ulp_wdt_ping(struct watchdog_device *wdog) ...@@ -79,17 +106,12 @@ static int imx7ulp_wdt_ping(struct watchdog_device *wdog)
static int imx7ulp_wdt_start(struct watchdog_device *wdog) static int imx7ulp_wdt_start(struct watchdog_device *wdog)
{ {
return imx7ulp_wdt_enable(wdog, true);
imx7ulp_wdt_enable(wdog, true);
return 0;
} }
static int imx7ulp_wdt_stop(struct watchdog_device *wdog) static int imx7ulp_wdt_stop(struct watchdog_device *wdog)
{ {
imx7ulp_wdt_enable(wdog, false); return imx7ulp_wdt_enable(wdog, false);
return 0;
} }
static int imx7ulp_wdt_set_timeout(struct watchdog_device *wdog, static int imx7ulp_wdt_set_timeout(struct watchdog_device *wdog,
...@@ -97,22 +119,37 @@ 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); struct imx7ulp_wdt_device *wdt = watchdog_get_drvdata(wdog);
u32 val = WDOG_CLOCK_RATE * timeout; u32 val = WDOG_CLOCK_RATE * timeout;
int ret;
local_irq_disable();
writel(UNLOCK, wdt->base + WDOG_CNT); 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); writel(val, wdt->base + WDOG_TOVAL);
imx7ulp_wdt_wait(wdt->base, WDOG_CS_RCS);
wdog->timeout = timeout; wdog->timeout = timeout;
return 0; timeout_out:
local_irq_enable();
return ret;
} }
static int imx7ulp_wdt_restart(struct watchdog_device *wdog, static int imx7ulp_wdt_restart(struct watchdog_device *wdog,
unsigned long action, void *data) unsigned long action, void *data)
{ {
struct imx7ulp_wdt_device *wdt = watchdog_get_drvdata(wdog); struct imx7ulp_wdt_device *wdt = watchdog_get_drvdata(wdog);
int ret;
imx7ulp_wdt_enable(wdog, true); ret = imx7ulp_wdt_enable(wdog, true);
imx7ulp_wdt_set_timeout(&wdt->wdd, 1); if (ret)
return ret;
ret = imx7ulp_wdt_set_timeout(&wdt->wdd, 1);
if (ret)
return ret;
/* wait for wdog to fire */ /* wait for wdog to fire */
while (true) while (true)
...@@ -136,19 +173,31 @@ static const struct watchdog_info imx7ulp_wdt_info = { ...@@ -136,19 +173,31 @@ static const struct watchdog_info imx7ulp_wdt_info = {
WDIOF_MAGICCLOSE, 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; u32 val;
int ret;
local_irq_disable();
/* unlock the wdog for reconfiguration */ /* unlock the wdog for reconfiguration */
writel_relaxed(UNLOCK_SEQ0, base + WDOG_CNT); writel_relaxed(UNLOCK_SEQ0, base + WDOG_CNT);
writel_relaxed(UNLOCK_SEQ1, 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 */ /* set an initial timeout value in TOVAL */
writel(timeout, base + WDOG_TOVAL); writel(timeout, base + WDOG_TOVAL);
/* enable 32bit command sequence and reconfigure */ /* 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); 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) static void imx7ulp_wdt_action(void *data)
...@@ -199,7 +248,9 @@ static int imx7ulp_wdt_probe(struct platform_device *pdev) ...@@ -199,7 +248,9 @@ static int imx7ulp_wdt_probe(struct platform_device *pdev)
watchdog_stop_on_reboot(wdog); watchdog_stop_on_reboot(wdog);
watchdog_stop_on_unregister(wdog); watchdog_stop_on_unregister(wdog);
watchdog_set_drvdata(wdog, imx7ulp_wdt); 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); return devm_watchdog_register_device(dev, wdog);
} }
......
...@@ -15,7 +15,7 @@ ...@@ -15,7 +15,7 @@
* Support of the watchdog timers, which are available on * Support of the watchdog timers, which are available on
* IT8607, IT8620, IT8622, IT8625, IT8628, IT8655, IT8665, IT8686, * IT8607, IT8620, IT8622, IT8625, IT8628, IT8655, IT8665, IT8686,
* IT8702, IT8712, IT8716, IT8718, IT8720, IT8721, IT8726, IT8728, * IT8702, IT8712, IT8716, IT8718, IT8720, IT8721, IT8726, IT8728,
* and IT8783. * IT8772, IT8783 and IT8784.
*/ */
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
...@@ -66,7 +66,9 @@ ...@@ -66,7 +66,9 @@
#define IT8721_ID 0x8721 #define IT8721_ID 0x8721
#define IT8726_ID 0x8726 /* the data sheet suggest wrongly 0x8716 */ #define IT8726_ID 0x8726 /* the data sheet suggest wrongly 0x8716 */
#define IT8728_ID 0x8728 #define IT8728_ID 0x8728
#define IT8772_ID 0x8772
#define IT8783_ID 0x8783 #define IT8783_ID 0x8783
#define IT8784_ID 0x8784
#define IT8786_ID 0x8786 #define IT8786_ID 0x8786
/* GPIO Configuration Registers LDN=0x07 */ /* GPIO Configuration Registers LDN=0x07 */
...@@ -294,7 +296,9 @@ static int __init it87_wdt_init(void) ...@@ -294,7 +296,9 @@ static int __init it87_wdt_init(void)
case IT8720_ID: case IT8720_ID:
case IT8721_ID: case IT8721_ID:
case IT8728_ID: case IT8728_ID:
case IT8772_ID:
case IT8783_ID: case IT8783_ID:
case IT8784_ID:
case IT8786_ID: case IT8786_ID:
max_units = 65535; max_units = 65535;
break; break;
......
...@@ -656,7 +656,7 @@ static int usb_pcwd_probe(struct usb_interface *interface, ...@@ -656,7 +656,7 @@ static int usb_pcwd_probe(struct usb_interface *interface,
/* set up the memory buffer's */ /* set up the memory buffer's */
usb_pcwd->intr_buffer = usb_alloc_coherent(udev, usb_pcwd->intr_size, 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) { if (!usb_pcwd->intr_buffer) {
pr_err("Out of memory\n"); pr_err("Out of memory\n");
goto error; goto error;
......
...@@ -231,6 +231,8 @@ static int rdc321x_wdt_probe(struct platform_device *pdev) ...@@ -231,6 +231,8 @@ static int rdc321x_wdt_probe(struct platform_device *pdev)
rdc321x_wdt_device.sb_pdev = pdata->sb_pdev; rdc321x_wdt_device.sb_pdev = pdata->sb_pdev;
rdc321x_wdt_device.base_reg = r->start; rdc321x_wdt_device.base_reg = r->start;
rdc321x_wdt_device.queue = 0;
rdc321x_wdt_device.default_ticks = ticks;
err = misc_register(&rdc321x_wdt_misc); err = misc_register(&rdc321x_wdt_misc);
if (err < 0) { if (err < 0) {
...@@ -245,14 +247,11 @@ static int rdc321x_wdt_probe(struct platform_device *pdev) ...@@ -245,14 +247,11 @@ static int rdc321x_wdt_probe(struct platform_device *pdev)
rdc321x_wdt_device.base_reg, RDC_WDT_RST); rdc321x_wdt_device.base_reg, RDC_WDT_RST);
init_completion(&rdc321x_wdt_device.stop); init_completion(&rdc321x_wdt_device.stop);
rdc321x_wdt_device.queue = 0;
clear_bit(0, &rdc321x_wdt_device.inuse); clear_bit(0, &rdc321x_wdt_device.inuse);
timer_setup(&rdc321x_wdt_device.timer, rdc321x_wdt_trigger, 0); timer_setup(&rdc321x_wdt_device.timer, rdc321x_wdt_trigger, 0);
rdc321x_wdt_device.default_ticks = ticks;
dev_info(&pdev->dev, "watchdog init success\n"); dev_info(&pdev->dev, "watchdog init success\n");
return 0; return 0;
......
...@@ -194,6 +194,7 @@ static int rwdt_probe(struct platform_device *pdev) ...@@ -194,6 +194,7 @@ static int rwdt_probe(struct platform_device *pdev)
struct clk *clk; struct clk *clk;
unsigned long clks_per_sec; unsigned long clks_per_sec;
int ret, i; int ret, i;
u8 csra;
if (rwdt_blacklisted(dev)) if (rwdt_blacklisted(dev))
return -ENODEV; return -ENODEV;
...@@ -213,8 +214,8 @@ static int rwdt_probe(struct platform_device *pdev) ...@@ -213,8 +214,8 @@ static int rwdt_probe(struct platform_device *pdev)
pm_runtime_enable(dev); pm_runtime_enable(dev);
pm_runtime_get_sync(dev); pm_runtime_get_sync(dev);
priv->clk_rate = clk_get_rate(clk); priv->clk_rate = clk_get_rate(clk);
priv->wdev.bootstatus = (readb_relaxed(priv->base + RWTCSRA) & csra = readb_relaxed(priv->base + RWTCSRA);
RWTCSRA_WOVF) ? WDIOF_CARDRESET : 0; priv->wdev.bootstatus = csra & RWTCSRA_WOVF ? WDIOF_CARDRESET : 0;
pm_runtime_put(dev); pm_runtime_put(dev);
if (!priv->clk_rate) { if (!priv->clk_rate) {
...@@ -252,6 +253,13 @@ static int rwdt_probe(struct platform_device *pdev) ...@@ -252,6 +253,13 @@ static int rwdt_probe(struct platform_device *pdev)
/* This overrides the default timeout only if DT configuration was found */ /* This overrides the default timeout only if DT configuration was found */
watchdog_init_timeout(&priv->wdev, 0, dev); 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); ret = watchdog_register_device(&priv->wdev);
if (ret < 0) if (ret < 0)
goto out_pm_disable; goto out_pm_disable;
......
...@@ -205,11 +205,8 @@ static int rti_wdt_probe(struct platform_device *pdev) ...@@ -205,11 +205,8 @@ static int rti_wdt_probe(struct platform_device *pdev)
return -ENOMEM; return -ENOMEM;
clk = clk_get(dev, NULL); clk = clk_get(dev, NULL);
if (IS_ERR(clk)) { if (IS_ERR(clk))
if (PTR_ERR(clk) != -EPROBE_DEFER) return dev_err_probe(dev, PTR_ERR(clk), "failed to get clock\n");
dev_err(dev, "failed to get clock\n");
return PTR_ERR(clk);
}
wdt->freq = clk_get_rate(clk); wdt->freq = clk_get_rate(clk);
...@@ -230,11 +227,8 @@ static int rti_wdt_probe(struct platform_device *pdev) ...@@ -230,11 +227,8 @@ static int rti_wdt_probe(struct platform_device *pdev)
pm_runtime_enable(dev); pm_runtime_enable(dev);
ret = pm_runtime_get_sync(dev); ret = pm_runtime_get_sync(dev);
if (ret) { if (ret)
if (ret != -EPROBE_DEFER) return dev_err_probe(dev, ret, "runtime pm failed\n");
dev_err(&pdev->dev, "runtime pm failed\n");
return ret;
}
platform_set_drvdata(pdev, wdt); platform_set_drvdata(pdev, wdt);
......
...@@ -17,6 +17,12 @@ ...@@ -17,6 +17,12 @@
* AMD Publication 51192 "AMD Bolton FCH Register Reference Guide" * AMD Publication 51192 "AMD Bolton FCH Register Reference Guide"
* AMD Publication 52740 "BIOS and Kernel Developer’s Guide (BKDG) * AMD Publication 52740 "BIOS and Kernel Developer’s Guide (BKDG)
* for AMD Family 16h Models 30h-3Fh Processors" * 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, ...@@ -241,6 +247,18 @@ static int sp5100_tco_setupdevice(struct device *dev,
break; break;
case efch: case efch:
dev_name = SB800_DEVNAME; 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); val = sp5100_tco_read_pm_reg8(EFCH_PM_DECODEEN);
if (val & EFCH_PM_DECODEEN_WDT_TMREN) if (val & EFCH_PM_DECODEEN_WDT_TMREN)
mmio_addr = EFCH_PM_WDT_ADDR; mmio_addr = EFCH_PM_WDT_ADDR;
......
...@@ -70,7 +70,7 @@ ...@@ -70,7 +70,7 @@
#define EFCH_PM_DECODEEN_WDT_TMREN BIT(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_DECODEEN_SECOND_RES GENMASK(1, 0)
#define EFCH_PM_WATCHDOG_DISABLE ((u8)GENMASK(3, 2)) #define EFCH_PM_WATCHDOG_DISABLE ((u8)GENMASK(3, 2))
......
// 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 @@ ...@@ -43,8 +43,6 @@
#include <linux/watchdog.h> /* For watchdog specific items */ #include <linux/watchdog.h> /* For watchdog specific items */
#include <linux/uaccess.h> /* For copy_to_user/put_user/... */ #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_core.h"
#include "watchdog_pretimeout.h" #include "watchdog_pretimeout.h"
...@@ -994,8 +992,10 @@ static int watchdog_cdev_register(struct watchdog_device *wdd) ...@@ -994,8 +992,10 @@ static int watchdog_cdev_register(struct watchdog_device *wdd)
wd_data->wdd = wdd; wd_data->wdd = wdd;
wdd->wd_data = wd_data; wdd->wd_data = wd_data;
if (IS_ERR_OR_NULL(watchdog_kworker)) if (IS_ERR_OR_NULL(watchdog_kworker)) {
kfree(wd_data);
return -ENODEV; return -ENODEV;
}
device_initialize(&wd_data->dev); device_initialize(&wd_data->dev);
wd_data->dev.devt = MKDEV(MAJOR(watchdog_devt), wdd->id); wd_data->dev.devt = MKDEV(MAJOR(watchdog_devt), wdd->id);
...@@ -1021,7 +1021,7 @@ static int watchdog_cdev_register(struct watchdog_device *wdd) ...@@ -1021,7 +1021,7 @@ static int watchdog_cdev_register(struct watchdog_device *wdd)
pr_err("%s: a legacy watchdog module is probably present.\n", pr_err("%s: a legacy watchdog module is probably present.\n",
wdd->info->identity); wdd->info->identity);
old_wd_data = NULL; old_wd_data = NULL;
kfree(wd_data); put_device(&wd_data->dev);
return err; return err;
} }
} }
......
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