[PATCH v2] power_supply: Added support for power supply attribute sources

From: Ramakrishna Pallala
Date: Thu Jul 26 2012 - 12:40:13 EST


On some platforms one driver(or HW chip) may not be able to provide all
the necessary attributes of the power supply connected to the platform or
may provide very limited info which can be used by core/primary drivers.

For example a temperature sensor chip placed near the battery can be used
to report battery ambient temperature but it does not makes sense to register
sensor driver with power supply class. Or even a ADC driver or platform
driver may report power supply properties like voltage/current or charging
status but registering all those driver with power supply class is not a
practical or ideal approach.

This patch adds the generic support to register the drivers as power
supply attribute(properties) sources and adds an interface to read
these attributes from power supply class drivers.

If there are multiple attribute sources of the same type then caller has
to do source selection by passing the source string in the query struct.

Signed-off-by: Ramakrishna Pallala <ramakrishna.pallala@xxxxxxxxx>
---
Documentation/power/power_supply_class.txt | 30 ++++++++++
drivers/power/power_supply_core.c | 81 ++++++++++++++++++++++++++++
include/linux/power_supply.h | 32 +++++++++++
3 files changed, 143 insertions(+), 0 deletions(-)

diff --git a/Documentation/power/power_supply_class.txt b/Documentation/power/power_supply_class.txt
index c0f62ae..f8ceb45 100644
--- a/Documentation/power/power_supply_class.txt
+++ b/Documentation/power/power_supply_class.txt
@@ -130,6 +130,36 @@ while battery powers a load)
TIME_TO_FULL - seconds left for battery to be considered full (i.e.
while battery is charging)

+Power supply attribute sources
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+On some platforms one driver(or HW chip) may not be able to provide all
+the necessary attributes of the power supply connected to the platform.
+
+For example a temperature sensor chip placed near the battery can be used
+to report battery ambient temperature but it does not makes sense to register
+sensor driver with power supply class. Or even a ADC driver or platform
+driver may report power supply properties like voltage/current or charging
+status but registering all those driver with power supply class is not a
+practical or ideal approach.
+
+Power supply subsystem provides an interface to register and report about
+these power supply attributes to the primary driver which is registered
+with power supply class.
+
+Power supply attribute source driver can use the following functions to
+register/unregister as attributes source.
+
+int power_supply_attributes_register(struct device *parent,
+ struct power_supply_attr_source *psy_attr);
+
+void power_supply_attributes_unregister(
+ struct power_supply_attr_source *psy_attr);
+
+Power supply class driver(consumer driver) which needs to get
+power supply attributes can call the following function.
+
+int power_supply_get_external_attr(
+ struct power_supply_attr_query *query);

Battery <-> external power supply interaction
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
diff --git a/drivers/power/power_supply_core.c b/drivers/power/power_supply_core.c
index ff990d2..a4b52f4 100644
--- a/drivers/power/power_supply_core.c
+++ b/drivers/power/power_supply_core.c
@@ -14,6 +14,7 @@
#include <linux/types.h>
#include <linux/init.h>
#include <linux/slab.h>
+#include <linux/list.h>
#include <linux/device.h>
#include <linux/err.h>
#include <linux/power_supply.h>
@@ -26,6 +27,8 @@ EXPORT_SYMBOL_GPL(power_supply_class);

static struct device_type power_supply_dev_type;

+static LIST_HEAD(list_head_source_attr);
+
static int __power_supply_changed_work(struct device *dev, void *data)
{
struct power_supply *psy = (struct power_supply *)data;
@@ -164,6 +167,37 @@ int power_supply_powers(struct power_supply *psy, struct device *dev)
}
EXPORT_SYMBOL_GPL(power_supply_powers);

+int power_supply_get_external_attr(struct power_supply_attr_query *query)
+{
+ struct list_head *list;
+ struct power_supply_attr_source *psy_attr;
+ int ret = -ENODEV;
+
+ if (!query || list_empty(&list_head_source_attr))
+ return -EINVAL;
+
+ list_for_each(list, &list_head_source_attr) {
+ psy_attr = list_entry(list,
+ struct power_supply_attr_source, attr_pool);
+
+ if (psy_attr->type != query->type)
+ continue;
+ if (query->src_name && strcmp(query->src_name,
+ psy_attr->name))
+ continue;
+
+ ret = psy_attr->get_property(psy_attr,
+ query->property, &query->res);
+ if (ret < 0)
+ continue;
+ else
+ break;
+ }
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(power_supply_get_external_attr);
+
static void power_supply_dev_release(struct device *dev)
{
pr_debug("device: '%s': %s\n", dev_name(dev), __func__);
@@ -289,6 +323,53 @@ void power_supply_unregister(struct power_supply *psy)
}
EXPORT_SYMBOL_GPL(power_supply_unregister);

+int power_supply_attributes_register(struct device *parent,
+ struct power_supply_attr_source *psy_attr)
+{
+ struct device *dev;
+ int rc;
+
+ dev = kzalloc(sizeof(*dev), GFP_KERNEL);
+ if (!dev)
+ return -ENOMEM;
+
+ device_initialize(dev);
+
+ dev->parent = parent;
+ dev->release = power_supply_dev_release;
+ dev_set_drvdata(dev, psy_attr);
+ psy_attr->dev = dev;
+
+ rc = kobject_set_name(&dev->kobj, "%s", psy_attr->name);
+ if (rc)
+ goto kobject_set_name_failed;
+
+ rc = device_add(dev);
+ if (rc)
+ goto device_add_failed;
+
+ INIT_LIST_HEAD(&psy_attr->attr_pool);
+ /* add to the list head */
+ list_add(&psy_attr->attr_pool, &list_head_source_attr);
+
+ goto success;
+
+kobject_set_name_failed:
+device_add_failed:
+ put_device(dev);
+success:
+ return rc;
+}
+EXPORT_SYMBOL_GPL(power_supply_attributes_register);
+
+void power_supply_attributes_unregister(struct power_supply_attr_source
+ *psy_attr)
+{
+ list_del(&psy_attr->attr_pool);
+ device_unregister(psy_attr->dev);
+}
+EXPORT_SYMBOL_GPL(power_supply_attributes_unregister);
+
static int __init power_supply_class_init(void)
{
power_supply_class = class_create(THIS_MODULE, "power_supply");
diff --git a/include/linux/power_supply.h b/include/linux/power_supply.h
index 53f177d..b0f175a 100644
--- a/include/linux/power_supply.h
+++ b/include/linux/power_supply.h
@@ -193,6 +193,29 @@ struct power_supply {
#endif
};

+struct power_supply_attr_query {
+ char *src_name;
+ enum power_supply_property property;
+ enum power_supply_type type;
+ /* variable to store result */
+ union power_supply_propval res;
+};
+
+struct power_supply_attr_source {
+ const char *name;
+ enum power_supply_type type;
+ int (*get_property)(struct power_supply_attr_source *psy_attr,
+ enum power_supply_property psp,
+ union power_supply_propval *val);
+ int (*set_property)(struct power_supply_attr_source *psy_attr,
+ enum power_supply_property psp,
+ union power_supply_propval *val);
+
+ /* private */
+ struct device *dev;
+ struct list_head attr_pool;
+};
+
/*
* This is recommended structure to specify static power supply parameters.
* Generic one, parametrizable for different power supplies. Power supply
@@ -216,6 +239,8 @@ extern struct power_supply *power_supply_get_by_name(char *name);
extern void power_supply_changed(struct power_supply *psy);
extern int power_supply_am_i_supplied(struct power_supply *psy);
extern int power_supply_set_battery_charged(struct power_supply *psy);
+extern int power_supply_get_external_attr(
+ struct power_supply_attr_query *query);

#ifdef CONFIG_POWER_SUPPLY
extern int power_supply_is_system_supplied(void);
@@ -226,6 +251,13 @@ static inline int power_supply_is_system_supplied(void) { return -ENOSYS; }
extern int power_supply_register(struct device *parent,
struct power_supply *psy);
extern void power_supply_unregister(struct power_supply *psy);
+
+extern int power_supply_attributes_register(struct device *parent,
+ struct power_supply_attr_source *psy_attr);
+
+extern void power_supply_attributes_unregister(
+ struct power_supply_attr_source *psy_attr);
+
extern int power_supply_powers(struct power_supply *psy, struct device *dev);

/* For APM emulation, think legacy userspace. */
--
1.7.0.4

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/