mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/chenhuacai/linux-loongson
synced 2025-09-02 16:44:59 +00:00
power: supply: core: add UAPI to discover currently used extensions
Userspace wants to now about the used power supply extensions, for example to handle a device extended by a certain extension differently or to discover information about the extending device. Add a sysfs directory to the power supply device. This directory contains links which are named after the used extension and point to the device implementing that extension. Signed-off-by: Thomas Weißschuh <linux@weissschuh.net> Reviewed-by: Armin Wolf <W_Armin@gmx.de> Link: https://lore.kernel.org/r/20241211-power-supply-extensions-v6-4-9d9dc3f3d387@weissschuh.net Signed-off-by: Sebastian Reichel <sebastian.reichel@collabora.com>
This commit is contained in:
parent
bcfe7d6ba2
commit
288a2cabcf
@ -813,3 +813,12 @@ Description:
|
|||||||
|
|
||||||
Access: Read
|
Access: Read
|
||||||
Valid values: 1-31
|
Valid values: 1-31
|
||||||
|
|
||||||
|
What: /sys/class/power_supply/<supply_name>/extensions/<extension_name>
|
||||||
|
Date: March 2025
|
||||||
|
Contact: linux-pm@vger.kernel.org
|
||||||
|
Description:
|
||||||
|
Reports the extensions registered to the power supply.
|
||||||
|
Each entry is a link to the device which registered the extension.
|
||||||
|
|
||||||
|
Access: Read
|
||||||
|
@ -31,6 +31,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
struct cros_chctl_priv {
|
struct cros_chctl_priv {
|
||||||
|
struct device *dev;
|
||||||
struct cros_ec_device *cros_ec;
|
struct cros_ec_device *cros_ec;
|
||||||
struct acpi_battery_hook battery_hook;
|
struct acpi_battery_hook battery_hook;
|
||||||
struct power_supply *hooked_battery;
|
struct power_supply *hooked_battery;
|
||||||
@ -202,6 +203,7 @@ static int cros_chctl_psy_prop_is_writeable(struct power_supply *psy,
|
|||||||
}; \
|
}; \
|
||||||
\
|
\
|
||||||
static const struct power_supply_ext _name = { \
|
static const struct power_supply_ext _name = { \
|
||||||
|
.name = "cros-charge-control", \
|
||||||
.properties = _name ## _props, \
|
.properties = _name ## _props, \
|
||||||
.num_properties = ARRAY_SIZE(_name ## _props), \
|
.num_properties = ARRAY_SIZE(_name ## _props), \
|
||||||
.charge_behaviours = EC_CHARGE_CONTROL_BEHAVIOURS, \
|
.charge_behaviours = EC_CHARGE_CONTROL_BEHAVIOURS, \
|
||||||
@ -233,7 +235,7 @@ static int cros_chctl_add_battery(struct power_supply *battery, struct acpi_batt
|
|||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
priv->hooked_battery = battery;
|
priv->hooked_battery = battery;
|
||||||
return power_supply_register_extension(battery, priv->psy_ext, priv);
|
return power_supply_register_extension(battery, priv->psy_ext, priv->dev, priv);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int cros_chctl_remove_battery(struct power_supply *battery, struct acpi_battery_hook *hook)
|
static int cros_chctl_remove_battery(struct power_supply *battery, struct acpi_battery_hook *hook)
|
||||||
@ -299,6 +301,7 @@ static int cros_chctl_probe(struct platform_device *pdev)
|
|||||||
|
|
||||||
dev_dbg(dev, "Command version: %u\n", (unsigned int)priv->cmd_version);
|
dev_dbg(dev, "Command version: %u\n", (unsigned int)priv->cmd_version);
|
||||||
|
|
||||||
|
priv->dev = dev;
|
||||||
priv->cros_ec = cros_ec;
|
priv->cros_ec = cros_ec;
|
||||||
|
|
||||||
if (priv->cmd_version == 1)
|
if (priv->cmd_version == 1)
|
||||||
|
@ -25,6 +25,7 @@ extern bool power_supply_ext_has_property(const struct power_supply_ext *ext,
|
|||||||
struct power_supply_ext_registration {
|
struct power_supply_ext_registration {
|
||||||
struct list_head list_head;
|
struct list_head list_head;
|
||||||
const struct power_supply_ext *ext;
|
const struct power_supply_ext *ext;
|
||||||
|
struct device *dev;
|
||||||
void *data;
|
void *data;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -39,6 +40,7 @@ struct power_supply_ext_registration {
|
|||||||
|
|
||||||
extern void __init power_supply_init_attrs(void);
|
extern void __init power_supply_init_attrs(void);
|
||||||
extern int power_supply_uevent(const struct device *dev, struct kobj_uevent_env *env);
|
extern int power_supply_uevent(const struct device *dev, struct kobj_uevent_env *env);
|
||||||
|
extern const struct attribute_group power_supply_extension_group;
|
||||||
extern const struct attribute_group *power_supply_attr_groups[];
|
extern const struct attribute_group *power_supply_attr_groups[];
|
||||||
|
|
||||||
#else
|
#else
|
||||||
|
@ -1358,17 +1358,21 @@ static int power_supply_update_sysfs_and_hwmon(struct power_supply *psy)
|
|||||||
}
|
}
|
||||||
|
|
||||||
int power_supply_register_extension(struct power_supply *psy, const struct power_supply_ext *ext,
|
int power_supply_register_extension(struct power_supply *psy, const struct power_supply_ext *ext,
|
||||||
void *data)
|
struct device *dev, void *data)
|
||||||
{
|
{
|
||||||
struct power_supply_ext_registration *reg;
|
struct power_supply_ext_registration *reg;
|
||||||
size_t i;
|
size_t i;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
if (!psy || !ext || !ext->properties || !ext->num_properties)
|
if (!psy || !dev || !ext || !ext->name || !ext->properties || !ext->num_properties)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
guard(rwsem_write)(&psy->extensions_sem);
|
guard(rwsem_write)(&psy->extensions_sem);
|
||||||
|
|
||||||
|
power_supply_for_each_extension(reg, psy)
|
||||||
|
if (strcmp(ext->name, reg->ext->name) == 0)
|
||||||
|
return -EEXIST;
|
||||||
|
|
||||||
for (i = 0; i < ext->num_properties; i++)
|
for (i = 0; i < ext->num_properties; i++)
|
||||||
if (power_supply_has_property(psy, ext->properties[i]))
|
if (power_supply_has_property(psy, ext->properties[i]))
|
||||||
return -EEXIST;
|
return -EEXIST;
|
||||||
@ -1378,9 +1382,15 @@ int power_supply_register_extension(struct power_supply *psy, const struct power
|
|||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
reg->ext = ext;
|
reg->ext = ext;
|
||||||
|
reg->dev = dev;
|
||||||
reg->data = data;
|
reg->data = data;
|
||||||
list_add(®->list_head, &psy->extensions);
|
list_add(®->list_head, &psy->extensions);
|
||||||
|
|
||||||
|
ret = sysfs_add_link_to_group(&psy->dev.kobj, power_supply_extension_group.name,
|
||||||
|
&dev->kobj, ext->name);
|
||||||
|
if (ret)
|
||||||
|
goto sysfs_link_failed;
|
||||||
|
|
||||||
ret = power_supply_update_sysfs_and_hwmon(psy);
|
ret = power_supply_update_sysfs_and_hwmon(psy);
|
||||||
if (ret)
|
if (ret)
|
||||||
goto sysfs_hwmon_failed;
|
goto sysfs_hwmon_failed;
|
||||||
@ -1388,6 +1398,8 @@ int power_supply_register_extension(struct power_supply *psy, const struct power
|
|||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
sysfs_hwmon_failed:
|
sysfs_hwmon_failed:
|
||||||
|
sysfs_remove_link_from_group(&psy->dev.kobj, power_supply_extension_group.name, ext->name);
|
||||||
|
sysfs_link_failed:
|
||||||
list_del(®->list_head);
|
list_del(®->list_head);
|
||||||
kfree(reg);
|
kfree(reg);
|
||||||
return ret;
|
return ret;
|
||||||
@ -1403,6 +1415,9 @@ void power_supply_unregister_extension(struct power_supply *psy, const struct po
|
|||||||
power_supply_for_each_extension(reg, psy) {
|
power_supply_for_each_extension(reg, psy) {
|
||||||
if (reg->ext == ext) {
|
if (reg->ext == ext) {
|
||||||
list_del(®->list_head);
|
list_del(®->list_head);
|
||||||
|
sysfs_remove_link_from_group(&psy->dev.kobj,
|
||||||
|
power_supply_extension_group.name,
|
||||||
|
reg->ext->name);
|
||||||
kfree(reg);
|
kfree(reg);
|
||||||
power_supply_update_sysfs_and_hwmon(psy);
|
power_supply_update_sysfs_and_hwmon(psy);
|
||||||
return;
|
return;
|
||||||
|
@ -458,8 +458,18 @@ static const struct attribute_group power_supply_attr_group = {
|
|||||||
.is_visible = power_supply_attr_is_visible,
|
.is_visible = power_supply_attr_is_visible,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static struct attribute *power_supply_extension_attrs[] = {
|
||||||
|
NULL
|
||||||
|
};
|
||||||
|
|
||||||
|
const struct attribute_group power_supply_extension_group = {
|
||||||
|
.name = "extensions",
|
||||||
|
.attrs = power_supply_extension_attrs,
|
||||||
|
};
|
||||||
|
|
||||||
const struct attribute_group *power_supply_attr_groups[] = {
|
const struct attribute_group *power_supply_attr_groups[] = {
|
||||||
&power_supply_attr_group,
|
&power_supply_attr_group,
|
||||||
|
&power_supply_extension_group,
|
||||||
NULL
|
NULL
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -293,6 +293,7 @@ static int test_power_battery_extproperty_is_writeable(struct power_supply *psy,
|
|||||||
}
|
}
|
||||||
|
|
||||||
static const struct power_supply_ext test_power_battery_ext = {
|
static const struct power_supply_ext test_power_battery_ext = {
|
||||||
|
.name = "test_power",
|
||||||
.properties = test_power_battery_extprops,
|
.properties = test_power_battery_extprops,
|
||||||
.num_properties = ARRAY_SIZE(test_power_battery_extprops),
|
.num_properties = ARRAY_SIZE(test_power_battery_extprops),
|
||||||
.get_property = test_power_battery_extget_property,
|
.get_property = test_power_battery_extget_property,
|
||||||
@ -307,7 +308,8 @@ static void test_power_configure_battery_extension(bool enable)
|
|||||||
psy = test_power_supplies[TEST_BATTERY];
|
psy = test_power_supplies[TEST_BATTERY];
|
||||||
|
|
||||||
if (enable) {
|
if (enable) {
|
||||||
if (power_supply_register_extension(psy, &test_power_battery_ext, NULL)) {
|
if (power_supply_register_extension(psy, &test_power_battery_ext, &psy->dev,
|
||||||
|
NULL)) {
|
||||||
pr_err("registering battery extension failed\n");
|
pr_err("registering battery extension failed\n");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -286,6 +286,7 @@ struct power_supply_desc {
|
|||||||
};
|
};
|
||||||
|
|
||||||
struct power_supply_ext {
|
struct power_supply_ext {
|
||||||
|
const char *const name;
|
||||||
u8 charge_behaviours;
|
u8 charge_behaviours;
|
||||||
const enum power_supply_property *properties;
|
const enum power_supply_property *properties;
|
||||||
size_t num_properties;
|
size_t num_properties;
|
||||||
@ -911,6 +912,7 @@ extern int power_supply_powers(struct power_supply *psy, struct device *dev);
|
|||||||
extern int __must_check
|
extern int __must_check
|
||||||
power_supply_register_extension(struct power_supply *psy,
|
power_supply_register_extension(struct power_supply *psy,
|
||||||
const struct power_supply_ext *ext,
|
const struct power_supply_ext *ext,
|
||||||
|
struct device *dev,
|
||||||
void *data);
|
void *data);
|
||||||
extern void power_supply_unregister_extension(struct power_supply *psy,
|
extern void power_supply_unregister_extension(struct power_supply *psy,
|
||||||
const struct power_supply_ext *ext);
|
const struct power_supply_ext *ext);
|
||||||
|
Loading…
Reference in New Issue
Block a user