Loading Documentation/devicetree/bindings/batterydata/batterydata.txt +16 −0 Original line number Diff line number Diff line Loading @@ -41,6 +41,13 @@ Profile data node required subnodes: resistance lookup. The units for this lookup table should be degrees celsius and percent to milliohms. Profile data node optional subnodes: - qcom,ibat-acc-luit: A 2-dimentional lookup table that encodes temperature and battery current to battery ACC (apparent charge capacity). The units for this lookup table should be temperature in degrees celsius, ibat in milli-amps and ACC in milli-ampere-hour. 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 Loading Loading @@ -164,5 +171,14 @@ qcom,palladium-batterydata { <12886 1026 637 422 3269>, <170899 127211 98968 88907 77102>; }; qcom,ibat-acc-lut { qcom,lut-col-legend = <(-20) 0 25>; qcom,lut-row-legend = <0 250 500 1000>; qcom,lut-data = <1470 1470 1473>, <1406 1406 1430>, <1247 1247 1414>, <764 764 1338>; }; }; drivers/of/of_batterydata.c +30 −1 Original line number Diff line number Diff line /* Copyright (c) 2013, The Linux Foundation. All rights reserved. /* Copyright (c) 2013-2014, 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 Loading Loading @@ -131,6 +131,30 @@ static int of_batterydata_read_pc_temp_ocv_lut(struct device_node *data_node, return 0; } static int of_batterydata_read_ibat_temp_acc_lut(struct device_node *data_node, const char *name, struct ibat_temp_acc_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_debug("Couldn't find %s node.\n", name); return 0; } rc = of_batterydata_read_lut(node, ACC_TEMP_COLS, ACC_IBAT_ROWS, &lut->cols, &lut->rows, lut->temp, lut->ibat, *lut->acc); 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) { Loading Loading @@ -222,6 +246,11 @@ static int of_batterydata_load_battery_data(struct device_node *node, if (rc) return rc; rc = of_batterydata_read_ibat_temp_acc_lut(node, "qcom,ibat-acc-lut", batt_data->ibat_acc_lut); if (rc) return rc; rc = of_property_read_string(node, "qcom,battery-type", &batt_data->battery_type); if (rc) { Loading drivers/power/batterydata-lib.c +82 −0 Original line number Diff line number Diff line Loading @@ -410,3 +410,85 @@ int interpolate_slope(struct pc_temp_ocv_lut *pc_temp_ocv, return slope; } int interpolate_acc(struct ibat_temp_acc_lut *ibat_acc_lut, int batt_temp, int ibat) { int i, accrow1, accrow2, rows, cols; int row1 = 0; int row2 = 0; int acc; rows = ibat_acc_lut->rows; cols = ibat_acc_lut->cols; if (ibat > ibat_acc_lut->ibat[rows - 1]) { pr_debug("ibatt(%d) > max range(%d)\n", ibat, ibat_acc_lut->ibat[rows - 1]); row1 = rows - 1; row2 = rows - 2; } else if (ibat < ibat_acc_lut->ibat[0]) { pr_debug("ibatt(%d) < max range(%d)\n", ibat, ibat_acc_lut->ibat[0]); row1 = 0; row2 = 0; } else { for (i = 0; i < rows; i++) { if (ibat == ibat_acc_lut->ibat[i]) { row1 = i; row2 = i; break; } if (ibat < ibat_acc_lut->ibat[i]) { row1 = i; row2 = i - 1; break; } } } if (batt_temp < ibat_acc_lut->temp[0] * DEGC_SCALE) batt_temp = ibat_acc_lut->temp[0] * DEGC_SCALE; if (batt_temp > ibat_acc_lut->temp[cols - 1] * DEGC_SCALE) batt_temp = ibat_acc_lut->temp[cols - 1] * DEGC_SCALE; for (i = 0; i < cols; i++) if (batt_temp <= ibat_acc_lut->temp[i] * DEGC_SCALE) break; if (batt_temp == (ibat_acc_lut->temp[i] * DEGC_SCALE)) { acc = linear_interpolate( ibat_acc_lut->acc[row1][i], ibat_acc_lut->ibat[row1], ibat_acc_lut->acc[row2][i], ibat_acc_lut->ibat[row2], ibat); return acc; } accrow1 = linear_interpolate( ibat_acc_lut->acc[row1][i - 1], ibat_acc_lut->temp[i - 1] * DEGC_SCALE, ibat_acc_lut->acc[row1][i], ibat_acc_lut->temp[i] * DEGC_SCALE, batt_temp); accrow2 = linear_interpolate( ibat_acc_lut->acc[row2][i - 1], ibat_acc_lut->temp[i - 1] * DEGC_SCALE, ibat_acc_lut->acc[row2][i], ibat_acc_lut->temp[i] * DEGC_SCALE, batt_temp); acc = linear_interpolate(accrow1, ibat_acc_lut->ibat[row1], accrow2, ibat_acc_lut->ibat[row2], ibat); if (acc < 0) acc = 0; return acc; } include/linux/batterydata-lib.h +19 −0 Original line number Diff line number Diff line Loading @@ -24,6 +24,9 @@ #define PC_TEMP_ROWS 31 #define PC_TEMP_COLS 8 #define ACC_IBAT_ROWS 4 #define ACC_TEMP_COLS 3 #define MAX_SINGLE_LUT_COLS 20 #define MAX_BATT_ID_NUM 4 Loading Loading @@ -72,6 +75,14 @@ struct pc_temp_ocv_lut { int ocv[PC_TEMP_ROWS][PC_TEMP_COLS]; }; struct ibat_temp_acc_lut { int rows; int cols; int temp[ACC_TEMP_COLS]; int ibat[ACC_IBAT_ROWS]; int acc[ACC_IBAT_ROWS][ACC_TEMP_COLS]; }; struct batt_ids { int kohm[MAX_BATT_ID_NUM]; int num; Loading Loading @@ -115,6 +126,7 @@ struct bms_battery_data { struct single_row_lut *fcc_temp_lut; struct single_row_lut *fcc_sf_lut; struct pc_temp_ocv_lut *pc_temp_ocv_lut; struct ibat_temp_acc_lut *ibat_acc_lut; struct sf_lut *pc_sf_lut; struct sf_lut *rbatt_sf_lut; int default_rbatt_mohm; Loading Loading @@ -148,6 +160,8 @@ int interpolate_ocv(struct pc_temp_ocv_lut *pc_temp_ocv, int batt_temp_degc, int pc); int interpolate_slope(struct pc_temp_ocv_lut *pc_temp_ocv, int batt_temp, int pc); int interpolate_acc(struct ibat_temp_acc_lut *ibat_acc_lut, int batt_temp, int ibat); int linear_interpolate(int y0, int x0, int y1, int x1, int x); int is_between(int left, int right, int value); #else Loading Loading @@ -189,6 +203,11 @@ static inline int is_between(int left, int right, int value) { return -EINVAL; } static inline int interpolate_acc(struct ibat_temp_acc_lut *ibat_acc_lut, int batt_temp, int ibat) { return -EINVAL; } #endif #endif Loading
Documentation/devicetree/bindings/batterydata/batterydata.txt +16 −0 Original line number Diff line number Diff line Loading @@ -41,6 +41,13 @@ Profile data node required subnodes: resistance lookup. The units for this lookup table should be degrees celsius and percent to milliohms. Profile data node optional subnodes: - qcom,ibat-acc-luit: A 2-dimentional lookup table that encodes temperature and battery current to battery ACC (apparent charge capacity). The units for this lookup table should be temperature in degrees celsius, ibat in milli-amps and ACC in milli-ampere-hour. 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 Loading Loading @@ -164,5 +171,14 @@ qcom,palladium-batterydata { <12886 1026 637 422 3269>, <170899 127211 98968 88907 77102>; }; qcom,ibat-acc-lut { qcom,lut-col-legend = <(-20) 0 25>; qcom,lut-row-legend = <0 250 500 1000>; qcom,lut-data = <1470 1470 1473>, <1406 1406 1430>, <1247 1247 1414>, <764 764 1338>; }; };
drivers/of/of_batterydata.c +30 −1 Original line number Diff line number Diff line /* Copyright (c) 2013, The Linux Foundation. All rights reserved. /* Copyright (c) 2013-2014, 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 Loading Loading @@ -131,6 +131,30 @@ static int of_batterydata_read_pc_temp_ocv_lut(struct device_node *data_node, return 0; } static int of_batterydata_read_ibat_temp_acc_lut(struct device_node *data_node, const char *name, struct ibat_temp_acc_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_debug("Couldn't find %s node.\n", name); return 0; } rc = of_batterydata_read_lut(node, ACC_TEMP_COLS, ACC_IBAT_ROWS, &lut->cols, &lut->rows, lut->temp, lut->ibat, *lut->acc); 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) { Loading Loading @@ -222,6 +246,11 @@ static int of_batterydata_load_battery_data(struct device_node *node, if (rc) return rc; rc = of_batterydata_read_ibat_temp_acc_lut(node, "qcom,ibat-acc-lut", batt_data->ibat_acc_lut); if (rc) return rc; rc = of_property_read_string(node, "qcom,battery-type", &batt_data->battery_type); if (rc) { Loading
drivers/power/batterydata-lib.c +82 −0 Original line number Diff line number Diff line Loading @@ -410,3 +410,85 @@ int interpolate_slope(struct pc_temp_ocv_lut *pc_temp_ocv, return slope; } int interpolate_acc(struct ibat_temp_acc_lut *ibat_acc_lut, int batt_temp, int ibat) { int i, accrow1, accrow2, rows, cols; int row1 = 0; int row2 = 0; int acc; rows = ibat_acc_lut->rows; cols = ibat_acc_lut->cols; if (ibat > ibat_acc_lut->ibat[rows - 1]) { pr_debug("ibatt(%d) > max range(%d)\n", ibat, ibat_acc_lut->ibat[rows - 1]); row1 = rows - 1; row2 = rows - 2; } else if (ibat < ibat_acc_lut->ibat[0]) { pr_debug("ibatt(%d) < max range(%d)\n", ibat, ibat_acc_lut->ibat[0]); row1 = 0; row2 = 0; } else { for (i = 0; i < rows; i++) { if (ibat == ibat_acc_lut->ibat[i]) { row1 = i; row2 = i; break; } if (ibat < ibat_acc_lut->ibat[i]) { row1 = i; row2 = i - 1; break; } } } if (batt_temp < ibat_acc_lut->temp[0] * DEGC_SCALE) batt_temp = ibat_acc_lut->temp[0] * DEGC_SCALE; if (batt_temp > ibat_acc_lut->temp[cols - 1] * DEGC_SCALE) batt_temp = ibat_acc_lut->temp[cols - 1] * DEGC_SCALE; for (i = 0; i < cols; i++) if (batt_temp <= ibat_acc_lut->temp[i] * DEGC_SCALE) break; if (batt_temp == (ibat_acc_lut->temp[i] * DEGC_SCALE)) { acc = linear_interpolate( ibat_acc_lut->acc[row1][i], ibat_acc_lut->ibat[row1], ibat_acc_lut->acc[row2][i], ibat_acc_lut->ibat[row2], ibat); return acc; } accrow1 = linear_interpolate( ibat_acc_lut->acc[row1][i - 1], ibat_acc_lut->temp[i - 1] * DEGC_SCALE, ibat_acc_lut->acc[row1][i], ibat_acc_lut->temp[i] * DEGC_SCALE, batt_temp); accrow2 = linear_interpolate( ibat_acc_lut->acc[row2][i - 1], ibat_acc_lut->temp[i - 1] * DEGC_SCALE, ibat_acc_lut->acc[row2][i], ibat_acc_lut->temp[i] * DEGC_SCALE, batt_temp); acc = linear_interpolate(accrow1, ibat_acc_lut->ibat[row1], accrow2, ibat_acc_lut->ibat[row2], ibat); if (acc < 0) acc = 0; return acc; }
include/linux/batterydata-lib.h +19 −0 Original line number Diff line number Diff line Loading @@ -24,6 +24,9 @@ #define PC_TEMP_ROWS 31 #define PC_TEMP_COLS 8 #define ACC_IBAT_ROWS 4 #define ACC_TEMP_COLS 3 #define MAX_SINGLE_LUT_COLS 20 #define MAX_BATT_ID_NUM 4 Loading Loading @@ -72,6 +75,14 @@ struct pc_temp_ocv_lut { int ocv[PC_TEMP_ROWS][PC_TEMP_COLS]; }; struct ibat_temp_acc_lut { int rows; int cols; int temp[ACC_TEMP_COLS]; int ibat[ACC_IBAT_ROWS]; int acc[ACC_IBAT_ROWS][ACC_TEMP_COLS]; }; struct batt_ids { int kohm[MAX_BATT_ID_NUM]; int num; Loading Loading @@ -115,6 +126,7 @@ struct bms_battery_data { struct single_row_lut *fcc_temp_lut; struct single_row_lut *fcc_sf_lut; struct pc_temp_ocv_lut *pc_temp_ocv_lut; struct ibat_temp_acc_lut *ibat_acc_lut; struct sf_lut *pc_sf_lut; struct sf_lut *rbatt_sf_lut; int default_rbatt_mohm; Loading Loading @@ -148,6 +160,8 @@ int interpolate_ocv(struct pc_temp_ocv_lut *pc_temp_ocv, int batt_temp_degc, int pc); int interpolate_slope(struct pc_temp_ocv_lut *pc_temp_ocv, int batt_temp, int pc); int interpolate_acc(struct ibat_temp_acc_lut *ibat_acc_lut, int batt_temp, int ibat); int linear_interpolate(int y0, int x0, int y1, int x1, int x); int is_between(int left, int right, int value); #else Loading Loading @@ -189,6 +203,11 @@ static inline int is_between(int left, int right, int value) { return -EINVAL; } static inline int interpolate_acc(struct ibat_temp_acc_lut *ibat_acc_lut, int batt_temp, int ibat) { return -EINVAL; } #endif #endif