diff mbox series

[RFC,3/4] watchdog: da9062: avoid regmap in restart handler

Message ID 20180813211614.16440-4-contact@stefanchrist.eu (mailing list archive)
State New, archived
Headers show
Series I2C writes with interrupts disabled | expand

Commit Message

Stefan Lengfeld Aug. 13, 2018, 9:16 p.m. UTC
Using i2c_transfer() to set the shutdown bit is more reliable than using
regmap.  Furthermore you have to void the default lock configuration in
the regmap configuration, e.g. by setting use_hwlock to true, to used in
atomic/IRQ disabled contexts.

Tested on a phyCORE-i.MX6 Solo board.

Signed-off-by: Stefan Lengfeld <contact@stefanchrist.eu>
---
 drivers/mfd/da9062-core.c       |  1 +
 drivers/watchdog/da9062_wdt.c   | 17 +++++++++++++----
 include/linux/mfd/da9062/core.h |  1 +
 3 files changed, 15 insertions(+), 4 deletions(-)

Comments

Tony Lindgren Aug. 20, 2018, 2:55 p.m. UTC | #1
* Stefan Lengfeld <contact@stefanchrist.eu> [180813 21:19]:
> Using i2c_transfer() to set the shutdown bit is more reliable than using
> regmap.  Furthermore you have to void the default lock configuration in
> the regmap configuration, e.g. by setting use_hwlock to true, to used in
> atomic/IRQ disabled contexts.

Hmm so this is a bit worrying, does this mean that regmap can't deal
with atomic i2c then?

Regards,

Tony
Stefan Lengfeld Aug. 21, 2018, 6:48 p.m. UTC | #2
Hi Tony,

On Mon, Aug 20, 2018 at 07:55:22AM -0700, Tony Lindgren wrote:
> * Stefan Lengfeld <contact@stefanchrist.eu> [180813 21:19]:
> > Using i2c_transfer() to set the shutdown bit is more reliable than using
> > regmap.  Furthermore you have to void the default lock configuration in
> > the regmap configuration, e.g. by setting use_hwlock to true, to used in
> > atomic/IRQ disabled contexts.
> 
> Hmm so this is a bit worrying, does this mean that regmap can't deal
> with atomic i2c then?

In theory yes. Regmap can support IRQ/sleep less operation, but you have
to ensure that the whole callstack trough the regmap interface and code
never calls sleep or waits for a lock.

First you have to use a sleep-less lock/unlock implementation in the
regmap_config e.g.

    static struct regmap_config da9062_regmap_config = {
            [...]
            /* Enable hwclock to use a sleep-free lock implementation */
            .use_hwlock = 1,

Or providing an own implementations of the regmap callbacks

    struct regmap_config {
            [...]
            regmap_lock lock;
            regmap_unlock unlock;

If have tried that and tested the code with 

     CONFIG_DEBUG_ATOMIC_SLEEP

But I have still seen a sleep violation in the regmap cache
implementation. Somewhere a cache handler was acquiring a lock or made a
memory allocation. At that point I gave up and just called the I²C
interface directly.

So I would say, regmap can support atomic I²C transfers, but you need a
lot of tweaking and on device testing. 

Kind regards,
Stefan
diff mbox series

Patch

diff --git a/drivers/mfd/da9062-core.c b/drivers/mfd/da9062-core.c
index 9f6105906c09..b6a01054ba0f 100644
--- a/drivers/mfd/da9062-core.c
+++ b/drivers/mfd/da9062-core.c
@@ -609,6 +609,7 @@  static int da9062_i2c_probe(struct i2c_client *i2c,
 
 	i2c_set_clientdata(i2c, chip);
 	chip->dev = &i2c->dev;
+	chip->i2c = i2c;
 
 	if (!i2c->irq) {
 		dev_err(chip->dev, "No IRQ configured\n");
diff --git a/drivers/watchdog/da9062_wdt.c b/drivers/watchdog/da9062_wdt.c
index fe169d8e1fb2..ad6483d25f83 100644
--- a/drivers/watchdog/da9062_wdt.c
+++ b/drivers/watchdog/da9062_wdt.c
@@ -11,6 +11,7 @@ 
 #include <linux/platform_device.h>
 #include <linux/uaccess.h>
 #include <linux/slab.h>
+#include <linux/i2c.h>
 #include <linux/delay.h>
 #include <linux/jiffies.h>
 #include <linux/mfd/da9062/registers.h>
@@ -152,12 +153,20 @@  static int da9062_wdt_restart(struct watchdog_device *wdd, unsigned long action,
 			      void *data)
 {
 	struct da9062_watchdog *wdt = watchdog_get_drvdata(wdd);
+	struct i2c_client *client = wdt->hw->i2c;
+	__u8 buf[3] = {DA9062AA_CONTROL_F, DA9062AA_SHUTDOWN_MASK, 0x0};
+	struct i2c_msg msgs[1] = {
+		{
+			.addr = client->addr,
+			.flags = (client->flags & I2C_M_TEN),
+			.len = sizeof(buf),
+			.buf = buf,
+		}
+	};
 	int ret;
 
-	ret = regmap_write(wdt->hw->regmap,
-			   DA9062AA_CONTROL_F,
-			   DA9062AA_SHUTDOWN_MASK);
-	if (ret)
+	ret = i2c_transfer(client->adapter, msgs, sizeof(msgs));
+	if (ret < 0)
 		dev_alert(wdt->hw->dev, "Failed to shutdown (err = %d)\n",
 			  ret);
 
diff --git a/include/linux/mfd/da9062/core.h b/include/linux/mfd/da9062/core.h
index 74d33a01ddae..c994293b3aef 100644
--- a/include/linux/mfd/da9062/core.h
+++ b/include/linux/mfd/da9062/core.h
@@ -68,6 +68,7 @@  enum da9062_irqs {
 struct da9062 {
 	struct device *dev;
 	struct regmap *regmap;
+	struct i2c_client *i2c;
 	struct regmap_irq_chip_data *regmap_irq;
 	enum da9062_compatible_types chip_type;
 };