From: Jeremy Gebben <jgebben@sweptlaser.com>
To: Alexandre Belloni <alexandre.belloni@bootlin.com>
Cc: Alessandro Zummo <a.zummo@towertech.it>,
linux-rtc@vger.kernel.org, linux-kernel@vger.kernel.org
Subject: Re: [PATCH] rtc: abx80x: add basic watchdog support
Date: Mon, 10 Sep 2018 10:39:21 -0600 [thread overview]
Message-ID: <CAB48C-pVeDLGSGK6reWgrrqkeJhQv-R75E2Gh=bA6itOchQtbA@mail.gmail.com> (raw)
In-Reply-To: <20180908150957.GD2598@piout.net>
[-- Attachment #1: Type: text/plain, Size: 10418 bytes --]
On Sat, Sep 8, 2018 at 9:10 AM Alexandre Belloni <
alexandre.belloni@bootlin.com> wrote:
> Hi,
>
> Please Cc the watchdog maintainers. Else, I have very few comments.
>
Sure, I'll add them on the next version.
> On 07/09/2018 09:17:43-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>
> > ---
> > drivers/rtc/Kconfig | 11 +++
> > drivers/rtc/rtc-abx80x.c | 160 ++++++++++++++++++++++++++++++++++++---
> > 2 files changed, 159 insertions(+), 12 deletions(-)
> >
> > diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig
> > index 7d7be60a2413..4771e3a89721 100644
> > --- a/drivers/rtc/Kconfig
> > +++ b/drivers/rtc/Kconfig
> > @@ -195,6 +195,17 @@ config RTC_DRV_ABX80X
> > This driver can also be built as a module. If so, the module
> > will be called rtc-abx80x.
> >
> > +config RTC_DRV_ABX80X_WDT
> > + bool "Abracon ABx80x Watchdog"
> > + default y
> > + depends on RTC_DRV_ABX80X
> > + help
> > + If you say yes here you get watchdog support for Abracon ABX804
> and ABX805
> > + clock chips. This watchdog supports timeouts up to 31 seconds
> with 1 second
> > + granularity.
> > +
> > + This watchdog device is part of the rtc-abx80x module.
> > +
>
> I'm not sure it is worth having a separate config option for that. You
> may as well just always include the watchdog support code. However,
> doesn't it need to depend on CONFIG_WATCHDOG?
>
Yes, you're right about CONFIG_WATCHDOG, I thought I had that already but I
guess it got dropped.
> > config RTC_DRV_AC100
> > tristate "X-Powers AC100"
> > depends on MFD_AC100
> > diff --git a/drivers/rtc/rtc-abx80x.c b/drivers/rtc/rtc-abx80x.c
> > index 2cefa67a1132..fa558939e944 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,20 +90,27 @@ 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;
> > +};
> > +
>
> To make the watchdog part easier to review, you could separate the
> introduction of abx80x_priv in a preliminary patch and then introduce
> the watchdog code.
>
Ok.
> static int abx80x_is_rc_mode(struct i2c_client *client)
> > {
> > int flags = 0;
> > @@ -218,7 +235,8 @@ static int abx80x_rtc_set_time(struct device *dev,
> struct rtc_time *tm)
> > static irqreturn_t abx80x_handle_irq(int irq, void *dev_id)
> > {
> > struct i2c_client *client = dev_id;
> > - struct rtc_device *rtc = i2c_get_clientdata(client);
> > + struct abx80x_priv *priv = i2c_get_clientdata(client);
> > + struct rtc_device *rtc = priv->rtc;
> > int status;
> >
> > status = i2c_smbus_read_byte_data(client, ABX8XX_REG_STATUS);
> > @@ -228,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_err(&client->dev, "watchdog timeout interrupt!\n");
> > +
>
> dev_alert maybe?
>
Ok.
Thanks,
Jeremy
>
> > i2c_smbus_write_byte_data(client, ABX8XX_REG_STATUS, 0);
> >
> > return IRQ_HANDLED;
> > @@ -529,11 +554,110 @@ static void rtc_calib_remove_sysfs_group(void
> *_dev)
> > sysfs_remove_group(&dev->kobj, &rtc_calib_attr_group);
> > }
> >
> > +#ifdef CONFIG_RTC_DRV_ABX80X_WDT
> > +
> > +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)
> > +{
> > + int err = __abx80x_wdog_set_timeout(wdog, new_timeout);
> > +
> > + 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);
> > + 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);
> > + 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;
> > +
> > + err = devm_watchdog_register_device(&priv->client->dev,
> > + &priv->wdog);
> > + if (err)
> > + return err;
> > +
> > + watchdog_set_drvdata(&priv->wdog, priv);
> > + watchdog_init_timeout(&priv->wdog, ABX8XX_WDT_MAX_TIME,
> > + &priv->client->dev);
> > + 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)
> > {
> > struct device_node *np = client->dev.of_node;
> > - struct rtc_device *rtc;
> > + struct abx80x_priv *priv;
> > int i, data, err, trickle_cfg = -EINVAL;
> > char buf[7];
> > unsigned int part = id->driver_data;
> > @@ -610,13 +734,25 @@ static int abx80x_probe(struct i2c_client *client,
> > if (err)
> > return err;
> >
> > - rtc = devm_rtc_allocate_device(&client->dev);
> > - if (IS_ERR(rtc))
> > - return PTR_ERR(rtc);
> > + priv = devm_kzalloc(&client->dev, sizeof(struct abx80x_priv),
> > + GFP_KERNEL);
> > + if (priv == NULL)
> > + return -ENOMEM;
> > +
> > + priv->rtc = devm_rtc_allocate_device(&client->dev);
> > + if (IS_ERR(priv->rtc))
> > + return PTR_ERR(priv->rtc);
> >
> > - rtc->ops = &abx80x_rtc_ops;
> > + priv->rtc->ops = &abx80x_rtc_ops;
> > + priv->client = client;
> >
> > - i2c_set_clientdata(client, rtc);
> > + 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);
> > @@ -649,7 +785,7 @@ static int abx80x_probe(struct i2c_client *client,
> > return err;
> > }
> >
> > - err = rtc_register_device(rtc);
> > + err = rtc_register_device(priv->rtc);
> >
> > return err;
> > }
> > --
> > 2.17.1
> >
>
> --
> Alexandre Belloni, Bootlin
> Embedded Linux and Kernel engineering
> https://bootlin.com
>
[-- Attachment #2: Type: text/html, Size: 14309 bytes --]
next prev parent reply other threads:[~2018-09-10 16:39 UTC|newest]
Thread overview: 4+ messages / expand[flat|nested] mbox.gz Atom feed top
2018-09-07 15:17 [PATCH] rtc: abx80x: add basic watchdog support Jeremy Gebben
2018-09-08 15:09 ` Alexandre Belloni
2018-09-10 16:39 ` Jeremy Gebben [this message]
2018-09-10 11:30 ` kbuild test robot
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='CAB48C-pVeDLGSGK6reWgrrqkeJhQv-R75E2Gh=bA6itOchQtbA@mail.gmail.com' \
--to=jgebben@sweptlaser.com \
--cc=a.zummo@towertech.it \
--cc=alexandre.belloni@bootlin.com \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-rtc@vger.kernel.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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).