From 150d224592da72d0f9d99f2789432af90ffe220d Mon Sep 17 00:00:00 2001
From: "H. Nikolaus Schaller" <hns@goldelico.com>
Date: Sat, 8 Dec 2018 22:14:54 +0100
Subject: [PATCH] bq24297: enable and retrigger WDT

the timeout can be set by a DT property "ti,watchdog".
Allowed values are 0, 40, 80, 160 (seconds).

Note that this watchdog only resets the bq24297 chip to power-on values.
This does not completely cut power after kernel inactivity (e.g. kernel
panic, omap5 stall). But it may reduce charging current limit to 500mA
which is not enough for operating the Pyra without battery.

What is not in place is
a) a mechanism to actively let the timer expire (i.e. not reset in time)
This can be simulated by writing a reset command through i2c
b) a mechanis to reinitialize the bq24297 after it did expire (WDT_RESET_FAULT)

Signed-off-by: H. Nikolaus Schaller <hns@goldelico.com>
---
 drivers/power/supply/bq2429x_charger.c | 63 +++++++++++++++++++++++++-
 1 file changed, 61 insertions(+), 2 deletions(-)

diff --git a/drivers/power/supply/bq2429x_charger.c b/drivers/power/supply/bq2429x_charger.c
index eef08e881eef9..acbb9661edc3c 100644
--- a/drivers/power/supply/bq2429x_charger.c
+++ b/drivers/power/supply/bq2429x_charger.c
@@ -20,7 +20,6 @@
 #include <linux/i2c.h>
 #include <linux/idr.h>
 #include <linux/interrupt.h>
-#include <linux/jiffies.h>
 #include <linux/module.h>
 #include <linux/of.h>
 #include <linux/of_irq.h>
@@ -199,6 +198,7 @@ struct bq2429x_device_info {
 	struct mutex var_lock;
 
 	struct delayed_work usb_detect_work;
+	struct delayed_work watchdog_work;
 	struct work_struct irq_work;
 	struct workqueue_struct	*workqueue;
 
@@ -218,6 +218,8 @@ struct bq2429x_device_info {
 	unsigned int adp_input_current_uA;
 	unsigned int battery_voltage_max_design_uV;
 	unsigned int max_VSYS_uV;
+
+	u32 wdt_timeout;
 };
 
 /* helper tables */
@@ -978,6 +980,33 @@ static int bq2429x_usb_detect(struct bq2429x_device_info *di)
 	return 0;
 }
 
+static void watchdog_work_func(struct work_struct *wp)
+{ /* reset watchdog timer every now and then */
+	struct delayed_work *dwp =
+		(struct delayed_work *)container_of(wp, struct delayed_work,
+						    work);
+	struct bq2429x_device_info *di =
+		(struct bq2429x_device_info *)container_of(dwp,
+				struct bq2429x_device_info, usb_detect_work);
+	int ret;
+
+	ret = bq2429x_field_write(di, F_WD_RESET, 1);
+	if (ret < 0) {
+		dev_err(&di->client->dev, "%s(): Failed to reset the watchdog\n",
+			__func__);
+		return;
+		}
+
+	/* we can not define a single bit as volatile so we have to tell regmap */
+	msleep(1);
+	ret = bq2429x_field_write(di, F_WD_RESET, 0);
+
+	dev_info(di->dev, "%s(%d)\n", __func__, di->wdt_timeout * 70 * HZ / 100);
+
+	/* min. WDT time span is 70% of the nominal value (see section 8.6 in data sheet */
+	schedule_delayed_work(&di->watchdog_work, di->wdt_timeout * 70 * HZ / 100);
+}
+
 static void usb_detect_work_func(struct work_struct *wp)
 { /* polling if we have no interrupt configured */
 	struct delayed_work *dwp =
@@ -1285,7 +1314,7 @@ static int bq2429x_init_registers(struct bq2429x_device_info *di)
 	if (ret < 0)
 		return ret;
 
-	ret = bq2429x_field_write(di, F_WATCHDOG, 0);
+	ret = bq2429x_field_write(di, F_WATCHDOG, 0);	/* disable watchdog */
 	if (ret < 0)
 		return ret;
 
@@ -1336,6 +1365,29 @@ static int bq2429x_init_registers(struct bq2429x_device_info *di)
 		return ret;
 	}
 
+	/* NOTE: the WDT is not for monitoring the processor and shutting down on kernel panic!
+	 * It is for actively resetting the bq2429x to power-on state
+	 *
+	 * NOTE: U-Boot has probably disabled the watchdog because it can't reset the wdt every
+	 * now and then while it is waiting for console commands which may interrupt power
+	 * in a situation without battery.
+	 */
+
+	if (di->wdt_timeout) {
+		ret = -EINVAL;
+		if (di->wdt_timeout == 40)
+			ret = bq2429x_field_write(di, F_WATCHDOG, 1);
+		else if (di->wdt_timeout == 80)
+			ret = bq2429x_field_write(di, F_WATCHDOG, 2);
+		else if (di->wdt_timeout == 160)
+			ret = bq2429x_field_write(di, F_WATCHDOG, 3);
+		if (ret < 0) {
+			dev_err(&di->client->dev, "%s(): Invalid watchdog timeout (%lu) or failed\n",
+				__func__, di->wdt_timeout == 40);
+			return ret;
+		}
+	}
+
 	return 0;
 }
 
@@ -1571,6 +1623,9 @@ static int bq2429x_parse_dt(struct bq2429x_device_info *di)
 			     &di->usb_input_current_uA);
 	of_property_read_u32(np, "ti,adp-input-current-microamp",
 			     &di->adp_input_current_uA);
+	if (of_property_read_u32(np, "ti,watchdog",
+			     &di->wdt_timeout))
+		di->wdt_timeout = 0;
 
 	/*
 	 * optional dc_det_pin
@@ -1787,6 +1842,7 @@ static int bq2429x_charger_probe(struct i2c_client *client,
 	di->workqueue = create_singlethread_workqueue("bq2429x_irq");
 	INIT_WORK(&di->irq_work, bq2729x_irq_work_func);
 	INIT_DELAYED_WORK(&di->usb_detect_work, usb_detect_work_func);
+	INIT_DELAYED_WORK(&di->watchdog_work, watchdog_work_func);
 	ret = devm_request_threaded_irq(dev, client->irq,
 				NULL, bq2729x_chg_irq_func,
 				IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
@@ -1810,6 +1866,9 @@ static int bq2429x_charger_probe(struct i2c_client *client,
 	if (!client->irq)
 		schedule_delayed_work(&di->usb_detect_work, 0);
 
+	if (di->wdt_timeout)
+		schedule_delayed_work(&di->watchdog_work, 0);
+
 	return 0;
 
 err_unreg_psy:
-- 
2.26.2

