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: <20251231093152.2817579-2-Qing-wu.Li@leica-geosystems.com.cn>
Date: Wed, 31 Dec 2025 09:31:52 +0000
From: LI Qingwu <Qing-wu.Li@...ca-geosystems.com.cn>
To: sre@...nel.org,
	linux-pm@...r.kernel.org,
	linux-kernel@...r.kernel.org
Cc: bsp-development.geo@...ca-geosystems.com,
	LI Qingwu <Qing-wu.Li@...ca-geosystems.com.cn>
Subject: [PATCH 2/2] power: supply: sbs-battery: Add support for polling battery status

Enable periodic polling of SBS battery status on systems where the
battery interrupt line is not connected. Polling is configured via
the poll_interval parameter (ms, default 0/disabled) and automatically
disabled when a working GPIO interrupt is available.

Example usage:
  echo 5000 > /sys/module/sbs_battery/parameters/poll_interval

Tested on i.MX 8M Plus platform with SBS-compliant battery.

Signed-off-by: LI Qingwu <Qing-wu.Li@...ca-geosystems.com.cn>
---
 drivers/power/supply/sbs-battery.c | 84 +++++++++++++++++++++++++++++-
 1 file changed, 83 insertions(+), 1 deletion(-)

diff --git a/drivers/power/supply/sbs-battery.c b/drivers/power/supply/sbs-battery.c
index 0b9ecfc1f3f7..f4f1189fec3c 100644
--- a/drivers/power/supply/sbs-battery.c
+++ b/drivers/power/supply/sbs-battery.c
@@ -14,6 +14,7 @@
 #include <linux/init.h>
 #include <linux/interrupt.h>
 #include <linux/kernel.h>
+#include <linux/list.h>
 #include <linux/module.h>
 #include <linux/property.h>
 #include <linux/of.h>
@@ -214,8 +215,10 @@ struct sbs_info {
 	u32				poll_retry_count;
 	struct delayed_work		work;
 	struct mutex			mode_lock;
+	u32				poll_interval;
 	u32				flags;
 	int				technology;
+	struct list_head		list;
 	char				strings[NR_STRING_BUFFERS][I2C_SMBUS_BLOCK_MAX + 1];
 };
 
@@ -242,6 +245,52 @@ static void sbs_invalidate_cached_props(struct sbs_info *chip)
 }
 
 static bool force_load;
+static DEFINE_MUTEX(sbs_list_lock);
+static LIST_HEAD(sbs_battery_devices);
+static unsigned int poll_interval;
+
+static int poll_interval_param_set(const char *val,
+				   const struct kernel_param *kp)
+{
+	struct sbs_info *chip;
+	unsigned int prev_val = *(unsigned int *)kp->arg;
+	int ret;
+
+	ret = param_set_uint(val, kp);
+	if (ret < 0 || prev_val == *(unsigned int *)kp->arg)
+		return ret;
+
+	mutex_lock(&sbs_list_lock);
+	list_for_each_entry(chip, &sbs_battery_devices, list) {
+		if (!chip->gpio_detect) {
+			chip->poll_interval = poll_interval;
+			if (chip->poll_interval)
+				mod_delayed_work(system_wq, &chip->work, 0);
+		}
+	}
+	mutex_unlock(&sbs_list_lock);
+
+	return 0;
+}
+
+static const struct kernel_param_ops param_ops_poll_interval = {
+	.set = poll_interval_param_set,
+	.get = param_get_uint,
+};
+
+static void sbs_list_remove(void *data)
+{
+	struct sbs_info *chip = data;
+
+	mutex_lock(&sbs_list_lock);
+	list_del(&chip->list);
+	mutex_unlock(&sbs_list_lock);
+}
+
+module_param_cb(poll_interval, &param_ops_poll_interval, &poll_interval, 0644);
+MODULE_PARM_DESC(
+	poll_interval,
+	"Polling interval in milliseconds for devices without GPIO interrupt (0=disabled)");
 
 static int sbs_read_word_data(struct i2c_client *client, u8 address);
 static int sbs_write_word_data(struct i2c_client *client, u8 address, u16 value);
@@ -1091,6 +1140,10 @@ static void sbs_delayed_work(struct work_struct *work)
 	/* if the read failed, give up on this work */
 	if (ret < 0) {
 		chip->poll_time = 0;
+		if (chip->poll_interval)
+			schedule_delayed_work(
+				&chip->work,
+				msecs_to_jiffies(chip->poll_interval));
 		return;
 	}
 
@@ -1106,6 +1159,11 @@ static void sbs_delayed_work(struct work_struct *work)
 	if (chip->last_state != ret) {
 		chip->poll_time = 0;
 		power_supply_changed(chip->power_supply);
+	}
+
+	if (chip->poll_interval) {
+		schedule_delayed_work(&chip->work,
+				      msecs_to_jiffies(chip->poll_interval));
 		return;
 	}
 	if (chip->poll_time > 0) {
@@ -1173,6 +1231,8 @@ static int sbs_probe(struct i2c_client *client)
 	}
 	chip->i2c_retry_count = chip->i2c_retry_count + 1;
 
+	chip->poll_interval = poll_interval;
+
 	chip->charger_broadcasts = !device_property_read_bool(&client->dev,
 					"sbs,disable-charger-broadcasts");
 
@@ -1201,12 +1261,18 @@ static int sbs_probe(struct i2c_client *client)
 		goto skip_gpio;
 	}
 
+	if (chip->poll_interval) {
+		dev_dbg(&client->dev,
+			"GPIO-based IRQ configured, polling disabled\n");
+		chip->poll_interval = 0;
+	}
+
 skip_gpio:
 	/*
 	 * Before we register, we might need to make sure we can actually talk
 	 * to the battery.
 	 */
-	if (!(force_load || chip->gpio_detect)) {
+	if (!(force_load || chip->gpio_detect || chip->poll_interval)) {
 		union power_supply_propval val;
 
 		rc = sbs_get_battery_presence_and_health(
@@ -1230,6 +1296,22 @@ static int sbs_probe(struct i2c_client *client)
 	dev_info(&client->dev,
 		"%s: battery gas gauge device registered\n", client->name);
 
+	mutex_lock(&sbs_list_lock);
+	list_add(&chip->list, &sbs_battery_devices);
+	mutex_unlock(&sbs_list_lock);
+
+	rc = devm_add_action(&client->dev, sbs_list_remove, chip);
+	if (rc) {
+		mutex_lock(&sbs_list_lock);
+		list_del(&chip->list);
+		mutex_unlock(&sbs_list_lock);
+		return rc;
+	}
+
+	if (chip->poll_interval > 0)
+		schedule_delayed_work(&chip->work,
+				      msecs_to_jiffies(chip->poll_interval));
+
 	return 0;
 }
 
-- 
2.43.0


Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ