Three fixes I'd like to get into stable. These conflict with my psy extensions series [0], I'd like to apply the fixes first.
[0] https://lore.kernel.org/lkml/20241205-power-supply-extensions-v5-0-f0f996db4...
Signed-off-by: Thomas Weißschuh linux@weissschuh.net --- Thomas Weißschuh (3): power: supply: cros_charge-control: add mutex for driver data power: supply: cros_charge-control: allow start_threshold == end_threshold power: supply: cros_charge-control: hide start threshold on v2 cmd
drivers/power/supply/cros_charge-control.c | 36 ++++++++++++++++++++++-------- 1 file changed, 27 insertions(+), 9 deletions(-) --- base-commit: 7503345ac5f5e82fd9a36d6e6b447c016376403a change-id: 20241202-cros_charge-control-v2-d155194f5304
Best regards,
Concurrent accesses through sysfs may lead to inconsistent state in the priv data. Introduce a mutex to avoid this.
Fixes: c6ed48ef5259 ("power: supply: add ChromeOS EC based charge control driver") Cc: stable@vger.kernel.org Signed-off-by: Thomas Weißschuh linux@weissschuh.net --- drivers/power/supply/cros_charge-control.c | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-)
diff --git a/drivers/power/supply/cros_charge-control.c b/drivers/power/supply/cros_charge-control.c index 17c53591ce197d08d97c94d3d4359a282026dd7d..58ca6d9ed6132af63a36ea4c5bf212acf066936c 100644 --- a/drivers/power/supply/cros_charge-control.c +++ b/drivers/power/supply/cros_charge-control.c @@ -7,8 +7,10 @@ #include <acpi/battery.h> #include <linux/container_of.h> #include <linux/dmi.h> +#include <linux/lockdep.h> #include <linux/mod_devicetable.h> #include <linux/module.h> +#include <linux/mutex.h> #include <linux/platform_data/cros_ec_commands.h> #include <linux/platform_data/cros_ec_proto.h> #include <linux/platform_device.h> @@ -49,6 +51,7 @@ struct cros_chctl_priv { struct attribute *attributes[_CROS_CHCTL_ATTR_COUNT]; struct attribute_group group;
+ struct mutex lock; /* protects fields below and cros_ec */ enum power_supply_charge_behaviour current_behaviour; u8 current_start_threshold, current_end_threshold; }; @@ -85,6 +88,8 @@ static int cros_chctl_configure_ec(struct cros_chctl_priv *priv) { struct ec_params_charge_control req = {};
+ lockdep_assert_held(&priv->lock); + req.cmd = EC_CHARGE_CONTROL_CMD_SET;
switch (priv->current_behaviour) { @@ -159,6 +164,7 @@ static ssize_t charge_control_start_threshold_show(struct device *dev, struct cros_chctl_priv *priv = cros_chctl_attr_to_priv(&attr->attr, CROS_CHCTL_ATTR_START_THRESHOLD);
+ guard(mutex)(&priv->lock); return sysfs_emit(buf, "%u\n", (unsigned int)priv->current_start_threshold); }
@@ -169,6 +175,7 @@ static ssize_t charge_control_start_threshold_store(struct device *dev, struct cros_chctl_priv *priv = cros_chctl_attr_to_priv(&attr->attr, CROS_CHCTL_ATTR_START_THRESHOLD);
+ guard(mutex)(&priv->lock); return cros_chctl_store_threshold(dev, priv, 0, buf, count); }
@@ -178,6 +185,7 @@ static ssize_t charge_control_end_threshold_show(struct device *dev, struct devi struct cros_chctl_priv *priv = cros_chctl_attr_to_priv(&attr->attr, CROS_CHCTL_ATTR_END_THRESHOLD);
+ guard(mutex)(&priv->lock); return sysfs_emit(buf, "%u\n", (unsigned int)priv->current_end_threshold); }
@@ -187,6 +195,7 @@ static ssize_t charge_control_end_threshold_store(struct device *dev, struct dev struct cros_chctl_priv *priv = cros_chctl_attr_to_priv(&attr->attr, CROS_CHCTL_ATTR_END_THRESHOLD);
+ guard(mutex)(&priv->lock); return cros_chctl_store_threshold(dev, priv, 1, buf, count); }
@@ -195,6 +204,7 @@ static ssize_t charge_behaviour_show(struct device *dev, struct device_attribute struct cros_chctl_priv *priv = cros_chctl_attr_to_priv(&attr->attr, CROS_CHCTL_ATTR_CHARGE_BEHAVIOUR);
+ guard(mutex)(&priv->lock); return power_supply_charge_behaviour_show(dev, EC_CHARGE_CONTROL_BEHAVIOURS, priv->current_behaviour, buf); } @@ -210,6 +220,7 @@ static ssize_t charge_behaviour_store(struct device *dev, struct device_attribut if (ret < 0) return ret;
+ guard(mutex)(&priv->lock); priv->current_behaviour = ret;
ret = cros_chctl_configure_ec(priv); @@ -290,6 +301,10 @@ static int cros_chctl_probe(struct platform_device *pdev) if (!priv) return -ENOMEM;
+ ret = devm_mutex_init(dev, &priv->lock); + if (ret) + return ret; + ret = cros_ec_get_cmd_versions(cros_ec, EC_CMD_CHARGE_CONTROL); if (ret < 0) return ret; @@ -327,7 +342,8 @@ static int cros_chctl_probe(struct platform_device *pdev) priv->current_end_threshold = 100;
/* Bring EC into well-known state */ - ret = cros_chctl_configure_ec(priv); + scoped_guard(mutex, &priv->lock) + ret = cros_chctl_configure_ec(priv); if (ret < 0) return ret;
Allow setting the start and stop thresholds to the same value. There is no reason to disallow it.
Suggested-by: Thomas Koch linrunner@gmx.net Fixes: c6ed48ef5259 ("power: supply: add ChromeOS EC based charge control driver") Cc: stable@vger.kernel.org Signed-off-by: Thomas Weißschuh linux@weissschuh.net --- drivers/power/supply/cros_charge-control.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/drivers/power/supply/cros_charge-control.c b/drivers/power/supply/cros_charge-control.c index 58ca6d9ed6132af63a36ea4c5bf212acf066936c..108b121db4423187fb65548396fb9195b8801006 100644 --- a/drivers/power/supply/cros_charge-control.c +++ b/drivers/power/supply/cros_charge-control.c @@ -139,11 +139,11 @@ static ssize_t cros_chctl_store_threshold(struct device *dev, struct cros_chctl_ return -EINVAL;
if (is_end_threshold) { - if (val <= priv->current_start_threshold) + if (val < priv->current_start_threshold) return -EINVAL; priv->current_end_threshold = val; } else { - if (val >= priv->current_end_threshold) + if (val > priv->current_end_threshold) return -EINVAL; priv->current_start_threshold = val; }
ECs implementing the v2 command will not stop charging when the end threshold is reached. Instead they will begin discharging until the start threshold is reached, leading to permanent charge and discharge cycles. This defeats the point of the charge control mechanism.
Avoid the issue by hiding the start threshold on v2 systems. Instead on those systems program the EC with start == end which forces the EC to reach and stay at that level.
v1 does not support thresholds and v3 works correctly, at least judging from the code.
Reported-by: Thomas Koch linrunner@gmx.net Fixes: c6ed48ef5259 ("power: supply: add ChromeOS EC based charge control driver") Cc: stable@vger.kernel.org Signed-off-by: Thomas Weißschuh linux@weissschuh.net --- drivers/power/supply/cros_charge-control.c | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-)
diff --git a/drivers/power/supply/cros_charge-control.c b/drivers/power/supply/cros_charge-control.c index 108b121db4423187fb65548396fb9195b8801006..9b0a7500296b4d7eb8cd53153e148926bb98aec1 100644 --- a/drivers/power/supply/cros_charge-control.c +++ b/drivers/power/supply/cros_charge-control.c @@ -139,6 +139,10 @@ static ssize_t cros_chctl_store_threshold(struct device *dev, struct cros_chctl_ return -EINVAL;
if (is_end_threshold) { + /* Start threshold is not exposed, use fixed value */ + if (priv->cmd_version == 2) + priv->current_start_threshold = val == 100 ? 0 : val; + if (val < priv->current_start_threshold) return -EINVAL; priv->current_end_threshold = val; @@ -234,12 +238,10 @@ static umode_t cros_chtl_attr_is_visible(struct kobject *kobj, struct attribute { struct cros_chctl_priv *priv = cros_chctl_attr_to_priv(attr, n);
- if (priv->cmd_version < 2) { - if (n == CROS_CHCTL_ATTR_START_THRESHOLD) - return 0; - if (n == CROS_CHCTL_ATTR_END_THRESHOLD) - return 0; - } + if (n == CROS_CHCTL_ATTR_START_THRESHOLD && priv->cmd_version < 3) + return 0; + else if (n == CROS_CHCTL_ATTR_END_THRESHOLD && priv->cmd_version < 2) + return 0;
return attr->mode; }
On Sun, 08 Dec 2024 15:59:25 +0100, Thomas Weißschuh wrote:
Three fixes I'd like to get into stable. These conflict with my psy extensions series [0], I'd like to apply the fixes first.
[0] https://lore.kernel.org/lkml/20241205-power-supply-extensions-v5-0-f0f996db4...
[...]
Applied, thanks!
[1/3] power: supply: cros_charge-control: add mutex for driver data commit: e5f84d1cf562f7b45e28d6e5f6490626f870f81c [2/3] power: supply: cros_charge-control: allow start_threshold == end_threshold commit: e65a1b7fad0e112573eea7d64d4ab4fc513b8695 [3/3] power: supply: cros_charge-control: hide start threshold on v2 cmd commit: c28dc9fc24f5fa802d44ef7620a511035bdd803e
Best regards,
linux-stable-mirror@lists.linaro.org