All of lore.kernel.org
 help / color / mirror / Atom feed
From: Guenter Roeck <linux@roeck-us.net>
To: Jeremy Gebben <jgebben@sweptlaser.com>
Cc: Alessandro Zummo <a.zummo@towertech.it>,
	Alexandre Belloni <alexandre.belloni@bootlin.com>,
	Wim Van Sebroeck <wim@linux-watchdog.org>,
	linux-rtc@vger.kernel.org, linux-watchdog@vger.kernel.org,
	linux-kernel@vger.kernel.org
Subject: Re: [PATCH 2/2] rtc: abx80x: add basic watchdog support
Date: Mon, 10 Sep 2018 16:20:19 -0700	[thread overview]
Message-ID: <20180910232019.GA8439@roeck-us.net> (raw)
In-Reply-To: <20180910213654.26059-3-jgebben@sweptlaser.com>

On Mon, Sep 10, 2018 at 03:36:54PM -0600, Jeremy Gebben wrote:
> The abx804 and abx805 chips have support for a simple watchdog
> function that can trigger an external reset.
> 
> Signed-off-by: Jeremy Gebben <jgebben@sweptlaser.com>

I am missing the "select WATCHDOG_CORE" that would normally go
with watchdog drivers. If you don't want a separate configuration
option for the watchdog, you'll still need something like

	select WATCHDOG_CORE if WATCHDOG

in the Kconfig entry for this driver.

> ---
>  drivers/rtc/rtc-abx80x.c | 132 +++++++++++++++++++++++++++++++++++++--
>  1 file changed, 128 insertions(+), 4 deletions(-)
> 
> diff --git a/drivers/rtc/rtc-abx80x.c b/drivers/rtc/rtc-abx80x.c
> index 9d49054a0a4a..e1ec4cce093a 100644
> --- a/drivers/rtc/rtc-abx80x.c
> +++ b/drivers/rtc/rtc-abx80x.c
> @@ -17,6 +17,7 @@
>  #include <linux/i2c.h>
>  #include <linux/module.h>
>  #include <linux/rtc.h>
> +#include <linux/watchdog.h>
>  
>  #define ABX8XX_REG_HTH		0x00
>  #define ABX8XX_REG_SC		0x01
> @@ -37,6 +38,7 @@
>  
>  #define ABX8XX_REG_STATUS	0x0f
>  #define ABX8XX_STATUS_AF	BIT(2)
> +#define ABX8XX_STATUS_WDT	BIT(6)
>  
>  #define ABX8XX_REG_CTRL1	0x10
>  #define ABX8XX_CTRL_WRITE	BIT(0)
> @@ -61,6 +63,14 @@
>  #define ABX8XX_OSS_OF		BIT(1)
>  #define ABX8XX_OSS_OMODE	BIT(4)
>  
> +#define ABX8XX_REG_WDT		0x1b
> +#define ABX8XX_WDT_WDS		BIT(7)
> +#define ABX8XX_WDT_BMB_MASK	0x7c
> +#define ABX8XX_WDT_BMB_SHIFT	2
> +#define ABX8XX_WDT_MAX_TIME	(ABX8XX_WDT_BMB_MASK >> ABX8XX_WDT_BMB_SHIFT)
> +#define ABX8XX_WDT_WRB_MASK	0x03
> +#define ABX8XX_WDT_WRB_1HZ	0x02
> +
>  #define ABX8XX_REG_CFG_KEY	0x1f
>  #define ABX8XX_CFG_KEY_OSC	0xa1
>  #define ABX8XX_CFG_KEY_MISC	0x9d
> @@ -80,23 +90,25 @@ enum abx80x_chip {AB0801, AB0803, AB0804, AB0805,
>  struct abx80x_cap {
>  	u16 pn;
>  	bool has_tc;
> +	bool has_wdog;
>  };
>  
>  static struct abx80x_cap abx80x_caps[] = {
>  	[AB0801] = {.pn = 0x0801},
>  	[AB0803] = {.pn = 0x0803},
> -	[AB0804] = {.pn = 0x0804, .has_tc = true},
> -	[AB0805] = {.pn = 0x0805, .has_tc = true},
> +	[AB0804] = {.pn = 0x0804, .has_tc = true, .has_wdog = true},
> +	[AB0805] = {.pn = 0x0805, .has_tc = true, .has_wdog = true},
>  	[AB1801] = {.pn = 0x1801},
>  	[AB1803] = {.pn = 0x1803},
> -	[AB1804] = {.pn = 0x1804, .has_tc = true},
> -	[AB1805] = {.pn = 0x1805, .has_tc = true},
> +	[AB1804] = {.pn = 0x1804, .has_tc = true, .has_wdog = true},
> +	[AB1805] = {.pn = 0x1805, .has_tc = true, .has_wdog = true},
>  	[ABX80X] = {.pn = 0}
>  };
>  
>  struct abx80x_priv {
>  	struct rtc_device *rtc;
>  	struct i2c_client *client;
> +	struct watchdog_device wdog;
>  };
>  
>  static int abx80x_is_rc_mode(struct i2c_client *client)
> @@ -234,6 +246,13 @@ static irqreturn_t abx80x_handle_irq(int irq, void *dev_id)
>  	if (status & ABX8XX_STATUS_AF)
>  		rtc_update_irq(rtc, 1, RTC_AF | RTC_IRQF);
>  
> +	/*
> +	 * It is unclear if we'll get an interrupt before the external
> +	 * reset kicks in.
> +	 */
> +	if (status & ABX8XX_STATUS_WDT)
> +		dev_alert(&client->dev, "watchdog timeout interrupt.\n");
> +
>  	i2c_smbus_write_byte_data(client, ABX8XX_REG_STATUS, 0);
>  
>  	return IRQ_HANDLED;
> @@ -535,6 +554,105 @@ static void rtc_calib_remove_sysfs_group(void *_dev)
>  	sysfs_remove_group(&dev->kobj, &rtc_calib_attr_group);
>  }
>  
> +#ifdef CONFIG_WATCHDOG
> +
> +static inline u8 timeout_bits(unsigned int timeout)
> +{
> +	return ((timeout << ABX8XX_WDT_BMB_SHIFT) & ABX8XX_WDT_BMB_MASK) |
> +		 ABX8XX_WDT_WRB_1HZ;
> +}
> +
> +static int __abx80x_wdog_set_timeout(struct watchdog_device *wdog,
> +				    unsigned int timeout)
> +{
> +	struct abx80x_priv *priv = watchdog_get_drvdata(wdog);
> +	u8 val = ABX8XX_WDT_WDS | timeout_bits(timeout);
> +
> +	/*
> +	 * Writing any timeout to the WDT register resets the watchdog timer.
> +	 * Writing 0 disables it.
> +	 */
> +	return i2c_smbus_write_byte_data(priv->client, ABX8XX_REG_WDT, val);
> +}
> +
> +static int abx80x_wdog_set_timeout(struct watchdog_device *wdog,
> +				unsigned int new_timeout)

I'll leave multi-line alignment issues to the driver maintainer.

> +{
> +	int err = __abx80x_wdog_set_timeout(wdog, new_timeout);
> +

This will unconditionally enable the watchdog, even if it is currently
disabled.

> +	if (err)
> +		return err;
> +
> +	wdog->timeout = new_timeout;
> +	return 0;
> +}
> +
> +static int abx80x_wdog_ping(struct watchdog_device *wdog)
> +{
> +	return __abx80x_wdog_set_timeout(wdog, wdog->timeout);
> +}
> +
> +static int abx80x_wdog_start(struct watchdog_device *wdog)
> +{
> +	int err = __abx80x_wdog_set_timeout(wdog, wdog->timeout);
> +
> +	if (err)
> +		return err;
> +
> +	set_bit(WDOG_HW_RUNNING, &wdog->status);

This is only necessary if the watchdog is running at probe,
or if it can not be stopped.

> +	return 0;
> +}
> +
> +static int abx80x_wdog_stop(struct watchdog_device *wdog)
> +{
> +	int err = __abx80x_wdog_set_timeout(wdog, 0);
> +
> +	if (err)
> +		return err;
> +
> +	clear_bit(WDOG_HW_RUNNING, &wdog->status);

This suggests that the watchdog can be stopped, thus it is
unnecessary to set or clear the bit.

> +	return 0;
> +}
> +
> +static const struct watchdog_info abx80x_wdog_info = {
> +	.identity = "abx80x watchdog",
> +	.options = WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT | WDIOF_MAGICCLOSE,
> +};
> +
> +static const struct watchdog_ops abx80x_wdog_ops = {
> +	.owner = THIS_MODULE,
> +	.start = abx80x_wdog_start,
> +	.stop = abx80x_wdog_stop,
> +	.ping = abx80x_wdog_ping,
> +	.set_timeout = abx80x_wdog_set_timeout,
> +};
> +
> +static int abx80x_setup_watchdog(struct abx80x_priv *priv)
> +{
> +	int err;
> +
> +	priv->wdog.ops = &abx80x_wdog_ops;
> +	priv->wdog.info = &abx80x_wdog_info;
> +	priv->wdog.min_timeout = 1;
> +	priv->wdog.max_timeout = ABX8XX_WDT_MAX_TIME;

priv->wdog.parent should be set.

> +
> +	err = devm_watchdog_register_device(&priv->client->dev,
> +					    &priv->wdog);
> +	if (err)
> +		return err;
> +
> +	watchdog_set_drvdata(&priv->wdog, priv);

This is too late and racy; it must be called prior to registration.

> +	watchdog_init_timeout(&priv->wdog, ABX8XX_WDT_MAX_TIME,
> +			      &priv->client->dev);

This is quite pointless. Just set
	priv->wdog.timeout = ABX8XX_WDT_MAX_TIME;
above (prior to registration). If the idea is to be able to pick
a timeout value from devicetree, you would still set the default
value above and make the call
	watchdog_init_timeout(&priv->wdog, 0, &priv->client->dev);
but again prior to registration.

> +	return 0;
> +}
> +#else
> +static int abx80x_setup_watchdog(struct abx80x_priv *priv)
> +{
> +	return 0;
> +}
> +#endif
> +
>  static int abx80x_probe(struct i2c_client *client,
>  			const struct i2c_device_id *id)
>  {
> @@ -629,6 +747,12 @@ static int abx80x_probe(struct i2c_client *client,
>  
>  	i2c_set_clientdata(client, priv);
>  
> +	if (abx80x_caps[part].has_wdog) {
> +		err = abx80x_setup_watchdog(priv);
> +		if (err)
> +			return err;
> +	}
> +
>  	if (client->irq > 0) {
>  		dev_info(&client->dev, "IRQ %d supplied\n", client->irq);
>  		err = devm_request_threaded_irq(&client->dev, client->irq, NULL,
> -- 
> 2.17.1
> 

  reply	other threads:[~2018-09-11  4:16 UTC|newest]

Thread overview: 7+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2018-09-10 21:36 [PATCH v2 0/2] rtc: abx80x: add basic watchdog support Jeremy Gebben
2018-09-10 21:36 ` [PATCH 1/2] rtc: abx80x: use a 'priv' struct for client data Jeremy Gebben
2018-09-10 21:36 ` [PATCH 2/2] rtc: abx80x: add basic watchdog support Jeremy Gebben
2018-09-10 23:20   ` Guenter Roeck [this message]
2018-09-11  7:39     ` Alexandre Belloni
  -- strict thread matches above, loose matches on Subject: below --
2018-09-11 17:28 [PATCH v3 0/2] " Jeremy Gebben
2018-09-11 17:28 ` [PATCH 2/2] " Jeremy Gebben
2018-09-11 21:49   ` Guenter Roeck

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20180910232019.GA8439@roeck-us.net \
    --to=linux@roeck-us.net \
    --cc=a.zummo@towertech.it \
    --cc=alexandre.belloni@bootlin.com \
    --cc=jgebben@sweptlaser.com \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-rtc@vger.kernel.org \
    --cc=linux-watchdog@vger.kernel.org \
    --cc=wim@linux-watchdog.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.