Updates for the time(r) subsystem:

Core:
 
   - Make the clocksource watchdog more robust by better validation checks
     of the measurement.
 
  Drivers:
 
   - New drivers for MStar and SSD20xd SOCs
 
   - The usual cleanups and improvements all over the place
 -----BEGIN PGP SIGNATURE-----
 
 iQJHBAABCgAxFiEEQp8+kY+LLUocC4bMphj1TA10mKEFAmHf+n0THHRnbHhAbGlu
 dXRyb25peC5kZQAKCRCmGPVMDXSYoQ2lD/9WCp+fGTmOt5zb8dOyuyLFjDljStPZ
 zNi4d4Iu3gcBIRcjACtbSI2rAPK5gQyM38c9nlmtFv3zihfmz5bQkMTQ1N7O84Nu
 c1iEuTW69l/ZvykSJWApsGIY8zgA41efoLYzhg/dCpQGE2fINiRDyU5ZxbJXmwMW
 ipjBCf3F9/WLWoTgvl3cTayd/l+7fnpeM6w9MfujHLyCXCwz484KW/7UIMkTCcxF
 b7Y3bTLxP4a/iT/ltFDqvLUjUuJWdmCh6gihcEL+9PD/h6KmQnND+p9KB7tbMRy/
 DUOBTCi5gY66RQeGRJPVe+Cx/Wi+8vCiyfXUuSoQGqE39HVYOUzMwWOjOncjLad4
 fXSzzCIKRwsB3qKw+2GnDeEx1hIw1/K88V2tA+OgQjdWIginOClzy0jb0dkBRbo5
 H1U6mPxb+CTKAl1hXAkfDDCenLTiiGBFbvJUydiJYMcFEZYM166e/jA53xIKHNAz
 WEphVRAPA269uIxYBXJU7pA6M5bYqbHhhmrxyWOBbhhZGGj3x685PA1wioeNayMp
 SMA7s7kZaOBDuTtjRY/dFDkd/27HKWDkxjZCbbslRRKKO0Zz7qixzspV5LETnABO
 NzR5TcNimCyvfKEzSG1PFmzx9P/cnspyLvWj560xL0Z9x1MnsHtiUpibJ8a/Gb45
 riPKWGedog8BgQ==
 =7vCU
 -----END PGP SIGNATURE-----

Merge tag 'timers-core-2022-01-13' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip

Pull timer updates from Thomas Gleixner:
 "Updates for the time(r) subsystem:

  Core:

   - Make the clocksource watchdog more robust by better validation
     checks of the measurement.

  Drivers:

   - New drivers for MStar and SSD20xd SOCs

   - The usual cleanups and improvements all over the place"

* tag 'timers-core-2022-01-13' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip:
  dt-bindings: timer: Add Mstar MSC313e timer devicetree bindings documentation
  clocksource/drivers/msc313e: Add support for ssd20xd-based platforms
  clocksource/drivers: Add MStar MSC313e timer support
  clocksource/drivers/pistachio: Fix -Wunused-but-set-variable warning
  clocksource/drivers/timer-imx-sysctr: Set cpumask to cpu_possible_mask
  clocksource/drivers/imx-sysctr: Mark two variable with __ro_after_init
  clocksource/drivers/renesas,ostm: Make RENESAS_OSTM symbol visible
  clocksource/drivers/renesas-ostm: Add RZ/G2L OSTM support
  dt-bindings: timer: renesas: ostm: Document Renesas RZ/G2L OSTM
  clocksource/drivers/exynos_mct: Fix silly typo resulting in checkpatch warning
  clocksource: Reduce the default clocksource_watchdog() retries to 2
  clocksource: Avoid accidental unstable marking of clocksources
  dt-bindings: timer: tpm-timer: Add imx8ulp compatible string
  reset: Add of_reset_control_get_optional_exclusive()
  clocksource/drivers/exynos_mct: Refactor resources allocation
  dt-bindings: timer: remove rockchip,rk3066-timer compatible string from rockchip,rk-timer.yaml
  dt-bindings: timer: cadence_ttc: Add power-domains
This commit is contained in:
Linus Torvalds 2022-01-13 09:02:27 -08:00
commit fd04899208
16 changed files with 475 additions and 44 deletions

View File

@ -612,8 +612,8 @@
clocksource.max_cswd_read_retries= [KNL]
Number of clocksource_watchdog() retries due to
external delays before the clock will be marked
unstable. Defaults to three retries, that is,
four attempts to read the clock under test.
unstable. Defaults to two retries, that is,
three attempts to read the clock under test.
clocksource.verify_n_cpus= [KNL]
Limit the number of CPUs checked for clocksources

View File

@ -25,6 +25,9 @@ properties:
clocks:
maxItems: 1
power-domains:
maxItems: 1
timer-width:
$ref: "/schemas/types.yaml#/definitions/uint32"
description: |

View File

@ -0,0 +1,46 @@
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/timer/mstar,msc313e-timer.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Mstar MSC313e Timer Device Tree Bindings
maintainers:
- Daniel Palmer <daniel@0x0f.com>
- Romain Perier <romain.perier@gmail.com>
properties:
compatible:
enum:
- mstar,msc313e-timer
- sstar,ssd20xd-timer
reg:
maxItems: 1
interrupts:
maxItems: 1
clocks:
maxItems: 1
required:
- compatible
- reg
- interrupts
- clocks
additionalProperties: false
examples:
- |
#include <dt-bindings/interrupt-controller/arm-gic.h>
timer@6040 {
compatible = "mstar,msc313e-timer";
reg = <0x6040 0x40>;
clocks = <&xtal_div2>;
interrupts-extended = <&intc_fiq GIC_SPI 0 IRQ_TYPE_LEVEL_HIGH>;
};
...

View File

@ -19,7 +19,11 @@ description: |
properties:
compatible:
const: fsl,imx7ulp-tpm
oneOf:
- const: fsl,imx7ulp-tpm
- items:
- const: fsl,imx8ulp-tpm
- const: fsl,imx7ulp-tpm
reg:
maxItems: 1

View File

@ -21,9 +21,10 @@ properties:
compatible:
items:
- enum:
- renesas,r7s72100-ostm # RZ/A1H
- renesas,r7s9210-ostm # RZ/A2M
- const: renesas,ostm # Generic
- renesas,r7s72100-ostm # RZ/A1H
- renesas,r7s9210-ostm # RZ/A2M
- renesas,r9a07g044-ostm # RZ/G2{L,LC}
- const: renesas,ostm # Generic
reg:
maxItems: 1
@ -37,6 +38,9 @@ properties:
power-domains:
maxItems: 1
resets:
maxItems: 1
required:
- compatible
- reg
@ -44,6 +48,16 @@ required:
- clocks
- power-domains
if:
properties:
compatible:
contains:
enum:
- renesas,r9a07g044-ostm
then:
required:
- resets
additionalProperties: false
examples:

View File

@ -18,7 +18,6 @@ properties:
- enum:
- rockchip,rv1108-timer
- rockchip,rk3036-timer
- rockchip,rk3066-timer
- rockchip,rk3188-timer
- rockchip,rk3228-timer
- rockchip,rk3229-timer

View File

@ -2304,6 +2304,7 @@ F: Documentation/devicetree/bindings/gpio/mstar,msc313-gpio.yaml
F: arch/arm/boot/dts/mstar-*
F: arch/arm/mach-mstar/
F: drivers/clk/mstar/
F: drivers/clocksource/timer-msc313e.c
F: drivers/gpio/gpio-msc313.c
F: drivers/rtc/rtc-msc313.c
F: drivers/watchdog/msc313e_wdt.c

View File

@ -510,7 +510,8 @@ config SH_TIMER_MTU2
This hardware comes with 16-bit timer registers.
config RENESAS_OSTM
bool "Renesas OSTM timer driver" if COMPILE_TEST
bool "Renesas OSTM timer driver"
depends on ARCH_RENESAS || COMPILE_TEST
select CLKSRC_MMIO
select TIMER_OF
help
@ -671,6 +672,15 @@ config MILBEAUT_TIMER
help
Enables the support for Milbeaut timer driver.
config MSC313E_TIMER
bool "MSC313E timer driver" if COMPILE_TEST
select TIMER_OF
select CLKSRC_MMIO
help
Enables support for the MStar MSC313E timer driver.
This provides access to multiple interrupt generating
programmable 32-bit free running incrementing counters.
config INGENIC_TIMER
bool "Clocksource/timer using the TCU in Ingenic JZ SoCs"
default MACH_INGENIC

View File

@ -88,3 +88,4 @@ obj-$(CONFIG_CSKY_MP_TIMER) += timer-mp-csky.o
obj-$(CONFIG_GX6605S_TIMER) += timer-gx6605s.o
obj-$(CONFIG_HYPERV_TIMER) += hyperv_timer.o
obj-$(CONFIG_MICROCHIP_PIT64B) += timer-microchip-pit64b.o
obj-$(CONFIG_MSC313E_TIMER) += timer-msc313e.o

View File

@ -467,7 +467,7 @@ static int exynos4_mct_starting_cpu(unsigned int cpu)
evt->tick_resume = set_state_shutdown;
evt->features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT |
CLOCK_EVT_FEAT_PERCPU;
evt->rating = MCT_CLKEVENTS_RATING,
evt->rating = MCT_CLKEVENTS_RATING;
exynos4_mct_write(TICK_BASE_CNT, mevt->base + MCT_L_TCNTB_OFFSET);
@ -504,11 +504,14 @@ static int exynos4_mct_dying_cpu(unsigned int cpu)
return 0;
}
static int __init exynos4_timer_resources(struct device_node *np, void __iomem *base)
static int __init exynos4_timer_resources(struct device_node *np)
{
int err, cpu;
struct clk *mct_clk, *tick_clk;
reg_base = of_iomap(np, 0);
if (!reg_base)
panic("%s: unable to ioremap mct address space\n", __func__);
tick_clk = of_clk_get_by_name(np, "fin_pll");
if (IS_ERR(tick_clk))
panic("%s: unable to determine tick clock rate\n", __func__);
@ -519,9 +522,27 @@ static int __init exynos4_timer_resources(struct device_node *np, void __iomem *
panic("%s: unable to retrieve mct clock instance\n", __func__);
clk_prepare_enable(mct_clk);
reg_base = base;
if (!reg_base)
panic("%s: unable to ioremap mct address space\n", __func__);
return 0;
}
static int __init exynos4_timer_interrupts(struct device_node *np,
unsigned int int_type)
{
int nr_irqs, i, err, cpu;
mct_int_type = int_type;
/* This driver uses only one global timer interrupt */
mct_irqs[MCT_G0_IRQ] = irq_of_parse_and_map(np, MCT_G0_IRQ);
/*
* Find out the number of local irqs specified. The local
* timer irqs are specified after the four global timer
* irqs are specified.
*/
nr_irqs = of_irq_count(np);
for (i = MCT_L0_IRQ; i < nr_irqs; i++)
mct_irqs[i] = irq_of_parse_and_map(np, i);
if (mct_int_type == MCT_INT_PPI) {
@ -581,24 +602,13 @@ out_irq:
static int __init mct_init_dt(struct device_node *np, unsigned int int_type)
{
u32 nr_irqs, i;
int ret;
mct_int_type = int_type;
ret = exynos4_timer_resources(np);
if (ret)
return ret;
/* This driver uses only one global timer interrupt */
mct_irqs[MCT_G0_IRQ] = irq_of_parse_and_map(np, MCT_G0_IRQ);
/*
* Find out the number of local irqs specified. The local
* timer irqs are specified after the four global timer
* irqs are specified.
*/
nr_irqs = of_irq_count(np);
for (i = MCT_L0_IRQ; i < nr_irqs; i++)
mct_irqs[i] = irq_of_parse_and_map(np, i);
ret = exynos4_timer_resources(np, of_iomap(np, 0));
ret = exynos4_timer_interrupts(np, int_type);
if (ret)
return ret;

View File

@ -9,6 +9,8 @@
#include <linux/clk.h>
#include <linux/clockchips.h>
#include <linux/interrupt.h>
#include <linux/platform_device.h>
#include <linux/reset.h>
#include <linux/sched_clock.h>
#include <linux/slab.h>
@ -159,6 +161,7 @@ static int __init ostm_init_clkevt(struct timer_of *to)
static int __init ostm_init(struct device_node *np)
{
struct reset_control *rstc;
struct timer_of *to;
int ret;
@ -166,6 +169,14 @@ static int __init ostm_init(struct device_node *np)
if (!to)
return -ENOMEM;
rstc = of_reset_control_get_optional_exclusive(np, NULL);
if (IS_ERR(rstc)) {
ret = PTR_ERR(rstc);
goto err_free;
}
reset_control_deassert(rstc);
to->flags = TIMER_OF_BASE | TIMER_OF_CLOCK;
if (system_clock) {
/*
@ -178,7 +189,7 @@ static int __init ostm_init(struct device_node *np)
ret = timer_of_init(np, to);
if (ret)
goto err_free;
goto err_reset;
/*
* First probed device will be used as system clocksource. Any
@ -203,9 +214,35 @@ static int __init ostm_init(struct device_node *np)
err_cleanup:
timer_of_cleanup(to);
err_reset:
reset_control_assert(rstc);
reset_control_put(rstc);
err_free:
kfree(to);
return ret;
}
TIMER_OF_DECLARE(ostm, "renesas,ostm", ostm_init);
#ifdef CONFIG_ARCH_R9A07G044
static int __init ostm_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
return ostm_init(dev->of_node);
}
static const struct of_device_id ostm_of_table[] = {
{ .compatible = "renesas,ostm", },
{ /* sentinel */ }
};
static struct platform_driver ostm_device_driver = {
.driver = {
.name = "renesas_ostm",
.of_match_table = of_match_ptr(ostm_of_table),
.suppress_bind_attrs = true,
},
};
builtin_platform_driver_probe(ostm_device_driver, ostm_probe);
#endif

View File

@ -20,8 +20,8 @@
#define SYS_CTR_CLK_DIV 0x3
static void __iomem *sys_ctr_base;
static u32 cmpcr;
static void __iomem *sys_ctr_base __ro_after_init;
static u32 cmpcr __ro_after_init;
static void sysctr_timer_enable(bool enable)
{
@ -119,7 +119,7 @@ static struct timer_of to_sysctr = {
static void __init sysctr_clockevent_init(void)
{
to_sysctr.clkevt.cpumask = cpumask_of(0);
to_sysctr.clkevt.cpumask = cpu_possible_mask;
clockevents_config_and_register(&to_sysctr.clkevt,
timer_of_rate(&to_sysctr),

View File

@ -0,0 +1,253 @@
// SPDX-License-Identifier: GPL-2.0
/*
* MStar timer driver
*
* Copyright (C) 2021 Daniel Palmer
* Copyright (C) 2021 Romain Perier
*
*/
#include <linux/clk.h>
#include <linux/clockchips.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/irqreturn.h>
#include <linux/sched_clock.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_irq.h>
#ifdef CONFIG_ARM
#include <linux/delay.h>
#endif
#include "timer-of.h"
#define TIMER_NAME "msc313e_timer"
#define MSC313E_REG_CTRL 0x00
#define MSC313E_REG_CTRL_TIMER_EN BIT(0)
#define MSC313E_REG_CTRL_TIMER_TRIG BIT(1)
#define MSC313E_REG_CTRL_TIMER_INT_EN BIT(8)
#define MSC313E_REG_TIMER_MAX_LOW 0x08
#define MSC313E_REG_TIMER_MAX_HIGH 0x0c
#define MSC313E_REG_COUNTER_LOW 0x10
#define MSC313E_REG_COUNTER_HIGH 0x14
#define MSC313E_REG_TIMER_DIVIDE 0x18
#define MSC313E_CLK_DIVIDER 9
#define TIMER_SYNC_TICKS 3
#ifdef CONFIG_ARM
struct msc313e_delay {
void __iomem *base;
struct delay_timer delay;
};
static struct msc313e_delay msc313e_delay;
#endif
static void __iomem *msc313e_clksrc;
static void msc313e_timer_stop(void __iomem *base)
{
writew(0, base + MSC313E_REG_CTRL);
}
static void msc313e_timer_start(void __iomem *base, bool periodic)
{
u16 reg;
reg = readw(base + MSC313E_REG_CTRL);
if (periodic)
reg |= MSC313E_REG_CTRL_TIMER_EN;
else
reg |= MSC313E_REG_CTRL_TIMER_TRIG;
writew(reg | MSC313E_REG_CTRL_TIMER_INT_EN, base + MSC313E_REG_CTRL);
}
static void msc313e_timer_setup(void __iomem *base, unsigned long delay)
{
unsigned long flags;
local_irq_save(flags);
writew(delay >> 16, base + MSC313E_REG_TIMER_MAX_HIGH);
writew(delay & 0xffff, base + MSC313E_REG_TIMER_MAX_LOW);
local_irq_restore(flags);
}
static unsigned long msc313e_timer_current_value(void __iomem *base)
{
unsigned long flags;
u16 l, h;
local_irq_save(flags);
l = readw(base + MSC313E_REG_COUNTER_LOW);
h = readw(base + MSC313E_REG_COUNTER_HIGH);
local_irq_restore(flags);
return (((u32)h) << 16 | l);
}
static int msc313e_timer_clkevt_shutdown(struct clock_event_device *evt)
{
struct timer_of *timer = to_timer_of(evt);
msc313e_timer_stop(timer_of_base(timer));
return 0;
}
static int msc313e_timer_clkevt_set_oneshot(struct clock_event_device *evt)
{
struct timer_of *timer = to_timer_of(evt);
msc313e_timer_stop(timer_of_base(timer));
msc313e_timer_start(timer_of_base(timer), false);
return 0;
}
static int msc313e_timer_clkevt_set_periodic(struct clock_event_device *evt)
{
struct timer_of *timer = to_timer_of(evt);
msc313e_timer_stop(timer_of_base(timer));
msc313e_timer_setup(timer_of_base(timer), timer_of_period(timer));
msc313e_timer_start(timer_of_base(timer), true);
return 0;
}
static int msc313e_timer_clkevt_next_event(unsigned long evt, struct clock_event_device *clkevt)
{
struct timer_of *timer = to_timer_of(clkevt);
msc313e_timer_stop(timer_of_base(timer));
msc313e_timer_setup(timer_of_base(timer), evt);
msc313e_timer_start(timer_of_base(timer), false);
return 0;
}
static irqreturn_t msc313e_timer_clkevt_irq(int irq, void *dev_id)
{
struct clock_event_device *evt = dev_id;
evt->event_handler(evt);
return IRQ_HANDLED;
}
static u64 msc313e_timer_clksrc_read(struct clocksource *cs)
{
return msc313e_timer_current_value(msc313e_clksrc) & cs->mask;
}
#ifdef CONFIG_ARM
static unsigned long msc313e_read_delay_timer_read(void)
{
return msc313e_timer_current_value(msc313e_delay.base);
}
#endif
static u64 msc313e_timer_sched_clock_read(void)
{
return msc313e_timer_current_value(msc313e_clksrc);
}
static struct clock_event_device msc313e_clkevt = {
.name = TIMER_NAME,
.rating = 300,
.features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT,
.set_state_shutdown = msc313e_timer_clkevt_shutdown,
.set_state_periodic = msc313e_timer_clkevt_set_periodic,
.set_state_oneshot = msc313e_timer_clkevt_set_oneshot,
.tick_resume = msc313e_timer_clkevt_shutdown,
.set_next_event = msc313e_timer_clkevt_next_event,
};
static int __init msc313e_clkevt_init(struct device_node *np)
{
int ret;
struct timer_of *to;
to = kzalloc(sizeof(*to), GFP_KERNEL);
if (!to)
return -ENOMEM;
to->flags = TIMER_OF_IRQ | TIMER_OF_CLOCK | TIMER_OF_BASE;
to->of_irq.handler = msc313e_timer_clkevt_irq;
ret = timer_of_init(np, to);
if (ret)
return ret;
if (of_device_is_compatible(np, "sstar,ssd20xd-timer")) {
to->of_clk.rate = clk_get_rate(to->of_clk.clk) / MSC313E_CLK_DIVIDER;
to->of_clk.period = DIV_ROUND_UP(to->of_clk.rate, HZ);
writew(MSC313E_CLK_DIVIDER - 1, timer_of_base(to) + MSC313E_REG_TIMER_DIVIDE);
}
msc313e_clkevt.cpumask = cpu_possible_mask;
msc313e_clkevt.irq = to->of_irq.irq;
to->clkevt = msc313e_clkevt;
clockevents_config_and_register(&to->clkevt, timer_of_rate(to),
TIMER_SYNC_TICKS, 0xffffffff);
return 0;
}
static int __init msc313e_clksrc_init(struct device_node *np)
{
struct timer_of to = { 0 };
int ret;
u16 reg;
to.flags = TIMER_OF_BASE | TIMER_OF_CLOCK;
ret = timer_of_init(np, &to);
if (ret)
return ret;
msc313e_clksrc = timer_of_base(&to);
reg = readw(msc313e_clksrc + MSC313E_REG_CTRL);
reg |= MSC313E_REG_CTRL_TIMER_EN;
writew(reg, msc313e_clksrc + MSC313E_REG_CTRL);
#ifdef CONFIG_ARM
msc313e_delay.base = timer_of_base(&to);
msc313e_delay.delay.read_current_timer = msc313e_read_delay_timer_read;
msc313e_delay.delay.freq = timer_of_rate(&to);
register_current_timer_delay(&msc313e_delay.delay);
#endif
sched_clock_register(msc313e_timer_sched_clock_read, 32, timer_of_rate(&to));
return clocksource_mmio_init(timer_of_base(&to), TIMER_NAME, timer_of_rate(&to), 300, 32,
msc313e_timer_clksrc_read);
}
static int __init msc313e_timer_init(struct device_node *np)
{
int ret = 0;
static int num_called;
switch (num_called) {
case 0:
ret = msc313e_clksrc_init(np);
if (ret)
return ret;
break;
default:
ret = msc313e_clkevt_init(np);
if (ret)
return ret;
break;
}
num_called++;
return 0;
}
TIMER_OF_DECLARE(msc313, "mstar,msc313e-timer", msc313e_timer_init);
TIMER_OF_DECLARE(ssd20xd, "sstar,ssd20xd-timer", msc313e_timer_init);

View File

@ -71,7 +71,8 @@ static u64 notrace
pistachio_clocksource_read_cycles(struct clocksource *cs)
{
struct pistachio_clocksource *pcs = to_pistachio_clocksource(cs);
u32 counter, overflow;
__maybe_unused u32 overflow;
u32 counter;
unsigned long flags;
/*

View File

@ -454,6 +454,26 @@ static inline struct reset_control *of_reset_control_get_exclusive(
return __of_reset_control_get(node, id, 0, false, false, true);
}
/**
* of_reset_control_get_optional_exclusive - Lookup and obtain an optional exclusive
* reference to a reset controller.
* @node: device to be reset by the controller
* @id: reset line name
*
* Optional variant of of_reset_control_get_exclusive(). If the requested reset
* is not specified in the device tree, this function returns NULL instead of
* an error.
*
* Returns a struct reset_control or IS_ERR() condition containing errno.
*
* Use of id names is optional.
*/
static inline struct reset_control *of_reset_control_get_optional_exclusive(
struct device_node *node, const char *id)
{
return __of_reset_control_get(node, id, 0, false, true, true);
}
/**
* of_reset_control_get_shared - Lookup and obtain a shared reference
* to a reset controller.

View File

@ -107,7 +107,7 @@ static u64 suspend_start;
* This delay could be due to SMIs, NMIs, or to VCPU preemptions. Used as
* a lower bound for cs->uncertainty_margin values when registering clocks.
*/
#define WATCHDOG_MAX_SKEW (50 * NSEC_PER_USEC)
#define WATCHDOG_MAX_SKEW (100 * NSEC_PER_USEC)
#ifdef CONFIG_CLOCKSOURCE_WATCHDOG
static void clocksource_watchdog_work(struct work_struct *work);
@ -199,23 +199,30 @@ void clocksource_mark_unstable(struct clocksource *cs)
spin_unlock_irqrestore(&watchdog_lock, flags);
}
ulong max_cswd_read_retries = 3;
ulong max_cswd_read_retries = 2;
module_param(max_cswd_read_retries, ulong, 0644);
EXPORT_SYMBOL_GPL(max_cswd_read_retries);
static int verify_n_cpus = 8;
module_param(verify_n_cpus, int, 0644);
static bool cs_watchdog_read(struct clocksource *cs, u64 *csnow, u64 *wdnow)
enum wd_read_status {
WD_READ_SUCCESS,
WD_READ_UNSTABLE,
WD_READ_SKIP
};
static enum wd_read_status cs_watchdog_read(struct clocksource *cs, u64 *csnow, u64 *wdnow)
{
unsigned int nretries;
u64 wd_end, wd_delta;
int64_t wd_delay;
u64 wd_end, wd_end2, wd_delta;
int64_t wd_delay, wd_seq_delay;
for (nretries = 0; nretries <= max_cswd_read_retries; nretries++) {
local_irq_disable();
*wdnow = watchdog->read(watchdog);
*csnow = cs->read(cs);
wd_end = watchdog->read(watchdog);
wd_end2 = watchdog->read(watchdog);
local_irq_enable();
wd_delta = clocksource_delta(wd_end, *wdnow, watchdog->mask);
@ -226,13 +233,34 @@ static bool cs_watchdog_read(struct clocksource *cs, u64 *csnow, u64 *wdnow)
pr_warn("timekeeping watchdog on CPU%d: %s retried %d times before success\n",
smp_processor_id(), watchdog->name, nretries);
}
return true;
return WD_READ_SUCCESS;
}
/*
* Now compute delay in consecutive watchdog read to see if
* there is too much external interferences that cause
* significant delay in reading both clocksource and watchdog.
*
* If consecutive WD read-back delay > WATCHDOG_MAX_SKEW/2,
* report system busy, reinit the watchdog and skip the current
* watchdog test.
*/
wd_delta = clocksource_delta(wd_end2, wd_end, watchdog->mask);
wd_seq_delay = clocksource_cyc2ns(wd_delta, watchdog->mult, watchdog->shift);
if (wd_seq_delay > WATCHDOG_MAX_SKEW/2)
goto skip_test;
}
pr_warn("timekeeping watchdog on CPU%d: %s read-back delay of %lldns, attempt %d, marking unstable\n",
smp_processor_id(), watchdog->name, wd_delay, nretries);
return false;
return WD_READ_UNSTABLE;
skip_test:
pr_info("timekeeping watchdog on CPU%d: %s wd-wd read-back delay of %lldns\n",
smp_processor_id(), watchdog->name, wd_seq_delay);
pr_info("wd-%s-wd read-back delay of %lldns, clock-skew test skipped!\n",
cs->name, wd_delay);
return WD_READ_SKIP;
}
static u64 csnow_mid;
@ -356,6 +384,7 @@ static void clocksource_watchdog(struct timer_list *unused)
int next_cpu, reset_pending;
int64_t wd_nsec, cs_nsec;
struct clocksource *cs;
enum wd_read_status read_ret;
u32 md;
spin_lock(&watchdog_lock);
@ -373,9 +402,12 @@ static void clocksource_watchdog(struct timer_list *unused)
continue;
}
if (!cs_watchdog_read(cs, &csnow, &wdnow)) {
/* Clock readout unreliable, so give it up. */
__clocksource_unstable(cs);
read_ret = cs_watchdog_read(cs, &csnow, &wdnow);
if (read_ret != WD_READ_SUCCESS) {
if (read_ret == WD_READ_UNSTABLE)
/* Clock readout unreliable, so give it up. */
__clocksource_unstable(cs);
continue;
}