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: <1407931799-25596-3-git-send-email-jenny.tc@intel.com>
Date:	Wed, 13 Aug 2014 17:39:58 +0530
From:	Jenny TC <jenny.tc@...el.com>
To:	linux-kernel@...r.kernel.org, Sebastian Reichel <sre@...nel.org>,
	Pavel Machek <pavel@....cz>
Cc:	Dmitry Eremin-Solenikov <dbaryshkov@...il.com>,
	Stephen Rothwell <sfr@...b.auug.org.au>,
	Anton Vorontsov <anton.vorontsov@...aro.org>,
	David Woodhouse <dwmw2@...radead.org>,
	David Cohen <david.a.cohen@...ux.intel.com>,
	Pallala Ramakrishna <ramakrishna.pallala@...el.com>,
	Jenny TC <jenny.tc@...el.com>
Subject: [PATCH 2/3] 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                      |   15 ++
 drivers/power/Makefile                     |    1 +
 drivers/power/charging_algo_pse.c          |  216 ++++++++++++++++++++++++++++
 include/linux/power/power_supply_charger.h |   66 +++++++++
 4 files changed, 298 insertions(+)
 create mode 100644 drivers/power/charging_algo_pse.c

diff --git a/drivers/power/Kconfig b/drivers/power/Kconfig
index f679f82..54a0321 100644
--- a/drivers/power/Kconfig
+++ b/drivers/power/Kconfig
@@ -22,6 +22,21 @@ 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"
+	depends on POWER_SUPPLY_CHARGER
+	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. Select this if your charging algorithm need to change the
+	  charging parameters based on the battery temperature and the battery
+	  charging profile follows the struct psy_pse_chrg_prof definition.
+	  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.
+
 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..cbedebe
--- /dev/null
+++ b/drivers/power/charging_algo_pse.c
@@ -0,0 +1,216 @@
+/*
+ * Copyright (C) 2014 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, temp_range_cnt;
+
+	temp_range_cnt =  pse_mod_bprof->temp_mon_ranges;
+
+	if (!temp_range_cnt || temp_range_cnt > BATT_TEMP_NR_RNG)
+		return -EINVAL;
+
+	/* Basic validity check. Ensure zones are in decreasing order */
+
+	for (i = 0; i < temp_range_cnt - 1; i++) {
+		if (pse_mod_bprof->temp_mon_range[i].temp_max <=
+				pse_mod_bprof->temp_mon_range[i + 1].temp_max)
+			return -EINVAL;
+	}
+
+	if (pse_mod_bprof->temp_mon_range[i].temp_max <=
+					pse_mod_bprof->temp_min)
+		return -EINVAL;
+
+	if ((temp < pse_mod_bprof->temp_min) ||
+			(temp > pse_mod_bprof->temp_mon_range[0].temp_max))
+		return -EINVAL;
+
+	for (i = 0; i < temp_range_cnt; i++)
+		if (temp > pse_mod_bprof->temp_mon_range[i].temp_max)
+			break;
+	return i-1;
+}
+
+static inline bool is_charge_terminated(long volt, long cur,
+				long iterm, unsigned long cv)
+{
+	return (cur > 0) && (cur <= iterm) &&
+				((volt * 100)  >= (FULL_CV_MIN * cv));
+}
+
+static inline bool is_battery_full(struct psy_batt_context *batt_cxt,
+		struct psy_pse_chrg_prof *pse_mod_bprof, unsigned long cv)
+{
+	int i;
+	struct psy_batt_props batt_props;
+
+	batt_props =  batt_cxt->batt_props;
+
+	/*
+	 * Software full detection. Check the battery charge current to detect
+	 * battery Full. The voltage also verified to avoid false charge
+	 * full detection.
+	 */
+	for (i = MAX_CUR_VOLT_SAMPLES - 1; i >= 0; --i) {
+
+		if (!(is_charge_terminated(batt_cxt->voltage_now_cache[i],
+				batt_cxt->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_min;
+	bat_thresh->temp_max = pse_mod_bprof->temp_mon_range[0].temp_max;
+
+	return 0;
+}
+
+static enum psy_algo_stat pse_get_next_cc_cv(struct psy_batt_context *batt_cxt,
+	struct psy_batt_chrg_prof  bprof, unsigned long *cc, unsigned long *cv)
+{
+	int tzone;
+	struct psy_pse_chrg_prof *pse_mod_bprof;
+	struct psy_batt_props batt_props;
+	enum psy_algo_stat algo_stat;
+	int maint_exit_volt;
+
+	pse_mod_bprof = (struct psy_pse_chrg_prof *) bprof.batt_prof;
+	algo_stat = batt_cxt->algo_stat;
+
+	batt_props = batt_cxt->batt_props;
+
+	*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.
+	 */
+
+	if (!pse_mod_bprof)
+		return PSY_ALGO_STAT_NOT_CHARGE;
+
+	tzone = get_tempzone(pse_mod_bprof, batt_props.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_volt_min -
+				MAINT_EXIT_OFFSET;
+
+	if ((batt_props.status == POWER_SUPPLY_STATUS_DISCHARGING) ||
+		(batt_props.status == POWER_SUPPLY_STATUS_NOT_CHARGING) ||
+			batt_props.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 &&
+			(batt_props.voltage_now <=
+			pse_mod_bprof->temp_mon_range[tzone]
+						.maint_chrg_volt_min))
+				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_volt_max;
+			*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(batt_cxt, pse_mod_bprof, *cv)) {
+		*cc = *cv = 0;
+		algo_stat = PSY_ALGO_STAT_FULL;
+	}
+
+	return algo_stat;
+}
+
+struct psy_charging_algo pse_algo = {
+	.name = "pse-algo",
+	.chrg_prof_type = PSY_CHRG_PROF_PSE,
+	.get_next_cc_cv = pse_get_next_cc_cv,
+	.get_batt_thresholds = pse_get_bat_thresholds,
+};
+static int __init pse_algo_init(void)
+{
+	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 f1b9567..1718198 100644
--- a/include/linux/power/power_supply_charger.h
+++ b/include/linux/power/power_supply_charger.h
@@ -94,8 +94,74 @@ enum battery_events {
 
 enum psy_batt_chrg_prof_type {
 	PSY_CHRG_PROF_NONE = 0,
+	PSY_CHRG_PROF_PSE,
 };
 
+/* Product Safety Engineering (PSE) compliant charging profile */
+
+/**
+ * struct psy_ps_temp_chg_table - charging temperature zones definition
+ * @temp_max: upper temperature limit for each zone in Degree Celsius
+ * @full_chrg_vol: charge voltage till battery full in mV
+ * @full_chrg_cur: charge current till battery full in mA
+ * @maint_chrg_volt_min: voltage at which maintenance charging should
+ *		start in mV
+ * @maint_chrg_volt_max: voltage at which maintenance charging should
+ *	stop in mV. This is the charging voltage in maintenance charging mode
+ * @maint_chrg_cur: charge current in maintenance charging mode
+ *
+ * Charging temperature zone definition to decide the charging parameters on
+ * each zone. An array of the structure is used to define multiple temperature
+ * zones
+ */
+
+struct psy_ps_temp_chg_table {
+	short int temp_max;
+	short int full_chrg_vol;
+	short int full_chrg_cur;
+	short int maint_chrg_volt_min;
+	short int maint_chrg_volt_max;
+	short int maint_chrg_cur;
+} __packed;
+
+#define BATTID_STR_LEN		8
+#define BATT_TEMP_NR_RNG	6
+
+/**
+ * struct psy_pse_chrg_prof - PSE charging profile structure
+ * @batt_id: battery identifier
+ * @battery_type: as defined in POWER_SUPPLY_TECHNOLOGY_*
+ * @capacity: battery capacity in mAh
+ * @voltage_max: maximum battery volatge in mV
+ * @chrg_term_ma: charge termination current in mA
+ * @low_batt_mv: Low battery level voltage in mV
+ * @disch_temp_max: maximum operating temperature when battery is discharging
+ *		in degree Celsius
+ * @disch_temp_min: lowest operating temperature when battery is discharging
+ *	in degree Celsius
+ * @temp_mon_ranges: number of temperature zones
+ * @psy_ps_temp_chg_table: temperature zone table array
+ * @temp_min: minimum charging temperature
+ *
+ * PSE compliant charging profile which can be stored in battery EEPROM
+ * (if digital battery interface like MIPI BIF/SDQ supported) or in secondary
+ * storage to support analog battery (with BSI sensing support)
+ */
+
+struct psy_pse_chrg_prof {
+	char batt_id[BATTID_STR_LEN];
+	u16 battery_type;
+	u16 capacity;
+	u16 voltage_max;
+	u16 chrg_term_mA;
+	u16 low_batt_mV;
+	s8 disch_temp_max;
+	s8 disch_temp_min;
+	u16 temp_mon_ranges;
+	struct psy_ps_temp_chg_table temp_mon_range[BATT_TEMP_NR_RNG];
+	s8 temp_min;
+} __packed;
+
 /**
  * struct psy_batt_chrg_prof - power supply charging profile structure
  * @chrg_prof_type: charging profile 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