lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-Id: <1392875640-29230-4-git-send-email-jenny.tc@intel.com>
Date:	Thu, 20 Feb 2014 11:23:59 +0530
From:	Jenny TC <jenny.tc@...el.com>
To:	linux-kernel@...r.kernel.org,
	Dmitry Eremin-Solenikov <dbaryshkov@...il.com>
Cc:	Anton Vorontsov <cbouatmailru@...il.com>,
	Anton Vorontsov <anton.vorontsov@...aro.org>,
	Jenny TC <jenny.tc@...el.com>, Kim Milo <Milo.Kim@...com>,
	Lee Jones <lee.jones@...aro.org>,
	Jingoo Han <jg1.han@...sung.com>,
	Chanwoo Choi <cw00.choi@...sung.com>,
	Sachin Kamat <sachin.kamat@...aro.org>,
	Lars-Peter Clausen <lars@...afoo.de>,
	Pali Rohár <pali.rohar@...il.com>,
	Rhyland Klein <rklein@...dia.com>, Pavel Machek <pavel@....cz>,
	"Rafael J. Wysocki" <rjw@...ysocki.net>,
	David Woodhouse <dwmw2@...radead.org>,
	Tony Lindgren <tony@...mide.com>,
	Russell King <linux@....linux.org.uk>,
	Sebastian Reichel <sre@...g0.de>, aaro.koskinen@....fi,
	Pallala Ramakrishna <ramakrishna.pallala@...el.com>,
	freemangordon@....bg, linux-omap@...r.kernel.org
Subject: [PATCH 3/4] power_supply: Introduce PSE compliant algorithm

As per Product Safety Engineering (PSE) specification for battery charging, 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 power supply
charging driver based on the type of the battery charging profile.

Signed-off-by: Jenny TC <jenny.tc@...el.com>
---
 drivers/power/Kconfig                      |   13 ++
 drivers/power/Makefile                     |    1 +
 drivers/power/charging_algo_pse.c          |  204 ++++++++++++++++++++++++++++
 include/linux/power/power_supply_charger.h |   48 +++++++
 4 files changed, 266 insertions(+)
 create mode 100644 drivers/power/charging_algo_pse.c

diff --git a/drivers/power/Kconfig b/drivers/power/Kconfig
index f679f82..913ec36 100644
--- a/drivers/power/Kconfig
+++ b/drivers/power/Kconfig
@@ -22,6 +22,19 @@ 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 Product Safety Engineering (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. At runtime the algorithm will be
+	  selected by the psy charger driver based on the type of the battery
+	  charging profile.
+
+	depends on POWER_SUPPLY_CHARGER
+
 config PDA_POWER
 	tristate "Generic PDA/phone power driver"
 	depends on !S390
diff --git a/drivers/power/Makefile b/drivers/power/Makefile
index 405f0f4..77535fd 100644
--- a/drivers/power/Makefile
+++ b/drivers/power/Makefile
@@ -8,6 +8,7 @@ obj-$(CONFIG_POWER_SUPPLY)	+= power_supply.o
 obj-$(CONFIG_GENERIC_ADC_BATTERY)	+= generic-adc-battery.o
 
 obj-$(CONFIG_POWER_SUPPLY_CHARGER) += power_supply_charger.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
 obj-$(CONFIG_MAX8925_POWER)	+= max8925_power.o
diff --git a/drivers/power/charging_algo_pse.c b/drivers/power/charging_algo_pse.c
new file mode 100644
index 0000000..a092e30
--- /dev/null
+++ b/drivers/power/charging_algo_pse.c
@@ -0,0 +1,204 @@
+/*
+ * 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.
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ * Author: Jenny TC <jenny.tc@...el.com>
+ */
+
+#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 "power_supply.h"
+#include "power_supply_charger.h"
+
+/* 98% of CV is considered as voltage to detect Full */
+#define FULL_CV_MIN 98
+
+/*
+ * Offset to exit from maintenance charging. In maintenance charging
+ * if the volatge is less than the (maintenance_lower_threshold -
+ * MAINT_EXIT_OFFSET) then system can switch to normal charging
+ */
+
+#define MAINT_EXIT_OFFSET 50  /* mV */
+
+static int get_tempzone(struct psy_pse_chrg_prof *pse_mod_bprof,
+		int temp)
+{
+	int i = 0;
+	int temp_range_cnt = min_t(u16, pse_mod_bprof->temp_mon_ranges,
+					BATT_TEMP_NR_RNG);
+
+	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-1;
+}
+
+static inline bool __is_battery_full(long volt, long cur,
+				long iterm, unsigned long cv)
+{
+	pr_devel("%s:current=%ld pse_mod_bprof->chrg_term_mA =%ld voltage_now=%ld full_cond=%ld",
+			__func__, cur, iterm, volt * 100, (FULL_CV_MIN * cv));
+
+	return (cur > 0) && (cur <= iterm) &&
+				((volt * 100)  >= (FULL_CV_MIN * cv));
+
+}
+
+static inline bool is_battery_full(struct psy_batt_props bat_prop,
+		struct psy_pse_chrg_prof *pse_mod_bprof, unsigned long cv)
+{
+
+	int i;
+
+	/*
+	* Software full detection. Check the battery charge current to detect
+	* battery Full. The voltage also verified to avoid false charge
+	* full detection.
+	*/
+	pr_devel("%s:current=%ld pse_mod_bprof->chrg_term_mA =%d bat_prop.voltage_now=%ld full_cond=%ld",
+		__func__, bat_prop.current_now, (pse_mod_bprof->chrg_term_mA),
+		bat_prop.voltage_now * 100, (FULL_CV_MIN * cv));
+
+	for (i = (MAX_CUR_VOLT_SAMPLES - 1); i >= 0; --i) {
+
+		if (!(__is_battery_full(bat_prop.voltage_now_cache[i],
+				bat_prop.current_now_cache[i],
+				pse_mod_bprof->chrg_term_mA, cv)))
+			return false;
+	}
+
+	return true;
+}
+
+static int  pse_get_bat_thresholds(struct psy_batt_chrg_prof  bprof,
+			struct psy_batt_thresholds *bat_thresh)
+{
+	struct psy_pse_chrg_prof *pse_mod_bprof =
+			(struct psy_pse_chrg_prof *) bprof.batt_prof;
+
+	if ((bprof.chrg_prof_type != PSY_CHRG_PROF_PSE) || (!pse_mod_bprof))
+		return -EINVAL;
+
+	bat_thresh->iterm = pse_mod_bprof->chrg_term_mA;
+	bat_thresh->temp_min = pse_mod_bprof->temp_low_lim;
+	bat_thresh->temp_max = pse_mod_bprof->temp_mon_range[0].temp_up_lim;
+
+	return 0;
+}
+
+static enum psy_algo_stat pse_get_next_cc_cv(struct psy_batt_props bat_prop,
+	struct psy_batt_chrg_prof  bprof,
+			unsigned long *cc, unsigned long *cv)
+{
+	int tzone;
+	struct psy_pse_chrg_prof *pse_mod_bprof =
+			(struct psy_pse_chrg_prof *) bprof.batt_prof;
+	enum psy_algo_stat algo_stat = bat_prop.algo_stat;
+	int maint_exit_volt;
+
+	*cc = *cv = 0;
+
+	/*
+	* If STATUS is discharging, assume that charger is not connected.
+	* If charger is not connected, no need to take any action.
+	* If charge profile type is not PSY_CHRG_PROF_PSE or the charge profile
+	* is not present, no need to take any action.
+	*/
+
+	pr_devel("%s:battery status = %ld algo_status=%d\n",
+			__func__, bat_prop.status, algo_stat);
+
+	if ((bprof.chrg_prof_type != PSY_CHRG_PROF_PSE) || (!pse_mod_bprof))
+		return PSY_ALGO_STAT_NOT_CHARGE;
+
+	tzone = get_tempzone(pse_mod_bprof, bat_prop.temperature);
+
+	if (tzone < 0)
+		return PSY_ALGO_STAT_NOT_CHARGE;
+
+	/*
+	* Change the algo status to not charging, if battery is
+	* not really charging or less than maintenance exit threshold.
+	* This way algorithm can switch to normal charging if current
+	* status is full/maintenance.
+	*/
+	maint_exit_volt = pse_mod_bprof->
+			temp_mon_range[tzone].maint_chrg_vol_ll -
+				MAINT_EXIT_OFFSET;
+
+	if ((bat_prop.status == POWER_SUPPLY_STATUS_DISCHARGING) ||
+		(bat_prop.status == POWER_SUPPLY_STATUS_NOT_CHARGING) ||
+			bat_prop.voltage_now < maint_exit_volt) {
+
+		algo_stat = PSY_ALGO_STAT_NOT_CHARGE;
+
+	}
+
+	/* read cc and cv based on temperature and algorithm status */
+	if (algo_stat == PSY_ALGO_STAT_FULL ||
+			algo_stat == PSY_ALGO_STAT_MAINT) {
+
+		/*
+		* if status is full and voltage is lower than maintenance lower
+		* threshold change status to maintenance
+		*/
+
+		if (algo_stat == PSY_ALGO_STAT_FULL && (bat_prop.voltage_now <=
+			pse_mod_bprof->temp_mon_range[tzone].maint_chrg_vol_ll))
+				algo_stat = PSY_ALGO_STAT_MAINT;
+
+		/* Read maintenance CC and CV */
+		if (algo_stat == PSY_ALGO_STAT_MAINT) {
+			*cv = pse_mod_bprof->temp_mon_range
+					[tzone].maint_chrg_vol_ul;
+			*cc = pse_mod_bprof->temp_mon_range
+					[tzone].maint_chrg_cur;
+		}
+	} else {
+		*cv = pse_mod_bprof->temp_mon_range[tzone].full_chrg_vol;
+		*cc = pse_mod_bprof->temp_mon_range[tzone].full_chrg_cur;
+		algo_stat = PSY_ALGO_STAT_CHARGE;
+	}
+
+	if (is_battery_full(bat_prop, pse_mod_bprof, *cv)) {
+		*cc = *cv = 0;
+		algo_stat = PSY_ALGO_STAT_FULL;
+	}
+
+	return algo_stat;
+}
+
+static int __init pse_algo_init(void)
+{
+	struct psy_charging_algo pse_algo;
+	pse_algo.chrg_prof_type = PSY_CHRG_PROF_PSE;
+	pse_algo.name = "pse_algo";
+	pse_algo.get_next_cc_cv = pse_get_next_cc_cv;
+	pse_algo.get_batt_thresholds = pse_get_bat_thresholds;
+	power_supply_register_charging_algo(&pse_algo);
+	return 0;
+}
+
+module_init(pse_algo_init);
diff --git a/include/linux/power/power_supply_charger.h b/include/linux/power/power_supply_charger.h
index 22a7b77..117b75b 100644
--- a/include/linux/power/power_supply_charger.h
+++ b/include/linux/power/power_supply_charger.h
@@ -77,8 +77,56 @@ enum {
 
 enum psy_batt_chrg_prof_type {
 	PSY_CHRG_PROF_NONE = 0,
+	PSY_CHRG_PROF_PSE,
 };
 
+/* Product Safety Engineering (PSE) compliant charging profile */
+
+/* Parameters defining the charging range */
+struct psy_ps_temp_chg_table {
+	/* upper temperature limit for each zone */
+	short int temp_up_lim; /* Degree Celsius */
+
+	/* charge current and voltage */
+	short int full_chrg_vol; /* mV */
+	short int full_chrg_cur; /* mA */
+
+	/*
+	*  Maintenance charging thresholds.
+	*  Maintenance charging voltage lower limit - Once battery hits full,
+	*  charging will be resumed when battery voltage <= this voltage
+	*/
+	short int maint_chrg_vol_ll; /* mV */
+
+	/* Charge current and voltage in maintenance charging mode */
+	short int maint_chrg_vol_ul; /* mV */
+	short int maint_chrg_cur;   /* mA */
+} __packed;
+
+
+#define BATTID_STR_LEN		8
+#define BATT_TEMP_NR_RNG	6
+
+struct psy_pse_chrg_prof {
+	/* battery id */
+	char batt_id[BATTID_STR_LEN];
+	u16 battery_type; /* Defined as POWER_SUPPLY_TECHNOLOGY_* */
+	u16 capacity;	/* mAh */
+	u16 voltage_max; /* mV */
+	/* charge termination current */
+	u16 chrg_term_mA;
+	/* Low battery level voltage */
+	u16 low_batt_mV;
+	/* upper and lower temperature limits on discharging */
+	s8 disch_temp_ul; /* Degree Celsius */
+	s8 disch_temp_ll; /* Degree Celsius */
+	/* number of temperature monitoring ranges */
+	u16 temp_mon_ranges;
+	struct psy_ps_temp_chg_table temp_mon_range[BATT_TEMP_NR_RNG];
+	/* lowest temperature supported */
+	s8 temp_low_lim;
+} __packed;
+
 /* charging profile structure definition */
 struct psy_batt_chrg_prof {
 	enum psy_batt_chrg_prof_type chrg_prof_type;
-- 
1.7.9.5

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

Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ