diff options
author | Johan Palsson <johan.palsson@stericsson.com> | 2011-06-01 18:46:04 +0200 |
---|---|---|
committer | said m bagheri <ebgheri@steludxu2848.(none)> | 2011-06-29 10:30:29 +0200 |
commit | 41b14b4e605ee1acbbf8eabb6eec67d4fe23f9e7 (patch) | |
tree | dcf92380ce4ac859d411001f05e3c3fc623bee42 | |
parent | 39066062cdf7065d32965d4184f06b8766d74453 (diff) |
power: ab8500_bm: Add sysfs node for charge_full
In order for a userspace application to save the maximum capacity
in persistant storage, this driver needs to expose this parameter
in sysfs. This patch enables that functionality
ST-Ericsson Linux Next: -
ST-Ericsson ID: ER334609
ST-Ericsson FOSS-OUT ID: Trivial
Change-Id: I9dd5e9c1faeb6d41da051417999ebdf53a2eaa72
Signed-off-by: Johan Palsson <johan.palsson@stericsson.com>
Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/24951
Reviewed-by: QATEST
Tested-by: Karl KOMIEROWSKI <karl.komierowski@stericsson.com>
Reviewed-by: Jonas ABERG <jonas.aberg@stericsson.com>
-rw-r--r-- | drivers/power/ab8500_fg.c | 122 |
1 files changed, 122 insertions, 0 deletions
diff --git a/drivers/power/ab8500_fg.c b/drivers/power/ab8500_fg.c index 8af5216b90b..29237aa2746 100644 --- a/drivers/power/ab8500_fg.c +++ b/drivers/power/ab8500_fg.c @@ -19,6 +19,7 @@ #include <linux/interrupt.h> #include <linux/platform_device.h> #include <linux/power_supply.h> +#include <linux/kobject.h> #include <linux/mfd/ab8500.h> #include <linux/mfd/abx500.h> #include <linux/slab.h> @@ -154,6 +155,7 @@ struct ab8500_fg_flags { * @fg_work: Work to run the FG algorithm instantly * @fg_acc_cur_work: Work to read the FG accumulator * @cc_lock: Mutex for locking the CC + * @fg_kobject: Structure of type kobject */ struct ab8500_fg { struct device *dev; @@ -186,6 +188,7 @@ struct ab8500_fg { struct work_struct fg_work; struct work_struct fg_acc_cur_work; struct mutex cc_lock; + struct kobject fg_kobject; }; /* Main battery properties */ @@ -1617,6 +1620,8 @@ static int ab8500_fg_get_ext_psy_data(struct device *dev, void *data) di->flags.fully_charged = true; /* Save current capacity as maximum */ di->bat_cap.max_mah = di->bat_cap.mah; + sysfs_notify(&di->fg_kobject, + NULL, "charge_full"); queue_work(di->fg_wq, &di->fg_work); break; case POWER_SUPPLY_STATUS_CHARGING: @@ -1703,6 +1708,116 @@ static void ab8500_fg_external_power_changed(struct power_supply *psy) &di->fg_psy, ab8500_fg_get_ext_psy_data); } +/* Exposure to the sysfs interface */ + +struct ab8500_fg_sysfs_entry { + struct attribute attr; + ssize_t (*show)(struct ab8500_fg *, char *); + ssize_t (*store)(struct ab8500_fg *, const char *, size_t); +}; + +static ssize_t charge_full_show(struct ab8500_fg *di, char *buf) +{ + return sprintf(buf, "%d\n", di->bat_cap.max_mah); +} + +static ssize_t charge_full_store(struct ab8500_fg *di, const char *buf, + size_t count) +{ + unsigned long charge_full; + ssize_t ret = -EINVAL; + + ret = strict_strtoul(buf, 10, &charge_full); + + dev_dbg(di->dev, "Ret %d charge_full %d", ret, charge_full); + if (!ret) { + di->bat_cap.max_mah = (int) charge_full; + ret = count; + } + + return ret; +} +static struct ab8500_fg_sysfs_entry charge_full_attr = + __ATTR(charge_full, 0644, charge_full_show, charge_full_store); + +static ssize_t +ab8500_fg_show(struct kobject *kobj, struct attribute *attr, char *buf) +{ + struct ab8500_fg_sysfs_entry *entry; + struct ab8500_fg *di; + + entry = container_of(attr, struct ab8500_fg_sysfs_entry, attr); + di = container_of(kobj, struct ab8500_fg, fg_kobject); + + if (!entry->show) + return -EIO; + + return entry->show(di, buf); +} + +static ssize_t +ab8500_fg_store(struct kobject *kobj, struct attribute *attr, const char *buf, + size_t count) +{ + struct ab8500_fg_sysfs_entry *entry; + struct ab8500_fg *di; + + entry = container_of(attr, struct ab8500_fg_sysfs_entry, attr); + di = container_of(kobj, struct ab8500_fg, fg_kobject); + + if (!entry->store) + return -EIO; + + return entry->store(di, buf, count); +} + +const struct sysfs_ops ab8500_fg_sysfs_ops = { + .show = ab8500_fg_show, + .store = ab8500_fg_store, +}; + +static struct attribute *ab8500_fg_attrs[] = { + &charge_full_attr.attr, + NULL, +}; + +static struct kobj_type ab8500_fg_ktype = { + .sysfs_ops = &ab8500_fg_sysfs_ops, + .default_attrs = ab8500_fg_attrs, +}; + +/** + * ab8500_chargalg_sysfs_exit() - de-init of sysfs entry + * @di: pointer to the struct ab8500_chargalg + * + * This function removes the entry in sysfs. + */ +static void ab8500_fg_sysfs_exit(struct ab8500_fg *di) +{ + kobject_del(&di->fg_kobject); +} + +/** + * ab8500_chargalg_sysfs_init() - init of sysfs entry + * @di: pointer to the struct ab8500_chargalg + * + * This function adds an entry in sysfs. + * Returns error code in case of failure else 0(on success) + */ +static int ab8500_fg_sysfs_init(struct ab8500_fg *di) +{ + int ret = 0; + + ret = kobject_init_and_add(&di->fg_kobject, + &ab8500_fg_ktype, + NULL, "ab8500_fg"); + if (ret < 0) + dev_err(di->dev, "failed to create sysfs entry\n"); + + return ret; +} +/* Exposure to the sysfs interface <<END>> */ + #if defined(CONFIG_PM) static int ab8500_fg_resume(struct platform_device *pdev) { @@ -1752,6 +1867,7 @@ static int __devexit ab8500_fg_remove(struct platform_device *pdev) dev_err(di->dev, "failed to disable coulomb counter\n"); destroy_workqueue(di->fg_wq); + ab8500_fg_sysfs_exit(di); flush_scheduled_work(); power_supply_unregister(&di->fg_psy); @@ -1884,6 +2000,12 @@ static int __devinit ab8500_fg_probe(struct platform_device *pdev) platform_set_drvdata(pdev, di); + ret = ab8500_fg_sysfs_init(di); + if (ret) { + dev_err(di->dev, "failed to create sysfs entry\n"); + goto free_irq; + } + /* Calibrate the fg first time */ di->flags.calibrate = true; di->calib_state = AB8500_FG_CALIB_INIT; |