lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [<thread-prev] [day] [month] [year] [list]
Message-ID: <06fbcdd3-4368-44e0-9a31-6cf83fa625d5@roeck-us.net>
Date: Wed, 19 Nov 2025 07:37:23 -0800
From: Guenter Roeck <linux@...ck-us.net>
To: Lakshay Piplani <lakshay.piplani@....com>, alexandre.belloni@...tlin.com,
 linux-rtc@...r.kernel.org, linux-kernel@...r.kernel.org, robh@...nel.org,
 krzk+dt@...nel.org, conor+dt@...nel.org, devicetree@...r.kernel.org,
 wim@...ux-watchdog.org, linux-watchdog@...r.kernel.org
Cc: vikash.bansal@....com, priyanka.jain@....com,
 shashank.rebbapragada@....com
Subject: Re: [PATCH v3 5/5] rtc: pcf85363: add watchdog support with
 configurable step size

On 11/19/25 00:33, Lakshay Piplani wrote:
> Add watchdog timer support to PCF85263/PCF85363 using the linux watchdog
> subsystem. The driver programs the hardware watchdog timeout based on
> the requested period.
> 
> Also use rtc_add_group() instead of sysfs_create_group() to register
> timestamp attributes under the RTC class device (/sys/class/rtc/rtcX).
> 
> Signed-off-by: Lakshay Piplani <lakshay.piplani@....com>
> ---
> V2 -> V3:
> - Split into separate patches as suggested:
>    - Battery switch-over detection.
>    - Timestamp recording for TS pin and battery switch-over events.
>    - Offset calibration.
>    - Watchdog timer (to be reviewed by watchdog maintainers).
> - Dropped Alarm2 support
> - Switched to rtc_add_group() for sysfs attributes
> - Removed failure paths after RTC device registration as per subsystem guidelines.
> V1 -> V2:
> - Watchdog related changes due to removal of vendor specific properties
>    from device tree
>    * remove vendor DT knobs (enable/timeout/stepsize/repeat)
>    * use watchdog_init_timeout (with 10s default)
>    * derive clock_sel from final timeout
>    * default, repeat=true (repeat mode)
> - Fixed uninitalised warning on 'ret' (reported by kernel test robot)
> - Use dev_dbg instead of dev_info for debug related print messages
> - Minor cleanup and comments
> 
>   drivers/rtc/rtc-pcf85363.c | 168 +++++++++++++++++++++++++++++++++++--
>   1 file changed, 160 insertions(+), 8 deletions(-)
> 
> diff --git a/drivers/rtc/rtc-pcf85363.c b/drivers/rtc/rtc-pcf85363.c
> index 3d733375187b..34d4c2e16774 100644
> --- a/drivers/rtc/rtc-pcf85363.c
> +++ b/drivers/rtc/rtc-pcf85363.c
> @@ -5,6 +5,10 @@
>    * Driver for NXP PCF85363 real-time clock.
>    *
>    * Copyright (C) 2017 Eric Nelson
> + *
> + * Copyright 2025 NXP
> + * Added support for timestamps, battery switch-over,
> + * watchdog, offset calibration.
>    */
>   #include <linux/module.h>
>   #include <linux/i2c.h>
> @@ -17,6 +21,8 @@
>   #include <linux/device.h>
>   #include <linux/of.h>
>   #include <linux/regmap.h>
> +#include <linux/rtc.h>
> +#include <linux/watchdog.h>
>   
>   /*
>    * Date/Time registers
> @@ -127,6 +133,18 @@
>   #define OFFSET_MAXIMUM  127
>   #define OFFSET_MASK     0xFF
>   
> +#define WD_MODE_REPEAT  BIT(7)
> +#define WD_TIMEOUT_MASK GENMASK(6, 2)
> +#define WD_TIMEOUT_SHIFT        2
> +#define WD_CLKSEL_MASK  GENMASK(1, 0)
> +#define WD_CLKSEL_0_25HZ        0x00
> +#define WD_CLKSEL_1HZ   0x01
> +#define WD_CLKSEL_4HZ   0x02
> +#define WD_CLKSEL_16HZ  0x03
> +
> +#define WD_TIMEOUT_MIN  1
> +#define WD_TIMEOUT_MAX  0x1F
> +
>   struct pcf85363 {
>   	struct rtc_device	*rtc;
>   	struct regmap		*regmap;
> @@ -138,6 +156,15 @@ struct pcf85x63_config {
>   	unsigned int num_nvram;
>   };
>   
> +struct pcf85363_watchdog {
> +	struct watchdog_device wdd;
> +	struct regmap *regmap;
> +	struct device *dev;
> +	u8 timeout_val;
> +	u8 clock_sel;
> +	bool repeat;
> +};
> +
>   static int pcf85363_load_capacitance(struct pcf85363 *pcf85363, struct device_node *node)
>   {
>   	u32 load = 7000;
> @@ -323,12 +350,13 @@ static irqreturn_t pcf85363_rtc_handle_irq(int irq, void *dev_id)
>   		return IRQ_NONE;
>   
>   	if (flags) {
> -		dev_dbg(&pcf85363->rtc->dev, "IRQ flags: 0x%02x%s%s%s%s%s\n",
> +		dev_dbg(&pcf85363->rtc->dev, "IRQ flags: 0x%02x%s%s%s%s%s%s\n",
>   			flags, (flags & FLAGS_A1F) ? " [A1F]" : "",
>   			(flags & FLAGS_TSR1F) ? " [TSR1F]" : "",
>   			(flags & FLAGS_TSR2F) ? " [TSR2F]" : "",
>   			(flags & FLAGS_TSR3F) ? " [TSR3F]" : "",
> -			(flags & FLAGS_BSF) ? " [BSF]" : "");
> +			(flags & FLAGS_BSF) ? " [BSF]" : "",
> +			(flags & FLAGS_WDF) ? " [WDF]" : "");
>   	}
>   
>   	if (flags & FLAGS_A1F) {
> @@ -360,6 +388,11 @@ static irqreturn_t pcf85363_rtc_handle_irq(int irq, void *dev_id)
>   		handled = true;
>   	}
>   
> +	if (flags & FLAGS_WDF) {
> +		regmap_update_bits(pcf85363->regmap, CTRL_FLAGS, FLAGS_WDF, 0);
> +		handled = true;
> +	}
> +
>   	return handled ? IRQ_HANDLED : IRQ_NONE;
>   }
>   
> @@ -503,6 +536,123 @@ static const struct pcf85x63_config pcf_85363_config = {
>   	.num_nvram = 2
>   };
>   
> +/*
> + * This function sets the watchdog control register based on the timeout,
> + * clock selection and repeat mode settings. It prepares the value to
> + * write into the watchdog control register (CTRL_WDOG).
> + */
> +static int pcf85363_wdt_reload(struct pcf85363_watchdog *wd)
> +{
> +	u8 val;
> +
> +	val = (wd->repeat ? WD_MODE_REPEAT : 0) |
> +	       ((wd->timeout_val & WD_TIMEOUT_MAX) << WD_TIMEOUT_SHIFT) |
> +	       (wd->clock_sel & WD_CLKSEL_MASK);
> +
> +	return regmap_write(wd->regmap, CTRL_WDOG, val);
> +}
> +
> +static int pcf85363_wdt_start(struct watchdog_device *wdd)
> +{
> +	struct pcf85363_watchdog *wd = watchdog_get_drvdata(wdd);
> +
> +	return pcf85363_wdt_reload(wd);
> +}
> +
> +static int pcf85363_wdt_stop(struct watchdog_device *wdd)
> +{
> +	struct pcf85363_watchdog *wd = watchdog_get_drvdata(wdd);
> +
> +	return regmap_write(wd->regmap, CTRL_WDOG, 0);
> +}
> +
> +static int pcf85363_wdt_ping(struct watchdog_device *wdd)
> +{
> +	struct pcf85363_watchdog *wd = watchdog_get_drvdata(wdd);
> +
> +	regmap_update_bits(wd->regmap, CTRL_FLAGS, FLAGS_WDF, 0);
> +
> +	return pcf85363_wdt_reload(wd);
> +}
> +
> +static int pcf85363_wdt_set_timeout(struct watchdog_device *wdd,
> +				    unsigned int timeout)
> +{
> +	struct pcf85363_watchdog *wd = watchdog_get_drvdata(wdd);
> +
> +	wd->timeout_val = clamp(timeout, WD_TIMEOUT_MIN, WD_TIMEOUT_MAX);
> +	wdd->timeout = wd->timeout_val;
> +
> +	return pcf85363_wdt_reload(wd);
> +}
> +
> +static const struct watchdog_info pcf85363_wdt_info = {
> +	.identity = "PCF85363 Watchdog",
> +	.options = WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT,
> +};
> +
> +static const struct watchdog_ops pcf85363_wdt_ops = {
> +	.owner = THIS_MODULE,
> +	.start = pcf85363_wdt_start,
> +	.stop = pcf85363_wdt_stop,
> +	.ping = pcf85363_wdt_ping,
> +	.set_timeout = pcf85363_wdt_set_timeout,
> +};
> +
> +static int pcf85363_watchdog_init(struct device *dev, struct regmap *regmap)
> +{
> +	struct pcf85363_watchdog *wd;
> +	unsigned int timeout_sec;
> +	int ret;
> +
> +	if (!IS_ENABLED(CONFIG_WATCHDOG))
> +		return 0;
> +
> +	wd = devm_kzalloc(dev, sizeof(*wd), GFP_KERNEL);
> +	if (!wd)
> +		return -ENOMEM;
> +
> +	wd->regmap = regmap;
> +	wd->dev = dev;
> +
> +	wd->wdd.info = &pcf85363_wdt_info;
> +	wd->wdd.ops = &pcf85363_wdt_ops;
> +	wd->wdd.min_timeout = WD_TIMEOUT_MIN;
> +	wd->wdd.max_timeout = WD_TIMEOUT_MAX;
> +	wd->wdd.parent = dev;
> +	wd->wdd.status = WATCHDOG_NOWAYOUT_INIT_STATUS;
> +
> +	ret = watchdog_init_timeout(&wd->wdd, 10, dev);

Calling watchdog_init_timeout() with a value other than 0 means that
a parameter from devicetree won't be accepted. Calling it with a fixed
value is usually pointless unless the value is out of the valid range,
which by itself would be pointless.

watchdog_init_timeout() is normally called to pass and validate a module
parameter or to pick a timeout from devicetree. Calling it with a constant
value other than 0 is unnecessary.

> +	if (ret)
> +		wd->wdd.timeout = clamp(10U, WD_TIMEOUT_MIN, WD_TIMEOUT_MAX);

So if 10 seconds is invalid, 10 is clamped to [1, 31] and applied directly.
That is an odd and complicated way of setting the timeout to 10 seconds.

If you don't want a timeout value from devicetree to be accepted, just make this

	wd->wdd.timeout = 10;

and do not call watchdog_init_timeout() in the first place.

> +
> +	timeout_sec = wd->wdd.timeout;
> +
> +	if (timeout_sec <= 2)
> +		wd->clock_sel = WD_CLKSEL_16HZ;
> +	else if (timeout_sec <= 8)
> +		wd->clock_sel = WD_CLKSEL_4HZ;
> +	else if (timeout_sec <= 16)
> +		wd->clock_sel = WD_CLKSEL_1HZ;
> +	else
> +		wd->clock_sel = WD_CLKSEL_0_25HZ;
> +

This seems an odd location for this code. What if the timeout changes
later on to one of the other values ?

Also, the timeout is set to a fixed value of 10. That means the above
can be simplified to
	wd->clock_sel = WD_CLKSEL_1HZ;
... and that in turn means that the variable is pointless, and that
WD_CLKSEL_1HZ could be used as constant instead.

Why all that complexity ? Am I missing something ? I am quite concerned that
I may be missing trees in the forest, meaning that the real problems are hiding
behind the noise.

Guenter

> +	wd->repeat = true;

What is the purpose of this variable ? It is always set to true.
You might as well drop it.

> +
> +	ret = regmap_update_bits(regmap, CTRL_FLAGS, FLAGS_WDF, 0);
> +	if (ret) {
> +		dev_err(dev, "failed to clear WDF:%d\n", ret);
> +		return ret;
> +	}
> +
> +	watchdog_set_drvdata(&wd->wdd, wd);
> +
> +	dev_dbg(dev, "pcf85363 watchdog registered (timeout=%us, clk_sel=%u)\n",
> +		timeout_sec, wd->clock_sel);
> +
> +	return devm_watchdog_register_device(dev, &wd->wdd);
> +}
> +
>   /*
>    * Reads 6 bytes of timestamp data starting at the given base register,
>    * converts them from BCD to binary, and formats the result into a
> @@ -684,20 +834,22 @@ static int pcf85363_probe(struct i2c_client *client)
>   			   PIN_IO_TSPM | PIN_IO_TSIM,
>   			   PIN_IO_TSPM | PIN_IO_TSIM);
>   
> +	ret = pcf85363_watchdog_init(dev, pcf85363->regmap);
> +	if (ret)
> +		dev_err_probe(dev, ret, "Watchdog init failed\n");
> +
>   	if (irq_a > 0 || wakeup_source)
>   		device_init_wakeup(dev, true);
>   
>   	dev_set_drvdata(&pcf85363->rtc->dev, pcf85363);
>   
> -	ret = devm_rtc_register_device(pcf85363->rtc);
> -
> +	ret = rtc_add_group(pcf85363->rtc, &pcf85363_attr_group);
>   	if (ret)
> -		return dev_err_probe(dev, ret, "RTC registration failed\n");
> -
> -	ret = sysfs_create_group(&pcf85363->rtc->dev.kobj, &pcf85363_attr_group);
> +		return ret;
>   
> +	ret = devm_rtc_register_device(pcf85363->rtc);
>   	if (ret)
> -		return dev_err_probe(dev, ret, "Timestamp sysfs creation failed\n");
> +		return dev_err_probe(dev, ret, "RTC registration failed\n");
>   
It is not entirely obvious how those changes are related to adding watchdog support
to this driver.

>   	for (i = 0; i < config->num_nvram; i++) {
>   		nvmem_cfg[i].priv = pcf85363;


Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ