2012-10-18 11:12:18

by Tc, Jenny

[permalink] [raw]
Subject: [PATCH 0/7] power_supply: Introduce charging Framework

The Power Supply charging framework connects multiple subsystems
to do charging in a generic way. The subsystems involves power_supply,
extcon, and thermal. With this the charging is handled in a generic way.

The framework makes use of different new features - Battery Identification
framework, pluggable charging algorithms, charger cable arbitrations.
PSE compliant charging algorithm also enabled as part of this patchset.

With this framework the charging logic can be kept outside the charger driver.
The charger driver just need to expose th get_property and set_property functions
to get and set a specific set of power supply properties. The driver can
convert these values to the hardware configurations to setup charging.


Jenny TC (7):
power_supply : Introduce battery identification framework
power_supply: Add charger control properties
power_supply : add supported charger cable feature
power_supply: add throttle state
power_supply: Introduce Power Supply charging framework
power_supply: enable charger framework callbacks
power_supply: Introduce PSE compliant algorithm

drivers/power/Kconfig | 28 ++
drivers/power/Makefile | 3 +
drivers/power/battery_id.c | 94 +++++
drivers/power/charging_algo_pse.c | 79 +++++
drivers/power/power_supply.h | 20 ++
drivers/power/power_supply_charger.c | 634 ++++++++++++++++++++++++++++++++++
drivers/power/power_supply_charger.h | 165 +++++++++
drivers/power/power_supply_core.c | 12 +
drivers/power/power_supply_sysfs.c | 16 +-
include/linux/power/battery_id.h | 97 ++++++
include/linux/power_supply.h | 53 ++-
11 files changed, 1189 insertions(+), 12 deletions(-)
create mode 100644 drivers/power/battery_id.c
create mode 100644 drivers/power/charging_algo_pse.c
create mode 100644 drivers/power/power_supply_charger.c
create mode 100644 drivers/power/power_supply_charger.h
create mode 100644 include/linux/power/battery_id.h

--
1.7.9.5


2012-10-18 11:12:25

by Tc, Jenny

[permalink] [raw]
Subject: [PATCH 1/7] power_supply : Introduce battery identification framework

This patch introduces generic battid framework. Different battid
drivers sitting on different linux kernel subsystem (1wire,
I2C, SFI etc) can interface with the power supply susbsytem using
the APIs exposed in the power supply usbsystem. The consumers (charger
driver/battery driver/power management drivers) can register
for notification from the battery id drivers using the APIs from this
framework

Signed-off-by: Jenny TC <[email protected]>
Signed-off-by: adavidra <[email protected]>
---
drivers/power/Kconfig | 8 ++++
drivers/power/Makefile | 1 +
drivers/power/battery_id.c | 94 ++++++++++++++++++++++++++++++++++++++
include/linux/power/battery_id.h | 50 ++++++++++++++++++++
4 files changed, 153 insertions(+)
create mode 100644 drivers/power/battery_id.c
create mode 100644 include/linux/power/battery_id.h

diff --git a/drivers/power/Kconfig b/drivers/power/Kconfig
index 49a8939..74b297d 100644
--- a/drivers/power/Kconfig
+++ b/drivers/power/Kconfig
@@ -8,6 +8,14 @@ menuconfig POWER_SUPPLY

if POWER_SUPPLY

+config POWER_SUPPLY_BATTID
+ bool "Power Supply Battery Identification Framework"
+ help
+ Say Y here to enable the power supply battery idnetification
+ framework. The framework would allow different battery identification
+ drivers to interface with power supply subsystem. Also it allows consumer
+ drivers to register for notification from the power_supply subsystem.
+
config POWER_SUPPLY_DEBUG
bool "Power supply debug"
help
diff --git a/drivers/power/Makefile b/drivers/power/Makefile
index b949cf8..6a63f45 100644
--- a/drivers/power/Makefile
+++ b/drivers/power/Makefile
@@ -3,6 +3,7 @@ ccflags-$(CONFIG_POWER_SUPPLY_DEBUG) := -DDEBUG
power_supply-y := power_supply_core.o
power_supply-$(CONFIG_SYSFS) += power_supply_sysfs.o
power_supply-$(CONFIG_LEDS_TRIGGERS) += power_supply_leds.o
+power_supply-$(CONFIG_POWER_SUPPLY_BATTID) += battery_id.o

obj-$(CONFIG_POWER_SUPPLY) += power_supply.o
obj-$(CONFIG_GENERIC_ADC_BATTERY) += generic-adc-battery.o
diff --git a/drivers/power/battery_id.c b/drivers/power/battery_id.c
new file mode 100644
index 0000000..f6160b6
--- /dev/null
+++ b/drivers/power/battery_id.c
@@ -0,0 +1,94 @@
+/*
+ * battery_id.c - Power supply battery identification framework
+ *
+ * Copyright (C) 2012 Intel Corporation
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ * Author: Jenny TC <[email protected]>
+ * Author: David Rajamanickam, Ajay Thomas
+ * <[email protected]>
+ */
+
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/io.h>
+#include <linux/err.h>
+#include <linux/notifier.h>
+#include <linux/power/battery_id.h>
+
+ATOMIC_NOTIFIER_HEAD(batt_id_notifier);
+
+static struct ps_batt_chg_prof *batt_property;
+static int batt_status;
+
+int batt_id_reg_notifier(struct notifier_block *nb)
+{
+ return atomic_notifier_chain_register(&batt_id_notifier, nb);
+}
+EXPORT_SYMBOL_GPL(batt_id_reg_notifier);
+
+void batt_id_unreg_notifier(struct notifier_block *nb)
+{
+ atomic_notifier_chain_unregister(&batt_id_notifier, nb);
+}
+EXPORT_SYMBOL_GPL(batt_id_unreg_notifier);
+
+
+/**
+ * battery_prop_changed - Update properties when battery connection status
+ * changes
+ * @battery_conn_stat : The current connection status of battery
+ * @batt_prop : Address of the ps_batt_chg_prof structure with the updated
+ * values passed from the calling function
+ *
+ * Whenever the battery connection status changes this function will be called
+ * to indicate a change in the status and to update the status and value of
+ * properties
+ */
+void battery_prop_changed(int battery_conn_stat,
+ struct ps_batt_chg_prof *batt_prop)
+{
+ if (batt_status != battery_conn_stat) {
+ if (battery_conn_stat == POWER_SUPPLY_BATTERY_INSERTED)
+ batt_property = batt_prop;
+ else
+ batt_property = NULL;
+
+ batt_status = battery_conn_stat;
+ }
+
+ atomic_notifier_call_chain(&batt_id_notifier,
+ 0, &(batt_property));
+
+}
+EXPORT_SYMBOL_GPL(battery_prop_changed);
+
+/**
+ * get_batt_prop - Get the battery connection status and updated properties
+ * @batt_prop : battery properties structure copied to this address
+ */
+int get_batt_prop(struct ps_batt_chg_prof *batt_prop)
+{
+ if (batt_property)
+ memcpy(batt_prop, batt_property,
+ sizeof(struct ps_batt_chg_prof));
+ else
+ return -ENOMEM;
+ return 0;
+}
+EXPORT_SYMBOL_GPL(get_batt_prop);
diff --git a/include/linux/power/battery_id.h b/include/linux/power/battery_id.h
new file mode 100644
index 0000000..9a4832e
--- /dev/null
+++ b/include/linux/power/battery_id.h
@@ -0,0 +1,50 @@
+/*
+ * battery_id.h - Power supply battery identification framework header file
+ *
+ * Copyright (C) 2012 Intel Corporation
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ * Author: Jenny TC <[email protected]>
+ * Author: David Rajamanickam, Ajay Thomas
+ * <[email protected]>
+ */
+
+#ifndef __BATTERY_ID_H__
+
+#define __BATTERY_ID_H__
+
+enum {
+ POWER_SUPPLY_BATTERY_REMOVED = 0,
+ POWER_SUPPLY_BATTERY_INSERTED,
+};
+
+/* charging profile structure definition */
+struct ps_batt_chg_prof {
+ enum batt_chrg_prof_type chrg_prof_type;
+ void *batt_prof;
+};
+
+/*For notification during battery change event*/
+extern struct atomic_notifier_head batt_id_notifier;
+
+extern void battery_prop_changed(int battery_conn_stat,
+ struct ps_batt_chg_prof *batt_prop);
+extern int get_batt_prop(struct ps_batt_chg_prof *batt_prop);
+extern int batt_id_reg_notifier(struct notifier_block *nb);
+extern void batt_id_unreg_notifier(struct notifier_block *nb);
+#endif
--
1.7.9.5

2012-10-18 11:12:32

by Tc, Jenny

[permalink] [raw]
Subject: [PATCH 3/7] power_supply : add supported charger cable feature

A charger can support different types of charger sources (cables).
It make sense to define a field to inform the power supply subsystem
what kind of cable a charger driver supports. Since a bitmask would
be the easy way to do define, it's good to have a enum which has
the bitmask definition for each cable types

Signed-off-by: Jenny TC <[email protected]>
---
include/linux/power_supply.h | 16 ++++++++++++++++
1 file changed, 16 insertions(+)

diff --git a/include/linux/power_supply.h b/include/linux/power_supply.h
index 7c06956..eea1709 100644
--- a/include/linux/power_supply.h
+++ b/include/linux/power_supply.h
@@ -163,6 +163,21 @@ enum power_supply_type {
POWER_SUPPLY_TYPE_USB_ACA, /* Accessory Charger Adapters */
};

+enum power_supply_charger_cable_type {
+ POWER_SUPPLY_CHARGER_TYPE_NONE = 0,
+ POWER_SUPPLY_CHARGER_TYPE_USB_SDP = 1 << 0,
+ POWER_SUPPLY_CHARGER_TYPE_USB_DCP = 1 << 1,
+ POWER_SUPPLY_CHARGER_TYPE_USB_CDP = 1 << 2,
+ POWER_SUPPLY_CHARGER_TYPE_USB_ACA = 1 << 3,
+ POWER_SUPPLY_CHARGER_TYPE_AC = 1 << 4,
+};
+
+#define POWER_SUPPLY_CHARGER_TYPE_USB \
+ (POWER_SUPPLY_CHARGER_TYPE_USB_SDP | \
+ POWER_SUPPLY_CHARGER_TYPE_USB_DCP | \
+ POWER_SUPPLY_CHARGER_TYPE_USB_CDP | \
+ POWER_SUPPLY_CHARGER_TYPE_USB_ACA)
+
union power_supply_propval {
int intval;
const char *strval;
@@ -175,6 +190,7 @@ struct power_supply {
size_t num_properties;

char **supplied_to;
+ unsigned long supported_cables;
size_t num_supplicants;

int (*get_property)(struct power_supply *psy,
--
1.7.9.5

2012-10-18 11:12:36

by Tc, Jenny

[permalink] [raw]
Subject: [PATCH 4/7] power_supply: add throttle state

The charger and battery temperature contribute to the
platform thermal. The only way to control the temperature
is to control the charging. The charging can be controlled in different
way. This could be disabling charger, disabling charging, adjusting CC,
or by adjusting the INLMT. This patch adds a structure to define the
charger throttle actions. Also this patch adds a throttle_states
field to the struct power_supply which can be used by the charger
driver to define it's throttle actions for different states

Signed-off-by: Jenny TC <[email protected]>
---
include/linux/power_supply.h | 13 +++++++++++++
1 file changed, 13 insertions(+)

diff --git a/include/linux/power_supply.h b/include/linux/power_supply.h
index eea1709..b4eb0af 100644
--- a/include/linux/power_supply.h
+++ b/include/linux/power_supply.h
@@ -183,6 +183,18 @@ union power_supply_propval {
const char *strval;
};

+enum psy_throttle_action {
+
+ PSY_THROTTLE_DISABLE_CHARGER = 0,
+ PSY_THROTTLE_DISABLE_CHARGING,
+ PSY_THROTTLE_CC_LIMIT,
+ PSY_THROTTLE_INPUT_LIMIT,
+};
+
+struct power_supply_throttle {
+ enum psy_throttle_action throttle_action;
+ unsigned throttle_val;
+};
struct power_supply {
const char *name;
enum power_supply_type type;
@@ -192,6 +204,7 @@ struct power_supply {
char **supplied_to;
unsigned long supported_cables;
size_t num_supplicants;
+ struct power_supply_throttle *throttle_states;

int (*get_property)(struct power_supply *psy,
enum power_supply_property psp,
--
1.7.9.5

2012-10-18 11:12:42

by Tc, Jenny

[permalink] [raw]
Subject: [PATCH 5/7] power_supply: Introduce Power Supply charging framework

The Power Supply charging framework connects multiple subsystems
to do charging in a generic way. The subsystems involves power_supply,
extcon, and thermal. With this the charging is handled in a generic way.

The framework makes use of different new features - Battery Identification
framework, pluggable charging algorithms, charger cable arbitrations etc.

At present the charging is done based on the static battery characteristics.
This is done at the boot time by passing the battery properties (max_voltage,
capacity) etc. as platform data to the charger/battery driver.
But new generation high volt batteries needs to be identified dynamically
to do charging in a safe manner. The batteries are coming with different
communication protocols. It become necessary to communicate with battery and
identify it's charging profiles before setup charging

Also the charging algorithms can vary based on the battery characteristics
and the platform characteristics. To handle charging in a generic way it's
necessary to support pluggable charging algorithms. Charging framework uses
selected algorithms based on the type of battery charging profile.
This is a simple binding and can be improved later. This may be improved to
select the algorithms based on the platform requirements. Also we can extend
this framework to plug algorithms from the user space.

The framework also introduces the charger cable arbitration. A charger may
supports multiple cables, but it may not be able to charge with multiple
cables at a time. The arbitration logic inside the framework selects the cable
based on it's capabilities and the maximum charge current the platform can
support

Also this framework exposes features to control charging on different platform
states. One such feature is thermal. The framework register with the thermal
subsystem and control charging based on the thermal subsystem requirements.

Overall this framework removes the charging logic out of the charger driver and
the charger driver can just listen to the request from the framework to set
the charger properties. This can be implemented by exposing get_property and set
property callbacks.

Signed-off-by: Jenny TC <[email protected]>
---
drivers/power/Kconfig | 8 +
drivers/power/Makefile | 1 +
drivers/power/power_supply_charger.c | 634 ++++++++++++++++++++++++++++++++++
drivers/power/power_supply_charger.h | 165 +++++++++
4 files changed, 808 insertions(+)
create mode 100644 drivers/power/power_supply_charger.c
create mode 100644 drivers/power/power_supply_charger.h

diff --git a/drivers/power/Kconfig b/drivers/power/Kconfig
index 74b297d..34850e4 100644
--- a/drivers/power/Kconfig
+++ b/drivers/power/Kconfig
@@ -8,6 +8,14 @@ menuconfig POWER_SUPPLY

if POWER_SUPPLY

+config POWER_SUPPLY_CHARGER
+ bool "Power Supply Charger"
+ help
+ Say Y here to enable the power supply charger framework. Charger
+ framework supports charging in a generic way. This allows the charger
+ drivers to keep the charging logic outside and the charger driver
+ just need to abstract the charger hardware
+
config POWER_SUPPLY_BATTID
bool "Power Supply Battery Identification Framework"
help
diff --git a/drivers/power/Makefile b/drivers/power/Makefile
index 6a63f45..ce8c230 100644
--- a/drivers/power/Makefile
+++ b/drivers/power/Makefile
@@ -3,6 +3,7 @@ ccflags-$(CONFIG_POWER_SUPPLY_DEBUG) := -DDEBUG
power_supply-y := power_supply_core.o
power_supply-$(CONFIG_SYSFS) += power_supply_sysfs.o
power_supply-$(CONFIG_LEDS_TRIGGERS) += power_supply_leds.o
+power_supply-$(CONFIG_POWER_SUPPLY_CHARGER) += power_supply_charger.o
power_supply-$(CONFIG_POWER_SUPPLY_BATTID) += battery_id.o

obj-$(CONFIG_POWER_SUPPLY) += power_supply.o
diff --git a/drivers/power/power_supply_charger.c b/drivers/power/power_supply_charger.c
new file mode 100644
index 0000000..413a26c
--- /dev/null
+++ b/drivers/power/power_supply_charger.c
@@ -0,0 +1,634 @@
+#define DEBUG
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/power_supply.h>
+#include <linux/thermal.h>
+#include <linux/extcon.h>
+#include <linux/power/battery_id.h>
+#include "power_supply.h"
+#include "power_supply_charger.h"
+
+#define MAX_CHARGER_COUNT 5
+
+static LIST_HEAD(algo_list);
+
+struct power_supply_charger {
+ bool is_cable_evt_reg;
+ /*cache battery and charger properties */
+ struct list_head chrgr_cache_lst;
+ struct list_head batt_cache_lst;
+ struct list_head evt_queue;
+ spinlock_t evt_lock;
+};
+
+struct charger_cable {
+ struct work_struct work;
+ struct notifier_block nb;
+ struct extcon_chrgr_cbl_props cable_props;
+ enum extcon_cable_name extcon_cable_type;
+ enum power_supply_charger_cable_type psy_cable_type;
+ struct extcon_specific_cable_nb extcon_dev;
+ struct extcon_dev *edev;
+};
+
+static struct power_supply_charger psy_chrgr;
+
+static struct charger_cable cable_list[] = {
+ {
+ .psy_cable_type = POWER_SUPPLY_CHARGER_TYPE_USB_SDP,
+ .extcon_cable_type = EXTCON_SDP,
+ },
+ {
+ .psy_cable_type = POWER_SUPPLY_CHARGER_TYPE_USB_CDP,
+ .extcon_cable_type = EXTCON_CDP,
+ },
+ {
+ .psy_cable_type = POWER_SUPPLY_CHARGER_TYPE_USB_DCP,
+ .extcon_cable_type = EXTCON_DCP,
+ },
+ {
+ .psy_cable_type = POWER_SUPPLY_CHARGER_TYPE_USB_ACA,
+ .extcon_cable_type = EXTCON_ACA,
+ },
+ {
+ .psy_cable_type = POWER_SUPPLY_CHARGER_TYPE_AC,
+ .extcon_cable_type = EXTCON_AC,
+ },
+};
+
+static int charger_cable_notifier(struct notifier_block *nb,
+ unsigned long event, void *ptr);
+static void charger_cable_event_worker(struct work_struct *work);
+struct charging_algo *power_supply_get_charging_algo
+ (struct power_supply *, struct ps_batt_chg_prof *);
+
+static void init_charger_cables(struct charger_cable *cable_lst, int count)
+{
+ struct charger_cable *cable;
+ struct extcon_chrgr_cbl_props cable_props;
+ const char *cable_name;
+
+ while (--count) {
+ cable = cable_lst++;
+ /* initialize cable instance */
+ INIT_WORK(&cable->work, charger_cable_event_worker);
+ cable->nb.notifier_call = charger_cable_notifier;
+ cable->cable_props.cable_stat = EXTCON_CHRGR_CABLE_DISCONNECTED;
+ cable->cable_props.mA = 0;
+ cable_name = extcon_cable_name[cable->extcon_cable_type];
+
+ if (extcon_register_interest(&cable->extcon_dev,
+ NULL, cable_name, &cable->nb))
+ continue;
+
+ cable->edev = cable->extcon_dev.edev;
+
+ if (!cable->edev)
+ continue;
+
+ if (cable->edev->get_cable_properties(cable_name,
+ (void *)&cable_props)) {
+ continue;
+
+ } else if (cable_props.cable_stat !=
+ cable->cable_props.cable_stat) {
+ cable->cable_props.cable_stat = cable_props.cable_stat;
+ cable->cable_props.mA = cable_props.mA;
+ }
+ }
+}
+
+static inline void get_cur_chrgr_prop(struct power_supply *psy,
+ struct charger_props *chrgr_prop)
+{
+ chrgr_prop->name = psy->name;
+ chrgr_prop->online = IS_ONLINE(psy);
+ chrgr_prop->present = IS_PRESENT(psy);
+ chrgr_prop->cable = CABLE_TYPE(psy);
+
+}
+
+static inline int get_chrgr_prop_cache(struct power_supply *psy,
+ struct charger_props *chrgr_cache)
+{
+
+ struct charger_props *chrgr_prop;
+ int ret = -ENODEV;
+
+ list_for_each_entry(chrgr_prop, &psy_chrgr.chrgr_cache_lst, node) {
+ if (!strcmp(chrgr_prop->name, psy->name)) {
+ memcpy(chrgr_cache, chrgr_prop, sizeof(*chrgr_cache));
+ ret = 0;
+ break;
+ }
+ }
+
+ return ret;
+}
+
+static inline void cache_chrgr_prop(struct charger_props *chrgr_prop_new)
+{
+
+ struct charger_props *chrgr_cache;
+
+ list_for_each_entry(chrgr_cache, &psy_chrgr.chrgr_cache_lst, node) {
+ if (!strcmp(chrgr_cache->name, chrgr_prop_new->name))
+ goto update_props;
+ }
+
+ chrgr_cache = kzalloc(sizeof(*chrgr_cache), GFP_KERNEL);
+ if (IS_ERR(chrgr_cache)) {
+ pr_err("%s:%dError in allocating memory\n", __FILE__, __LINE__);
+ return;
+ }
+
+ INIT_LIST_HEAD(&chrgr_cache->node);
+ list_add_tail(&chrgr_cache->node, &psy_chrgr.chrgr_cache_lst);
+
+ chrgr_cache->name = chrgr_prop_new->name;
+
+update_props:
+ chrgr_cache->status = chrgr_prop_new->status;
+ chrgr_cache->present = chrgr_prop_new->present;
+ chrgr_cache->cable = chrgr_prop_new->cable;
+}
+
+
+static inline bool is_chrgr_prop_changed(struct power_supply *psy)
+{
+
+ struct charger_props chrgr_prop_cache, chrgr_prop;
+
+ get_cur_chrgr_prop(psy, &chrgr_prop);
+ /* Get cached battery property. If no cached property available
+ * then cache the new property and return true
+ */
+ if (get_chrgr_prop_cache(psy, &chrgr_prop_cache)) {
+ cache_chrgr_prop(&chrgr_prop);
+ return true;
+ }
+
+ if (!IS_CHARGER_PROP_CHANGED(chrgr_prop, chrgr_prop_cache))
+ return false;
+
+ cache_chrgr_prop(&chrgr_prop);
+ return true;
+}
+
+static inline void cache_bat_prop(struct batt_props *bat_prop_new)
+{
+
+ struct batt_props *bat_cache;
+
+ /* Find entry in cache list. If an entry is located update
+ * the existing entry else create new entry in the list */
+ list_for_each_entry(bat_cache, &psy_chrgr.batt_cache_lst, node) {
+ if (!strcmp(bat_cache->name, bat_prop_new->name))
+ goto update_props;
+ }
+
+ bat_cache = kzalloc(sizeof(*bat_cache), GFP_KERNEL);
+ if (IS_ERR(bat_cache)) {
+ pr_err("%s:%dError in allocating memory\n", __FILE__, __LINE__);
+ return;
+ }
+ INIT_LIST_HEAD(&bat_cache->node);
+ list_add_tail(&bat_cache->node, &psy_chrgr.batt_cache_lst);
+
+ bat_cache->name = bat_prop_new->name;
+
+update_props:
+ bat_cache->voltage_now = bat_prop_new->voltage_now;
+ bat_cache->current_now = bat_prop_new->current_now;
+ bat_cache->temperature = bat_prop_new->temperature;
+ bat_cache->status = bat_prop_new->status;
+}
+
+static inline int get_bat_prop_cache(struct power_supply *psy,
+ struct batt_props *bat_cache)
+{
+
+ struct batt_props *bat_prop;
+ int ret = -ENODEV;
+
+ list_for_each_entry(bat_prop, &psy_chrgr.batt_cache_lst, node) {
+ if (!strcmp(bat_prop->name, psy->name)) {
+ memcpy(bat_cache, bat_prop, sizeof(*bat_cache));
+ ret = 0;
+ break;
+ }
+ }
+
+ return ret;
+}
+
+static inline void get_cur_bat_prop(struct power_supply *psy,
+ struct batt_props *bat_prop)
+{
+ bat_prop->name = psy->name;
+ bat_prop->voltage_now = VOLTAGE_NOW(psy) / 1000;
+ bat_prop->current_now = CURRENT_NOW(psy) / 1000;
+ bat_prop->temperature = TEMPERATURE(psy) / 10;
+ bat_prop->status = STATUS(psy);
+
+}
+
+static inline bool is_batt_prop_changed(struct power_supply *psy)
+{
+
+ struct batt_props bat_prop_cache, bat_prop;
+
+ /* Get cached battery property. If no cached property available
+ * then cache the new property and return true
+ */
+ get_cur_bat_prop(psy, &bat_prop);
+ if (get_bat_prop_cache(psy, &bat_prop_cache)) {
+ cache_bat_prop(&bat_prop);
+ return true;
+ }
+
+ if (!IS_BAT_PROP_CHANGED(bat_prop, bat_prop_cache))
+ return false;
+
+ cache_bat_prop(&bat_prop);
+ return true;
+}
+
+static inline bool is_trigger_charging_algo(struct power_supply *psy)
+{
+
+ /* trigger charging alorithm if battery or
+ * charger properties are changed
+ */
+
+ if ((IS_CHARGER(psy)) && is_chrgr_prop_changed(psy))
+ return true;
+
+ if ((IS_BATTERY(psy)) && is_batt_prop_changed(psy))
+ return true;
+
+ return false;
+}
+
+static int get_supplied_by_list(struct power_supply *psy,
+ struct power_supply *psy_lst[])
+{
+ struct class_dev_iter iter;
+ struct device *dev;
+ struct power_supply *pst;
+ int cnt = 0, i, j;
+
+ if (!IS_BATTERY(psy))
+ return 0;
+
+ /* Identify chargers which are supplying power to the battery */
+ class_dev_iter_init(&iter, power_supply_class, NULL, NULL);
+ while ((dev = class_dev_iter_next(&iter))) {
+ pst = (struct power_supply *)dev_get_drvdata(dev);
+ if (!IS_CHARGER(pst))
+ continue;
+ for (i = 0; i < pst->num_supplicants; i++) {
+ if ((!strcmp(pst->supplied_to[i], psy->name)) &&
+ IS_ONLINE(pst))
+ psy_lst[cnt++] = pst;
+ }
+ }
+ class_dev_iter_exit(&iter);
+
+ if (cnt <= 1)
+ return cnt;
+
+ /*sort based on priority. 0 has the highest priority */
+ for (i = 0; i < cnt; ++i)
+ for (j = 0; j < cnt; ++j)
+ if (PRIORITY(psy_lst[j]) > PRIORITY(psy_lst[i]))
+ swap(psy_lst[j], psy_lst[i]);
+
+ return cnt;
+}
+
+static int trigger_algo(struct power_supply *psy)
+{
+ unsigned long cc = 0, cv = 0, cc_min;
+ struct power_supply *chrgr_lst[MAX_CHARGER_COUNT];
+ struct batt_props bat_prop;
+ struct charging_algo *algo;
+ struct ps_batt_chg_prof chrg_profile;
+ int cnt;
+
+ if (psy->type != POWER_SUPPLY_TYPE_BATTERY)
+ return 0;
+
+ if (get_batt_prop(&chrg_profile)) {
+ pr_err("Error in getting charge profile:%s:%d\n", __FILE__,
+ __LINE__);
+ return -EINVAL;
+ }
+
+ get_bat_prop_cache(psy, &bat_prop);
+
+ algo = power_supply_get_charging_algo(psy, &chrg_profile);
+ if (!algo)
+ return -EINVAL;
+
+ algo->get_next_cc_cv(bat_prop, chrg_profile, &cc, &cv);
+
+ if (!cc || !cv)
+ return -ENODATA;
+
+ /* CC needs to be updated for all chargers which are supplying
+ * power to this battery to ensure that the sum of CCs of all
+ * chargers are never more than the CC selected by the algo.
+ * The CC is set based on the charger priority.
+ */
+ cnt = get_supplied_by_list(psy, chrgr_lst);
+
+ while (cnt--) {
+ cc_min = min_t(unsigned long, MAX_CC(chrgr_lst[cnt]), cc);
+ cc_min = min_t(unsigned long, INLMT(chrgr_lst[cnt]), cc_min);
+ if (cc_min < 0)
+ cc_min = 0;
+ cc -= cc_min;
+ set_cc(chrgr_lst[cnt], cc_min);
+ set_cv(chrgr_lst[cnt], cv);
+ }
+ return 0;
+}
+
+static inline void enable_supplied_by_charging
+ (struct power_supply *psy, bool is_enable)
+{
+ struct power_supply *chrgr_lst[MAX_CHARGER_COUNT];
+ int cnt;
+
+ if (psy->type != POWER_SUPPLY_TYPE_BATTERY)
+ return;
+ /* Get list of chargers supplying power to this battery and
+ * disable charging for all chargers
+ */
+ cnt = get_supplied_by_list(psy, chrgr_lst);
+ while (--cnt) {
+ if (is_enable && IS_CHARGING_CAN_BE_ENABLED(psy))
+ enable_charging(chrgr_lst[cnt]);
+ else
+ disable_charging(chrgr_lst[cnt]);
+ }
+}
+
+void power_supply_trigger_charging_handler(struct power_supply *psy)
+{
+ int i;
+ struct power_supply *psb = NULL;
+
+ if (!psy_chrgr.is_cable_evt_reg)
+ return;
+
+ if (is_trigger_charging_algo(psy)) {
+
+ if (IS_BATTERY(psy)) {
+ if (trigger_algo(psy))
+ enable_supplied_by_charging(psy, false);
+ else
+ enable_supplied_by_charging(psy, true);
+
+ } else if (IS_CHARGER(psy)) {
+ for (i = 0; i < psy->num_supplicants; i++) {
+ psb =
+ power_supply_get_by_name(psy->
+ supplied_to[i]);
+
+ if (psb && IS_BATTERY(psb) && IS_PRESENT(psb)) {
+ if (trigger_algo(psb)) {
+ disable_charging(psy);
+ break;
+ } else if (IS_CHARGING_CAN_BE_ENABLED
+ (psy)) {
+ enable_charging(psy);
+ }
+ }
+ }
+ }
+
+ }
+}
+EXPORT_SYMBOL(power_supply_trigger_charging_handler);
+
+
+static int select_chrgr_cable(struct device *dev, void *data)
+{
+ struct power_supply *psy = dev_get_drvdata(dev);
+ struct charger_cable *cable, *max_mA_cable = NULL;
+ struct charger_cable *cable_lst = (struct charger_cable *)data;
+ unsigned int max_mA = 0;
+ int i;
+
+ if (!IS_CHARGER(psy))
+ return 0;
+
+ /* get cable with maximum capability */
+ for (i = 0; i < ARRAY_SIZE(cable_list); ++i) {
+ cable = cable_lst + i;
+ if ((!IS_CABLE_ACTIVE(cable->cable_props.cable_stat)) ||
+ (!IS_SUPPORTED_CABLE(psy, cable->psy_cable_type)))
+ continue;
+
+ if (cable->cable_props.mA > max_mA) {
+ max_mA_cable = cable;
+ max_mA = cable->cable_props.mA;
+ }
+ }
+
+ /* no cable connected. disable charging */
+ if (!max_mA_cable) {
+
+ if ((IS_CHARGER_ENABLED(psy) || IS_CHARGING_ENABLED(psy))) {
+ disable_charging(psy);
+ disable_charger(psy);
+ }
+ set_cc(psy, 0);
+ set_cv(psy, 0);
+ set_inlmt(psy, 0);
+ switch_cable(psy, POWER_SUPPLY_CHARGER_TYPE_NONE);
+ return 0;
+ }
+
+ /* cable type changed.New cable connected or existing cable
+ * capabilities changed.switch cable and enable charger and charging
+ */
+
+ if (CABLE_TYPE(psy) != max_mA_cable->psy_cable_type) {
+ switch_cable(psy, max_mA_cable->psy_cable_type);
+ set_inlmt(psy, max_mA_cable->cable_props.mA);
+ } else if (INLMT(psy) != max_mA_cable->cable_props.mA) {
+ set_inlmt(psy, max_mA_cable->cable_props.mA);
+ }
+
+ power_supply_trigger_charging_handler(psy);
+ /* Cable status is same as previous. No action to be taken */
+ return 0;
+
+}
+
+static void configure_chrgr_source(struct charger_cable *cable_lst)
+{
+
+ class_for_each_device(power_supply_class, NULL,
+ cable_lst, select_chrgr_cable);
+
+}
+
+static void charger_cable_event_worker(struct work_struct *work)
+{
+ struct charger_cable *cable =
+ container_of(work, struct charger_cable, work);
+ struct extcon_chrgr_cbl_props cable_props;
+
+ if (cable->edev->
+ get_cable_properties(extcon_cable_name[cable->extcon_cable_type],
+ (void *)&cable_props)) {
+ pr_err("Erron in getting cable(%s) properties from extcon device(%s):%s:%d",
+ extcon_cable_name[cable->extcon_cable_type],
+ cable->edev->name, __FILE__, __LINE__);
+ return;
+ } else {
+ if (cable_props.cable_stat != cable->cable_props.cable_stat) {
+ cable->cable_props.cable_stat = cable_props.cable_stat;
+ cable->cable_props.mA = cable_props.mA;
+ configure_chrgr_source(cable_list);
+ }
+ }
+
+}
+
+static int charger_cable_notifier(struct notifier_block *nb,
+ unsigned long stat, void *ptr)
+{
+
+ struct charger_cable *cable =
+ container_of(nb, struct charger_cable, nb);
+
+ schedule_work(&cable->work);
+
+ return NOTIFY_DONE | NOTIFY_STOP_MASK;
+}
+
+int psy_charger_throttle_charger(struct power_supply *psy,
+ unsigned long state)
+{
+
+ if (state < 0 || state > MAX_THROTTLE_STATE(psy))
+ return -EINVAL;
+
+ switch THROTTLE_ACTION(psy, state)
+ {
+
+ case PSY_THROTTLE_DISABLE_CHARGER:
+ disable_charger(psy);
+ break;
+ case PSY_THROTTLE_DISABLE_CHARGING:
+ disable_charging(psy);
+ break;
+ case PSY_THROTTLE_CC_LIMIT:
+ SET_MAX_CC(psy, THROTTLE_CC_VALUE(psy, state));
+ power_supply_trigger_charging_handler(psy);
+ break;
+ case PSY_THROTTLE_INPUT_LIMIT:
+ set_inlmt(psy, THROTTLE_CC_VALUE(psy, state));
+ power_supply_trigger_charging_handler(psy);
+ break;
+ default:
+ pr_err("Invalid throttle action for %s\n", psy->name);
+ return -EINVAL;
+ }
+ return 0;
+}
+EXPORT_SYMBOL(psy_charger_throttle_charger);
+
+int power_supply_register_charger(struct power_supply *psy)
+{
+ int ret = 0;
+
+ if (!psy_chrgr.is_cable_evt_reg) {
+ init_charger_cables(cable_list, ARRAY_SIZE(cable_list));
+ psy_chrgr.is_cable_evt_reg = true;
+ INIT_LIST_HEAD(&psy_chrgr.chrgr_cache_lst);
+ INIT_LIST_HEAD(&psy_chrgr.batt_cache_lst);
+ }
+ return ret;
+}
+EXPORT_SYMBOL(power_supply_register_charger);
+
+int power_supply_unregister_charger(struct power_supply *psy)
+{
+ /*TBD*/
+ return 0;
+}
+EXPORT_SYMBOL(power_supply_unregister_charger);
+
+int power_supply_register_charging_algo(struct charging_algo *algo)
+{
+
+ struct charging_algo *algo_new;
+
+ algo_new = kzalloc(sizeof(*algo_new), GFP_KERNEL);
+ algo_new->get_next_cc_cv = algo->get_next_cc_cv;
+ algo_new->name = algo->name;
+ algo_new->chrg_prof_type = algo->chrg_prof_type;
+
+ list_add_tail(&algo_new->node, &algo_list);
+ return 0;
+}
+EXPORT_SYMBOL(power_supply_register_charging_algo);
+
+int power_supply_unregister_charging_algo(struct charging_algo *algo)
+{
+ struct charging_algo *algo_l, *tmp;
+
+ list_for_each_entry_safe(algo_l, tmp, &algo_list, node) {
+ if (!strcmp(algo_l->name, algo->name)) {
+ list_del(&algo_l->node);
+ kfree(algo_l);
+ }
+ }
+ return 0;
+
+}
+EXPORT_SYMBOL(power_supply_unregister_charging_algo);
+
+static struct charging_algo *get_charging_algo_byname(char *algo_name)
+{
+ struct charging_algo *algo;
+
+ list_for_each_entry(algo, &algo_list, node) {
+ if (!strcmp(algo->name, algo_name))
+ return algo;
+ }
+
+ return NULL;
+}
+
+static struct charging_algo *get_charging_algo_by_type
+ (enum batt_chrg_prof_type chrg_prof_type)
+{
+ struct charging_algo *algo;
+
+ list_for_each_entry(algo, &algo_list, node) {
+ if (algo->chrg_prof_type == chrg_prof_type)
+ return algo;
+ }
+
+ return NULL;
+}
+
+struct charging_algo *power_supply_get_charging_algo
+ (struct power_supply *psy, struct ps_batt_chg_prof *batt_prof)
+{
+
+ return get_charging_algo_by_type(batt_prof->chrg_prof_type);
+
+}
+EXPORT_SYMBOL_GPL(power_supply_get_charging_algo);
diff --git a/drivers/power/power_supply_charger.h b/drivers/power/power_supply_charger.h
new file mode 100644
index 0000000..cb17581
--- /dev/null
+++ b/drivers/power/power_supply_charger.h
@@ -0,0 +1,165 @@
+#include <linux/power_supply.h>
+
+#ifndef __POWER_SUPPLY_CHARGER_H__
+
+#define __POWER_SUPPLY_CHARGER_H__
+
+struct batt_props {
+ struct list_head node;
+ const char *name;
+ unsigned long voltage_now;
+ long current_now;
+ int temperature;
+ long status;
+};
+
+struct charger_props {
+ struct list_head node;
+ const char *name;
+ bool present;
+ bool status;
+ bool online;
+ unsigned long cable;
+};
+
+struct charging_algo {
+ struct list_head node;
+ unsigned int chrg_prof_type;
+ char *name;
+ int (*get_next_cc_cv)(struct batt_props, struct ps_batt_chg_prof,
+ unsigned long *cc, unsigned long *cv);
+};
+
+extern int power_supply_register_charging_algo(struct charging_algo *);
+extern int power_supply_unregister_charging_algo(struct charging_algo *);
+
+static inline int set_ps_int_property(struct power_supply *psy,
+ enum power_supply_property psp,
+ int prop_val)
+{
+
+ union power_supply_propval val;
+
+ val.intval = prop_val;
+ return psy->set_property(psy, psp, &val);
+}
+
+static inline int get_ps_int_property(struct power_supply *psy,
+ enum power_supply_property psp)
+{
+ union power_supply_propval val;
+
+ psy->get_property(psy, psp, &val);
+ return val.intval;
+}
+
+#define enable_charging(psy) \
+ ({if ((CABLE_TYPE(psy) != POWER_SUPPLY_CHARGER_TYPE_NONE) &&\
+ !IS_CHARGING_ENABLED(psy)) \
+ set_ps_int_property(psy, POWER_SUPPLY_PROP_ENABLE_CHARGING,\
+ true);\
+ enable_charger(psy); })
+#define disable_charging(psy) \
+ ({if ((CABLE_TYPE(psy) != POWER_SUPPLY_CHARGER_TYPE_NONE) &&\
+ IS_CHARGING_ENABLED(psy)) \
+ set_ps_int_property(psy,\
+ POWER_SUPPLY_PROP_ENABLE_CHARGING, false); })
+
+#define enable_charger(psy) \
+ set_ps_int_property(psy, POWER_SUPPLY_PROP_ENABLE_CHARGER, true)
+#define disable_charger(psy) \
+ set_ps_int_property(psy,\
+ POWER_SUPPLY_PROP_ENABLE_CHARGER, false)
+
+#define set_cc(psy, cc) \
+ set_ps_int_property(psy, POWER_SUPPLY_PROP_CHARGE_CURRENT, cc)
+
+#define set_cv(psy, cv) \
+ set_ps_int_property(psy, POWER_SUPPLY_PROP_CHARGE_VOLTAGE, cv)
+
+#define set_inlmt(psy, inlmt) \
+ set_ps_int_property(psy, POWER_SUPPLY_PROP_INLMT, inlmt)
+#define SET_MAX_CC(psy, max_cc) \
+ set_ps_int_property(psy,\
+ POWER_SUPPLY_PROP_MAX_CHARGE_CURRENT, max_cc)
+#define switch_cable(psy, new_cable) \
+ set_ps_int_property(psy,\
+ POWER_SUPPLY_PROP_CABLE_TYPE, new_cable)
+
+#define CV(psy) \
+ get_ps_int_property(psy, POWER_SUPPLY_PROP_CHARGE_VOLTAGE)
+#define CC(psy) \
+ get_ps_int_property(psy, POWER_SUPPLY_PROP_CHARGE_CURRENT)
+#define INLMT(psy) \
+ get_ps_int_property(psy, POWER_SUPPLY_PROP_INLMT)
+#define MAX_CC(psy) \
+ get_ps_int_property(psy, POWER_SUPPLY_PROP_MAX_CHARGE_CURRENT)
+#define MAX_CV(psy) \
+ get_ps_int_property(psy, POWER_SUPPLY_PROP_MAX_CHARGE_VOLTAGE)
+#define VOLTAGE_NOW(psy) \
+ get_ps_int_property(psy, POWER_SUPPLY_PROP_VOLTAGE_NOW)
+#define CURRENT_NOW(psy) \
+ get_ps_int_property(psy, POWER_SUPPLY_PROP_CURRENT_NOW)
+#define STATUS(psy) \
+ get_ps_int_property(psy, POWER_SUPPLY_PROP_STATUS)
+#define TEMPERATURE(psy) \
+ get_ps_int_property(psy, POWER_SUPPLY_PROP_TEMP)
+#define BATTERY_TYPE(psy) \
+ get_ps_int_property(psy, POWER_SUPPLY_PROP_TECHNOLOGY)
+#define PRIORITY(psy) \
+ get_ps_int_property(psy, POWER_SUPPLY_PROP_PRIORITY)
+#define CABLE_TYPE(psy) \
+ get_ps_int_property(psy, POWER_SUPPLY_PROP_CABLE_TYPE)
+
+#define IS_CHARGING_ENABLED(psy) \
+ get_ps_int_property(psy, POWER_SUPPLY_PROP_ENABLE_CHARGING)
+#define IS_CHARGER_ENABLED(psy) \
+ get_ps_int_property(psy, POWER_SUPPLY_PROP_ENABLE_CHARGER)
+#define IS_BATTERY(psy) (psy->type == POWER_SUPPLY_TYPE_BATTERY)
+#define IS_CHARGER(psy) (psy->type == POWER_SUPPLY_TYPE_USB ||\
+ psy->type == POWER_SUPPLY_TYPE_USB_CDP || \
+ psy->type == POWER_SUPPLY_TYPE_USB_DCP ||\
+ psy->type == POWER_SUPPLY_TYPE_USB_ACA)
+#define IS_ONLINE(psy) \
+ (get_ps_int_property(psy, POWER_SUPPLY_PROP_ONLINE) == 1)
+#define IS_PRESENT(psy) \
+ (get_ps_int_property(psy, POWER_SUPPLY_PROP_PRESENT) == 1)
+#define IS_SUPPORTED_CABLE(psy, cable_type) \
+ (psy->supported_cables & cable_type)
+#define IS_CABLE_ACTIVE(status) \
+ ((status != EXTCON_CHRGR_CABLE_DISCONNECTED) ||\
+ (status != EXTCON_CHRGR_CABLE_SUSPENDED))
+
+#define IS_CHARGER_PROP_CHANGED(prop, cache_prop)\
+ ((cache_prop.online != prop.online) || \
+ (cache_prop.present != prop.present))
+
+#define IS_BAT_PROP_CHANGED(bat_prop, bat_cache)\
+ ((bat_cache.voltage_now != bat_prop.voltage_now) || \
+ (bat_cache.current_now != bat_prop.current_now) || \
+ (bat_cache.temperature != bat_prop.temperature))
+
+#define THROTTLE_ACTION(psy, state)\
+ (((psy->throttle_states)+state)->throttle_action)
+
+#define MAX_THROTTLE_STATE(psy)\
+ (get_ps_int_property(psy,\
+ POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT_MAX))
+
+#define CURRENT_THROTTLE_STATE(psy)\
+ (get_ps_int_property(psy,\
+ POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT))
+
+#define CURRENT_THROTTLE_ACTION(psy)\
+ THROTTLE_ACTION(psy, CURRENT_THROTTLE_STATE(psy))
+
+#define THROTTLE_CC_VALUE(psy, state)\
+ (((psy->throttle_states)+state)->throttle_val)
+
+#define IS_CHARGING_CAN_BE_ENABLED(psy) \
+ ((CURRENT_THROTTLE_ACTION(psy) != PSY_THROTTLE_DISABLE_CHARGER) &&\
+ (CURRENT_THROTTLE_ACTION(psy) != PSY_THROTTLE_DISABLE_CHARGING))
+#define IS_CHARGER_CAN_BE_ENABLED(psy) \
+ (CURRENT_THROTTLE_ACTION(psy) != PSY_THROTTLE_DISABLE_CHARGER)
+
+#endif
--
1.7.9.5

2012-10-18 11:12:48

by Tc, Jenny

[permalink] [raw]
Subject: [PATCH 6/7] power_supply: enable charger framework callbacks

The charger framework needs to be notified on registering
a new charger, on power supply changed event or on a thermal
throttle request. This patch enables support for the same
Signed-off-by: Jenny TC <[email protected]>
---
drivers/power/power_supply.h | 20 ++++++++++++++++++++
drivers/power/power_supply_core.c | 12 ++++++++++++
2 files changed, 32 insertions(+)

diff --git a/drivers/power/power_supply.h b/drivers/power/power_supply.h
index cc439fd..ae2d66c 100644
--- a/drivers/power/power_supply.h
+++ b/drivers/power/power_supply.h
@@ -40,3 +40,23 @@ static inline int power_supply_create_triggers(struct power_supply *psy)
static inline void power_supply_remove_triggers(struct power_supply *psy) {}

#endif /* CONFIG_LEDS_TRIGGERS */
+#ifdef CONFIG_POWER_SUPPLY_CHARGER
+
+extern void power_supply_trigger_charging_handler(struct power_supply *psy);
+extern int power_supply_register_charger(struct power_supply *psy);
+extern int power_supply_unregister_charger(struct power_supply *psy);
+extern int psy_charger_throttle_charger(struct power_supply *psy,
+ unsigned long state);
+
+#else
+
+static inline void power_supply_trigger_charging_handler(struct power_supply *psy) { }
+static inline int power_supply_register_charger(struct power_supply *psy)
+{ return 0; }
+static inline int power_supply_unregister_charger(struct power_supply *psy)
+{ return 0; }
+static inline int psy_charger_throttle_charger(struct power_supply *psy,
+ unsigned long state)
+{ return 0; }
+
+#endif
diff --git a/drivers/power/power_supply_core.c b/drivers/power/power_supply_core.c
index 3338d49..73ac6e4 100644
--- a/drivers/power/power_supply_core.c
+++ b/drivers/power/power_supply_core.c
@@ -19,6 +19,7 @@
#include <linux/power_supply.h>
#include <linux/thermal.h>
#include "power_supply.h"
+#include "power_supply_charger.h"

/* exported for the APM Power driver, APM emulation */
struct class *power_supply_class;
@@ -49,6 +50,7 @@ static void power_supply_changed_work(struct work_struct *work)

class_for_each_device(power_supply_class, NULL, psy,
__power_supply_changed_work);
+ power_supply_trigger_charging_handler(psy);

power_supply_update_leds(psy);

@@ -265,6 +267,8 @@ static int ps_set_cur_charge_cntl_limit(struct thermal_cooling_device *tcd,
ret = psy->set_property(psy,
POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT, &val);

+ psy_charger_throttle_charger(psy,state);
+
return ret;
}

@@ -360,10 +364,16 @@ int power_supply_register(struct device *parent, struct power_supply *psy)
if (rc)
goto create_triggers_failed;

+ if (IS_CHARGER(psy))
+ rc = power_supply_register_charger(psy);
+ if (rc)
+ goto charger_register_failed;
+
power_supply_changed(psy);

goto success;

+charger_register_failed:
create_triggers_failed:
psy_unregister_cooler(psy);
register_cooler_failed:
@@ -382,6 +392,8 @@ void power_supply_unregister(struct power_supply *psy)
{
cancel_work_sync(&psy->changed_work);
sysfs_remove_link(&psy->dev->kobj, "powers");
+ if (IS_CHARGER(psy))
+ power_supply_unregister_charger(psy);
power_supply_remove_triggers(psy);
psy_unregister_cooler(psy);
psy_unregister_thermal(psy);
--
1.7.9.5

2012-10-18 11:13:03

by Tc, Jenny

[permalink] [raw]
Subject: [PATCH 7/7] power_supply: Introduce PSE compliant algorithm

As per PSE standard the battery characteristics and thereby the
charging rates can vary on different temperature zones. This
patch introduces a PSE compliant charging algorithm with
maintenance charging support. The algorithm can be selected by the
charging framework based on the type of the battery charging profile.

Signed-off-by: Jenny TC <[email protected]>
---
drivers/power/Kconfig | 12 ++++++
drivers/power/Makefile | 1 +
drivers/power/charging_algo_pse.c | 79 +++++++++++++++++++++++++++++++++++++
include/linux/power/battery_id.h | 47 ++++++++++++++++++++++
4 files changed, 139 insertions(+)
create mode 100644 drivers/power/charging_algo_pse.c

diff --git a/drivers/power/Kconfig b/drivers/power/Kconfig
index 34850e4..e09be69 100644
--- a/drivers/power/Kconfig
+++ b/drivers/power/Kconfig
@@ -16,6 +16,18 @@ config POWER_SUPPLY_CHARGER
drivers to keep the charging logic outside and the charger driver
just need to abstract the charger hardware

+config POWER_SUPPLY_CHARGING_ALGO_PSE
+ bool "PSE compliant charging algorithm"
+ help
+ Say Y here to select PSE compliant charging algorithm. As per PSE
+ standard the battery characteristics and thereby the charging rates
+ can vary on different temperature zones. This config will enable PSE
+ compliant charging algorithm with maintenance charging support. The
+ algorithm can be selected by the charging framework based on the type
+ of the battery charging profile.
+
+ depends on POWER_SUPPLY_CHARGER
+
config POWER_SUPPLY_BATTID
bool "Power Supply Battery Identification Framework"
help
diff --git a/drivers/power/Makefile b/drivers/power/Makefile
index ce8c230..8b250b2 100644
--- a/drivers/power/Makefile
+++ b/drivers/power/Makefile
@@ -8,6 +8,7 @@ power_supply-$(CONFIG_POWER_SUPPLY_BATTID) += battery_id.o

obj-$(CONFIG_POWER_SUPPLY) += power_supply.o
obj-$(CONFIG_GENERIC_ADC_BATTERY) += generic-adc-battery.o
+obj-$(CONFIG_POWER_SUPPLY_CHARGING_ALGO_PSE) += charging_algo_pse.o

obj-$(CONFIG_PDA_POWER) += pda_power.o
obj-$(CONFIG_APM_POWER) += apm_power.o
diff --git a/drivers/power/charging_algo_pse.c b/drivers/power/charging_algo_pse.c
new file mode 100644
index 0000000..66feff8
--- /dev/null
+++ b/drivers/power/charging_algo_pse.c
@@ -0,0 +1,79 @@
+#define DEBUG
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/power_supply.h>
+#include <linux/thermal.h>
+#include <linux/power/battery_id.h>
+#include "power_supply.h"
+#include "power_supply_charger.h"
+
+static int get_tempzone(struct ps_pse_mod_prof *pse_mod_bprof,
+ int temp)
+{
+
+ int i = 0;
+ int temp_range_cnt = pse_mod_bprof->temp_mon_ranges;
+
+ if (temp < pse_mod_bprof->temp_low_lim
+ || temp >
+ pse_mod_bprof->temp_mon_range[0].temp_up_lim)
+ return -EINVAL;
+
+ for (i = 0; i < temp_range_cnt; ++i)
+ if (temp <= pse_mod_bprof->temp_mon_range[i].temp_up_lim)
+ break;
+ return i;
+}
+
+static int pse_get_next_cc_cv(struct batt_props bat_prop,
+ struct ps_batt_chg_prof bprof, unsigned long *cc, unsigned long *cv)
+{
+ int tzone;
+ struct ps_pse_mod_prof *pse_mod_bprof;
+
+
+ if (bprof.chrg_prof_type != PSE_MOD_CHRG_PROF)
+ return -EINVAL;
+
+ pse_mod_bprof = (struct ps_pse_mod_prof *) bprof.batt_prof;
+
+ if (!pse_mod_bprof)
+ return -EINVAL;
+
+ tzone = get_tempzone(pse_mod_bprof, bat_prop.temperature);
+
+ if (tzone < 0)
+ return -ENODATA;
+
+ /* read cc and cv based on temperature and battery status*/
+
+ *cc = pse_mod_bprof->temp_mon_range[tzone].full_chrg_cur;
+ if (bat_prop.status == POWER_SUPPLY_STATUS_FULL)
+ *cv = pse_mod_bprof->temp_mon_range[tzone].maint_chrg_vol_ul;
+ else
+ *cv = pse_mod_bprof->temp_mon_range[tzone].full_chrg_vol;
+
+ /* Software full detection: Set cc and cv to zero if FULL battery
+ * condition is met
+ */
+ if ((bat_prop.current_now <= pse_mod_bprof->chrg_term_mA) &&
+ (pse_mod_bprof->temp_mon_range[tzone].full_chrg_vol >= cv))
+ *cc = *cv = 0;
+ return 0;
+}
+
+static int __init pse_algo_init(void)
+{
+ struct charging_algo pse_algo;
+ pse_algo.chrg_prof_type = PSE_MOD_CHRG_PROF;
+ pse_algo.name = "pse_algo";
+ pse_algo.get_next_cc_cv = pse_get_next_cc_cv;
+ power_supply_register_charging_algo(&pse_algo);
+ return 0;
+}
+
+module_init(pse_algo_init);
diff --git a/include/linux/power/battery_id.h b/include/linux/power/battery_id.h
index 9a4832e..860a8b8 100644
--- a/include/linux/power/battery_id.h
+++ b/include/linux/power/battery_id.h
@@ -33,12 +33,59 @@ enum {
POWER_SUPPLY_BATTERY_INSERTED,
};

+enum batt_chrg_prof_type {
+ PSE_MOD_CHRG_PROF = 0,
+};
+
/* charging profile structure definition */
struct ps_batt_chg_prof {
enum batt_chrg_prof_type chrg_prof_type;
void *batt_prof;
};

+/* PSE Modified Algo Structure */
+/* Parameters defining the charging range */
+struct ps_temp_chg_table {
+ /* upper temperature limit for each zone */
+ short int temp_up_lim;
+ /* charge current and voltage */
+ short int full_chrg_vol;
+ short int full_chrg_cur;
+ /* maintenance thresholds */
+ /* maintenance lower threshold. Once battery hits full, charging
+ * charging will be resumed when battery voltage <= this voltage
+ */
+ short int maint_chrg_vol_ll;
+ /* Charge current and voltage in maintenance mode */
+ short int maint_chrg_vol_ul;
+ short int maint_chrg_cur;
+} __packed;
+
+
+#define BATTID_STR_LEN 8
+#define BATT_TEMP_NR_RNG 6
+/* Charging Profile */
+struct ps_pse_mod_prof {
+ /* battery id */
+ char batt_id[BATTID_STR_LEN];
+ /* type of battery */
+ u16 battery_type;
+ u16 capacity;
+ u16 voltage_max;
+ /* charge termination current */
+ u16 chrg_term_mA;
+ /* Low battery level voltage */
+ u16 low_batt_mV;
+ /* upper and lower temperature limits on discharging */
+ u8 disch_tmp_ul;
+ u8 disch_tmp_ll;
+ /* number of temperature monitoring ranges */
+ u8 temp_mon_ranges;
+ struct ps_temp_chg_table temp_mon_range[BATT_TEMP_NR_RNG];
+ /* Lowest temperature supported */
+ short int temp_low_lim;
+} __packed;
+
/*For notification during battery change event*/
extern struct atomic_notifier_head batt_id_notifier;

--
1.7.9.5

2012-10-18 11:12:29

by Tc, Jenny

[permalink] [raw]
Subject: [PATCH 2/7] power_supply: Add charger control properties

The battery charger needs to have control path along
with the reporting charger properties. In existing solutions
this is implemented using regulator framework. A regulator
framework doesn't fit a charger driver requirement because of the
following reason
Charger needs support two paths - charger path (charger to platform)
and charging (charger to battery).Disabling the charging path alone
(eg over battery temperature) will allow the platform to work with
power from charger without discharging the battery. And the charger
may need to be disabled completely based on the charger temperature
or the platform temperature.
Charger has more than one pair of voltage/current to control (CC,CV,INLMT)
These features will not directly fit in the regulator framework

Since the charger driver sits in the power supply subsystem it make sense
to add the properties to control the charger.

Also this patch adds/modify the power supply properties to meet the
requirements for charger control

Signed-off-by: Jenny TC <[email protected]>
---
drivers/power/power_supply_sysfs.c | 16 ++++++++++++----
include/linux/power_supply.h | 24 ++++++++++++++++--------
2 files changed, 28 insertions(+), 12 deletions(-)

diff --git a/drivers/power/power_supply_sysfs.c b/drivers/power/power_supply_sysfs.c
index 40fa3b7..3166d00 100644
--- a/drivers/power/power_supply_sysfs.c
+++ b/drivers/power/power_supply_sysfs.c
@@ -160,12 +160,13 @@ static struct device_attribute power_supply_attrs[] = {
POWER_SUPPLY_ATTR(charge_now),
POWER_SUPPLY_ATTR(charge_avg),
POWER_SUPPLY_ATTR(charge_counter),
- POWER_SUPPLY_ATTR(constant_charge_current),
- POWER_SUPPLY_ATTR(constant_charge_current_max),
- POWER_SUPPLY_ATTR(constant_charge_voltage),
- POWER_SUPPLY_ATTR(constant_charge_voltage_max),
+ POWER_SUPPLY_ATTR(charge_current),
+ POWER_SUPPLY_ATTR(max_charge_current),
+ POWER_SUPPLY_ATTR(charge_voltage),
+ POWER_SUPPLY_ATTR(max_charge_voltage),
POWER_SUPPLY_ATTR(charge_control_limit),
POWER_SUPPLY_ATTR(charge_control_limit_max),
+ POWER_SUPPLY_ATTR(input_cur_limit),
POWER_SUPPLY_ATTR(energy_full_design),
POWER_SUPPLY_ATTR(energy_empty_design),
POWER_SUPPLY_ATTR(energy_full),
@@ -177,6 +178,8 @@ static struct device_attribute power_supply_attrs[] = {
POWER_SUPPLY_ATTR(capacity_alert_max),
POWER_SUPPLY_ATTR(capacity_level),
POWER_SUPPLY_ATTR(temp),
+ POWER_SUPPLY_ATTR(max_temp),
+ POWER_SUPPLY_ATTR(min_temp),
POWER_SUPPLY_ATTR(temp_alert_min),
POWER_SUPPLY_ATTR(temp_alert_max),
POWER_SUPPLY_ATTR(temp_ambient),
@@ -188,6 +191,11 @@ static struct device_attribute power_supply_attrs[] = {
POWER_SUPPLY_ATTR(time_to_full_avg),
POWER_SUPPLY_ATTR(type),
POWER_SUPPLY_ATTR(scope),
+ POWER_SUPPLY_ATTR(charge_term_cur),
+ POWER_SUPPLY_ATTR(enable_charging),
+ POWER_SUPPLY_ATTR(enable_charger),
+ POWER_SUPPLY_ATTR(cable_type),
+ POWER_SUPPLY_ATTR(priority),
/* Properties of type `const char *' */
POWER_SUPPLY_ATTR(model_name),
POWER_SUPPLY_ATTR(manufacturer),
diff --git a/include/linux/power_supply.h b/include/linux/power_supply.h
index 1f0ab90..7c06956 100644
--- a/include/linux/power_supply.h
+++ b/include/linux/power_supply.h
@@ -110,12 +110,13 @@ enum power_supply_property {
POWER_SUPPLY_PROP_CHARGE_NOW,
POWER_SUPPLY_PROP_CHARGE_AVG,
POWER_SUPPLY_PROP_CHARGE_COUNTER,
- POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT,
- POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX,
- POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE,
- POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE_MAX,
+ POWER_SUPPLY_PROP_CHARGE_CURRENT,
+ POWER_SUPPLY_PROP_MAX_CHARGE_CURRENT,
+ POWER_SUPPLY_PROP_CHARGE_VOLTAGE,
+ POWER_SUPPLY_PROP_MAX_CHARGE_VOLTAGE,
POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT,
POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT_MAX,
+ POWER_SUPPLY_PROP_INLMT,
POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN,
POWER_SUPPLY_PROP_ENERGY_EMPTY_DESIGN,
POWER_SUPPLY_PROP_ENERGY_FULL,
@@ -127,6 +128,8 @@ enum power_supply_property {
POWER_SUPPLY_PROP_CAPACITY_ALERT_MAX, /* in percents! */
POWER_SUPPLY_PROP_CAPACITY_LEVEL,
POWER_SUPPLY_PROP_TEMP,
+ POWER_SUPPLY_PROP_MAX_TEMP,
+ POWER_SUPPLY_PROP_MIN_TEMP,
POWER_SUPPLY_PROP_TEMP_ALERT_MIN,
POWER_SUPPLY_PROP_TEMP_ALERT_MAX,
POWER_SUPPLY_PROP_TEMP_AMBIENT,
@@ -138,6 +141,11 @@ enum power_supply_property {
POWER_SUPPLY_PROP_TIME_TO_FULL_AVG,
POWER_SUPPLY_PROP_TYPE, /* use power_supply.type instead */
POWER_SUPPLY_PROP_SCOPE,
+ POWER_SUPPLY_PROP_CHARGE_TERM_CUR,
+ POWER_SUPPLY_PROP_ENABLE_CHARGING,
+ POWER_SUPPLY_PROP_ENABLE_CHARGER,
+ POWER_SUPPLY_PROP_CABLE_TYPE,
+ POWER_SUPPLY_PROP_PRIORITY,
/* Properties of type `const char *' */
POWER_SUPPLY_PROP_MODEL_NAME,
POWER_SUPPLY_PROP_MANUFACTURER,
@@ -253,8 +261,8 @@ static inline bool power_supply_is_amp_property(enum power_supply_property psp)
case POWER_SUPPLY_PROP_CHARGE_NOW:
case POWER_SUPPLY_PROP_CHARGE_AVG:
case POWER_SUPPLY_PROP_CHARGE_COUNTER:
- case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT:
- case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX:
+ case POWER_SUPPLY_PROP_CHARGE_CURRENT:
+ case POWER_SUPPLY_PROP_MAX_CHARGE_CURRENT:
case POWER_SUPPLY_PROP_CURRENT_MAX:
case POWER_SUPPLY_PROP_CURRENT_NOW:
case POWER_SUPPLY_PROP_CURRENT_AVG:
@@ -281,9 +289,9 @@ static inline bool power_supply_is_watt_property(enum power_supply_property psp)
case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN:
case POWER_SUPPLY_PROP_VOLTAGE_NOW:
case POWER_SUPPLY_PROP_VOLTAGE_AVG:
+ case POWER_SUPPLY_PROP_CHARGE_VOLTAGE:
case POWER_SUPPLY_PROP_VOLTAGE_OCV:
- case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE:
- case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE_MAX:
+ case POWER_SUPPLY_PROP_MAX_CHARGE_VOLTAGE:
case POWER_SUPPLY_PROP_POWER_NOW:
return 1;
default:
--
1.7.9.5

2012-11-19 00:53:32

by Anton Vorontsov

[permalink] [raw]
Subject: Re: [PATCH 2/7] power_supply: Add charger control properties

On Thu, Oct 18, 2012 at 10:14:01PM +0530, Jenny TC wrote:
> The battery charger needs to have control path along
> with the reporting charger properties. In existing solutions
> this is implemented using regulator framework. A regulator
> framework doesn't fit a charger driver requirement because of the
> following reason
> Charger needs support two paths - charger path (charger to platform)
> and charging (charger to battery).Disabling the charging path alone
> (eg over battery temperature) will allow the platform to work with
> power from charger without discharging the battery. And the charger
> may need to be disabled completely based on the charger temperature
> or the platform temperature.
> Charger has more than one pair of voltage/current to control (CC,CV,INLMT)
> These features will not directly fit in the regulator framework
>
> Since the charger driver sits in the power supply subsystem it make sense
> to add the properties to control the charger.
>
> Also this patch adds/modify the power supply properties to meet the
> requirements for charger control
>
> Signed-off-by: Jenny TC <[email protected]>
> ---
> drivers/power/power_supply_sysfs.c | 16 ++++++++++++----
> include/linux/power_supply.h | 24 ++++++++++++++++--------
> 2 files changed, 28 insertions(+), 12 deletions(-)
>
> diff --git a/drivers/power/power_supply_sysfs.c b/drivers/power/power_supply_sysfs.c
> index 40fa3b7..3166d00 100644
> --- a/drivers/power/power_supply_sysfs.c
> +++ b/drivers/power/power_supply_sysfs.c
> @@ -160,12 +160,13 @@ static struct device_attribute power_supply_attrs[] = {
> POWER_SUPPLY_ATTR(charge_now),
> POWER_SUPPLY_ATTR(charge_avg),
> POWER_SUPPLY_ATTR(charge_counter),
> - POWER_SUPPLY_ATTR(constant_charge_current),
> - POWER_SUPPLY_ATTR(constant_charge_current_max),
> - POWER_SUPPLY_ATTR(constant_charge_voltage),
> - POWER_SUPPLY_ATTR(constant_charge_voltage_max),

Sorry, but you can't just remove properites like this. They're already
used by a lot of drivers.

Thanks,
Anton.

2012-11-19 00:56:06

by Anton Vorontsov

[permalink] [raw]
Subject: Re: [PATCH 3/7] power_supply : add supported charger cable feature

On Thu, Oct 18, 2012 at 10:14:02PM +0530, Jenny TC wrote:
> A charger can support different types of charger sources (cables).
> It make sense to define a field to inform the power supply subsystem
> what kind of cable a charger driver supports. Since a bitmask would
> be the easy way to do define, it's good to have a enum which has
> the bitmask definition for each cable types
>
> Signed-off-by: Jenny TC <[email protected]>
> ---
> include/linux/power_supply.h | 16 ++++++++++++++++
> 1 file changed, 16 insertions(+)
>
> diff --git a/include/linux/power_supply.h b/include/linux/power_supply.h
> index 7c06956..eea1709 100644
> --- a/include/linux/power_supply.h
> +++ b/include/linux/power_supply.h
> @@ -163,6 +163,21 @@ enum power_supply_type {
> POWER_SUPPLY_TYPE_USB_ACA, /* Accessory Charger Adapters */
> };
>
> +enum power_supply_charger_cable_type {
> + POWER_SUPPLY_CHARGER_TYPE_NONE = 0,
> + POWER_SUPPLY_CHARGER_TYPE_USB_SDP = 1 << 0,
> + POWER_SUPPLY_CHARGER_TYPE_USB_DCP = 1 << 1,
> + POWER_SUPPLY_CHARGER_TYPE_USB_CDP = 1 << 2,
> + POWER_SUPPLY_CHARGER_TYPE_USB_ACA = 1 << 3,
> + POWER_SUPPLY_CHARGER_TYPE_AC = 1 << 4,
> +};
> +
> +#define POWER_SUPPLY_CHARGER_TYPE_USB \
> + (POWER_SUPPLY_CHARGER_TYPE_USB_SDP | \
> + POWER_SUPPLY_CHARGER_TYPE_USB_DCP | \
> + POWER_SUPPLY_CHARGER_TYPE_USB_CDP | \
> + POWER_SUPPLY_CHARGER_TYPE_USB_ACA)
> +
> union power_supply_propval {
> int intval;
> const char *strval;
> @@ -175,6 +190,7 @@ struct power_supply {
> size_t num_properties;
>
> char **supplied_to;
> + unsigned long supported_cables;

Enums have an integer resolution. Why do we need long type here?..

Thanks.

> size_t num_supplicants;
>
> int (*get_property)(struct power_supply *psy,
> --
> 1.7.9.5

2012-11-19 02:42:22

by Anton Vorontsov

[permalink] [raw]
Subject: Re: [PATCH 5/7] power_supply: Introduce Power Supply charging framework

On Thu, Oct 18, 2012 at 10:14:04PM +0530, Jenny TC wrote:
> The Power Supply charging framework connects multiple subsystems
> to do charging in a generic way. The subsystems involves power_supply,
> extcon, and thermal. With this the charging is handled in a generic way.
[...]
> +void power_supply_trigger_charging_handler(struct power_supply *psy)
> +{
> + int i;
> + struct power_supply *psb = NULL;
> +
> + if (!psy_chrgr.is_cable_evt_reg)
> + return;
> +
> + if (is_trigger_charging_algo(psy)) {

if (!is_trigger_charging_algo(psy))
return;

> + if (IS_BATTERY(psy)) {
> + if (trigger_algo(psy))
> + enable_supplied_by_charging(psy, false);
> + else
> + enable_supplied_by_charging(psy, true);
> +
> + } else if (IS_CHARGER(psy)) {
> + for (i = 0; i < psy->num_supplicants; i++) {
> + psb =
> + power_supply_get_by_name(psy->
> + supplied_to[i]);
> +
> + if (psb && IS_BATTERY(psb) && IS_PRESENT(psb)) {
> + if (trigger_algo(psb)) {
> + disable_charging(psy);
> + break;
> + } else if (IS_CHARGING_CAN_BE_ENABLED
> + (psy)) {
> + enable_charging(psy);
> + }
> + }
> + }
> + }
> +
> + }
> +}
[...]
> @@ -0,0 +1,165 @@
> +#include <linux/power_supply.h>
> +
> +#ifndef __POWER_SUPPLY_CHARGER_H__
> +

No need for this empty line.

> +#define __POWER_SUPPLY_CHARGER_H__
> +
> +struct batt_props {
> + struct list_head node;
> + const char *name;
> + unsigned long voltage_now;
> + long current_now;
> + int temperature;
> + long status;
> +};
> +
> +struct charger_props {
> + struct list_head node;
> + const char *name;
> + bool present;
> + bool status;
> + bool online;
> + unsigned long cable;
> +};
> +
> +struct charging_algo {

I guess it's better to prefix these w/ psy_ or power_.

> + struct list_head node;
> + unsigned int chrg_prof_type;
> + char *name;
> + int (*get_next_cc_cv)(struct batt_props, struct ps_batt_chg_prof,
> + unsigned long *cc, unsigned long *cv);
> +};
> +
> +extern int power_supply_register_charging_algo(struct charging_algo *);
> +extern int power_supply_unregister_charging_algo(struct charging_algo *);
> +
> +static inline int set_ps_int_property(struct power_supply *psy,
> + enum power_supply_property psp,
> + int prop_val)
> +{
> +
> + union power_supply_propval val;
> +
> + val.intval = prop_val;
> + return psy->set_property(psy, psp, &val);
> +}
> +
> +static inline int get_ps_int_property(struct power_supply *psy,
> + enum power_supply_property psp)
> +{
> + union power_supply_propval val;
> +
> + psy->get_property(psy, psp, &val);
> + return val.intval;
> +}
> +
> +#define enable_charging(psy) \
> + ({if ((CABLE_TYPE(psy) != POWER_SUPPLY_CHARGER_TYPE_NONE) &&\
> + !IS_CHARGING_ENABLED(psy)) \
> + set_ps_int_property(psy, POWER_SUPPLY_PROP_ENABLE_CHARGING,\
> + true);\
> + enable_charger(psy); })
> +#define disable_charging(psy) \
> + ({if ((CABLE_TYPE(psy) != POWER_SUPPLY_CHARGER_TYPE_NONE) &&\
> + IS_CHARGING_ENABLED(psy)) \
> + set_ps_int_property(psy,\
> + POWER_SUPPLY_PROP_ENABLE_CHARGING, false); })
> +
> +#define enable_charger(psy) \
> + set_ps_int_property(psy, POWER_SUPPLY_PROP_ENABLE_CHARGER, true)
> +#define disable_charger(psy) \
> + set_ps_int_property(psy,\
> + POWER_SUPPLY_PROP_ENABLE_CHARGER, false)
> +
> +#define set_cc(psy, cc) \
> + set_ps_int_property(psy, POWER_SUPPLY_PROP_CHARGE_CURRENT, cc)
> +
> +#define set_cv(psy, cv) \
> + set_ps_int_property(psy, POWER_SUPPLY_PROP_CHARGE_VOLTAGE, cv)
> +
> +#define set_inlmt(psy, inlmt) \
> + set_ps_int_property(psy, POWER_SUPPLY_PROP_INLMT, inlmt)
> +#define SET_MAX_CC(psy, max_cc) \
> + set_ps_int_property(psy,\
> + POWER_SUPPLY_PROP_MAX_CHARGE_CURRENT, max_cc)
> +#define switch_cable(psy, new_cable) \
> + set_ps_int_property(psy,\
> + POWER_SUPPLY_PROP_CABLE_TYPE, new_cable)
> +
> +#define CV(psy) \
> + get_ps_int_property(psy, POWER_SUPPLY_PROP_CHARGE_VOLTAGE)
> +#define CC(psy) \
> + get_ps_int_property(psy, POWER_SUPPLY_PROP_CHARGE_CURRENT)
> +#define INLMT(psy) \
> + get_ps_int_property(psy, POWER_SUPPLY_PROP_INLMT)
> +#define MAX_CC(psy) \
> + get_ps_int_property(psy, POWER_SUPPLY_PROP_MAX_CHARGE_CURRENT)
> +#define MAX_CV(psy) \
> + get_ps_int_property(psy, POWER_SUPPLY_PROP_MAX_CHARGE_VOLTAGE)
> +#define VOLTAGE_NOW(psy) \
> + get_ps_int_property(psy, POWER_SUPPLY_PROP_VOLTAGE_NOW)
> +#define CURRENT_NOW(psy) \
> + get_ps_int_property(psy, POWER_SUPPLY_PROP_CURRENT_NOW)
> +#define STATUS(psy) \
> + get_ps_int_property(psy, POWER_SUPPLY_PROP_STATUS)
> +#define TEMPERATURE(psy) \
> + get_ps_int_property(psy, POWER_SUPPLY_PROP_TEMP)
> +#define BATTERY_TYPE(psy) \
> + get_ps_int_property(psy, POWER_SUPPLY_PROP_TECHNOLOGY)
> +#define PRIORITY(psy) \
> + get_ps_int_property(psy, POWER_SUPPLY_PROP_PRIORITY)
> +#define CABLE_TYPE(psy) \
> + get_ps_int_property(psy, POWER_SUPPLY_PROP_CABLE_TYPE)
> +
> +#define IS_CHARGING_ENABLED(psy) \
> + get_ps_int_property(psy, POWER_SUPPLY_PROP_ENABLE_CHARGING)
> +#define IS_CHARGER_ENABLED(psy) \
> + get_ps_int_property(psy, POWER_SUPPLY_PROP_ENABLE_CHARGER)
> +#define IS_BATTERY(psy) (psy->type == POWER_SUPPLY_TYPE_BATTERY)
> +#define IS_CHARGER(psy) (psy->type == POWER_SUPPLY_TYPE_USB ||\
> + psy->type == POWER_SUPPLY_TYPE_USB_CDP || \
> + psy->type == POWER_SUPPLY_TYPE_USB_DCP ||\
> + psy->type == POWER_SUPPLY_TYPE_USB_ACA)

I like these helpers. Don't hesitate to introduce them as much as you
like, they are useful and make the code more readable.

But can we make them generic and static inlines? Otherwise the helpers
themselves are not very readable. :)

static bool psy_is_charger(struct power_supply *psy)
{
return ....
}

It's more lines of code, but the code is human-readable and maintainable.

And place them into include/linux/power_supply.h.

> +#define IS_ONLINE(psy) \
> + (get_ps_int_property(psy, POWER_SUPPLY_PROP_ONLINE) == 1)
> +#define IS_PRESENT(psy) \
> + (get_ps_int_property(psy, POWER_SUPPLY_PROP_PRESENT) == 1)
> +#define IS_SUPPORTED_CABLE(psy, cable_type) \
> + (psy->supported_cables & cable_type)
> +#define IS_CABLE_ACTIVE(status) \
> + ((status != EXTCON_CHRGR_CABLE_DISCONNECTED) ||\
> + (status != EXTCON_CHRGR_CABLE_SUSPENDED))
> +
> +#define IS_CHARGER_PROP_CHANGED(prop, cache_prop)\
> + ((cache_prop.online != prop.online) || \
> + (cache_prop.present != prop.present))
> +
> +#define IS_BAT_PROP_CHANGED(bat_prop, bat_cache)\
> + ((bat_cache.voltage_now != bat_prop.voltage_now) || \
> + (bat_cache.current_now != bat_prop.current_now) || \
> + (bat_cache.temperature != bat_prop.temperature))
> +
> +#define THROTTLE_ACTION(psy, state)\
> + (((psy->throttle_states)+state)->throttle_action)
> +
> +#define MAX_THROTTLE_STATE(psy)\
> + (get_ps_int_property(psy,\
> + POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT_MAX))
> +
> +#define CURRENT_THROTTLE_STATE(psy)\
> + (get_ps_int_property(psy,\
> + POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT))
> +
> +#define CURRENT_THROTTLE_ACTION(psy)\
> + THROTTLE_ACTION(psy, CURRENT_THROTTLE_STATE(psy))
> +
> +#define THROTTLE_CC_VALUE(psy, state)\
> + (((psy->throttle_states)+state)->throttle_val)
> +
> +#define IS_CHARGING_CAN_BE_ENABLED(psy) \
> + ((CURRENT_THROTTLE_ACTION(psy) != PSY_THROTTLE_DISABLE_CHARGER) &&\
> + (CURRENT_THROTTLE_ACTION(psy) != PSY_THROTTLE_DISABLE_CHARGING))
> +#define IS_CHARGER_CAN_BE_ENABLED(psy) \
> + (CURRENT_THROTTLE_ACTION(psy) != PSY_THROTTLE_DISABLE_CHARGER)
> +
> +#endif
> --
> 1.7.9.5

2012-11-19 02:42:33

by Anton Vorontsov

[permalink] [raw]
Subject: Re: [PATCH 4/7] power_supply: add throttle state

On Thu, Oct 18, 2012 at 10:14:03PM +0530, Jenny TC wrote:
> The charger and battery temperature contribute to the
> platform thermal. The only way to control the temperature
> is to control the charging. The charging can be controlled in different
> way. This could be disabling charger, disabling charging, adjusting CC,
> or by adjusting the INLMT. This patch adds a structure to define the
> charger throttle actions. Also this patch adds a throttle_states
> field to the struct power_supply which can be used by the charger
> driver to define it's throttle actions for different states
>
> Signed-off-by: Jenny TC <[email protected]>
> ---
> include/linux/power_supply.h | 13 +++++++++++++
> 1 file changed, 13 insertions(+)
>
> diff --git a/include/linux/power_supply.h b/include/linux/power_supply.h
> index eea1709..b4eb0af 100644
> --- a/include/linux/power_supply.h
> +++ b/include/linux/power_supply.h
> @@ -183,6 +183,18 @@ union power_supply_propval {
> const char *strval;
> };
>
> +enum psy_throttle_action {
> +

No need for this empty line.

> + PSY_THROTTLE_DISABLE_CHARGER = 0,
> + PSY_THROTTLE_DISABLE_CHARGING,
> + PSY_THROTTLE_CC_LIMIT,
> + PSY_THROTTLE_INPUT_LIMIT,
> +};
> +
> +struct power_supply_throttle {
> + enum psy_throttle_action throttle_action;
> + unsigned throttle_val;

Let's be consistent. unsigned int.

> +};

Empty line is needed here.

> struct power_supply {
> const char *name;
> enum power_supply_type type;
> @@ -192,6 +204,7 @@ struct power_supply {
> char **supplied_to;
> unsigned long supported_cables;
> size_t num_supplicants;
> + struct power_supply_throttle *throttle_states;
>
> int (*get_property)(struct power_supply *psy,
> enum power_supply_property psp,
> --
> 1.7.9.5