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

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]


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/


[Other Archives]     [Linux Kernel Newbies]     [Linux Driver Development]     [Linux Kbuild]     [Fedora Kernel]     [Linux Kernel Testers]     [Linux SH]     [Linux Omap]     [Linux Tape]     [Linux Input]     [Linux Kernel Janitors]     [Linux Kernel Packagers]     [Linux Doc]     [Linux Man Pages]     [Linux API]     [Linux Memory Management]     [Linux Modules]     [Linux Standards]     [Kernel Announce]     [Netdev]     [Git]     [Linux PCI]     Linux CAN Development     [Linux I2C]     [Linux RDMA]     [Linux NUMA]     [Netfilter]     [Netfilter Devel]     [SELinux]     [Bugtraq]     [FIO]     [Linux Perf Users]     [Linux Serial]     [Linux PPP]     [Linux ISDN]     [Linux Next]     [Kernel Stable Commits]     [Linux Tip Commits]     [Kernel MM Commits]     [Linux Security Module]     [AutoFS]     [Filesystem Development]     [Ext3 Filesystem]     [Linux bcache]     [Ext4 Filesystem]     [Linux BTRFS]     [Linux CEPH Filesystem]     [Linux XFS]     [XFS]     [Linux NFS]     [Linux CIFS]     [Ecryptfs]     [Linux NILFS]     [Linux Cachefs]     [Reiser FS]     [Initramfs]     [Linux FB Devel]     [Linux OpenGL]     [DRI Devel]     [Fastboot]     [Linux RT Users]     [Linux RT Stable]     [eCos]     [Corosync]     [Linux Clusters]     [LVS Devel]     [Hot Plug]     [Linux Virtualization]     [KVM]     [KVM PPC]     [KVM ia64]     [Linux Containers]     [Linux Hexagon]     [Linux Cgroups]     [Util Linux]     [Wireless]     [Linux Bluetooth]     [Bluez Devel]     [Ethernet Bridging]     [Embedded Linux]     [Barebox]     [Linux MMC]     [Linux IIO]     [Sparse]     [Smatch]     [Linux Arch]     [x86 Platform Driver]     [Linux ACPI]     [Linux IBM ACPI]     [LM Sensors]     [CPU Freq]     [Linux Power Management]     [Linmodems]     [Linux DCCP]     [Linux SCTP]     [ALSA Devel]     [Linux USB]     [Linux PA RISC]     [Linux Samsung SOC]     [MIPS Linux]     [IBM S/390 Linux]     [ARM Linux]     [ARM Kernel]     [ARM MSM]     [Tegra Devel]     [Sparc Linux]     [Linux Security]     [Linux Sound]     [Linux Media]     [Video 4 Linux]     [Linux IRDA Users]     [Linux for the blind]     [Linux RAID]     [Linux ATA RAID]     [Device Mapper]     [Linux SCSI]     [SCSI Target Devel]     [Linux SCSI Target Infrastructure]     [Linux IDE]     [Linux SMP]     [Linux AXP]     [Linux Alpha]     [Linux M68K]     [Linux ia64]     [Linux 8086]     [Linux x86_64]     [Linux Config]     [Linux Apps]     [Linux MSDOS]     [Linux X.25]     [Linux Crypto]     [DM Crypt]     [Linux Trace Users]     [Linux Btrace]     [Linux Watchdog]     [Utrace Devel]     [Linux C Programming]     [Linux Assembly]     [Dash]     [DWARVES]     [Hail Devel]     [Linux Kernel Debugger]     [Linux gcc]     [Gcc Help]     [X.Org]     [Wine]

Add to Google Powered by Linux

[Older Kernel Discussion]     [Yosemite National Park Forum]     [Large Format Photos]     [Gimp]     [Yosemite Photos]     [Stuff]