of: batterydata: Introduce DT support for battery data

Add the of_batterydata driver to parse device tree battery data.

Battery data contains battery profile information and is used by the
charger and the BMS drivers.

Change-Id: Ibe03800be0d51e82194c9385c6348c5a6c92711d
Signed-off-by: Xiaozhe Shi <xiaozhes@codeaurora.org>
This commit is contained in:
Xiaozhe Shi
2013-07-12 14:09:22 -07:00
parent 502f45d349
commit 7ef86d94d9
16 changed files with 506 additions and 12 deletions
@@ -0,0 +1,163 @@
Battery Profile Data
Battery Data is a collection of battery profile data made available to
the QPNP Charger and BMS drivers via device tree.
qcom,battery-data node required properties:
- qcom,rpull-up-kohm : The vadc pullup resistor's resistance value in kOhms.
- qcom,vref-batt-therm-uv : The vadc voltage used to make readings.
For Qualcomm VADCs this should be 1800000uV.
qcom,battery-data can also include any number of children nodes. These children
nodes will be treated as battery profile data nodes.
Profile data node required properties:
- qcom,fcc-mah : Full charge count of the battery in milliamp-hours
- qcom,default-rbatt-mohm : The nominal battery resistance value
- qcom,rbatt-capacitive-mohm : The capacitive resistance of the battery.
- qcom,flat-ocv-threshold-uv : The threshold under which the battery can be
considered to be in the flat portion of the discharge
curve.
- qcom,max-voltage-uv : The maximum rated voltage of the battery
- qcom,v-cutoff-uv : The cutoff voltage of the battery at which the device
should shutdown gracefully.
- qcom,chg-term-ua : The termination charging current of the battery.
- qcom,batt-id-kohm : The battery id resistance of the battery.
Profile data node required subnodes:
- qcom,fcc-temp-lut : An 1-dimensional lookup table node that encodes
temperature to fcc lookup. The units for this lookup
table should be degrees celsius to milliamp-hours.
- qcom,pc-temp-ocv-lut : A 2-dimensional lookup table node that encodes
temperature and percent charge to open circuit voltage
lookup. The units for this lookup table should be
degrees celsius and percent to millivolts.
- qcom,rbatt-sf-lut : A 2-dimentional lookup table node that encodes
temperature and percent charge to battery internal
resistance lookup. The units for this lookup table
should be degrees celsius and percent to milliohms.
Lookup table required properties:
- qcom,lut-col-legend : An array that encodes the legend of the lookup table's
columns. The length of this array will determine the
lookup table's width.
- qcom,lut-data : An array that encodes the lookup table's data. The size of this
array should be equal to the size of qcom,lut-col-legend
multiplied by 1 if it's a 1-dimensional table, or
the size of qcom,lut-row-legend if it's a 2-dimensional
table. The data should be in a flattened row-major
representation.
Lookup table optional properties:
- qcom,lut-row-legend : An array that encodes the legend of the lookup table's rows.
If this property exists, then it is assumed that the
lookup table is a 2-dimensional table.
Example:
In msm8974-mtp.dtsi:
mtp_batterydata: qcom,battery-data {
qcom,rpull-up-kohm = <100>;
qcom,vref-batt-therm-uv = <1800000>;
/include/ "batterydata-palladium.dtsi"
/include/ "batterydata-mtp-3000mah.dtsi"
};
&pm8941_bms {
qcom,battery-data = <&mtp_batterydata>;
};
In batterydata-palladium.dtsi:
qcom,palladium-batterydata {
qcom,fcc-mah = <1500>;
qcom,default-rbatt-mohm = <236>;
qcom,rbatt-capacitive-mohm = <50>;
qcom,flat-ocv-threshold-uv = <3800000>;
qcom,max-voltage-uv = <4200000>;
qcom,v-cutoff-uv = <3400000>;
qcom,chg-term-ua = <100000>;
qcom,batt-id-kohm = <75>;
qcom,fcc-temp-lut {
qcom,lut-col-legend = <(-20) 0 25 40 65>;
qcom,lut-data = <1492 1492 1493 1483 1502>;
};
qcom,pc-temp-ocv-lut {
qcom,lut-col-legend = <(-20) 0 25 40 65>;
qcom,lut-row-legend = <100 95 90 85 80 75 70>,
<65 60 55 50 45 40 35>,
<30 25 20 15 10 9 8>,
<7 6 5 4 3 2 1 0>;
qcom,lut-data = <4173 4167 4163 4156 4154>,
<4104 4107 4108 4102 4104>,
<4057 4072 4069 4061 4060>,
<3973 4009 4019 4016 4020>,
<3932 3959 3981 3982 3983>,
<3899 3928 3954 3950 3950>,
<3868 3895 3925 3921 3920>,
<3837 3866 3898 3894 3892>,
<3812 3841 3853 3856 3862>,
<3794 3818 3825 3823 3822>,
<3780 3799 3804 3804 3803>,
<3768 3787 3790 3788 3788>,
<3757 3779 3778 3775 3776>,
<3747 3772 3771 3766 3765>,
<3736 3763 3766 3760 3746>,
<3725 3749 3756 3747 3729>,
<3714 3718 3734 3724 3706>,
<3701 3703 3696 3689 3668>,
<3675 3695 3682 3675 3662>,
<3670 3691 3680 3673 3661>,
<3661 3686 3679 3672 3656>,
<3649 3680 3676 3669 3641>,
<3633 3669 3667 3655 3606>,
<3610 3647 3640 3620 3560>,
<3580 3607 3596 3572 3501>,
<3533 3548 3537 3512 3425>,
<3457 3468 3459 3429 3324>,
<3328 3348 3340 3297 3172>,
<3000 3000 3000 3000 3000>;
};
qcom,rbatt-sf-lut {
qcom,lut-col-legend = <(-20) 0 25 40 65>;
qcom,lut-row-legend = <100 95 90 85 80 75 70>,
<65 60 55 50 45 40 35>,
<30 25 20 15 10 9 8>,
<7 6 5 4 3 2 1 0>;
qcom,lut-data = <357 187 100 91 91>,
<400 208 105 94 94>,
<390 204 106 95 96>,
<391 201 108 98 98>,
<391 202 110 98 100>,
<390 200 110 99 102>,
<389 200 110 99 102>,
<393 202 101 93 100>,
<407 205 99 89 94>,
<428 208 100 91 96>,
<455 212 102 92 98>,
<495 220 104 93 101>,
<561 232 107 95 102>,
<634 245 112 98 98>,
<714 258 114 98 98>,
<791 266 114 97 100>,
<871 289 108 95 97>,
<973 340 124 108 105>,
<489 241 109 96 99>,
<511 246 110 96 99>,
<534 252 111 95 98>,
<579 263 112 96 96>,
<636 276 111 95 97>,
<730 294 109 96 99>,
<868 328 112 98 104>,
<1089 374 119 101 115>,
<1559 457 128 105 213>,
<12886 1026 637 422 3269>,
<170899 127211 98968 88907 77102>;
};
};
@@ -13,6 +13,11 @@ device is to enabled:
to determine whether the BMS is using an internal or external
rsense to accumulate the Coulomb Counter and read current.
Additionally, an optional subnode may be included:
- qcom,battery-data : A phandle to a node containing the available batterydata
profiles. See the batterydata bindings documentation for more
details.
Parent node required properties:
- compatible : should be "qcom,qpnp-bms" for the BM driver.
- qcom,r-sense-uohm : sensor resistance in in micro-ohms.
@@ -138,6 +143,7 @@ pm8941_bms: qcom,bms {
qcom,high-ocv-correction-limit-uv = <50>;
qcom,hold-soc-est = <3>;
qcom,tm-temp-margin = <5000>;
qcom,battery-data = <&mtp_batterydata>;
qcom,bms-iadc@3800 {
reg = <0x3800 0x100>;
+2 -2
View File
@@ -1,4 +1,4 @@
/* Copyright (c) 2012, The Linux Foundation. All rights reserved.
/* Copyright (c) 2012-2013, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -13,7 +13,7 @@
#define pr_fmt(fmt) "%s: " fmt, __func__
#include <linux/module.h>
#include <linux/mfd/pm8xxx/batterydata-lib.h>
#include <linux/batterydata-lib.h>
int linear_interpolate(int y0, int x0, int y1, int x1, int x)
{
+1 -1
View File
@@ -10,7 +10,7 @@
* GNU General Public License for more details.
*/
#include <linux/mfd/pm8xxx/batterydata-lib.h>
#include <linux/batterydata-lib.h>
static struct single_row_lut desay_5200_fcc_temp = {
.x = {-20, 0, 25, 40},
+1 -1
View File
@@ -10,7 +10,7 @@
* GNU General Public License for more details.
*/
#include <linux/mfd/pm8xxx/batterydata-lib.h>
#include <linux/batterydata-lib.h>
static struct single_row_lut fcc_temp = {
.x = {-20, 0, 25, 40, 65},
@@ -10,7 +10,7 @@
* GNU General Public License for more details.
*/
#include <linux/mfd/pm8xxx/batterydata-lib.h>
#include <linux/batterydata-lib.h>
static struct single_row_lut fcc_temp = {
.x = {-20, 0, 25, 40, 60},
@@ -9,7 +9,7 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include <linux/mfd/pm8xxx/batterydata-lib.h>
#include <linux/batterydata-lib.h>
static struct single_row_lut fcc_temp = {
.x = {-20, 0, 25, 40, 60},
+1 -1
View File
@@ -10,7 +10,7 @@
* GNU General Public License for more details.
*/
#include <linux/mfd/pm8xxx/batterydata-lib.h>
#include <linux/batterydata-lib.h>
static struct single_row_lut fcc_temp = {
.x = {-20, 0, 25, 40, 65},
+5
View File
@@ -113,4 +113,9 @@ config OF_CORESIGHT
help
OpenFirmware CoreSight accessors
config OF_BATTERYDATA
def_bool y
help
OpenFirmware BatteryData accessors
endmenu # OF
+1
View File
@@ -16,3 +16,4 @@ obj-$(CONFIG_OF_SPMI) += of_spmi.o
obj-$(CONFIG_OF_MTD) += of_mtd.o
obj-$(CONFIG_OF_SLIMBUS) += of_slimbus.o
obj-$(CONFIG_OF_CORESIGHT) += of_coresight.o
obj-$(CONFIG_OF_BATTERYDATA) += of_batterydata.o
+265
View File
@@ -0,0 +1,265 @@
/* Copyright (c) 2013, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
* only version 2 as published by the Free Software Foundation.
*
* 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.
*/
#define pr_fmt(fmt) "%s: " fmt, __func__
#include <linux/of.h>
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/types.h>
#include <linux/batterydata-lib.h>
static int of_batterydata_read_lut(const struct device_node *np,
int max_cols, int max_rows, int *ncols, int *nrows,
int *col_legend_data, int *row_legend_data,
int *lut_data)
{
struct property *prop;
const __be32 *data;
int cols, rows, size, i, j, *out_values;
prop = of_find_property(np, "qcom,lut-col-legend", NULL);
if (!prop) {
pr_err("%s: No col legend found\n", np->name);
return -EINVAL;
} else if (!prop->value) {
pr_err("%s: No col legend value found, np->name\n", np->name);
return -ENODATA;
} else if (prop->length > max_cols * sizeof(int)) {
pr_err("%s: Too many columns\n", np->name);
return -EINVAL;
}
cols = prop->length/sizeof(int);
*ncols = cols;
data = prop->value;
for (i = 0; i < cols; i++)
*col_legend_data++ = be32_to_cpup(data++);
prop = of_find_property(np, "qcom,lut-row-legend", NULL);
if (!prop || row_legend_data == NULL) {
/* single row lut */
rows = 1;
} else if (!prop->value) {
pr_err("%s: No row legend value found\n", np->name);
return -ENODATA;
} else if (prop->length > max_rows * sizeof(int)) {
pr_err("%s: Too many rows\n", np->name);
return -EINVAL;
} else {
rows = prop->length/sizeof(int);
*nrows = rows;
data = prop->value;
for (i = 0; i < rows; i++)
*row_legend_data++ = be32_to_cpup(data++);
}
prop = of_find_property(np, "qcom,lut-data", NULL);
data = prop->value;
size = prop->length/sizeof(int);
if (!prop || size != cols * rows) {
pr_err("%s: data size mismatch, %dx%d != %d\n",
np->name, cols, rows, size);
return -EINVAL;
}
for (i = 0; i < rows; i++) {
out_values = lut_data + (max_cols * i);
for (j = 0; j < cols; j++) {
*out_values++ = be32_to_cpup(data++);
pr_debug("Value = %d\n", *(out_values-1));
}
}
return 0;
}
static int of_batterydata_read_sf_lut(struct device_node *data_node,
const char *name, struct sf_lut *lut)
{
struct device_node *node = of_find_node_by_name(data_node, name);
int rc;
if (!lut) {
pr_debug("No lut provided, skipping\n");
return 0;
} else if (!node) {
pr_err("Couldn't find %s node.\n", name);
return -EINVAL;
}
rc = of_batterydata_read_lut(node, PC_CC_COLS, PC_CC_ROWS,
&lut->cols, &lut->rows, lut->row_entries,
lut->percent, *lut->sf);
if (rc) {
pr_err("Failed to read %s node.\n", name);
return rc;
}
return 0;
}
static int of_batterydata_read_pc_temp_ocv_lut(struct device_node *data_node,
const char *name, struct pc_temp_ocv_lut *lut)
{
struct device_node *node = of_find_node_by_name(data_node, name);
int rc;
if (!lut) {
pr_debug("No lut provided, skipping\n");
return 0;
} else if (!node) {
pr_err("Couldn't find %s node.\n", name);
return -EINVAL;
}
rc = of_batterydata_read_lut(node, PC_TEMP_COLS, PC_TEMP_ROWS,
&lut->cols, &lut->rows, lut->temp, lut->percent,
*lut->ocv);
if (rc) {
pr_err("Failed to read %s node.\n", name);
return rc;
}
return 0;
}
static int of_batterydata_read_single_row_lut(struct device_node *data_node,
const char *name, struct single_row_lut *lut)
{
struct device_node *node = of_find_node_by_name(data_node, name);
int rc;
if (!lut) {
pr_debug("No lut provided, skipping\n");
return 0;
} else if (!node) {
pr_err("Couldn't find %s node.\n", name);
return -EINVAL;
}
rc = of_batterydata_read_lut(node, MAX_SINGLE_LUT_COLS, 1,
&lut->cols, NULL, lut->x, NULL, lut->y);
if (rc) {
pr_err("Failed to read %s node.\n", name);
return rc;
}
return 0;
}
#define OF_PROP_READ(property, qpnp_dt_property, node, rc, optional) \
do { \
rc = of_property_read_u32(node, "qcom," qpnp_dt_property, \
&property); \
\
if ((rc == -EINVAL) && optional) { \
property = -EINVAL; \
rc = 0; \
} else if (rc) { \
pr_err("Error reading " #qpnp_dt_property \
" property rc = %d\n", rc); \
return rc; \
} \
} while (0)
static int of_batterydata_load_battery_data(struct device_node *node,
struct bms_battery_data *batt_data)
{
int rc;
rc = of_batterydata_read_single_row_lut(node, "qcom,fcc-temp-lut",
batt_data->fcc_temp_lut);
if (rc)
return rc;
rc = of_batterydata_read_pc_temp_ocv_lut(node,
"qcom,pc-temp-ocv-lut",
batt_data->pc_temp_ocv_lut);
if (rc)
return rc;
rc = of_batterydata_read_sf_lut(node, "qcom,rbatt-sf-lut",
batt_data->rbatt_sf_lut);
if (rc)
return rc;
OF_PROP_READ(batt_data->fcc, "fcc-mah", node, rc, false);
OF_PROP_READ(batt_data->default_rbatt_mohm,
"default-rbatt-mohm", node, rc, false);
OF_PROP_READ(batt_data->rbatt_capacitive_mohm,
"rbatt-capacitive-mohm", node, rc, false);
OF_PROP_READ(batt_data->batt_id_kohm, "batt-id-kohm", node, rc, false);
OF_PROP_READ(batt_data->flat_ocv_threshold_uv,
"flat-ocv-threshold", node, rc, true);
OF_PROP_READ(batt_data->max_voltage_uv,
"max-voltage-uv", node, rc, true);
OF_PROP_READ(batt_data->cutoff_uv, "v-cutoff-uv", node, rc, true);
OF_PROP_READ(batt_data->iterm_ua, "chg-term-ua", node, rc, true);
return rc;
}
static int64_t of_batterydata_convert_battery_id_kohm(int batt_id_uv,
int rpull_up, int vadc_vdd)
{
int64_t resistor_value_kohm, denom;
/* calculate the battery id resistance reported via ADC */
denom = div64_s64(vadc_vdd * 1000000LL, batt_id_uv) - 1000000LL;
resistor_value_kohm = div64_s64(rpull_up * 1000000LL + denom/2, denom);
pr_debug("batt id voltage = %d, resistor value = %lld\n",
batt_id_uv, resistor_value_kohm);
return resistor_value_kohm;
}
int of_batterydata_read_data(struct device_node *batterydata_container_node,
struct bms_battery_data *batt_data,
int batt_id_uv)
{
struct device_node *node, *best_node;
uint32_t id_kohm;
int delta, best_delta, batt_id_kohm, rpull_up_kohm, vadc_vdd_uv, rc = 0;
node = batterydata_container_node;
OF_PROP_READ(rpull_up_kohm, "rpull-up-kohm", node, rc, false);
OF_PROP_READ(vadc_vdd_uv, "vref-batt-therm", node, rc, false);
batt_id_kohm = of_batterydata_convert_battery_id_kohm(batt_id_uv,
rpull_up_kohm, vadc_vdd_uv);
best_node = NULL;
best_delta = 0;
/*
* Find the battery data with a battery id resistor closest to this one
*/
for_each_child_of_node(batterydata_container_node, node) {
rc = of_property_read_u32(node, "qcom,batt-id-kohm", &id_kohm);
if (rc)
continue;
delta = abs((int)id_kohm - batt_id_kohm);
if (delta < best_delta || !best_node) {
best_node = node;
best_delta = delta;
}
}
if (best_node == NULL) {
pr_err("No battery data found\n");
return -ENODATA;
}
return of_batterydata_load_battery_data(best_node, batt_data);
}
MODULE_LICENSE("GPL v2");
+1 -1
View File
@@ -22,8 +22,8 @@
#include <linux/mfd/pm8xxx/pm8xxx-adc.h>
#include <linux/mfd/pm8xxx/pm8921-charger.h>
#include <linux/mfd/pm8xxx/ccadc.h>
#include <linux/mfd/pm8xxx/batterydata-lib.h>
#include <linux/mfd/pm8xxx/batt-alarm.h>
#include <linux/batterydata-lib.h>
#include <linux/interrupt.h>
#include <linux/bitops.h>
#include <linux/debugfs.h>
+1 -1
View File
@@ -25,7 +25,7 @@
#include <linux/delay.h>
#include <linux/qpnp/qpnp-adc.h>
#include <linux/qpnp/power-on.h>
#include <linux/mfd/pm8xxx/batterydata-lib.h>
#include <linux/batterydata-lib.h>
/* BMS Register Offsets */
#define BMS1_REVISION1 0x0
@@ -10,8 +10,8 @@
* GNU General Public License for more details.
*/
#ifndef __PM8XXX_BMS_BATTERYDATA_H
#define __PM8XXX_BMS_BATTERYDATA_H
#ifndef __BMS_BATTERYDATA_H
#define __BMS_BATTERYDATA_H
#include <linux/errno.h>
@@ -95,6 +95,11 @@ enum battery_type {
* battery capacitance
* @flat_ocv_threshold_uv: the voltage where the battery's discharge curve
* starts flattening out.
* @max_voltage_uv: max voltage of the battery
* @cutoff_uv: cutoff voltage of the battery
* @iterm_ua: termination current of the battery when charging
* to 100%
* @batt_id_kohm: battery id resistor value
*/
struct bms_battery_data {
@@ -108,6 +113,10 @@ struct bms_battery_data {
int delta_rbatt_mohm;
int rbatt_capacitive_mohm;
int flat_ocv_threshold_uv;
int max_voltage_uv;
int cutoff_uv;
int iterm_ua;
int batt_id_kohm;
};
#if defined(CONFIG_PM8921_BMS) || \
+1 -1
View File
@@ -14,7 +14,7 @@
#define __PM8XXX_BMS_H
#include <linux/errno.h>
#include <linux/mfd/pm8xxx/batterydata-lib.h>
#include <linux/batterydata-lib.h>
#define PM8921_BMS_DEV_NAME "pm8921-bms"
+45
View File
@@ -0,0 +1,45 @@
/* Copyright (c) 2013, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
* only version 2 as published by the Free Software Foundation.
*
* 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.
*/
#include <linux/of.h>
#include <linux/batterydata-lib.h>
#ifdef CONFIG_OF_BATTERYDATA
/**
* of_batterydata_read_data() - Populate battery data from the device tree
* @container_node: pointer to the battery-data container device node
* containing the profile nodes.
* @batt_data: pointer to an allocated bms_battery_data structure that the
* loaded profile will be written to.
* @batt_id_uv: ADC voltage of the battery id line used to differentiate
* between different battery profiles. If there are multiple
* battery data in the device tree, the one with the closest
* battery id resistance will be automatically loaded.
*
* This routine loads the closest match battery data from device tree based on
* the battery id reading. Then, it will try to load all the relevant data from
* the device tree battery data profile.
*
* If any of the lookup table pointers are NULL, this routine will skip trying
* to read them.
*/
int of_batterydata_read_data(struct device_node *container_node,
struct bms_battery_data *batt_data,
int batt_id_uv);
#else
static inline int of_batterydata_read_data(struct device_node *container_node,
struct bms_battery_data *batt_data,
int batt_id_uv);
{
return -ENXIO;
}
#endif /* CONFIG_OF_QPNP */