From fae0afd7be3adcb910ea44d772c09d69d0d31375 Mon Sep 17 00:00:00 2001 From: Abhijeet Dharmapurikar Date: Tue, 28 Aug 2012 19:33:49 -0700 Subject: [PATCH] msm: power: update power drivers to msm-3.4 tip This branch is currently very outdated. Cherry pick the following changes in order to bring recent fixes to this branch. Change-Ids Ib99cd49623698d0295bc3c3cf880225495d530cb Ifba2bd1248e8bb47a0700dec4d321d32db4f2066 I0fc1e116c0f5a5648f99df967bdbb41fe5a07cbc I8e3e9068e7b9a3bfd24c07a00bb3953180ce0741 I2bf31a23199f942a9ac843b9940a0f7377256238 I96b47c8603a291c41c5769fab9483d83fefb6084 If3c5f00f4b75d03838ee556235697369fd2e47dd I32d50caf7b7a84db2654b69baafb40a441f46bf0 Ibd1020113de05e8214d41ffb975d4c95d3bb1ccf I67c2ac422b0ba1e5eca11ffccfe7e112c2e65a3d I2c149e6edd7c194e7f31ece95e2003ffde935806 I1f4b65cba149480fb4a8f91667ce45a876dff2f0 I2605afc2d6f9f3569ec783b2d23881f31cc9038a I77b40f9cb8295cff1d4a24dfc7d85b0e57262f6e I8ec5a68cad573218a0b0bd0073277b76978417ff I9eaf7f21c73dd1ceaf97589cbf82b722706c48c2 I089725e20ae20956530e570d918a87b72e59626e Ifa70a4daf01414dfafed7830e3a6a682c42e9364 Iaace5bc1d0611619f11b760ea6986eadb7346da1 I415d7831f15c0d690fd118ff190cd710a31368c6 I1e55bbfe37c7cc6f62581af11280a565ed648c06 I11938c569a25b026ee6b7b5125cec930676ec01d I07577d1aa57bfa4bee40b52c18b5bfcb1cdb7070 Icdb0a28b5f9456135c60badf726c367b02f6d762 I4f14b7419f7a90813650601638f6563422c13ae4 I016a33c358be2e7a2d26202f2e9b168dd54b6d34 I43e26a2932ab3e3d9d79bb5af7daf2364ca133b7 I333c810568cdc656d2e13f0eeb9511857335b834 I830bf2e679e76efe7ed9d8bb30a8d459d8b9a09a I08d8d319d096c050ae53d90eeac293c9b1bab2bf I445676d558e05a6c5e9dd1e76e80ba2f5e8cd6c1 I9f38515f35fcddfbdc34ae4f59ffcfc6682033d4 Ia32e581c11954163bc8ff20a7b32432a843a17b2 I07c346475fb4a294bc8eb8da8c2048cc23394e20 Ic3f2c4e5bafd3a1556412005564fc0b549e6813c Ic68717f4410170814d3e3b141de77b35eca12f7b Ifad79991298e9d779b38a33a270af5197d2fc060 I3060c5ba31b59666aedfafad430e21329c4479d4 I9c87282ba5819a76a6f6697a5e9de6c92d74a665 I8144e2b83748a96ab83ba14894bfa5bd59995895 I8cac84296ddb1e323f28e2476f78d0190f4a1b73 Icef2b7fa70f274f671bd1b9469eb6a333ab462f9 Idc4ceb7973d1a424834f8c2a4bc5916ef7b1e961 I1bc779e5890f6994442c562ee5abf37c06dc6d3d Ib1161d3d8c4a9378eabf44f9de1eae9556d82643 I11a17fc105dea398140abc1a59e6355e9e55b322 I0aa523321f1eed4a0a5461b1a894452f77e6ff99 I827bd74e9892969bb4891b31af1515789fa76c1c I1bb658339e5a6f0adb07fda3ee0bc6c03670cc09 I135261c851067a25b20b30482764b5a61145f186 I4971d605c3475949be64372dc6bd3355ed258527 I08f6a7830c4b9ad44fb96f6d304ce257a899e287 I96886f8d0f3f03905163d93c4f8f3e637d98127f Idf2cf7f40780cf46ba95228764790b818bc72db5 Ib046fd7149d71fc62c8f2df7a9747d6b54e95138 I546ff57d76e50ad86eb5853782fdcc1f348c9f0e I1293ecc2974bae4ad530ecb775cd19072e413c72 I8db5a202a2e63ca0a3e355a6083d426acb7342cb I71482f9a4430a8bbcd02c954a415ff3fab2a0c8f I0c5656798444764b52f4d9d8889f831d674a1610 I2ac4ccd967a7a47284573c9b645055ebeca4635c I93b830d42e7d44435c790832c11d5d38861015e0 If24985f47cc9fcd6c2eb84a73165878559008120 I39b908451f4b678ed63b872ffc46590e2c728b2c If7e5622067c77c4c8d64ee466d1c62e4dcb0e53f If58279329f6c76978223369a2d08d47a7b1e3579 I3621a0a204360343ba684e2217a68a6ef51a7850 I358909c38b0771fe0283386841b6123ddb2931a6 I2e8fcb4a15af3a66331a921d378bfd5d2dfe4533 I0bbad22c5fed8c32dd7de387194e94c8d4b9ad75 I34f51c6202cbaa805c496360e8dbd391890decc6 Iaab82ea254444bba233cd9ebad93d38426154ca4 Ica2565031546a8281820c17edc95d258ad31ba65 Ic282fff7167a8224ec1a1e0f7c247a9c8d73f390 Icdc707a9438adae5c8bb2d9b97af95e65c4bc2a1 I26c4e1fb971f22ec721e1dddcce6b301f5db6608 I8882575084eefa6a23bd59b8d2a54a53d793298e Ic1e7cf4da4d2bf73d889aee7bac6cf260b26d532 I472b9da10e9f45b3c254f8440526f3f9d5116931 I1560e0df1c89be1db1e1d1aeaaeef2e6da5f85c1 Ie2842b28648084d8970329d7c76fcf0d0666a847 I2dccaedb559fc5e9865b137f980024e068bdafa3 I61a3d44f81d829f1ef22fd3044c65e4b3518b92b I0d657c2e0d47af32cc35cd66b902be72952e5438 I22d2dbdae5c9c859d4baca2bdb7bea71da09b85b I8c99fcbbf2f7229728236ae1059f4c9cb8dbb065 I1bb841e463658c8db42eb15ecc1687262df86a2f Change-Id: Ib04f801b0085e7d5da3d73594bbc41c281b06026 Signed-off-by: Xiaozhe Shi --- arch/arm/mach-msm/Makefile | 2 +- arch/arm/mach-msm/batterydata-lib.c | 338 ++++ arch/arm/mach-msm/bms-batterydata-desay.c | 4 +- arch/arm/mach-msm/bms-batterydata.c | 4 +- arch/arm/mach-msm/board-8064-pmic.c | 18 +- arch/arm/mach-msm/board-8064-regulator.c | 1 + arch/arm/mach-msm/board-8930-pmic.c | 18 +- arch/arm/mach-msm/board-8960-pmic.c | 19 +- arch/arm/mach-msm/board-8960-regulator.c | 1 + drivers/mfd/pm8018-core.c | 27 +- drivers/mfd/pm8038-core.c | 29 +- drivers/mfd/pm8921-core.c | 28 +- drivers/power/ltc4088-charger.c | 8 +- drivers/power/pm8921-bms.c | 1232 ++++++------- drivers/power/pm8921-charger.c | 1928 ++++++++++++-------- drivers/power/pm8xxx-ccadc.c | 118 +- drivers/usb/otg/msm_otg.c | 2 +- include/linux/mfd/pm8xxx/batterydata-lib.h | 155 ++ include/linux/mfd/pm8xxx/ccadc.h | 12 +- include/linux/mfd/pm8xxx/core.h | 31 + include/linux/mfd/pm8xxx/pm8921-bms.h | 122 +- include/linux/mfd/pm8xxx/pm8921-charger.h | 62 +- 22 files changed, 2576 insertions(+), 1583 deletions(-) create mode 100644 arch/arm/mach-msm/batterydata-lib.c create mode 100644 include/linux/mfd/pm8xxx/batterydata-lib.h diff --git a/arch/arm/mach-msm/Makefile b/arch/arm/mach-msm/Makefile index a0732c1a5eb..c5ce829a571 100644 --- a/arch/arm/mach-msm/Makefile +++ b/arch/arm/mach-msm/Makefile @@ -280,7 +280,7 @@ obj-$(CONFIG_MACH_MSM8960_FLUID) += board-8960-all.o board-8960-regulator.o obj-$(CONFIG_MACH_MSM8930_CDP) += board-8930-all.o board-8930-regulator-pm8038.o board-8930-regulator-pm8917.o obj-$(CONFIG_MACH_MSM8930_MTP) += board-8930-all.o board-8930-regulator-pm8038.o board-8930-regulator-pm8917.o obj-$(CONFIG_MACH_MSM8930_FLUID) += board-8930-all.o board-8930-regulator-pm8038.o board-8930-regulator-pm8917.o -obj-$(CONFIG_PM8921_BMS) += bms-batterydata.o bms-batterydata-desay.o +obj-$(CONFIG_PM8921_BMS) += bms-batterydata.o bms-batterydata-desay.o batterydata-lib.o obj-$(CONFIG_MACH_APQ8064_CDP) += board-8064-all.o board-8064-regulator.o obj-$(CONFIG_MACH_APQ8064_MTP) += board-8064-all.o board-8064-regulator.o obj-$(CONFIG_MACH_APQ8064_LIQUID) += board-8064-all.o board-8064-regulator.o diff --git a/arch/arm/mach-msm/batterydata-lib.c b/arch/arm/mach-msm/batterydata-lib.c new file mode 100644 index 00000000000..2be591c0aab --- /dev/null +++ b/arch/arm/mach-msm/batterydata-lib.c @@ -0,0 +1,338 @@ +/* Copyright (c) 2012, 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 +#include + +int linear_interpolate(int y0, int x0, int y1, int x1, int x) +{ + if (y0 == y1 || x == x0) + return y0; + if (x1 == x0 || x == x1) + return y1; + + return y0 + ((y1 - y0) * (x - x0) / (x1 - x0)); +} + +int is_between(int left, int right, int value) +{ + if (left >= right && left >= value && value >= right) + return 1; + if (left <= right && left <= value && value <= right) + return 1; + return 0; +} + +static int interpolate_single_lut(struct single_row_lut *lut, int x) +{ + int i, result; + + if (x < lut->x[0]) { + pr_debug("x %d less than known range return y = %d lut = %pS\n", + x, lut->y[0], lut); + return lut->y[0]; + } + if (x > lut->x[lut->cols - 1]) { + pr_debug("x %d more than known range return y = %d lut = %pS\n", + x, lut->y[lut->cols - 1], lut); + return lut->y[lut->cols - 1]; + } + + for (i = 0; i < lut->cols; i++) + if (x <= lut->x[i]) + break; + if (x == lut->x[i]) { + result = lut->y[i]; + } else { + result = linear_interpolate( + lut->y[i - 1], + lut->x[i - 1], + lut->y[i], + lut->x[i], + x); + } + return result; +} + +int interpolate_fcc(struct single_row_lut *fcc_temp_lut, int batt_temp) +{ + /* batt_temp is in tenths of degC - convert it to degC for lookups */ + batt_temp = batt_temp/10; + return interpolate_single_lut(fcc_temp_lut, batt_temp); +} + +int interpolate_scalingfactor_fcc(struct single_row_lut *fcc_sf_lut, + int cycles) +{ + /* + * sf table could be null when no battery aging data is available, in + * that case return 100% + */ + if (fcc_sf_lut) + return interpolate_single_lut(fcc_sf_lut, cycles); + else + return 100; +} + +int interpolate_scalingfactor(struct sf_lut *sf_lut, int row_entry, int pc) +{ + int i, scalefactorrow1, scalefactorrow2, scalefactor, rows, cols; + int row1 = 0; + int row2 = 0; + + /* + * sf table could be null when no battery aging data is available, in + * that case return 100% + */ + if (!sf_lut) + return 100; + + rows = sf_lut->rows; + cols = sf_lut->cols; + if (pc > sf_lut->percent[0]) { + pr_debug("pc %d greater than known pc ranges for sfd\n", pc); + row1 = 0; + row2 = 0; + } + if (pc < sf_lut->percent[rows - 1]) { + pr_debug("pc %d less than known pc ranges for sf\n", pc); + row1 = rows - 1; + row2 = rows - 1; + } + for (i = 0; i < rows; i++) { + if (pc == sf_lut->percent[i]) { + row1 = i; + row2 = i; + break; + } + if (pc > sf_lut->percent[i]) { + row1 = i - 1; + row2 = i; + break; + } + } + + if (row_entry < sf_lut->row_entries[0]) + row_entry = sf_lut->row_entries[0]; + if (row_entry > sf_lut->row_entries[cols - 1]) + row_entry = sf_lut->row_entries[cols - 1]; + + for (i = 0; i < cols; i++) + if (row_entry <= sf_lut->row_entries[i]) + break; + if (row_entry == sf_lut->row_entries[i]) { + scalefactor = linear_interpolate( + sf_lut->sf[row1][i], + sf_lut->percent[row1], + sf_lut->sf[row2][i], + sf_lut->percent[row2], + pc); + return scalefactor; + } + + scalefactorrow1 = linear_interpolate( + sf_lut->sf[row1][i - 1], + sf_lut->row_entries[i - 1], + sf_lut->sf[row1][i], + sf_lut->row_entries[i], + row_entry); + + scalefactorrow2 = linear_interpolate( + sf_lut->sf[row2][i - 1], + sf_lut->row_entries[i - 1], + sf_lut->sf[row2][i], + sf_lut->row_entries[i], + row_entry); + + scalefactor = linear_interpolate( + scalefactorrow1, + sf_lut->percent[row1], + scalefactorrow2, + sf_lut->percent[row2], + pc); + + return scalefactor; +} + +/* get ocv given a soc -- reverse lookup */ +int interpolate_ocv(struct pc_temp_ocv_lut *pc_temp_ocv, + int batt_temp_degc, int pc) +{ + int i, ocvrow1, ocvrow2, ocv, rows, cols; + int row1 = 0; + int row2 = 0; + + rows = pc_temp_ocv->rows; + cols = pc_temp_ocv->cols; + if (pc > pc_temp_ocv->percent[0]) { + pr_debug("pc %d greater than known pc ranges for sfd\n", pc); + row1 = 0; + row2 = 0; + } + if (pc < pc_temp_ocv->percent[rows - 1]) { + pr_debug("pc %d less than known pc ranges for sf\n", pc); + row1 = rows - 1; + row2 = rows - 1; + } + for (i = 0; i < rows; i++) { + if (pc == pc_temp_ocv->percent[i]) { + row1 = i; + row2 = i; + break; + } + if (pc > pc_temp_ocv->percent[i]) { + row1 = i - 1; + row2 = i; + break; + } + } + + if (batt_temp_degc < pc_temp_ocv->temp[0]) + batt_temp_degc = pc_temp_ocv->temp[0]; + if (batt_temp_degc > pc_temp_ocv->temp[cols - 1]) + batt_temp_degc = pc_temp_ocv->temp[cols - 1]; + + for (i = 0; i < cols; i++) + if (batt_temp_degc <= pc_temp_ocv->temp[i]) + break; + if (batt_temp_degc == pc_temp_ocv->temp[i]) { + ocv = linear_interpolate( + pc_temp_ocv->ocv[row1][i], + pc_temp_ocv->percent[row1], + pc_temp_ocv->ocv[row2][i], + pc_temp_ocv->percent[row2], + pc); + return ocv; + } + + ocvrow1 = linear_interpolate( + pc_temp_ocv->ocv[row1][i - 1], + pc_temp_ocv->temp[i - 1], + pc_temp_ocv->ocv[row1][i], + pc_temp_ocv->temp[i], + batt_temp_degc); + + ocvrow2 = linear_interpolate( + pc_temp_ocv->ocv[row2][i - 1], + pc_temp_ocv->temp[i - 1], + pc_temp_ocv->ocv[row2][i], + pc_temp_ocv->temp[i], + batt_temp_degc); + + ocv = linear_interpolate( + ocvrow1, + pc_temp_ocv->percent[row1], + ocvrow2, + pc_temp_ocv->percent[row2], + pc); + + return ocv; +} + +int interpolate_pc(struct pc_temp_ocv_lut *pc_temp_ocv, + int batt_temp_degc, int ocv) +{ + int i, j, pcj, pcj_minus_one, pc; + int rows = pc_temp_ocv->rows; + int cols = pc_temp_ocv->cols; + + if (batt_temp_degc < pc_temp_ocv->temp[0]) { + pr_debug("batt_temp %d < known temp range\n", batt_temp_degc); + batt_temp_degc = pc_temp_ocv->temp[0]; + } + + if (batt_temp_degc > pc_temp_ocv->temp[cols - 1]) { + pr_debug("batt_temp %d > known temp range\n", batt_temp_degc); + batt_temp_degc = pc_temp_ocv->temp[cols - 1]; + } + + for (j = 0; j < cols; j++) + if (batt_temp_degc <= pc_temp_ocv->temp[j]) + break; + if (batt_temp_degc == pc_temp_ocv->temp[j]) { + /* found an exact match for temp in the table */ + if (ocv >= pc_temp_ocv->ocv[0][j]) + return pc_temp_ocv->percent[0]; + if (ocv <= pc_temp_ocv->ocv[rows - 1][j]) + return pc_temp_ocv->percent[rows - 1]; + for (i = 0; i < rows; i++) { + if (ocv >= pc_temp_ocv->ocv[i][j]) { + if (ocv == pc_temp_ocv->ocv[i][j]) + return pc_temp_ocv->percent[i]; + pc = linear_interpolate( + pc_temp_ocv->percent[i], + pc_temp_ocv->ocv[i][j], + pc_temp_ocv->percent[i - 1], + pc_temp_ocv->ocv[i - 1][j], + ocv); + return pc; + } + } + } + + /* + * batt_temp_degc is within temperature for + * column j-1 and j + */ + if (ocv >= pc_temp_ocv->ocv[0][j]) + return pc_temp_ocv->percent[0]; + if (ocv <= pc_temp_ocv->ocv[rows - 1][j - 1]) + return pc_temp_ocv->percent[rows - 1]; + + pcj_minus_one = 0; + pcj = 0; + for (i = 0; i < rows-1; i++) { + if (pcj == 0 + && is_between(pc_temp_ocv->ocv[i][j], + pc_temp_ocv->ocv[i+1][j], ocv)) { + pcj = linear_interpolate( + pc_temp_ocv->percent[i], + pc_temp_ocv->ocv[i][j], + pc_temp_ocv->percent[i + 1], + pc_temp_ocv->ocv[i+1][j], + ocv); + } + + if (pcj_minus_one == 0 + && is_between(pc_temp_ocv->ocv[i][j-1], + pc_temp_ocv->ocv[i+1][j-1], ocv)) { + pcj_minus_one = linear_interpolate( + pc_temp_ocv->percent[i], + pc_temp_ocv->ocv[i][j-1], + pc_temp_ocv->percent[i + 1], + pc_temp_ocv->ocv[i+1][j-1], + ocv); + } + + if (pcj && pcj_minus_one) { + pc = linear_interpolate( + pcj_minus_one, + pc_temp_ocv->temp[j-1], + pcj, + pc_temp_ocv->temp[j], + batt_temp_degc); + return pc; + } + } + + if (pcj) + return pcj; + + if (pcj_minus_one) + return pcj_minus_one; + + pr_debug("%d ocv wasn't found for temp %d in the LUT returning 100%%\n", + ocv, batt_temp_degc); + return 100; +} diff --git a/arch/arm/mach-msm/bms-batterydata-desay.c b/arch/arm/mach-msm/bms-batterydata-desay.c index f362a72f325..d9fa06123a4 100644 --- a/arch/arm/mach-msm/bms-batterydata-desay.c +++ b/arch/arm/mach-msm/bms-batterydata-desay.c @@ -10,7 +10,7 @@ * GNU General Public License for more details. */ -#include +#include static struct single_row_lut desay_5200_fcc_temp = { .x = {-20, 0, 25, 40}, @@ -76,7 +76,7 @@ static struct sf_lut desay_5200_pc_sf = { }, }; -struct pm8921_bms_battery_data desay_5200_data = { +struct bms_battery_data desay_5200_data = { .fcc = 5200, .fcc_temp_lut = &desay_5200_fcc_temp, .fcc_sf_lut = &desay_5200_fcc_sf, diff --git a/arch/arm/mach-msm/bms-batterydata.c b/arch/arm/mach-msm/bms-batterydata.c index 81ab1217829..fb4f96776de 100644 --- a/arch/arm/mach-msm/bms-batterydata.c +++ b/arch/arm/mach-msm/bms-batterydata.c @@ -10,7 +10,7 @@ * GNU General Public License for more details. */ -#include +#include static struct single_row_lut fcc_temp = { .x = {-20, 0, 25, 40, 65}, @@ -99,7 +99,7 @@ static struct sf_lut rbatt_sf = { } }; -struct pm8921_bms_battery_data palladium_1500_data = { +struct bms_battery_data palladium_1500_data = { .fcc = 1500, .fcc_temp_lut = &fcc_temp, .pc_temp_ocv_lut = &pc_temp_ocv, diff --git a/arch/arm/mach-msm/board-8064-pmic.c b/arch/arm/mach-msm/board-8064-pmic.c index 9874cb692f6..c9a1a5e0379 100644 --- a/arch/arm/mach-msm/board-8064-pmic.c +++ b/arch/arm/mach-msm/board-8064-pmic.c @@ -382,16 +382,17 @@ static int apq8064_pm8921_therm_mitigation[] = { #define CHG_TERM_MA 100 static struct pm8921_charger_platform_data apq8064_pm8921_chg_pdata __devinitdata = { - .safety_time = 180, .update_time = 60000, .max_voltage = MAX_VOLTAGE_MV, .min_voltage = 3200, .uvd_thresh_voltage = 4050, - .alarm_voltage = 3400, - .resume_voltage_delta = 100, + .alarm_low_mv = 3400, + .alarm_high_mv = 4000, + .resume_voltage_delta = 60, + .resume_charge_percent = 99, .term_current = CHG_TERM_MA, .cool_temp = 10, - .warm_temp = 40, + .warm_temp = 45, .temp_check_period = 1, .max_bat_chg_current = 1100, .cool_bat_chg_current = 350, @@ -404,20 +405,22 @@ apq8064_pm8921_chg_pdata __devinitdata = { static struct pm8xxx_ccadc_platform_data apq8064_pm8xxx_ccadc_pdata = { - .r_sense = 10, + .r_sense_uohm = 10000, .calib_delay_ms = 600000, }; static struct pm8921_bms_platform_data apq8064_pm8921_bms_pdata __devinitdata = { .battery_type = BATT_UNKNOWN, - .r_sense = 10, + .r_sense_uohm = 10000, .v_cutoff = 3400, .max_voltage_uv = MAX_VOLTAGE_MV * 1000, .rconn_mohm = 18, .shutdown_soc_valid_limit = 20, .adjust_soc_low_threshold = 25, .chg_term_ua = CHG_TERM_MA * 1000, + .normal_voltage_calc_ms = 20000, + .low_voltage_calc_ms = 1000, }; static struct pm8921_platform_data @@ -497,4 +500,7 @@ void __init apq8064_init_pmic(void) } else if (machine_is_apq8064_cdp()) { apq8064_pm8921_chg_pdata.has_dc_supply = true; } + + if (!machine_is_apq8064_mtp() && !machine_is_apq8064_liquid()) + apq8064_pm8921_chg_pdata.battery_less_hardware = 1; } diff --git a/arch/arm/mach-msm/board-8064-regulator.c b/arch/arm/mach-msm/board-8064-regulator.c index 4ae811d0d68..125e7da03d0 100644 --- a/arch/arm/mach-msm/board-8064-regulator.c +++ b/arch/arm/mach-msm/board-8064-regulator.c @@ -90,6 +90,7 @@ VREG_CONSUMERS(L13) = { }; VREG_CONSUMERS(L14) = { REGULATOR_SUPPLY("8921_l14", NULL), + REGULATOR_SUPPLY("vreg_xoadc", "pm8921-charger"), }; VREG_CONSUMERS(L15) = { REGULATOR_SUPPLY("8921_l15", NULL), diff --git a/arch/arm/mach-msm/board-8930-pmic.c b/arch/arm/mach-msm/board-8930-pmic.c index 4bc1842393b..f997a03c298 100644 --- a/arch/arm/mach-msm/board-8930-pmic.c +++ b/arch/arm/mach-msm/board-8930-pmic.c @@ -313,16 +313,17 @@ static int pm8921_therm_mitigation[] = { #define MAX_VOLTAGE_MV 4200 #define CHG_TERM_MA 100 static struct pm8921_charger_platform_data pm8921_chg_pdata __devinitdata = { - .safety_time = 180, .update_time = 60000, .max_voltage = MAX_VOLTAGE_MV, .min_voltage = 3200, .uvd_thresh_voltage = 4050, - .alarm_voltage = 3400, - .resume_voltage_delta = 100, + .alarm_low_mv = 3400, + .alarm_high_mv = 4000, + .resume_voltage_delta = 60, + .resume_charge_percent = 99, .term_current = CHG_TERM_MA, .cool_temp = 10, - .warm_temp = 40, + .warm_temp = 45, .temp_check_period = 1, .max_bat_chg_current = 1100, .cool_bat_chg_current = 350, @@ -434,7 +435,7 @@ static struct pm8xxx_led_platform_data pm8xxx_leds_pdata = { }; static struct pm8xxx_ccadc_platform_data pm8xxx_ccadc_pdata = { - .r_sense = 10, + .r_sense_uohm = 10000, .calib_delay_ms = 600000, }; @@ -462,13 +463,15 @@ static struct pm8xxx_spk_platform_data pm8xxx_spk_pdata = { static struct pm8921_bms_platform_data pm8921_bms_pdata __devinitdata = { .battery_type = BATT_UNKNOWN, - .r_sense = 10, + .r_sense_uohm = 10000, .v_cutoff = 3400, .max_voltage_uv = MAX_VOLTAGE_MV * 1000, .shutdown_soc_valid_limit = 20, .adjust_soc_low_threshold = 25, .chg_term_ua = CHG_TERM_MA * 1000, .rconn_mohm = 18, + .normal_voltage_calc_ms = 20000, + .low_voltage_calc_ms = 1000, }; static struct pm8038_platform_data pm8038_platform_data __devinitdata = { @@ -590,4 +593,7 @@ void __init msm8930_init_pmic(void) else if (machine_is_msm8930_cdp()) pm8921_chg_pdata.has_dc_supply = true; } + + if (!machine_is_msm8930_mtp()) + pm8921_chg_pdata.battery_less_hardware = 1; } diff --git a/arch/arm/mach-msm/board-8960-pmic.c b/arch/arm/mach-msm/board-8960-pmic.c index 44d19d4b164..abf88e17483 100644 --- a/arch/arm/mach-msm/board-8960-pmic.c +++ b/arch/arm/mach-msm/board-8960-pmic.c @@ -396,16 +396,17 @@ static int pm8921_therm_mitigation[] = { #define MAX_VOLTAGE_MV 4200 #define CHG_TERM_MA 100 static struct pm8921_charger_platform_data pm8921_chg_pdata __devinitdata = { - .safety_time = 180, .update_time = 60000, .max_voltage = MAX_VOLTAGE_MV, .min_voltage = 3200, .uvd_thresh_voltage = 4050, - .alarm_voltage = 3400, - .resume_voltage_delta = 100, + .alarm_low_mv = 3400, + .alarm_high_mv = 4000, + .resume_voltage_delta = 60, + .resume_charge_percent = 99, .term_current = CHG_TERM_MA, .cool_temp = 10, - .warm_temp = 40, + .warm_temp = 45, .temp_check_period = 1, .max_bat_chg_current = 1100, .cool_bat_chg_current = 350, @@ -423,13 +424,15 @@ static struct pm8xxx_misc_platform_data pm8xxx_misc_pdata = { static struct pm8921_bms_platform_data pm8921_bms_pdata __devinitdata = { .battery_type = BATT_UNKNOWN, - .r_sense = 10, + .r_sense_uohm = 10000, .v_cutoff = 3400, .max_voltage_uv = MAX_VOLTAGE_MV * 1000, .rconn_mohm = 18, .shutdown_soc_valid_limit = 20, .adjust_soc_low_threshold = 25, .chg_term_ua = CHG_TERM_MA * 1000, + .normal_voltage_calc_ms = 20000, + .low_voltage_calc_ms = 1000, }; #define PM8921_LC_LED_MAX_CURRENT 4 /* I = 4mA */ @@ -551,7 +554,7 @@ static struct pm8xxx_led_platform_data pm8xxx_leds_pdata = { }; static struct pm8xxx_ccadc_platform_data pm8xxx_ccadc_pdata = { - .r_sense = 10, + .r_sense_uohm = 10000, .calib_delay_ms = 600000, }; @@ -610,4 +613,8 @@ void __init msm8960_init_pmic(void) if (machine_is_msm8960_fluid()) pm8921_bms_pdata.rconn_mohm = 20; + + if (!machine_is_msm8960_fluid() && !machine_is_msm8960_liquid() + && !machine_is_msm8960_mtp()) + pm8921_chg_pdata.battery_less_hardware = 1; } diff --git a/arch/arm/mach-msm/board-8960-regulator.c b/arch/arm/mach-msm/board-8960-regulator.c index 8fc26ea4678..b666ae9ec0c 100644 --- a/arch/arm/mach-msm/board-8960-regulator.c +++ b/arch/arm/mach-msm/board-8960-regulator.c @@ -89,6 +89,7 @@ VREG_CONSUMERS(L12) = { VREG_CONSUMERS(L14) = { REGULATOR_SUPPLY("8921_l14", NULL), REGULATOR_SUPPLY("pa_therm", "pm8xxx-adc"), + REGULATOR_SUPPLY("vreg_xoadc", "pm8921-charger"), }; VREG_CONSUMERS(L15) = { REGULATOR_SUPPLY("8921_l15", NULL), diff --git a/drivers/mfd/pm8018-core.c b/drivers/mfd/pm8018-core.c index b1b64cb9d1a..a91152fdb59 100644 --- a/drivers/mfd/pm8018-core.c +++ b/drivers/mfd/pm8018-core.c @@ -45,7 +45,6 @@ #define PM8018_REVISION_MASK 0x000F #define REG_PM8018_PON_CNTRL_3 0x01D -#define PM8018_RESTART_REASON_MASK 0x07 #define SINGLE_IRQ_RESOURCE(_name, _irq) \ { \ @@ -61,6 +60,7 @@ struct pm8018 { struct mfd_cell *mfd_regulators; struct pm8xxx_regulator_core_platform_data *regulator_cdata; u32 rev_registers; + u8 restart_reason; }; static int pm8018_readb(const struct device *dev, u16 addr, u8 *val) @@ -125,6 +125,14 @@ static int pm8018_get_revision(const struct device *dev) return pmic->rev_registers & PM8018_REVISION_MASK; } +static u8 pm8018_restart_reason(const struct device *dev) +{ + const struct pm8xxx_drvdata *pm8018_drvdata = dev_get_drvdata(dev); + const struct pm8018 *pmic = pm8018_drvdata->pm_chip_data; + + return pmic->restart_reason; +} + static struct pm8xxx_drvdata pm8018_drvdata = { .pmic_readb = pm8018_readb, .pmic_writeb = pm8018_writeb, @@ -133,6 +141,7 @@ static struct pm8xxx_drvdata pm8018_drvdata = { .pmic_read_irq_stat = pm8018_read_irq_stat, .pmic_get_version = pm8018_get_version, .pmic_get_revision = pm8018_get_revision, + .pmic_restart_reason = pm8018_restart_reason, }; static const struct resource gpio_cell_resources[] __devinitconst = { @@ -516,17 +525,6 @@ bail: return ret; } -static const char * const pm8018_restart_reason[] = { - [0] = "Unknown", - [1] = "Triggered from CBL (external charger)", - [2] = "Triggered from KPD (power key press)", - [3] = "Triggered from CHG (usb charger insertion)", - [4] = "Triggered from SMPL (sudden momentary power loss)", - [5] = "Triggered from RTC (real time clock)", - [6] = "Triggered by Hard Reset", - [7] = "Triggered by General Purpose Trigger", -}; - static const char * const pm8018_rev_names[] = { [PM8XXX_REVISION_8018_TEST] = "test", [PM8XXX_REVISION_8018_1p0] = "1.0", @@ -594,8 +592,9 @@ static int __devinit pm8018_probe(struct platform_device *pdev) pr_err("Cannot read restart reason rc=%d\n", rc); goto err_read_rev; } - val &= PM8018_RESTART_REASON_MASK; - pr_info("PMIC Restart Reason: %s\n", pm8018_restart_reason[val]); + val &= PM8XXX_RESTART_REASON_MASK; + pr_info("PMIC Restart Reason: %s\n", pm8xxx_restart_reason_str[val]); + pmic->restart_reason = val; rc = pm8018_add_subdevices(pdata, pmic); if (rc) { diff --git a/drivers/mfd/pm8038-core.c b/drivers/mfd/pm8038-core.c index c3203c672a1..479fc02c8fe 100644 --- a/drivers/mfd/pm8038-core.c +++ b/drivers/mfd/pm8038-core.c @@ -49,7 +49,6 @@ #define PM8038_REVISION_MASK 0x000F #define REG_PM8038_PON_CNTRL_3 0x01D -#define PM8038_RESTART_REASON_MASK 0x07 #define SINGLE_IRQ_RESOURCE(_name, _irq) \ { \ @@ -65,6 +64,7 @@ struct pm8038 { struct mfd_cell *mfd_regulators; struct pm8xxx_regulator_core_platform_data *regulator_cdata; u32 rev_registers; + u8 restart_reason; }; static int pm8038_readb(const struct device *dev, u16 addr, u8 *val) @@ -129,6 +129,14 @@ static int pm8038_get_revision(const struct device *dev) return pmic->rev_registers & PM8038_REVISION_MASK; } +static u8 pm8038_restart_reason(const struct device *dev) +{ + const struct pm8xxx_drvdata *pm8038_drvdata = dev_get_drvdata(dev); + const struct pm8038 *pmic = pm8038_drvdata->pm_chip_data; + + return pmic->restart_reason; +} + static struct pm8xxx_drvdata pm8038_drvdata = { .pmic_readb = pm8038_readb, .pmic_writeb = pm8038_writeb, @@ -137,6 +145,7 @@ static struct pm8xxx_drvdata pm8038_drvdata = { .pmic_read_irq_stat = pm8038_read_irq_stat, .pmic_get_version = pm8038_get_version, .pmic_get_revision = pm8038_get_revision, + .pmic_restart_reason = pm8038_restart_reason, }; static const struct resource gpio_cell_resources[] __devinitconst = { @@ -702,6 +711,8 @@ pm8038_add_subdevices(const struct pm8038_platform_data *pdata, } if (pdata->ccadc_pdata) { + pdata->ccadc_pdata->ccadc_cdata.batt_temp_channel + = CHANNEL_BATT_THERM; ccadc_cell.platform_data = pdata->ccadc_pdata; ccadc_cell.pdata_size = sizeof(struct pm8xxx_ccadc_platform_data); @@ -723,17 +734,6 @@ bail: return ret; } -static const char * const pm8038_restart_reason[] = { - [0] = "Unknown", - [1] = "Triggered from CBL (external charger)", - [2] = "Triggered from KPD (power key press)", - [3] = "Triggered from CHG (usb charger insertion)", - [4] = "Triggered from SMPL (sudden momentary power loss)", - [5] = "Triggered from RTC (real time clock)", - [6] = "Triggered by Hard Reset", - [7] = "Triggered by General Purpose Trigger", -}; - static const char * const pm8038_rev_names[] = { [PM8XXX_REVISION_8038_TEST] = "test", [PM8XXX_REVISION_8038_1p0] = "1.0", @@ -802,8 +802,9 @@ static int __devinit pm8038_probe(struct platform_device *pdev) pr_err("Cannot read restart reason rc=%d\n", rc); goto err_read_rev; } - val &= PM8038_RESTART_REASON_MASK; - pr_info("PMIC Restart Reason: %s\n", pm8038_restart_reason[val]); + val &= PM8XXX_RESTART_REASON_MASK; + pr_info("PMIC Restart Reason: %s\n", pm8xxx_restart_reason_str[val]); + pmic->restart_reason = val; rc = pm8038_add_subdevices(pdata, pmic); if (rc) { diff --git a/drivers/mfd/pm8921-core.c b/drivers/mfd/pm8921-core.c index f39a19f9420..685bb320688 100644 --- a/drivers/mfd/pm8921-core.c +++ b/drivers/mfd/pm8921-core.c @@ -63,6 +63,7 @@ struct pm8921 { struct mfd_cell *mfd_regulators; struct pm8xxx_regulator_core_platform_data *regulator_cdata; u32 rev_registers; + u8 restart_reason; }; static int pm8921_readb(const struct device *dev, u16 addr, u8 *val) @@ -133,6 +134,14 @@ static int pm8921_get_revision(const struct device *dev) return pmic->rev_registers & PM8921_REVISION_MASK; } +static u8 pm8921_restart_reason(const struct device *dev) +{ + const struct pm8xxx_drvdata *pm8921_drvdata = dev_get_drvdata(dev); + const struct pm8921 *pmic = pm8921_drvdata->pm_chip_data; + + return pmic->restart_reason; +} + static struct pm8xxx_drvdata pm8921_drvdata = { .pmic_readb = pm8921_readb, .pmic_writeb = pm8921_writeb, @@ -141,6 +150,7 @@ static struct pm8xxx_drvdata pm8921_drvdata = { .pmic_read_irq_stat = pm8921_read_irq_stat, .pmic_get_version = pm8921_get_version, .pmic_get_revision = pm8921_get_revision, + .pmic_restart_reason = pm8921_restart_reason, }; static struct resource gpio_cell_resources[] = { @@ -794,6 +804,8 @@ pm8921_add_subdevices(const struct pm8921_platform_data *pdata, } if (pdata->ccadc_pdata) { + pdata->ccadc_pdata->ccadc_cdata.batt_temp_channel + = CHANNEL_BATT_THERM; ccadc_cell.platform_data = pdata->ccadc_pdata; ccadc_cell.pdata_size = sizeof(struct pm8xxx_ccadc_platform_data); @@ -815,17 +827,6 @@ bail: return ret; } -static const char * const pm8921_restart_reason[] = { - [0] = "Unknown", - [1] = "Triggered from CBL (external charger)", - [2] = "Triggered from KPD (power key press)", - [3] = "Triggered from CHG (usb charger insertion)", - [4] = "Triggered from SMPL (sudden momentary power loss)", - [5] = "Triggered from RTC (real time clock)", - [6] = "Triggered by Hard Reset", - [7] = "Triggered by General Purpose Trigger", -}; - static const char * const pm8921_rev_names[] = { [PM8XXX_REVISION_8921_TEST] = "test", [PM8XXX_REVISION_8921_1p0] = "1.0", @@ -918,8 +919,9 @@ static int __devinit pm8921_probe(struct platform_device *pdev) pr_err("Cannot read restart reason rc=%d\n", rc); goto err_read_rev; } - val &= PM8921_RESTART_REASON_MASK; - pr_info("PMIC Restart Reason: %s\n", pm8921_restart_reason[val]); + val &= PM8XXX_RESTART_REASON_MASK; + pr_info("PMIC Restart Reason: %s\n", pm8xxx_restart_reason_str[val]); + pmic->restart_reason = val; rc = pm8921_add_subdevices(pdata, pmic); if (rc) { diff --git a/drivers/power/ltc4088-charger.c b/drivers/power/ltc4088-charger.c index dbc75cd463a..58503cf1a5f 100644 --- a/drivers/power/ltc4088-charger.c +++ b/drivers/power/ltc4088-charger.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011, Code Aurora Forum. All rights reserved. + * Copyright (c) 2011-2012, Code Aurora Forum. 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 @@ -206,9 +206,11 @@ static int pm_power_set_property(struct power_supply *psy, struct ltc4088_chg_chip *chip; if (psy->type == POWER_SUPPLY_TYPE_USB) { - chip = container_of(psy, struct ltc4088_chg_chip, - usb_psy); + chip = container_of(psy, struct ltc4088_chg_chip, usb_psy); switch (psp) { + case POWER_SUPPLY_PROP_TYPE: + psy.type = val->intval; + break; case POWER_SUPPLY_PROP_ONLINE: ltc4088_set_charging(chip, val->intval); break; diff --git a/drivers/power/pm8921-bms.c b/drivers/power/pm8921-bms.c index c19015f6490..f9ed816a01c 100644 --- a/drivers/power/pm8921-bms.c +++ b/drivers/power/pm8921-bms.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved. +/* Copyright (c) 2011-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 @@ -10,8 +10,8 @@ * GNU General Public License for more details. * */ -#define pr_fmt(fmt) "%s: " fmt, __func__ +#define pr_fmt(fmt) "%s: " fmt, __func__ #include #include #include @@ -22,6 +22,7 @@ #include #include #include +#include #include #include #include @@ -54,6 +55,9 @@ #define TEMP_IAVG_STORAGE 0x105 #define TEMP_IAVG_STORAGE_USE_MASK 0x0F +#define PON_CNTRL_6 0x018 +#define WD_BIT BIT(7) + enum pmic_bms_interrupts { PM8921_BMS_SBI_WRITE_OK, PM8921_BMS_CC_THR, @@ -86,7 +90,7 @@ struct pm8921_soc_params { struct pm8921_bms_chip { struct device *dev; struct dentry *dent; - unsigned int r_sense; + int r_sense_uohm; unsigned int v_cutoff; unsigned int fcc; struct single_row_lut *fcc_temp_lut; @@ -96,7 +100,8 @@ struct pm8921_bms_chip { struct sf_lut *rbatt_sf_lut; int delta_rbatt_mohm; struct work_struct calib_hkadc_work; - struct delayed_work calib_hkadc_delayed_work; + unsigned long last_calib_time; + int last_calib_temp; struct mutex calib_mutex; unsigned int revision; unsigned int xoadc_v0625_usb_present; @@ -126,9 +131,10 @@ struct pm8921_bms_chip { int default_rbatt_mohm; int amux_2_trim_delta; uint16_t prev_last_good_ocv_raw; - unsigned int rconn_mohm; + int rconn_mohm; struct mutex last_ocv_uv_mutex; int last_ocv_uv; + int last_ocv_temp_decidegc; int pon_ocv_uv; int last_cc_uah; unsigned long tm_sec; @@ -137,6 +143,7 @@ struct pm8921_bms_chip { int shutdown_iavg_ua; struct delayed_work calculate_soc_delayed_work; struct timespec t_soc_queried; + unsigned long last_recalc_time; int shutdown_soc_valid_limit; int ignore_shutdown_soc; int prev_iavg_ua; @@ -147,6 +154,14 @@ struct pm8921_bms_chip { int ibat_at_cv_ua; int soc_at_cv; int prev_chg_soc; + struct power_supply *batt_psy; + bool low_voltage_wake_lock_held; + struct wake_lock low_voltage_wake_lock; + int soc_calc_period; + int normal_voltage_calc_ms; + int low_voltage_calc_ms; + int imax_ua; + struct wake_lock soc_wake_lock; }; /* @@ -222,7 +237,6 @@ module_param_cb(bms_end_percent, &bms_ro_param_ops, &bms_end_percent, 0644); module_param_cb(bms_end_ocv_uv, &bms_ro_param_ops, &bms_end_ocv_uv, 0644); module_param_cb(bms_end_cc_uah, &bms_ro_param_ops, &bms_end_cc_uah, 0644); -static int interpolate_fcc(struct pm8921_bms_chip *chip, int batt_temp); static void readjust_fcc_table(void) { struct single_row_lut *temp, *old; @@ -239,7 +253,7 @@ static void readjust_fcc_table(void) return; } - fcc = interpolate_fcc(the_chip, last_real_fcc_batt_temp); + fcc = interpolate_fcc(the_chip->fcc_temp_lut, last_real_fcc_batt_temp); temp->cols = the_chip->fcc_temp_lut->cols; for (i = 0; i < the_chip->fcc_temp_lut->cols; i++) { @@ -346,13 +360,18 @@ static int pm_bms_masked_write(struct pm8921_bms_chip *chip, u16 addr, return 0; } -static int usb_chg_plugged_in(void) +static int usb_chg_plugged_in(struct pm8921_bms_chip *chip) { int val = pm8921_is_usb_chg_plugged_in(); - /* treat as if usb is not present in case of error */ - if (val == -EINVAL) - val = 0; + /* if the charger driver was not initialized, use the restart reason */ + if (val == -EINVAL) { + if (pm8xxx_restart_reason(chip->dev->parent) + == PM8XXX_RESTART_CHG) + val = 1; + else + val = 0; + } return val; } @@ -492,15 +511,15 @@ static s64 cc_to_microvolt(struct pm8921_bms_chip *chip, s64 cc) #define SLEEP_CLK_HZ 32764 #define SECONDS_PER_HOUR 3600 /** - * ccmicrovolt_to_nvh - + * ccmicrovolt_to_uvh - * @cc_uv: coulumb counter converted to uV * - * RETURNS: coulumb counter based charge in nVh - * (nano Volt Hour) + * RETURNS: coulumb counter based charge in uVh + * (micro Volt Hour) */ -static s64 ccmicrovolt_to_nvh(s64 cc_uv) +static s64 ccmicrovolt_to_uvh(s64 cc_uv) { - return div_s64(cc_uv * CC_READING_TICKS * 1000, + return div_s64(cc_uv * CC_READING_TICKS, SLEEP_CLK_HZ * SECONDS_PER_HOUR); } @@ -510,6 +529,7 @@ static int read_cc(struct pm8921_bms_chip *chip, int *result) int rc; uint16_t msw, lsw; + *result = 0; rc = pm_bms_read_output_data(chip, CC_LSB, &lsw); if (rc) { pr_err("fail to read CC_LSB rc = %d\n", rc); @@ -576,344 +596,6 @@ static int read_vsense_avg(struct pm8921_bms_chip *chip, int *result) return 0; } -static int linear_interpolate(int y0, int x0, int y1, int x1, int x) -{ - if (y0 == y1 || x == x0) - return y0; - if (x1 == x0 || x == x1) - return y1; - - return y0 + ((y1 - y0) * (x - x0) / (x1 - x0)); -} - -static int interpolate_single_lut(struct single_row_lut *lut, int x) -{ - int i, result; - - if (x < lut->x[0]) { - pr_debug("x %d less than known range return y = %d lut = %pS\n", - x, lut->y[0], lut); - return lut->y[0]; - } - if (x > lut->x[lut->cols - 1]) { - pr_debug("x %d more than known range return y = %d lut = %pS\n", - x, lut->y[lut->cols - 1], lut); - return lut->y[lut->cols - 1]; - } - - for (i = 0; i < lut->cols; i++) - if (x <= lut->x[i]) - break; - if (x == lut->x[i]) { - result = lut->y[i]; - } else { - result = linear_interpolate( - lut->y[i - 1], - lut->x[i - 1], - lut->y[i], - lut->x[i], - x); - } - return result; -} - -static int interpolate_fcc(struct pm8921_bms_chip *chip, int batt_temp) -{ - /* batt_temp is in tenths of degC - convert it to degC for lookups */ - batt_temp = batt_temp/10; - return interpolate_single_lut(chip->fcc_temp_lut, batt_temp); -} - -static int interpolate_fcc_adjusted(struct pm8921_bms_chip *chip, int batt_temp) -{ - /* batt_temp is in tenths of degC - convert it to degC for lookups */ - batt_temp = batt_temp/10; - return interpolate_single_lut(chip->adjusted_fcc_temp_lut, batt_temp); -} - -static int interpolate_scalingfactor_fcc(struct pm8921_bms_chip *chip, - int cycles) -{ - /* - * sf table could be null when no battery aging data is available, in - * that case return 100% - */ - if (chip->fcc_sf_lut) - return interpolate_single_lut(chip->fcc_sf_lut, cycles); - else - return 100; -} - -static int interpolate_scalingfactor(struct pm8921_bms_chip *chip, - struct sf_lut *sf_lut, - int row_entry, int pc) -{ - int i, scalefactorrow1, scalefactorrow2, scalefactor; - int rows, cols; - int row1 = 0; - int row2 = 0; - - /* - * sf table could be null when no battery aging data is available, in - * that case return 100% - */ - if (!sf_lut) - return 100; - - rows = sf_lut->rows; - cols = sf_lut->cols; - if (pc > sf_lut->percent[0]) { - pr_debug("pc %d greater than known pc ranges for sfd\n", pc); - row1 = 0; - row2 = 0; - } - if (pc < sf_lut->percent[rows - 1]) { - pr_debug("pc %d less than known pc ranges for sf", pc); - row1 = rows - 1; - row2 = rows - 1; - } - for (i = 0; i < rows; i++) { - if (pc == sf_lut->percent[i]) { - row1 = i; - row2 = i; - break; - } - if (pc > sf_lut->percent[i]) { - row1 = i - 1; - row2 = i; - break; - } - } - - if (row_entry < sf_lut->row_entries[0]) - row_entry = sf_lut->row_entries[0]; - if (row_entry > sf_lut->row_entries[cols - 1]) - row_entry = sf_lut->row_entries[cols - 1]; - - for (i = 0; i < cols; i++) - if (row_entry <= sf_lut->row_entries[i]) - break; - if (row_entry == sf_lut->row_entries[i]) { - scalefactor = linear_interpolate( - sf_lut->sf[row1][i], - sf_lut->percent[row1], - sf_lut->sf[row2][i], - sf_lut->percent[row2], - pc); - return scalefactor; - } - - scalefactorrow1 = linear_interpolate( - sf_lut->sf[row1][i - 1], - sf_lut->row_entries[i - 1], - sf_lut->sf[row1][i], - sf_lut->row_entries[i], - row_entry); - - scalefactorrow2 = linear_interpolate( - sf_lut->sf[row2][i - 1], - sf_lut->row_entries[i - 1], - sf_lut->sf[row2][i], - sf_lut->row_entries[i], - row_entry); - - scalefactor = linear_interpolate( - scalefactorrow1, - sf_lut->percent[row1], - scalefactorrow2, - sf_lut->percent[row2], - pc); - - return scalefactor; -} - -static int is_between(int left, int right, int value) -{ - if (left >= right && left >= value && value >= right) - return 1; - if (left <= right && left <= value && value <= right) - return 1; - - return 0; -} - -/* get ocv given a soc -- reverse lookup */ -static int interpolate_ocv(struct pm8921_bms_chip *chip, - int batt_temp_degc, int pc) -{ - int i, ocvrow1, ocvrow2, ocv; - int rows, cols; - int row1 = 0; - int row2 = 0; - - rows = chip->pc_temp_ocv_lut->rows; - cols = chip->pc_temp_ocv_lut->cols; - if (pc > chip->pc_temp_ocv_lut->percent[0]) { - pr_debug("pc %d greater than known pc ranges for sfd\n", pc); - row1 = 0; - row2 = 0; - } - if (pc < chip->pc_temp_ocv_lut->percent[rows - 1]) { - pr_debug("pc %d less than known pc ranges for sf\n", pc); - row1 = rows - 1; - row2 = rows - 1; - } - for (i = 0; i < rows; i++) { - if (pc == chip->pc_temp_ocv_lut->percent[i]) { - row1 = i; - row2 = i; - break; - } - if (pc > chip->pc_temp_ocv_lut->percent[i]) { - row1 = i - 1; - row2 = i; - break; - } - } - - if (batt_temp_degc < chip->pc_temp_ocv_lut->temp[0]) - batt_temp_degc = chip->pc_temp_ocv_lut->temp[0]; - if (batt_temp_degc > chip->pc_temp_ocv_lut->temp[cols - 1]) - batt_temp_degc = chip->pc_temp_ocv_lut->temp[cols - 1]; - - for (i = 0; i < cols; i++) - if (batt_temp_degc <= chip->pc_temp_ocv_lut->temp[i]) - break; - if (batt_temp_degc == chip->pc_temp_ocv_lut->temp[i]) { - ocv = linear_interpolate( - chip->pc_temp_ocv_lut->ocv[row1][i], - chip->pc_temp_ocv_lut->percent[row1], - chip->pc_temp_ocv_lut->ocv[row2][i], - chip->pc_temp_ocv_lut->percent[row2], - pc); - return ocv; - } - - ocvrow1 = linear_interpolate( - chip->pc_temp_ocv_lut->ocv[row1][i - 1], - chip->pc_temp_ocv_lut->temp[i - 1], - chip->pc_temp_ocv_lut->ocv[row1][i], - chip->pc_temp_ocv_lut->temp[i], - batt_temp_degc); - - ocvrow2 = linear_interpolate( - chip->pc_temp_ocv_lut->ocv[row2][i - 1], - chip->pc_temp_ocv_lut->temp[i - 1], - chip->pc_temp_ocv_lut->ocv[row2][i], - chip->pc_temp_ocv_lut->temp[i], - batt_temp_degc); - - ocv = linear_interpolate( - ocvrow1, - chip->pc_temp_ocv_lut->percent[row1], - ocvrow2, - chip->pc_temp_ocv_lut->percent[row2], - pc); - - return ocv; -} - -static int interpolate_pc(struct pm8921_bms_chip *chip, - int batt_temp, int ocv) -{ - int i, j, pcj, pcj_minus_one, pc; - int rows = chip->pc_temp_ocv_lut->rows; - int cols = chip->pc_temp_ocv_lut->cols; - - /* batt_temp is in tenths of degC - convert it to degC for lookups */ - batt_temp = batt_temp/10; - - if (batt_temp < chip->pc_temp_ocv_lut->temp[0]) { - pr_debug("batt_temp %d < known temp range for pc\n", batt_temp); - batt_temp = chip->pc_temp_ocv_lut->temp[0]; - } - if (batt_temp > chip->pc_temp_ocv_lut->temp[cols - 1]) { - pr_debug("batt_temp %d > known temp range for pc\n", batt_temp); - batt_temp = chip->pc_temp_ocv_lut->temp[cols - 1]; - } - - for (j = 0; j < cols; j++) - if (batt_temp <= chip->pc_temp_ocv_lut->temp[j]) - break; - if (batt_temp == chip->pc_temp_ocv_lut->temp[j]) { - /* found an exact match for temp in the table */ - if (ocv >= chip->pc_temp_ocv_lut->ocv[0][j]) - return chip->pc_temp_ocv_lut->percent[0]; - if (ocv <= chip->pc_temp_ocv_lut->ocv[rows - 1][j]) - return chip->pc_temp_ocv_lut->percent[rows - 1]; - for (i = 0; i < rows; i++) { - if (ocv >= chip->pc_temp_ocv_lut->ocv[i][j]) { - if (ocv == chip->pc_temp_ocv_lut->ocv[i][j]) - return - chip->pc_temp_ocv_lut->percent[i]; - pc = linear_interpolate( - chip->pc_temp_ocv_lut->percent[i], - chip->pc_temp_ocv_lut->ocv[i][j], - chip->pc_temp_ocv_lut->percent[i - 1], - chip->pc_temp_ocv_lut->ocv[i - 1][j], - ocv); - return pc; - } - } - } - - /* - * batt_temp is within temperature for - * column j-1 and j - */ - if (ocv >= chip->pc_temp_ocv_lut->ocv[0][j]) - return chip->pc_temp_ocv_lut->percent[0]; - if (ocv <= chip->pc_temp_ocv_lut->ocv[rows - 1][j - 1]) - return chip->pc_temp_ocv_lut->percent[rows - 1]; - - pcj_minus_one = 0; - pcj = 0; - for (i = 0; i < rows-1; i++) { - if (pcj == 0 - && is_between(chip->pc_temp_ocv_lut->ocv[i][j], - chip->pc_temp_ocv_lut->ocv[i+1][j], ocv)) { - pcj = linear_interpolate( - chip->pc_temp_ocv_lut->percent[i], - chip->pc_temp_ocv_lut->ocv[i][j], - chip->pc_temp_ocv_lut->percent[i + 1], - chip->pc_temp_ocv_lut->ocv[i+1][j], - ocv); - } - - if (pcj_minus_one == 0 - && is_between(chip->pc_temp_ocv_lut->ocv[i][j-1], - chip->pc_temp_ocv_lut->ocv[i+1][j-1], ocv)) { - - pcj_minus_one = linear_interpolate( - chip->pc_temp_ocv_lut->percent[i], - chip->pc_temp_ocv_lut->ocv[i][j-1], - chip->pc_temp_ocv_lut->percent[i + 1], - chip->pc_temp_ocv_lut->ocv[i+1][j-1], - ocv); - } - - if (pcj && pcj_minus_one) { - pc = linear_interpolate( - pcj_minus_one, - chip->pc_temp_ocv_lut->temp[j-1], - pcj, - chip->pc_temp_ocv_lut->temp[j], - batt_temp); - return pc; - } - } - - if (pcj) - return pcj; - - if (pcj_minus_one) - return pcj_minus_one; - - pr_debug("%d ocv wasn't found for temp %d in the LUT returning 100%%", - ocv, batt_temp); - return 100; -} - #define BMS_MODE_BIT BIT(6) #define EN_VBAT_BIT BIT(5) #define OVERRIDE_MODE_DELAY_MS 20 @@ -944,11 +626,11 @@ int override_mode_simultaneous_battery_voltage_and_current(int *ibat_ua, mutex_unlock(&the_chip->bms_output_lock); - usb_chg = usb_chg_plugged_in(); + usb_chg = usb_chg_plugged_in(the_chip); convert_vbatt_raw_to_uv(the_chip, usb_chg, vbat_raw, vbat_uv); convert_vsense_to_uv(the_chip, vsense_raw, &vsense_uv); - *ibat_ua = vsense_uv * 1000 / (int)the_chip->r_sense; + *ibat_ua = div_s64((s64)vsense_uv * 1000000LL, the_chip->r_sense_uohm); pr_debug("vsense_raw = 0x%x vbat_raw = 0x%x" " ibat_ua = %d vbat_uv = %d\n", @@ -957,23 +639,106 @@ int override_mode_simultaneous_battery_voltage_and_current(int *ibat_ua, return 0; } -#define MBG_TRANSIENT_ERROR_RAW 51 -static void adjust_pon_ocv_raw(struct pm8921_bms_chip *chip, - struct pm8921_soc_params *raw) +#define MBG_TRANSIENT_ERROR_UV 15000 +static void adjust_pon_ocv(struct pm8921_bms_chip *chip, int *uv) { - /* in 8921 parts the PON ocv is taken when the MBG is not settled. + /* + * In 8921 parts the PON ocv is taken when the MBG is not settled. * decrease the pon ocv by 15mV raw value to account for it * Since a 1/3rd of vbatt is supplied to the adc the raw value * needs to be adjusted by 5mV worth bits */ - if (raw->last_good_ocv_raw >= MBG_TRANSIENT_ERROR_RAW) - raw->last_good_ocv_raw -= MBG_TRANSIENT_ERROR_RAW; + if (*uv >= MBG_TRANSIENT_ERROR_UV) + *uv -= MBG_TRANSIENT_ERROR_UV; } +#define SEL_ALT_OREG_BIT BIT(2) +static int ocv_ir_compensation(struct pm8921_bms_chip *chip, int ocv) +{ + int compensated_ocv; + int ibatt_ua; + int rbatt_mohm = chip->default_rbatt_mohm + chip->rconn_mohm; + + pm_bms_masked_write(chip, BMS_TEST1, + SEL_ALT_OREG_BIT, SEL_ALT_OREG_BIT); + + /* since the SEL_ALT_OREG_BIT is set this will give us VSENSE_OCV */ + pm8921_bms_get_battery_current(&ibatt_ua); + compensated_ocv = ocv + div_s64((s64)ibatt_ua * rbatt_mohm, 1000); + pr_debug("comp ocv = %d, ocv = %d, ibatt_ua = %d, rbatt_mohm = %d\n", + compensated_ocv, ocv, ibatt_ua, rbatt_mohm); + + pm_bms_masked_write(chip, BMS_TEST1, SEL_ALT_OREG_BIT, 0); + return compensated_ocv; +} + +#define RESET_CC_BIT BIT(3) +static int reset_cc(struct pm8921_bms_chip *chip) +{ + int rc; + + rc = pm_bms_masked_write(chip, BMS_TEST1, RESET_CC_BIT, RESET_CC_BIT); + if (rc < 0) { + pr_err("err setting cc reset rc = %d\n", rc); + return rc; + } + + /* sleep 100uS for the coulomb counter to reset */ + udelay(100); + + rc = pm_bms_masked_write(chip, BMS_TEST1, RESET_CC_BIT, 0); + if (rc < 0) + pr_err("err clearing cc reset rc = %d\n", rc); + return rc; +} + +static int estimate_ocv(struct pm8921_bms_chip *chip) +{ + int ibat_ua, vbat_uv, ocv_est_uv; + int rc; + + int rbatt_mohm = chip->default_rbatt_mohm + chip->rconn_mohm; + + rc = pm8921_bms_get_simultaneous_battery_voltage_and_current( + &ibat_ua, + &vbat_uv); + if (rc) { + pr_err("simultaneous failed rc = %d\n", rc); + return rc; + } + + ocv_est_uv = vbat_uv + (ibat_ua * rbatt_mohm) / 1000; + pr_debug("estimated pon ocv = %d\n", ocv_est_uv); + return ocv_est_uv; +} + +static bool is_warm_restart(struct pm8921_bms_chip *chip) +{ + u8 reg; + int rc; + + rc = pm8xxx_readb(chip->dev->parent, PON_CNTRL_6, ®); + if (rc) { + pr_err("err reading pon 6 rc = %d\n", rc); + return false; + } + return reg & WD_BIT; +} +/* + * This reflects what should the CC readings should be for + * a 5mAh discharge. This value is dependent on + * CC_RESOLUTION_N, CC_RESOLUTION_D, CC_READING_TICKS + * and rsense + */ +#define CC_RAW_5MAH 0x00110000 +#define MIN_OCV_UV 2000000 +#define OCV_RAW_UNINITIALIZED 0xFFFF static int read_soc_params_raw(struct pm8921_bms_chip *chip, - struct pm8921_soc_params *raw) + struct pm8921_soc_params *raw, + int batt_temp_decidegc) { int usb_chg; + int est_ocv_uv; mutex_lock(&chip->bms_output_lock); pm_bms_lock_output_data(chip); @@ -985,20 +750,52 @@ static int read_soc_params_raw(struct pm8921_bms_chip *chip, pm_bms_unlock_output_data(chip); mutex_unlock(&chip->bms_output_lock); - usb_chg = usb_chg_plugged_in(); + usb_chg = usb_chg_plugged_in(chip); - if (chip->prev_last_good_ocv_raw == 0) { + if (chip->prev_last_good_ocv_raw == OCV_RAW_UNINITIALIZED) { chip->prev_last_good_ocv_raw = raw->last_good_ocv_raw; - adjust_pon_ocv_raw(chip, raw); + convert_vbatt_raw_to_uv(chip, usb_chg, raw->last_good_ocv_raw, &raw->last_good_ocv_uv); + adjust_pon_ocv(chip, &raw->last_good_ocv_uv); + raw->last_good_ocv_uv = ocv_ir_compensation(chip, + raw->last_good_ocv_uv); chip->last_ocv_uv = raw->last_good_ocv_uv; + + if (is_warm_restart(chip) + || raw->cc > CC_RAW_5MAH + || (raw->last_good_ocv_uv < MIN_OCV_UV + && raw->cc > 0)) { + /* + * The CC value is higher than 5mAh. + * The phone started without going through a pon + * sequence + * OR + * The ocv was very small and there was no + * charging in the bootloader + * - reset the CC and take an ocv again + */ + pr_debug("cc_raw = 0x%x may be > 5mAh(0x%x)\n", + raw->cc, CC_RAW_5MAH); + pr_debug("ocv_uv = %d ocv_raw = 0x%x may be < 2V\n", + chip->last_ocv_uv, + raw->last_good_ocv_raw); + est_ocv_uv = estimate_ocv(chip); + if (est_ocv_uv > 0) { + raw->last_good_ocv_uv = est_ocv_uv; + chip->last_ocv_uv = est_ocv_uv; + reset_cc(chip); + raw->cc = 0; + } + } + chip->last_ocv_temp_decidegc = batt_temp_decidegc; pr_debug("PON_OCV_UV = %d\n", chip->last_ocv_uv); } else if (chip->prev_last_good_ocv_raw != raw->last_good_ocv_raw) { chip->prev_last_good_ocv_raw = raw->last_good_ocv_raw; convert_vbatt_raw_to_uv(chip, usb_chg, raw->last_good_ocv_raw, &raw->last_good_ocv_uv); chip->last_ocv_uv = raw->last_good_ocv_uv; + chip->last_ocv_temp_decidegc = batt_temp_decidegc; /* forget the old cc value upon ocv */ chip->last_cc_uah = 0; } else { @@ -1007,7 +804,7 @@ static int read_soc_params_raw(struct pm8921_bms_chip *chip, /* fake a high OCV if we are just done charging */ if (chip->ocv_reading_at_100 != raw->last_good_ocv_raw) { - chip->ocv_reading_at_100 = 0; + chip->ocv_reading_at_100 = OCV_RAW_UNINITIALIZED; chip->cc_reading_at_100 = 0; } else { /* @@ -1016,6 +813,7 @@ static int read_soc_params_raw(struct pm8921_bms_chip *chip, */ raw->last_good_ocv_uv = chip->max_voltage_uv; chip->last_ocv_uv = chip->max_voltage_uv; + chip->last_ocv_temp_decidegc = batt_temp_decidegc; } pr_debug("0p625 = %duV\n", chip->xoadc_v0625); pr_debug("1p25 = %duV\n", chip->xoadc_v125); @@ -1037,7 +835,7 @@ static int get_rbatt(struct pm8921_bms_chip *chip, int soc_rbatt, int batt_temp) } /* Convert the batt_temp to DegC from deciDegC */ batt_temp = batt_temp / 10; - scalefactor = interpolate_scalingfactor(chip, chip->rbatt_sf_lut, + scalefactor = interpolate_scalingfactor(chip->rbatt_sf_lut, batt_temp, soc_rbatt); pr_debug("rbatt sf = %d for batt_temp = %d, soc_rbatt = %d\n", scalefactor, batt_temp, soc_rbatt); @@ -1064,16 +862,18 @@ static int calculate_fcc_uah(struct pm8921_bms_chip *chip, int batt_temp, int initfcc, result, scalefactor = 0; if (chip->adjusted_fcc_temp_lut == NULL) { - initfcc = interpolate_fcc(chip, batt_temp); + initfcc = interpolate_fcc(chip->fcc_temp_lut, batt_temp); - scalefactor = interpolate_scalingfactor_fcc(chip, chargecycles); + scalefactor = interpolate_scalingfactor_fcc(chip->fcc_sf_lut, + chargecycles); /* Multiply the initial FCC value by the scale factor. */ result = (initfcc * scalefactor * 1000) / 100; pr_debug("fcc = %d uAh\n", result); return result; } else { - return 1000 * interpolate_fcc_adjusted(chip, batt_temp); + return 1000 * interpolate_fcc(chip->adjusted_fcc_temp_lut, + batt_temp); } } @@ -1115,18 +915,20 @@ static int adc_based_ocv(struct pm8921_bms_chip *chip, int *ocv) return 0; } -static int calculate_pc(struct pm8921_bms_chip *chip, int ocv_uv, int batt_temp, - int chargecycles) +static int calculate_pc(struct pm8921_bms_chip *chip, int ocv_uv, + int batt_temp_decidegc, int chargecycles) { int pc, scalefactor; - pc = interpolate_pc(chip, batt_temp, ocv_uv / 1000); + pc = interpolate_pc(chip->pc_temp_ocv_lut, + batt_temp_decidegc / 10, ocv_uv / 1000); pr_debug("pc = %u for ocv = %dmicroVolts batt_temp = %d\n", - pc, ocv_uv, batt_temp); + pc, ocv_uv, batt_temp_decidegc); - scalefactor = interpolate_scalingfactor(chip, - chip->pc_sf_lut, chargecycles, pc); - pr_debug("scalefactor = %u batt_temp = %d\n", scalefactor, batt_temp); + scalefactor = interpolate_scalingfactor(chip->pc_sf_lut, + chargecycles, pc); + pr_debug("scalefactor = %u batt_temp = %d\n", + scalefactor, batt_temp_decidegc); /* Multiply the initial FCC value by the scale factor. */ pc = (pc * scalefactor) / 100; @@ -1145,7 +947,7 @@ static int calculate_pc(struct pm8921_bms_chip *chip, int ocv_uv, int batt_temp, */ static void calculate_cc_uah(struct pm8921_bms_chip *chip, int cc, int *val) { - int64_t cc_voltage_uv, cc_nvh, cc_uah; + int64_t cc_voltage_uv, cc_uvh, cc_uah; cc_voltage_uv = cc; cc_voltage_uv -= chip->cc_reading_at_100; @@ -1155,12 +957,28 @@ static void calculate_cc_uah(struct pm8921_bms_chip *chip, int cc, int *val) cc_voltage_uv = cc_to_microvolt(chip, cc_voltage_uv); cc_voltage_uv = pm8xxx_cc_adjust_for_gain(cc_voltage_uv); pr_debug("cc_voltage_uv = %lld microvolts\n", cc_voltage_uv); - cc_nvh = ccmicrovolt_to_nvh(cc_voltage_uv); - pr_debug("cc_nvh = %lld nano_volt_hour\n", cc_nvh); - cc_uah = div_s64(cc_nvh, chip->r_sense); + cc_uvh = ccmicrovolt_to_uvh(cc_voltage_uv); + pr_debug("cc_uvh = %lld micro_volt_hour\n", cc_uvh); + cc_uah = div_s64(cc_uvh * 1000000LL, chip->r_sense_uohm); *val = cc_uah; } +int pm8921_bms_cc_uah(int *cc_uah) +{ + int cc; + + *cc_uah = 0; + + if (!the_chip) + return -EINVAL; + + read_cc(the_chip, &cc); + calculate_cc_uah(the_chip, cc, cc_uah); + + return 0; +} +EXPORT_SYMBOL(pm8921_bms_cc_uah); + static int calculate_termination_uuc(struct pm8921_bms_chip *chip, int batt_temp, int chargecycles, int fcc_uah, int i_ma, @@ -1178,7 +996,8 @@ static int calculate_termination_uuc(struct pm8921_bms_chip *chip, int uuc_rbatt_uv; for (i = 0; i <= 100; i++) { - ocv_mv = interpolate_ocv(chip, batt_temp_degc, i); + ocv_mv = interpolate_ocv(chip->pc_temp_ocv_lut, + batt_temp_degc, i); rbatt_mohm = get_rbatt(chip, i, batt_temp); unusable_uv = (rbatt_mohm * i_ma) + (chip->v_cutoff * 1000); delta_uv = ocv_mv * 1000 - unusable_uv; @@ -1209,33 +1028,39 @@ static int calculate_termination_uuc(struct pm8921_bms_chip *chip, return uuc; } +#define TIME_PER_PERCENT_UUC 60 static int adjust_uuc(struct pm8921_bms_chip *chip, int fcc_uah, int new_pc_unusable, int new_uuc, int batt_temp, int rbatt, - int *iavg_ma) + int *iavg_ma, + int delta_time_s) { int new_unusable_mv; int batt_temp_degc = batt_temp / 10; + int max_percent_change; + + max_percent_change = max(delta_time_s / TIME_PER_PERCENT_UUC, 1); if (chip->prev_pc_unusable == -EINVAL - || abs(chip->prev_pc_unusable - new_pc_unusable) <= 1) { + || abs(chip->prev_pc_unusable - new_pc_unusable) + <= max_percent_change) { chip->prev_pc_unusable = new_pc_unusable; return new_uuc; } /* the uuc is trying to change more than 1% restrict it */ if (new_pc_unusable > chip->prev_pc_unusable) - chip->prev_pc_unusable++; + chip->prev_pc_unusable += max_percent_change; else - chip->prev_pc_unusable--; - + chip->prev_pc_unusable -= max_percent_change; + chip->prev_pc_unusable = clamp(chip->prev_pc_unusable, 0, 100); new_uuc = (fcc_uah * chip->prev_pc_unusable) / 100; /* also find update the iavg_ma accordingly */ - new_unusable_mv = interpolate_ocv(chip, batt_temp_degc, - chip->prev_pc_unusable); + new_unusable_mv = interpolate_ocv(chip->pc_temp_ocv_lut, + batt_temp_degc, chip->prev_pc_unusable); if (new_unusable_mv < chip->v_cutoff) new_unusable_mv = chip->v_cutoff; @@ -1249,75 +1074,89 @@ static int adjust_uuc(struct pm8921_bms_chip *chip, int fcc_uah, return new_uuc; } -static void calculate_iavg_ua(struct pm8921_bms_chip *chip, int cc_uah, - int *iavg_ua, int *delta_time_s) +static int get_current_time(unsigned long *now_tm_sec) { - int delta_cc_uah; struct rtc_time tm; struct rtc_device *rtc; - unsigned long now_tm_sec = 0; - int rc = 0; - - /* if anything fails report the previous iavg_ua */ - *iavg_ua = chip->prev_iavg_ua; + int rc; rtc = rtc_class_open(CONFIG_RTC_HCTOSYS_DEVICE); if (rtc == NULL) { pr_err("%s: unable to open rtc device (%s)\n", __FILE__, CONFIG_RTC_HCTOSYS_DEVICE); - goto out; + return -EINVAL; } rc = rtc_read_time(rtc, &tm); if (rc) { pr_err("Error reading rtc device (%s) : %d\n", CONFIG_RTC_HCTOSYS_DEVICE, rc); - goto out; + return rc; } rc = rtc_valid_tm(&tm); if (rc) { pr_err("Invalid RTC time (%s): %d\n", CONFIG_RTC_HCTOSYS_DEVICE, rc); - goto out; + return rc; } - rtc_tm_to_time(&tm, &now_tm_sec); + rtc_tm_to_time(&tm, now_tm_sec); - if (chip->tm_sec == 0) { - *delta_time_s = 0; + return 0; +} + +static int calculate_delta_time(struct pm8921_bms_chip *chip, int *delta_time_s) +{ + unsigned long now_tm_sec = 0; + + /* default to delta time = 0 if anything fails */ + *delta_time_s = 0; + + get_current_time(&now_tm_sec); + + *delta_time_s = (now_tm_sec - chip->tm_sec); + pr_debug("tm_sec = %ld, now_tm_sec = %ld delta_s = %d\n", + chip->tm_sec, now_tm_sec, *delta_time_s); + + /* remember this time */ + chip->tm_sec = now_tm_sec; + return 0; +} + +static void calculate_iavg_ua(struct pm8921_bms_chip *chip, int cc_uah, + int *iavg_ua, int delta_time_s) +{ + int delta_cc_uah = 0; + + /* if anything fails report the previous iavg_ua */ + *iavg_ua = chip->prev_iavg_ua; + + if (chip->last_cc_uah == INT_MIN) { pm8921_bms_get_battery_current(iavg_ua); goto out; } - *delta_time_s = (now_tm_sec - chip->tm_sec); - /* use the previous iavg if called within 15 seconds */ - if (*delta_time_s < 15) { + if (delta_time_s < 15) { *iavg_ua = chip->prev_iavg_ua; goto out; } delta_cc_uah = cc_uah - chip->last_cc_uah; - *iavg_ua = div_s64((s64)delta_cc_uah * 3600, *delta_time_s); - - pr_debug("tm_sec = %ld, now_tm_sec = %ld delta_s = %d delta_cc = %d iavg_ua = %d\n", - chip->tm_sec, now_tm_sec, - *delta_time_s, delta_cc_uah, (int)*iavg_ua); + *iavg_ua = div_s64((s64)delta_cc_uah * 3600, delta_time_s); out: + pr_debug("delta_cc = %d iavg_ua = %d\n", delta_cc_uah, (int)*iavg_ua); /* remember the iavg */ chip->prev_iavg_ua = *iavg_ua; /* remember cc_uah */ chip->last_cc_uah = cc_uah; - - /* remember this time */ - chip->tm_sec = now_tm_sec; } #define IAVG_SAMPLES 16 -#define CHARGING_IAVG_MA 250 +#define MIN_IAVG_MA 250 #define MIN_SECONDS_FOR_VALID_SAMPLE 20 static int calculate_unusable_charge_uah(struct pm8921_bms_chip *chip, int rbatt, int fcc_uah, int cc_uah, @@ -1338,7 +1177,7 @@ static int calculate_unusable_charge_uah(struct pm8921_bms_chip *chip, * samples with the the shutdown_iavg_ua */ if (firsttime && chip->shutdown_iavg_ua != 0) { - pr_emerg("Using shutdown_iavg_ua = %d in all samples\n", + pr_debug("Using shutdown_iavg_ua = %d in all samples\n", chip->shutdown_iavg_ua); for (i = 0; i < IAVG_SAMPLES; i++) iavg_samples[i] = chip->shutdown_iavg_ua; @@ -1351,8 +1190,8 @@ static int calculate_unusable_charge_uah(struct pm8921_bms_chip *chip, * if we are charging use a nominal avg current so that we keep * a reasonable UUC while charging */ - if (iavg_ma < 0) - iavg_ma = CHARGING_IAVG_MA; + if (iavg_ma < MIN_IAVG_MA) + iavg_ma = MIN_IAVG_MA; iavg_samples[iavg_index] = iavg_ma; iavg_index = (iavg_index + 1) % IAVG_SAMPLES; iavg_num_samples++; @@ -1376,9 +1215,9 @@ static int calculate_unusable_charge_uah(struct pm8921_bms_chip *chip, &pc_unusable); pr_debug("iavg = %d uuc_iavg = %d\n", iavg_ma, uuc_uah_iavg); - /* restrict the uuc such that it can increase only by one percent */ + /* restrict the uuc change to one percent per 60 seconds */ uuc_uah_iavg = adjust_uuc(chip, fcc_uah, pc_unusable, uuc_uah_iavg, - batt_temp, rbatt, &iavg_ma); + batt_temp, rbatt, &iavg_ma, delta_time_s); /* find out what the avg current should be for this uuc */ chip->prev_uuc_iavg_ma = iavg_ma; @@ -1393,10 +1232,11 @@ static int calculate_remaining_charge_uah(struct pm8921_bms_chip *chip, int fcc_uah, int batt_temp, int chargecycles) { - int ocv, pc; + int ocv, pc, batt_temp_decidegc; ocv = raw->last_good_ocv_uv; - pc = calculate_pc(chip, ocv, batt_temp, chargecycles); + batt_temp_decidegc = chip->last_ocv_temp_decidegc; + pc = calculate_pc(chip, ocv, batt_temp_decidegc, chargecycles); pr_debug("ocv = %d pc = %d\n", ocv, pc); return (fcc_uah * pc) / 100; } @@ -1409,11 +1249,17 @@ static void calculate_soc_params(struct pm8921_bms_chip *chip, int *remaining_charge_uah, int *cc_uah, int *rbatt, - int *iavg_ua, - int *delta_time_s) + int *iavg_ua) { int soc_rbatt; + int delta_time_s; + int rc; + rc = calculate_delta_time(chip, &delta_time_s); + if (rc) { + pr_err("Failed to get delta time from RTC: %d\n", rc); + delta_time_s = 0; + } *fcc_uah = calculate_fcc_uah(chip, batt_temp, chargecycles); pr_debug("FCC = %uuAh batt_temp = %d, cycles = %d\n", *fcc_uah, batt_temp, chargecycles); @@ -1441,7 +1287,7 @@ static void calculate_soc_params(struct pm8921_bms_chip *chip, *unusable_charge_uah = calculate_unusable_charge_uah(chip, *rbatt, *fcc_uah, *cc_uah, soc_rbatt, batt_temp, chargecycles, *iavg_ua, - *delta_time_s); + delta_time_s); pr_debug("UUC = %uuAh\n", *unusable_charge_uah); } @@ -1456,7 +1302,6 @@ static int calculate_real_fcc_uah(struct pm8921_bms_chip *chip, int real_fcc_uah; int rbatt; int iavg_ua; - int delta_time_s; calculate_soc_params(chip, raw, batt_temp, chargecycles, &fcc_uah, @@ -1464,8 +1309,7 @@ static int calculate_real_fcc_uah(struct pm8921_bms_chip *chip, &remaining_charge_uah, &cc_uah, &rbatt, - &iavg_ua, - &delta_time_s); + &iavg_ua); real_fcc_uah = remaining_charge_uah - cc_uah; *ret_fcc_uah = fcc_uah; @@ -1526,11 +1370,11 @@ static void find_ocv_for_soc(struct pm8921_bms_chip *chip, pc = DIV_ROUND_CLOSEST((int)rc * 100, fcc_uah); pc = clamp(pc, 0, 100); - ocv = interpolate_ocv(chip, batt_temp_degc, pc); + ocv = interpolate_ocv(chip->pc_temp_ocv_lut, batt_temp_degc, pc); pr_debug("s_soc = %d, fcc = %d uuc = %d rc = %d, pc = %d, ocv mv = %d\n", shutdown_soc, fcc_uah, uuc_uah, (int)rc, pc, ocv); - new_pc = interpolate_pc(chip, batt_temp_degc, ocv); + new_pc = interpolate_pc(chip->pc_temp_ocv_lut, batt_temp_degc, ocv); pr_debug("test revlookup pc = %d for ocv = %d\n", new_pc, ocv); while (abs(new_pc - pc) > 1) { @@ -1540,7 +1384,8 @@ static void find_ocv_for_soc(struct pm8921_bms_chip *chip, delta_mv = -1 * delta_mv; ocv = ocv + delta_mv; - new_pc = interpolate_pc(chip, batt_temp_degc, ocv); + new_pc = interpolate_pc(chip->pc_temp_ocv_lut, + batt_temp_degc, ocv); pr_debug("test revlookup pc = %d for ocv = %d\n", new_pc, ocv); } @@ -1575,6 +1420,13 @@ static void adjust_rc_and_uuc_for_specific_soc( *ret_rc = rc_uah; *ret_uuc = uuc_uah; } + +static void calc_current_max(struct pm8921_bms_chip *chip, int ocv_uv, + int rbatt_mohm) +{ + chip->imax_ua = 1000 * (ocv_uv - chip->v_cutoff * 1000) / rbatt_mohm; +} + static int bound_soc(int soc) { soc = max(0, soc); @@ -1588,14 +1440,16 @@ static int charging_adjustments(struct pm8921_bms_chip *chip, int fcc_uah, int cc_uah, int uuc_uah) { int chg_soc; + int vbat_batt_terminal_uv = vbat_uv + + (ibat_ua * chip->rconn_mohm) / 1000; if (chip->soc_at_cv == -EINVAL) { /* In constant current charging return the calc soc */ - if (vbat_uv <= chip->max_voltage_uv) + if (vbat_batt_terminal_uv <= chip->max_voltage_uv) pr_debug("CC CHG SOC %d\n", soc); /* Note the CC to CV point */ - if (vbat_uv >= chip->max_voltage_uv) { + if (vbat_batt_terminal_uv >= chip->max_voltage_uv) { chip->soc_at_cv = soc; chip->prev_chg_soc = soc; chip->ibat_at_cv_ua = ibat_ua; @@ -1611,18 +1465,20 @@ static int charging_adjustments(struct pm8921_bms_chip *chip, */ /* - * if voltage lessened (possibly because of a system load) - * keep reporting the prev chg soc + * if voltage lessened by more than 10mV (possibly because of + * a sudden increase in system load) keep reporting the prev chg soc */ - if (vbat_uv <= chip->max_voltage_uv) { - pr_debug("vbat %d < max = %d CC CHG SOC %d\n", - vbat_uv, chip->max_voltage_uv, chip->prev_chg_soc); + if (vbat_batt_terminal_uv <= chip->max_voltage_uv - 10000) { + pr_debug("vbat_terminals %d < max = %d CC CHG SOC %d\n", + vbat_batt_terminal_uv, + chip->max_voltage_uv, chip->prev_chg_soc); return chip->prev_chg_soc; } chg_soc = linear_interpolate(chip->soc_at_cv, chip->ibat_at_cv_ua, - 100, -100000, + 100, -1 * chip->chg_term_ua, ibat_ua); + chg_soc = bound_soc(chg_soc); /* always report a higher soc */ if (chg_soc > chip->prev_chg_soc) { @@ -1645,6 +1501,30 @@ static int charging_adjustments(struct pm8921_bms_chip *chip, return chip->prev_chg_soc; } +static void very_low_voltage_check(struct pm8921_bms_chip *chip, + int ibat_ua, int vbat_uv) +{ + /* + * if battery is very low (v_cutoff voltage + 20mv) hold + * a wakelock untill soc = 0% + */ + if (vbat_uv <= (chip->v_cutoff + 20) * 1000 + && !chip->low_voltage_wake_lock_held) { + pr_debug("voltage = %d low holding wakelock\n", vbat_uv); + wake_lock(&chip->low_voltage_wake_lock); + chip->low_voltage_wake_lock_held = 1; + chip->soc_calc_period = chip->low_voltage_calc_ms; + } + + if (vbat_uv > (chip->v_cutoff + 20) * 1000 + && chip->low_voltage_wake_lock_held) { + pr_debug("voltage = %d releasing wakelock\n", vbat_uv); + chip->low_voltage_wake_lock_held = 0; + wake_unlock(&chip->low_voltage_wake_lock); + chip->soc_calc_period = chip->normal_voltage_calc_ms; + } +} + static int last_soc_est = -EINVAL; static int adjust_soc(struct pm8921_bms_chip *chip, int soc, int batt_temp, int chargecycles, @@ -1669,16 +1549,18 @@ static int adjust_soc(struct pm8921_bms_chip *chip, int soc, goto out; } + very_low_voltage_check(chip, ibat_ua, vbat_uv); delta_ocv_uv_limit = DIV_ROUND_CLOSEST(ibat_ua, 1000); ocv_est_uv = vbat_uv + (ibat_ua * rbatt)/1000; + calc_current_max(chip, ocv_est_uv, rbatt); pc_est = calculate_pc(chip, ocv_est_uv, batt_temp, last_chargecycles); soc_est = div_s64((s64)fcc_uah * pc_est - uuc_uah*100, (s64)fcc_uah - uuc_uah); soc_est = bound_soc(soc_est); - if (ibat_ua < 0) { + if (ibat_ua < 0 && pm8921_is_batfet_closed()) { soc = charging_adjustments(chip, soc, vbat_uv, ibat_ua, batt_temp, chargecycles, fcc_uah, cc_uah, uuc_uah); @@ -1709,16 +1591,18 @@ static int adjust_soc(struct pm8921_bms_chip *chip, int soc, last_soc_est = soc_est; pc = calculate_pc(chip, chip->last_ocv_uv, - batt_temp, last_chargecycles); + chip->last_ocv_temp_decidegc, last_chargecycles); if (pc > 0) { pc_new = calculate_pc(chip, chip->last_ocv_uv - (++m * 1000), - batt_temp, last_chargecycles); + chip->last_ocv_temp_decidegc, + last_chargecycles); while (pc_new == pc) { /* start taking 10mV steps */ m = m + 10; pc_new = calculate_pc(chip, chip->last_ocv_uv - (m * 1000), - batt_temp, last_chargecycles); + chip->last_ocv_temp_decidegc, + last_chargecycles); } } else { /* @@ -1751,7 +1635,7 @@ static int adjust_soc(struct pm8921_bms_chip *chip, int soc, /* calculate the soc based on this new ocv */ pc_new = calculate_pc(chip, chip->last_ocv_uv, - batt_temp, last_chargecycles); + chip->last_ocv_temp_decidegc, last_chargecycles); rc_new_uah = (fcc_uah * pc_new) / 100; soc_new = (rc_new_uah - cc_uah - uuc_uah)*100 / (fcc_uah - uuc_uah); soc_new = bound_soc(soc_new); @@ -1876,12 +1760,10 @@ static int scale_soc_while_chg(struct pm8921_bms_chip *chip, if (the_chip->start_percent == -EINVAL) return prev_soc; - /* if soc is called in quick succession return the last soc */ - if (delta_time_us < USEC_PER_SEC) - return prev_soc; - chg_time_sec = DIV_ROUND_UP(the_chip->charge_time_us, USEC_PER_SEC); catch_up_sec = DIV_ROUND_UP(the_chip->catch_up_time_us, USEC_PER_SEC); + if (catch_up_sec == 0) + return new_soc; pr_debug("cts= %d catch_up_sec = %d\n", chg_time_sec, catch_up_sec); /* @@ -1918,6 +1800,116 @@ static bool is_shutdown_soc_within_limits(struct pm8921_bms_chip *chip, int soc) return 1; } + +static void update_power_supply(struct pm8921_bms_chip *chip) +{ + if (chip->batt_psy == NULL || chip->batt_psy < 0) + chip->batt_psy = power_supply_get_by_name("battery"); + + if (chip->batt_psy > 0) + power_supply_changed(chip->batt_psy); +} + +static int get_batt_temp(struct pm8921_bms_chip *chip, int *batt_temp) +{ + int rc; + struct pm8xxx_adc_chan_result result; + + rc = pm8xxx_adc_read(chip->batt_temp_channel, &result); + if (rc) { + pr_err("error reading batt_temp_channel = %d, rc = %d\n", + chip->batt_temp_channel, rc); + return rc; + } + *batt_temp = result.physical; + pr_debug("batt_temp phy = %lld meas = 0x%llx\n", result.physical, + result.measurement); + return 0; +} + +#define MIN_DELTA_625_UV 1000 +static void calib_hkadc(struct pm8921_bms_chip *chip) +{ + int voltage, rc; + struct pm8xxx_adc_chan_result result; + int usb_chg; + int this_delta; + + mutex_lock(&chip->calib_mutex); + rc = pm8xxx_adc_read(the_chip->ref1p25v_channel, &result); + if (rc) { + pr_err("ADC failed for 1.25volts rc = %d\n", rc); + goto out; + } + voltage = xoadc_reading_to_microvolt(result.adc_code); + + pr_debug("result 1.25v = 0x%x, voltage = %duV adc_meas = %lld\n", + result.adc_code, voltage, result.measurement); + + chip->xoadc_v125 = voltage; + + rc = pm8xxx_adc_read(the_chip->ref625mv_channel, &result); + if (rc) { + pr_err("ADC failed for 1.25volts rc = %d\n", rc); + goto out; + } + voltage = xoadc_reading_to_microvolt(result.adc_code); + + usb_chg = usb_chg_plugged_in(chip); + pr_debug("result 0.625V = 0x%x, voltage = %duV adc_meas = %lld usb_chg = %d\n", + result.adc_code, voltage, result.measurement, + usb_chg); + + if (usb_chg) + chip->xoadc_v0625_usb_present = voltage; + else + chip->xoadc_v0625_usb_absent = voltage; + + chip->xoadc_v0625 = voltage; + if (chip->xoadc_v0625_usb_present && chip->xoadc_v0625_usb_absent) { + this_delta = chip->xoadc_v0625_usb_present + - chip->xoadc_v0625_usb_absent; + pr_debug("this_delta= %duV\n", this_delta); + if (this_delta > MIN_DELTA_625_UV) + last_usb_cal_delta_uv = this_delta; + pr_debug("625V_present= %d, 625V_absent= %d, delta = %duV\n", + chip->xoadc_v0625_usb_present, + chip->xoadc_v0625_usb_absent, + last_usb_cal_delta_uv); + } + pr_debug("calibration batt_temp = %d\n", chip->last_calib_temp); +out: + mutex_unlock(&chip->calib_mutex); +} + +#define HKADC_CALIB_DELAY_S 600 +#define HKADC_CALIB_DELTA_TEMP 20 +static void calib_hkadc_check(struct pm8921_bms_chip *chip, int batt_temp) +{ + unsigned long time_since_last_calib; + unsigned long tm_now_sec; + int delta_temp; + int rc; + + rc = get_current_time(&tm_now_sec); + if (rc) { + pr_err("Could not read current time: %d\n", rc); + return; + } + if (tm_now_sec > chip->last_calib_time) { + time_since_last_calib = tm_now_sec - chip->last_calib_time; + delta_temp = abs(chip->last_calib_temp - batt_temp); + pr_debug("time since last calib: %lu, temp diff = %d\n", + time_since_last_calib, delta_temp); + if (time_since_last_calib >= HKADC_CALIB_DELAY_S + || delta_temp > HKADC_CALIB_DELTA_TEMP) { + chip->last_calib_temp = batt_temp; + chip->last_calib_time = tm_now_sec; + calib_hkadc(chip); + } + } +} + /* * Remaining Usable Charge = remaining_charge (charge at ocv instance) * - coloumb counter charge @@ -1933,22 +1925,22 @@ static int calculate_state_of_charge(struct pm8921_bms_chip *chip, int cc_uah; int rbatt; int iavg_ua; - int delta_time_s; int new_ocv; int new_rc_uah; int new_ucc_uah; int new_rbatt; int shutdown_soc; + int new_calculated_soc; static int firsttime = 1; + calib_hkadc_check(chip, batt_temp); calculate_soc_params(chip, raw, batt_temp, chargecycles, &fcc_uah, &unusable_charge_uah, &remaining_charge_uah, &cc_uah, &rbatt, - &iavg_ua, - &delta_time_s); + &iavg_ua); /* calculate remaining usable charge */ remaining_usable_charge_uah = remaining_charge_uah @@ -1957,7 +1949,7 @@ static int calculate_state_of_charge(struct pm8921_bms_chip *chip, pr_debug("RUC = %duAh\n", remaining_usable_charge_uah); if (fcc_uah - unusable_charge_uah <= 0) { - pr_warn("FCC = %duAh, UUC = %duAh forcing soc = 0\n", + pr_debug("FCC = %duAh, UUC = %duAh forcing soc = 0\n", fcc_uah, unusable_charge_uah); soc = 0; } else { @@ -2000,13 +1992,13 @@ static int calculate_state_of_charge(struct pm8921_bms_chip *chip, soc = 100; if (soc < 0) { - pr_err("bad rem_usb_chg = %d rem_chg %d," + pr_debug("bad rem_usb_chg = %d rem_chg %d," "cc_uah %d, unusb_chg %d\n", remaining_usable_charge_uah, remaining_charge_uah, cc_uah, unusable_charge_uah); - pr_err("for bad rem_usb_chg last_ocv_uv = %d" + pr_debug("for bad rem_usb_chg last_ocv_uv = %d" "chargecycles = %d, batt_temp = %d" "fcc = %d soc =%d\n", chip->last_ocv_uv, chargecycles, batt_temp, @@ -2056,45 +2048,48 @@ static int calculate_state_of_charge(struct pm8921_bms_chip *chip, mutex_unlock(&soc_invalidation_mutex); pr_debug("SOC before adjustment = %d\n", soc); - calculated_soc = adjust_soc(chip, soc, batt_temp, chargecycles, + new_calculated_soc = adjust_soc(chip, soc, batt_temp, chargecycles, rbatt, fcc_uah, unusable_charge_uah, cc_uah); - pr_debug("calculated SOC = %d\n", calculated_soc); + pr_debug("calculated SOC = %d\n", new_calculated_soc); + if (new_calculated_soc != calculated_soc) + update_power_supply(chip); + + calculated_soc = new_calculated_soc; firsttime = 0; + get_current_time(&chip->last_recalc_time); return calculated_soc; } -#define CALCULATE_SOC_MS 20000 +static int recalculate_soc(struct pm8921_bms_chip *chip) +{ + int batt_temp; + struct pm8921_soc_params raw; + int soc; + + wake_lock(&the_chip->soc_wake_lock); + get_batt_temp(chip, &batt_temp); + + mutex_lock(&chip->last_ocv_uv_mutex); + read_soc_params_raw(chip, &raw, batt_temp); + + soc = calculate_state_of_charge(chip, &raw, + batt_temp, last_chargecycles); + mutex_unlock(&chip->last_ocv_uv_mutex); + wake_unlock(&the_chip->soc_wake_lock); + return soc; +} + static void calculate_soc_work(struct work_struct *work) { struct pm8921_bms_chip *chip = container_of(work, struct pm8921_bms_chip, calculate_soc_delayed_work.work); - int batt_temp, rc; - struct pm8xxx_adc_chan_result result; - struct pm8921_soc_params raw; - int soc; - - rc = pm8xxx_adc_read(chip->batt_temp_channel, &result); - if (rc) { - pr_err("error reading adc channel = %d, rc = %d\n", - chip->batt_temp_channel, rc); - return; - } - pr_debug("batt_temp phy = %lld meas = 0x%llx\n", result.physical, - result.measurement); - batt_temp = (int)result.physical; - - mutex_lock(&chip->last_ocv_uv_mutex); - read_soc_params_raw(chip, &raw); - - soc = calculate_state_of_charge(chip, &raw, - batt_temp, last_chargecycles); - mutex_unlock(&chip->last_ocv_uv_mutex); + recalculate_soc(chip); schedule_delayed_work(&chip->calculate_soc_delayed_work, round_jiffies_relative(msecs_to_jiffies - (CALCULATE_SOC_MS))); + (chip->soc_calc_period))); } static int report_state_of_charge(struct pm8921_bms_chip *chip) @@ -2102,24 +2097,14 @@ static int report_state_of_charge(struct pm8921_bms_chip *chip) int soc = calculated_soc; int delta_time_us; struct timespec now; - struct pm8xxx_adc_chan_result result; int batt_temp; - int rc; if (bms_fake_battery != -EINVAL) { pr_debug("Returning Fake SOC = %d%%\n", bms_fake_battery); return bms_fake_battery; } - rc = pm8xxx_adc_read(the_chip->batt_temp_channel, &result); - if (rc) { - pr_err("error reading adc channel = %d, rc = %d\n", - the_chip->batt_temp_channel, rc); - return rc; - } - pr_debug("batt_temp phy = %lld meas = 0x%llx\n", result.physical, - result.measurement); - batt_temp = (int)result.physical; + get_batt_temp(chip, &batt_temp); do_posix_clock_monotonic_gettime(&now); if (chip->t_soc_queried.tv_sec != 0) { @@ -2179,10 +2164,6 @@ void pm8921_bms_invalidate_shutdown_soc(void) { int calculate_soc = 0; struct pm8921_bms_chip *chip = the_chip; - int batt_temp, rc; - struct pm8xxx_adc_chan_result result; - struct pm8921_soc_params raw; - int soc; pr_debug("Invalidating shutdown soc - the battery was removed\n"); if (shutdown_soc_invalid) @@ -2203,81 +2184,10 @@ void pm8921_bms_invalidate_shutdown_soc(void) mutex_unlock(&soc_invalidation_mutex); if (!calculate_soc) return; - - rc = pm8xxx_adc_read(chip->batt_temp_channel, &result); - if (rc) { - pr_err("error reading adc channel = %d, rc = %d\n", - chip->batt_temp_channel, rc); - return; - } - pr_debug("batt_temp phy = %lld meas = 0x%llx\n", result.physical, - result.measurement); - batt_temp = (int)result.physical; - - mutex_lock(&chip->last_ocv_uv_mutex); - read_soc_params_raw(chip, &raw); - - soc = calculate_state_of_charge(chip, &raw, - batt_temp, last_chargecycles); - mutex_unlock(&chip->last_ocv_uv_mutex); + recalculate_soc(chip); } EXPORT_SYMBOL(pm8921_bms_invalidate_shutdown_soc); -#define MIN_DELTA_625_UV 1000 -static void calib_hkadc(struct pm8921_bms_chip *chip) -{ - int voltage, rc; - struct pm8xxx_adc_chan_result result; - int usb_chg; - int this_delta; - - mutex_lock(&chip->calib_mutex); - rc = pm8xxx_adc_read(the_chip->ref1p25v_channel, &result); - if (rc) { - pr_err("ADC failed for 1.25volts rc = %d\n", rc); - goto out; - } - voltage = xoadc_reading_to_microvolt(result.adc_code); - - pr_debug("result 1.25v = 0x%x, voltage = %duV adc_meas = %lld\n", - result.adc_code, voltage, result.measurement); - - chip->xoadc_v125 = voltage; - - rc = pm8xxx_adc_read(the_chip->ref625mv_channel, &result); - if (rc) { - pr_err("ADC failed for 1.25volts rc = %d\n", rc); - goto out; - } - voltage = xoadc_reading_to_microvolt(result.adc_code); - - usb_chg = usb_chg_plugged_in(); - pr_debug("result 0.625V = 0x%x, voltage = %duV adc_meas = %lld " - "usb_chg = %d\n", - result.adc_code, voltage, result.measurement, - usb_chg); - - if (usb_chg) - chip->xoadc_v0625_usb_present = voltage; - else - chip->xoadc_v0625_usb_absent = voltage; - - chip->xoadc_v0625 = voltage; - if (chip->xoadc_v0625_usb_present && chip->xoadc_v0625_usb_absent) { - this_delta = chip->xoadc_v0625_usb_present - - chip->xoadc_v0625_usb_absent; - pr_debug("this_delta= %duV\n", this_delta); - if (this_delta > MIN_DELTA_625_UV) - last_usb_cal_delta_uv = this_delta; - pr_debug("625V_present= %d, 625V_absent= %d, delta = %duV\n", - chip->xoadc_v0625_usb_present, - chip->xoadc_v0625_usb_absent, - last_usb_cal_delta_uv); - } -out: - mutex_unlock(&chip->calib_mutex); -} - static void calibrate_hkadc_work(struct work_struct *work) { struct pm8921_bms_chip *chip = container_of(work, @@ -2291,19 +2201,6 @@ void pm8921_bms_calibrate_hkadc(void) schedule_work(&the_chip->calib_hkadc_work); } -#define HKADC_CALIB_DELAY_MS 600000 -static void calibrate_hkadc_delayed_work(struct work_struct *work) -{ - struct pm8921_bms_chip *chip = container_of(work, - struct pm8921_bms_chip, - calib_hkadc_delayed_work.work); - - calib_hkadc(chip); - schedule_delayed_work(&chip->calib_hkadc_delayed_work, - round_jiffies_relative(msecs_to_jiffies - (HKADC_CALIB_DELAY_MS))); -} - int pm8921_bms_get_vsense_avg(int *result) { int rc = -EINVAL; @@ -2323,27 +2220,35 @@ EXPORT_SYMBOL(pm8921_bms_get_vsense_avg); int pm8921_bms_get_battery_current(int *result_ua) { - int vsense; + int vsense_uv; + int rc = 0; + *result_ua = 0; if (!the_chip) { pr_err("called before initialization\n"); return -EINVAL; } - if (the_chip->r_sense == 0) { + if (the_chip->r_sense_uohm == 0) { pr_err("r_sense is zero\n"); return -EINVAL; } mutex_lock(&the_chip->bms_output_lock); pm_bms_lock_output_data(the_chip); - read_vsense_avg(the_chip, &vsense); + rc = read_vsense_avg(the_chip, &vsense_uv); pm_bms_unlock_output_data(the_chip); mutex_unlock(&the_chip->bms_output_lock); - pr_debug("vsense=%duV\n", vsense); + if (rc) { + pr_err("Unable to read vsense average\n"); + goto error_vsense; + } + pr_debug("vsense=%duV\n", vsense_uv); /* cast for signed division */ - *result_ua = vsense * 1000 / (int)the_chip->r_sense; + *result_ua = div_s64(vsense_uv * 1000000LL, the_chip->r_sense_uohm); pr_debug("ibat=%duA\n", *result_ua); - return 0; + +error_vsense: + return rc; } EXPORT_SYMBOL(pm8921_bms_get_battery_current); @@ -2358,71 +2263,26 @@ int pm8921_bms_get_percent_charge(void) } EXPORT_SYMBOL_GPL(pm8921_bms_get_percent_charge); -int pm8921_bms_get_rbatt(void) +int pm8921_bms_get_current_max(void) { - int batt_temp, rc; - struct pm8xxx_adc_chan_result result; - struct pm8921_soc_params raw; - int fcc_uah; - int unusable_charge_uah; - int remaining_charge_uah; - int cc_uah; - int rbatt; - int iavg_ua; - int delta_time_s; - if (!the_chip) { pr_err("called before initialization\n"); return -EINVAL; } - - rc = pm8xxx_adc_read(the_chip->batt_temp_channel, &result); - if (rc) { - pr_err("error reading adc channel = %d, rc = %d\n", - the_chip->batt_temp_channel, rc); - return rc; - } - pr_debug("batt_temp phy = %lld meas = 0x%llx\n", result.physical, - result.measurement); - batt_temp = (int)result.physical; - - mutex_lock(&the_chip->last_ocv_uv_mutex); - - read_soc_params_raw(the_chip, &raw); - - calculate_soc_params(the_chip, &raw, batt_temp, last_chargecycles, - &fcc_uah, - &unusable_charge_uah, - &remaining_charge_uah, - &cc_uah, - &rbatt, - &iavg_ua, - &delta_time_s); - mutex_unlock(&the_chip->last_ocv_uv_mutex); - - return rbatt; + return the_chip->imax_ua; } -EXPORT_SYMBOL_GPL(pm8921_bms_get_rbatt); +EXPORT_SYMBOL_GPL(pm8921_bms_get_current_max); int pm8921_bms_get_fcc(void) { - int batt_temp, rc; - struct pm8xxx_adc_chan_result result; + int batt_temp; if (!the_chip) { pr_err("called before initialization\n"); return -EINVAL; } - rc = pm8xxx_adc_read(the_chip->batt_temp_channel, &result); - if (rc) { - pr_err("error reading adc channel = %d, rc = %d\n", - the_chip->batt_temp_channel, rc); - return rc; - } - pr_debug("batt_temp phy = %lld meas = 0x%llx", result.physical, - result.measurement); - batt_temp = (int)result.physical; + get_batt_temp(the_chip, &batt_temp); return calculate_fcc_uah(the_chip, batt_temp, last_chargecycles); } EXPORT_SYMBOL_GPL(pm8921_bms_get_fcc); @@ -2436,9 +2296,12 @@ EXPORT_SYMBOL_GPL(pm8921_bms_get_fcc); void pm8921_bms_charging_began(void) { struct pm8921_soc_params raw; + int batt_temp; + + get_batt_temp(the_chip, &batt_temp); mutex_lock(&the_chip->last_ocv_uv_mutex); - read_soc_params_raw(the_chip, &raw); + read_soc_params_raw(the_chip, &raw, batt_temp); mutex_unlock(&the_chip->last_ocv_uv_mutex); the_chip->start_percent = report_state_of_charge(the_chip); @@ -2461,26 +2324,17 @@ EXPORT_SYMBOL_GPL(pm8921_bms_charging_began); #define MIN_START_PERCENT_FOR_LEARNING 30 void pm8921_bms_charging_end(int is_battery_full) { - int batt_temp, rc; - struct pm8xxx_adc_chan_result result; + int batt_temp; struct pm8921_soc_params raw; if (the_chip == NULL) return; - rc = pm8xxx_adc_read(the_chip->batt_temp_channel, &result); - if (rc) { - pr_err("error reading adc channel = %d, rc = %d\n", - the_chip->batt_temp_channel, rc); - return; - } - pr_debug("batt_temp phy = %lld meas = 0x%llx\n", result.physical, - result.measurement); - batt_temp = (int)result.physical; + get_batt_temp(the_chip, &batt_temp); mutex_lock(&the_chip->last_ocv_uv_mutex); - read_soc_params_raw(the_chip, &raw); + read_soc_params_raw(the_chip, &raw, batt_temp); calculate_cc_uah(the_chip, raw.cc, &bms_end_cc_uah); @@ -2623,12 +2477,6 @@ static irqreturn_t pm8921_bms_good_ocv_handler(int irq, void *data) return IRQ_HANDLED; } -static irqreturn_t pm8921_bms_vsense_avg_handler(int irq, void *data) -{ - pr_debug("irq = %d triggered", irq); - return IRQ_HANDLED; -} - struct pm_bms_irq_init_data { unsigned int irq_id; char *name; @@ -2657,8 +2505,6 @@ struct pm_bms_irq_init_data bms_irq_data[] = { pm8921_bms_ocv_for_r_handler), BMS_IRQ(PM8921_BMS_GOOD_OCV, IRQF_TRIGGER_RISING, pm8921_bms_good_ocv_handler), - BMS_IRQ(PM8921_BMS_VSENSE_AVG, IRQF_TRIGGER_RISING, - pm8921_bms_vsense_avg_handler), }; static void free_irqs(struct pm8921_bms_chip *chip) @@ -2738,7 +2584,7 @@ static void check_initial_ocv(struct pm8921_bms_chip *chip) */ ocv_uv = 0; pm_bms_read_output_data(chip, LAST_GOOD_OCV_VALUE, &ocv_raw); - usb_chg = usb_chg_plugged_in(); + usb_chg = usb_chg_plugged_in(chip); rc = convert_vbatt_raw_to_uv(chip, usb_chg, ocv_raw, &ocv_uv); if (rc || ocv_uv == 0) { rc = adc_based_ocv(chip, &ocv_uv); @@ -2881,7 +2727,7 @@ static int get_calc(void *data, u64 * val) int ibat_ua, vbat_uv; struct pm8921_soc_params raw; - read_soc_params_raw(the_chip, &raw); + read_soc_params_raw(the_chip, &raw, 300); *val = 0; @@ -2946,7 +2792,9 @@ static int get_reading(void *data, u64 * val) int ret = 0; struct pm8921_soc_params raw; - read_soc_params_raw(the_chip, &raw); + mutex_lock(&the_chip->bms_output_lock); + read_soc_params_raw(the_chip, &raw, 300); + mutex_unlock(&the_chip->bms_output_lock); *val = 0; @@ -3157,7 +3005,7 @@ static int __devinit pm8921_bms_probe(struct platform_device *pdev) mutex_init(&chip->bms_output_lock); mutex_init(&chip->last_ocv_uv_mutex); chip->dev = &pdev->dev; - chip->r_sense = pdata->r_sense; + chip->r_sense_uohm = pdata->r_sense_uohm; chip->v_cutoff = pdata->v_cutoff; chip->max_voltage_uv = pdata->max_voltage_uv; chip->chg_term_ua = pdata->chg_term_ua; @@ -3165,13 +3013,23 @@ static int __devinit pm8921_bms_probe(struct platform_device *pdev) chip->rconn_mohm = pdata->rconn_mohm; chip->start_percent = -EINVAL; chip->end_percent = -EINVAL; + chip->last_cc_uah = INT_MIN; + chip->ocv_reading_at_100 = OCV_RAW_UNINITIALIZED; + chip->prev_last_good_ocv_raw = OCV_RAW_UNINITIALIZED; chip->shutdown_soc_valid_limit = pdata->shutdown_soc_valid_limit; chip->adjust_soc_low_threshold = pdata->adjust_soc_low_threshold; + + chip->normal_voltage_calc_ms = pdata->normal_voltage_calc_ms; + chip->low_voltage_calc_ms = pdata->low_voltage_calc_ms; + + chip->soc_calc_period = pdata->normal_voltage_calc_ms; + if (chip->adjust_soc_low_threshold >= 45) chip->adjust_soc_low_threshold = 45; chip->prev_pc_unusable = -EINVAL; chip->soc_at_cv = -EINVAL; + chip->imax_ua = -EINVAL; chip->ignore_shutdown_soc = pdata->ignore_shutdown_soc; rc = set_battery_data(chip); @@ -3200,18 +3058,21 @@ static int __devinit pm8921_bms_probe(struct platform_device *pdev) mutex_init(&chip->calib_mutex); INIT_WORK(&chip->calib_hkadc_work, calibrate_hkadc_work); - INIT_DELAYED_WORK(&chip->calib_hkadc_delayed_work, - calibrate_hkadc_delayed_work); INIT_DELAYED_WORK(&chip->calculate_soc_delayed_work, calculate_soc_work); + wake_lock_init(&chip->soc_wake_lock, + WAKE_LOCK_SUSPEND, "pm8921_soc_lock"); rc = request_irqs(chip, pdev); if (rc) { pr_err("couldn't register interrupts rc = %d\n", rc); - goto free_chip; + goto destroy_soc_wl; } + wake_lock_init(&chip->low_voltage_wake_lock, + WAKE_LOCK_SUSPEND, "pm8921_bms_low"); + rc = pm8921_bms_hw_init(chip); if (rc) { pr_err("couldn't init hardware rc = %d\n", rc); @@ -3231,24 +3092,27 @@ static int __devinit pm8921_bms_probe(struct platform_device *pdev) } check_initial_ocv(chip); - /* start periodic hkadc calibration */ - schedule_delayed_work(&chip->calib_hkadc_delayed_work, 0); - /* enable the vbatt reading interrupts for scheduling hkadc calib */ pm8921_bms_enable_irq(chip, PM8921_BMS_GOOD_OCV); pm8921_bms_enable_irq(chip, PM8921_BMS_OCV_FOR_R); calculate_soc_work(&(chip->calculate_soc_delayed_work.work)); - get_battery_uvolts(chip, &vbatt); - pr_info("OK battery_capacity_at_boot=%d volt = %d ocv = %d\n", + rc = get_battery_uvolts(chip, &vbatt); + if (!rc) + pr_info("OK battery_capacity_at_boot=%d volt = %d ocv = %d\n", pm8921_bms_get_percent_charge(), vbatt, chip->last_ocv_uv); + else + pr_info("Unable to read battery voltage at boot\n"); return 0; free_irqs: + wake_lock_destroy(&chip->low_voltage_wake_lock); free_irqs(chip); +destroy_soc_wl: + wake_lock_destroy(&chip->soc_wake_lock); free_chip: kfree(chip); return rc; @@ -3266,12 +3130,42 @@ static int __devexit pm8921_bms_remove(struct platform_device *pdev) return 0; } +static int pm8921_bms_resume(struct device *dev) +{ + int rc; + unsigned long time_since_last_recalc; + unsigned long tm_now_sec; + + rc = get_current_time(&tm_now_sec); + if (rc) { + pr_err("Could not read current time: %d\n", rc); + return 0; + } + if (tm_now_sec > the_chip->last_recalc_time) { + time_since_last_recalc = tm_now_sec - + the_chip->last_recalc_time; + pr_debug("Time since last recalc: %lu\n", + time_since_last_recalc); + if (time_since_last_recalc >= the_chip->soc_calc_period) { + the_chip->last_recalc_time = tm_now_sec; + recalculate_soc(the_chip); + } + } + + return 0; +} + +static const struct dev_pm_ops pm8921_bms_pm_ops = { + .resume = pm8921_bms_resume, +}; + static struct platform_driver pm8921_bms_driver = { .probe = pm8921_bms_probe, .remove = __devexit_p(pm8921_bms_remove), .driver = { .name = PM8921_BMS_DEV_NAME, .owner = THIS_MODULE, + .pm = &pm8921_bms_pm_ops, }, }; diff --git a/drivers/power/pm8921-charger.c b/drivers/power/pm8921-charger.c index fc5ecb7d214..061be695b31 100644 --- a/drivers/power/pm8921-charger.c +++ b/drivers/power/pm8921-charger.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved. +/* Copyright (c) 2011-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 @@ -21,6 +21,7 @@ #include #include #include +#include #include #include #include @@ -28,11 +29,13 @@ #include #include #include +#include #include #include #define CHG_BUCK_CLOCK_CTRL 0x14 +#define CHG_BUCK_CLOCK_CTRL_8038 0xD #define PBL_ACCESS1 0x04 #define PBL_ACCESS2 0x05 @@ -85,6 +88,8 @@ #define EOC_CHECK_PERIOD_MS 10000 /* check for USB unplug every 200 msecs */ #define UNPLUG_CHECK_WAIT_PERIOD_MS 200 +#define UNPLUG_CHECK_RAMP_MS 25 +#define USB_TRIM_ENTRIES 16 enum chg_fsm_state { FSM_STATE_OFF_0 = 0, @@ -115,13 +120,6 @@ struct fsm_state_to_batt_status { int batt_state; }; -static int pm8921_battery_gauge_alarm_notify(struct notifier_block *nb, - unsigned long status, void *unused); - -static struct notifier_block alarm_notifier = { - .notifier_call = pm8921_battery_gauge_alarm_notify, -}; - static struct fsm_state_to_batt_status map[] = { {FSM_STATE_OFF_0, POWER_SUPPLY_STATUS_UNKNOWN}, {FSM_STATE_BATFETDET_START_12, POWER_SUPPLY_STATUS_UNKNOWN}, @@ -207,12 +205,12 @@ struct bms_notify { * @dc_present: present status of dc * @usb_charger_current: usb current to charge the battery with used when * the usb path is enabled or charging is resumed - * @safety_time: max time for which charging will happen * @update_time: how frequently the userland needs to be updated * @max_voltage_mv: the max volts the batt should be charged up to * @min_voltage_mv: the min battery voltage before turning the FETon * @uvd_voltage_mv: (PM8917 only) the falling UVD threshold voltage - * @alarm_voltage_mv: the battery alarm voltage + * @alarm_low_mv: the battery alarm voltage low + * @alarm_high_mv: the battery alarm voltage high * @cool_temp_dc: the cool temp threshold in deciCelcius * @warm_temp_dc: the warm temp threshold in deciCelcius * @resume_voltage_delta: the voltage delta from vdd max at which the @@ -227,13 +225,14 @@ struct pm8921_chg_chip { unsigned int usb_charger_current; unsigned int max_bat_chg_current; unsigned int pmic_chg_irq[PM_CHG_MAX_INTS]; - unsigned int safety_time; unsigned int ttrkl_time; unsigned int update_time; unsigned int max_voltage_mv; unsigned int min_voltage_mv; unsigned int uvd_voltage_mv; - unsigned int alarm_voltage_mv; + unsigned int safe_current_ma; + unsigned int alarm_low_mv; + unsigned int alarm_high_mv; int cool_temp_dc; int warm_temp_dc; unsigned int temp_check_period; @@ -244,6 +243,7 @@ struct pm8921_chg_chip { unsigned int is_bat_cool; unsigned int is_bat_warm; unsigned int resume_voltage_delta; + int resume_charge_percent; unsigned int term_current; unsigned int vbat_channel; unsigned int batt_temp_channel; @@ -254,12 +254,13 @@ struct pm8921_chg_chip { struct power_supply batt_psy; struct dentry *dent; struct bms_notify bms_notify; - bool keep_btm_on_suspend; + int *usb_trim_table; + struct regulator *vreg_xoadc; bool ext_charging; bool ext_charge_done; bool iusb_fine_res; - bool dc_unplug_check; - bool disable_hw_clock_switching; + bool final_kickstart; + bool lockup_lpm_wrkarnd; DECLARE_BITMAP(enabled_irqs, PM_CHG_MAX_INTS); struct work_struct battery_id_valid_work; int64_t batt_id_min; @@ -275,6 +276,7 @@ struct pm8921_chg_chip { struct delayed_work eoc_work; struct delayed_work unplug_check_work; struct delayed_work vin_collapse_check_work; + struct delayed_work btc_override_work; struct wake_lock eoc_wake_lock; enum pm8921_chg_cold_thr cold_thr; enum pm8921_chg_hot_thr hot_thr; @@ -284,6 +286,14 @@ struct pm8921_chg_chip { bool has_dc_supply; u8 active_path; int recent_reported_soc; + int battery_less_hardware; + int ibatmax_max_adj_ma; + int btc_override; + int btc_override_cold_decidegc; + int btc_override_hot_decidegc; + int btc_delay_ms; + bool btc_panic_if_cant_stop_chg; + int stop_chg_upon_expiry; }; /* user space parameter to limit usb current */ @@ -299,7 +309,70 @@ static int thermal_mitigation; static struct pm8921_chg_chip *the_chip; -static struct pm8xxx_adc_arb_btm_param btm_config; +static DEFINE_SPINLOCK(lpm_lock); +#define LPM_ENABLE_BIT BIT(2) +static int pm8921_chg_set_lpm(struct pm8921_chg_chip *chip, int enable) +{ + int rc; + u8 reg; + + rc = pm8xxx_readb(chip->dev->parent, CHG_CNTRL, ®); + if (rc) { + pr_err("pm8xxx_readb failed: addr=%03X, rc=%d\n", + CHG_CNTRL, rc); + return rc; + } + reg &= ~LPM_ENABLE_BIT; + reg |= (enable ? LPM_ENABLE_BIT : 0); + + rc = pm8xxx_writeb(chip->dev->parent, CHG_CNTRL, reg); + if (rc) { + pr_err("pm_chg_write failed: addr=%03X, rc=%d\n", + CHG_CNTRL, rc); + return rc; + } + + return rc; +} + +static int pm_chg_write(struct pm8921_chg_chip *chip, u16 addr, u8 reg) +{ + int rc; + unsigned long flags = 0; + + /* Disable LPM */ + if (chip->lockup_lpm_wrkarnd) { + spin_lock_irqsave(&lpm_lock, flags); + + /* + * This write could have initiated right after a previous write. + * Allow time to settle to go in to lpm from the previous write + */ + udelay(200); + rc = pm8921_chg_set_lpm(chip, 0); + if (rc) + goto lpm_err; + + /* Wait to come out of LPM */ + udelay(200); + } + + rc = pm8xxx_writeb(chip->dev->parent, addr, reg); + if (rc) { + pr_err("pm_chg_write failed: addr=%03X, rc=%d\n", addr, rc); + goto lpm_err; + } + + /* Enable LPM */ + if (chip->lockup_lpm_wrkarnd) + rc = pm8921_chg_set_lpm(chip, 1); + +lpm_err: + if (chip->lockup_lpm_wrkarnd) + spin_unlock_irqrestore(&lpm_lock, flags); + + return rc; +} static int pm_chg_masked_write(struct pm8921_chg_chip *chip, u16 addr, u8 mask, u8 val) @@ -314,9 +387,9 @@ static int pm_chg_masked_write(struct pm8921_chg_chip *chip, u16 addr, } reg &= ~mask; reg |= val & mask; - rc = pm8xxx_writeb(chip->dev->parent, addr, reg); + rc = pm_chg_write(chip, addr, reg); if (rc) { - pr_err("pm8xxx_writeb failed: addr=%03X, rc=%d\n", addr, rc); + pr_err("pm_chg_write failed: addr=%03X, rc=%d\n", addr, rc); return rc; } return 0; @@ -328,6 +401,23 @@ static int pm_chg_get_rt_status(struct pm8921_chg_chip *chip, int irq_id) chip->pmic_chg_irq[irq_id]); } +static int is_chg_on_bat(struct pm8921_chg_chip *chip) +{ + return !(pm_chg_get_rt_status(chip, DCIN_VALID_IRQ) + || pm_chg_get_rt_status(chip, USBIN_VALID_IRQ)); +} + +static void pm8921_chg_bypass_bat_gone_debounce(struct pm8921_chg_chip *chip, + int bypass) +{ + int rc; + + rc = pm_chg_write(chip, COMPARATOR_OVERRIDE, bypass ? 0x89 : 0x88); + if (rc) { + pr_err("Failed to set bypass bit to %d rc=%d\n", bypass, rc); + } +} + /* Treat OverVoltage/UnderVoltage as source missing */ static int is_usb_chg_plugged_in(struct pm8921_chg_chip *chip) { @@ -353,14 +443,14 @@ static int pm_chg_get_fsm_state(struct pm8921_chg_chip *chip) int err, ret = 0; temp = CAPTURE_FSM_STATE_CMD; - err = pm8xxx_writeb(chip->dev->parent, CHG_TEST, temp); + err = pm_chg_write(chip, CHG_TEST, temp); if (err) { pr_err("Error %d writing %d to addr %d\n", err, temp, CHG_TEST); return err; } temp = READ_BANK_7; - err = pm8xxx_writeb(chip->dev->parent, CHG_TEST, temp); + err = pm_chg_write(chip, CHG_TEST, temp); if (err) { pr_err("Error %d writing %d to addr %d\n", err, temp, CHG_TEST); return err; @@ -375,7 +465,7 @@ static int pm_chg_get_fsm_state(struct pm8921_chg_chip *chip) ret = temp & 0xF; temp = READ_BANK_4; - err = pm8xxx_writeb(chip->dev->parent, CHG_TEST, temp); + err = pm_chg_write(chip, CHG_TEST, temp); if (err) { pr_err("Error %d writing %d to addr %d\n", err, temp, CHG_TEST); return err; @@ -398,7 +488,7 @@ static int pm_chg_get_regulation_loop(struct pm8921_chg_chip *chip) int err; temp = READ_BANK_6; - err = pm8xxx_writeb(chip->dev->parent, CHG_TEST, temp); + err = pm_chg_write(chip, CHG_TEST, temp); if (err) { pr_err("Error %d writing %d to addr %d\n", err, temp, CHG_TEST); return err; @@ -480,7 +570,7 @@ static int __pm_chg_vddmax_set(struct pm8921_chg_chip *chip, int voltage) } pr_debug("voltage=%d setting %02x\n", voltage, temp); - return pm8xxx_writeb(chip->dev->parent, CHG_VDD_MAX, temp); + return pm_chg_write(chip, CHG_VDD_MAX, temp); } static int pm_chg_vddmax_get(struct pm8921_chg_chip *chip, int *voltage) @@ -628,10 +718,26 @@ static int pm_chg_uvd_threshold_set(struct pm8921_chg_chip *chip, int thresh_mv) } #define PM8921_CHG_IBATMAX_MIN 325 -#define PM8921_CHG_IBATMAX_MAX 2000 +#define PM8921_CHG_IBATMAX_MAX 3025 #define PM8921_CHG_I_MIN_MA 225 #define PM8921_CHG_I_STEP_MA 50 #define PM8921_CHG_I_MASK 0x3F +static int pm_chg_ibatmax_get(struct pm8921_chg_chip *chip, int *ibat_ma) +{ + u8 temp; + int rc; + + rc = pm8xxx_readb(chip->dev->parent, CHG_IBAT_MAX, &temp); + if (rc) { + pr_err("rc = %d while reading ibat max\n", rc); + *ibat_ma = 0; + return rc; + } + *ibat_ma = (int)(temp & PM8921_CHG_I_MASK) * PM8921_CHG_I_STEP_MA + + PM8921_CHG_I_MIN_MA; + return 0; +} + static int pm_chg_ibatmax_set(struct pm8921_chg_chip *chip, int chg_current) { u8 temp; @@ -703,6 +809,46 @@ struct usb_ma_limit_entry { u8 value; }; +/* USB Trim tables */ +static int usb_trim_8038_table[USB_TRIM_ENTRIES] = { + 0x0, + 0x0, + -0x9, + 0x0, + -0xD, + 0x0, + -0x10, + -0x11, + 0x0, + 0x0, + -0x25, + 0x0, + -0x28, + 0x0, + -0x32, + 0x0 +}; + +static int usb_trim_8917_table[USB_TRIM_ENTRIES] = { + 0x0, + 0x0, + 0xA, + 0xC, + 0x10, + 0x10, + 0x13, + 0x14, + 0x13, + 0x3, + 0x1A, + 0x1D, + 0x1D, + 0x21, + 0x24, + 0x26 +}; + +/* Maximum USB setting table */ static struct usb_ma_limit_entry usb_ma_table[] = { {100, 0x0}, {200, 0x1}, @@ -722,18 +868,113 @@ static struct usb_ma_limit_entry usb_ma_table[] = { {1600, 0xF}, }; +#define REG_SBI_CONFIG 0x04F +#define PAGE3_ENABLE_MASK 0x6 +#define USB_OVP_TRIM_MASK 0x3F +#define USB_OVP_TRIM_PM8917_MASK 0x7F +#define USB_OVP_TRIM_MIN 0x00 +#define REG_USB_OVP_TRIM_ORIG_LSB 0x10A +#define REG_USB_OVP_TRIM_ORIG_MSB 0x09C +#define REG_USB_OVP_TRIM_PM8917 0x2B5 +#define REG_USB_OVP_TRIM_PM8917_BIT BIT(0) +static int pm_chg_usb_trim(struct pm8921_chg_chip *chip, int index) +{ + u8 temp, sbi_config, msb, lsb, mask; + s8 trim; + int rc = 0; + static u8 usb_trim_reg_orig = 0xFF; + + /* No trim data for PM8921 */ + if (!chip->usb_trim_table) + return 0; + + if (usb_trim_reg_orig == 0xFF) { + rc = pm8xxx_readb(chip->dev->parent, + REG_USB_OVP_TRIM_ORIG_MSB, &msb); + if (rc) { + pr_err("error = %d reading sbi config reg\n", rc); + return rc; + } + + rc = pm8xxx_readb(chip->dev->parent, + REG_USB_OVP_TRIM_ORIG_LSB, &lsb); + if (rc) { + pr_err("error = %d reading sbi config reg\n", rc); + return rc; + } + + msb = msb >> 5; + lsb = lsb >> 5; + usb_trim_reg_orig = msb << 3 | lsb; + + if (pm8xxx_get_version(chip->dev->parent) + == PM8XXX_VERSION_8917) { + rc = pm8xxx_readb(chip->dev->parent, + REG_USB_OVP_TRIM_PM8917, &msb); + if (rc) { + pr_err("error = %d reading config reg\n", rc); + return rc; + } + + msb = msb & REG_USB_OVP_TRIM_PM8917_BIT; + usb_trim_reg_orig |= msb << 6; + } + } + + /* use the original trim value */ + trim = usb_trim_reg_orig; + + trim += chip->usb_trim_table[index]; + if (trim < 0) + trim = 0; + + pr_debug("trim_orig %d write 0x%x index=%d value 0x%x to USB_OVP_TRIM\n", + usb_trim_reg_orig, trim, index, chip->usb_trim_table[index]); + + rc = pm8xxx_readb(chip->dev->parent, REG_SBI_CONFIG, &sbi_config); + if (rc) { + pr_err("error = %d reading sbi config reg\n", rc); + return rc; + } + + temp = sbi_config | PAGE3_ENABLE_MASK; + rc = pm_chg_write(chip, REG_SBI_CONFIG, temp); + if (rc) { + pr_err("error = %d writing sbi config reg\n", rc); + return rc; + } + + mask = USB_OVP_TRIM_MASK; + + if (pm8xxx_get_version(chip->dev->parent) == PM8XXX_VERSION_8917) + mask = USB_OVP_TRIM_PM8917_MASK; + + rc = pm_chg_masked_write(chip, USB_OVP_TRIM, mask, trim); + if (rc) { + pr_err("error = %d writing USB_OVP_TRIM\n", rc); + return rc; + } + + rc = pm_chg_write(chip, REG_SBI_CONFIG, sbi_config); + if (rc) { + pr_err("error = %d writing sbi config reg\n", rc); + return rc; + } + return rc; +} + #define PM8921_CHG_IUSB_MASK 0x1C #define PM8921_CHG_IUSB_SHIFT 2 #define PM8921_CHG_IUSB_MAX 7 #define PM8921_CHG_IUSB_MIN 0 #define PM8917_IUSB_FINE_RES BIT(0) -static int pm_chg_iusbmax_set(struct pm8921_chg_chip *chip, int reg_val) +static int pm_chg_iusbmax_set(struct pm8921_chg_chip *chip, int index) { - u8 temp, fineres; + u8 temp, fineres, reg_val; int rc; - fineres = PM8917_IUSB_FINE_RES & usb_ma_table[reg_val].value; - reg_val = usb_ma_table[reg_val].value >> 1; + reg_val = usb_ma_table[index].value >> 1; + fineres = PM8917_IUSB_FINE_RES & usb_ma_table[index].value; if (reg_val < PM8921_CHG_IUSB_MIN || reg_val > PM8921_CHG_IUSB_MAX) { pr_err("bad mA=%d asked to set\n", reg_val); @@ -758,17 +999,25 @@ static int pm_chg_iusbmax_set(struct pm8921_chg_chip *chip, int reg_val) if (fineres) { rc = pm_chg_masked_write(chip, IUSB_FINE_RES, PM8917_IUSB_FINE_RES, fineres); - if (rc) + if (rc) { pr_err("Failed to write ISUB_FINE_RES rc=%d\n", rc); + return rc; + } } } else { rc = pm_chg_masked_write(chip, PBL_ACCESS2, PM8921_CHG_IUSB_MASK, temp); - if (rc) + if (rc) { pr_err("Failed to write PBL_ACCESS2 rc=%d\n", rc); + return rc; + } } + rc = pm_chg_usb_trim(chip, index); + if (rc) + pr_err("unable to set usb trim rc = %d\n", rc); + return rc; } @@ -801,6 +1050,11 @@ static int pm_chg_iusbmax_get(struct pm8921_chg_chip *chip, int *mA) break; } + if (i < 0) { + pr_err("can't find %d in usb_ma_table. Use min.\n", temp); + i = 0; + } + *mA = usb_ma_table[i].usb_ma; return rc; @@ -831,7 +1085,7 @@ static int pm_chg_tchg_max_set(struct pm8921_chg_chip *chip, int minutes) temp); } -#define PM8921_CHG_TTRKL_MASK 0x1F +#define PM8921_CHG_TTRKL_MASK 0x3F #define PM8921_CHG_TTRKL_MIN 1 #define PM8921_CHG_TTRKL_MAX 64 static int pm_chg_ttrkl_max_set(struct pm8921_chg_chip *chip, int minutes) @@ -980,55 +1234,6 @@ static int pm_chg_led_src_config(struct pm8921_chg_chip *chip, PM8921_CHG_LED_SRC_CONFIG_MASK, temp); } -static void disable_input_voltage_regulation(struct pm8921_chg_chip *chip) -{ - u8 temp; - int rc; - - rc = pm8xxx_writeb(chip->dev->parent, CHG_BUCK_CTRL_TEST3, 0x70); - if (rc) { - pr_err("Failed to write 0x70 to CTRL_TEST3 rc = %d\n", rc); - return; - } - rc = pm8xxx_readb(chip->dev->parent, CHG_BUCK_CTRL_TEST3, &temp); - if (rc) { - pr_err("Failed to read CTRL_TEST3 rc = %d\n", rc); - return; - } - /* set the input voltage disable bit and the write bit */ - temp |= 0x81; - rc = pm8xxx_writeb(chip->dev->parent, CHG_BUCK_CTRL_TEST3, temp); - if (rc) { - pr_err("Failed to write 0x%x to CTRL_TEST3 rc=%d\n", temp, rc); - return; - } -} - -static void enable_input_voltage_regulation(struct pm8921_chg_chip *chip) -{ - u8 temp; - int rc; - - rc = pm8xxx_writeb(chip->dev->parent, CHG_BUCK_CTRL_TEST3, 0x70); - if (rc) { - pr_err("Failed to write 0x70 to CTRL_TEST3 rc = %d\n", rc); - return; - } - rc = pm8xxx_readb(chip->dev->parent, CHG_BUCK_CTRL_TEST3, &temp); - if (rc) { - pr_err("Failed to read CTRL_TEST3 rc = %d\n", rc); - return; - } - /* unset the input voltage disable bit */ - temp &= 0xFE; - /* set the write bit */ - temp |= 0x80; - rc = pm8xxx_writeb(chip->dev->parent, CHG_BUCK_CTRL_TEST3, temp); - if (rc) { - pr_err("Failed to write 0x%x to CTRL_TEST3 rc=%d\n", temp, rc); - return; - } -} static int64_t read_battery_id(struct pm8921_chg_chip *chip) { @@ -1199,6 +1404,8 @@ static int pm_power_get_property_mains(struct power_supply *psy, enum power_supply_property psp, union power_supply_propval *val) { + int type; + /* Check if called before init */ if (!the_chip) return -EINVAL; @@ -1213,29 +1420,16 @@ static int pm_power_get_property_mains(struct power_supply *psy, return 0; } - if (charging_disabled) - return 0; - - /* check external charger first before the dc path */ - if (is_ext_charging(the_chip)) { - val->intval = 1; - return 0; - } - - if (pm_is_chg_charge_dis(the_chip)) { - val->intval = 0; - return 0; - } - if (the_chip->dc_present) { val->intval = 1; return 0; } - /* USB with max current greater than 500 mA connected */ - if (usb_target_ma > USB_WALL_THRESHOLD_MA) + type = the_chip->usb_psy.type; + if (type == POWER_SUPPLY_TYPE_USB_DCP || + type == POWER_SUPPLY_TYPE_USB_ACA || + type == POWER_SUPPLY_TYPE_USB_CDP) val->intval = is_usb_chg_plugged_in(the_chip); - return 0; break; default: @@ -1252,7 +1446,7 @@ static int switch_usb_to_charge_mode(struct pm8921_chg_chip *chip) return 0; /* enable usbin valid comparator and remove force usb ovp fet off */ - rc = pm8xxx_writeb(chip->dev->parent, USB_OVP_TEST, 0xB2); + rc = pm_chg_write(chip, USB_OVP_TEST, 0xB2); if (rc < 0) { pr_err("Failed to write 0xB2 to USB_OVP_TEST rc = %d\n", rc); return rc; @@ -1271,7 +1465,7 @@ static int switch_usb_to_host_mode(struct pm8921_chg_chip *chip) return 0; /* disable usbin valid comparator and force usb ovp fet off */ - rc = pm8xxx_writeb(chip->dev->parent, USB_OVP_TEST, 0xB3); + rc = pm_chg_write(chip, USB_OVP_TEST, 0xB3); if (rc < 0) { pr_err("Failed to write 0xB3 to USB_OVP_TEST rc = %d\n", rc); return rc; @@ -1299,6 +1493,8 @@ static int pm_power_set_property_usb(struct power_supply *psy, else return -EINVAL; break; + case POWER_SUPPLY_PROP_TYPE: + return pm8921_set_usb_power_supply_type(val->intval); default: return -EINVAL; } @@ -1327,21 +1523,10 @@ static int pm_power_get_property_usb(struct power_supply *psy, case POWER_SUPPLY_PROP_PRESENT: case POWER_SUPPLY_PROP_ONLINE: val->intval = 0; - if (charging_disabled) - return 0; - /* - * if drawing any current from usb is disabled behave - * as if no usb cable is connected - */ - if (pm_is_chg_charge_dis(the_chip)) - return 0; - - /* USB charging */ - if (usb_target_ma < USB_WALL_THRESHOLD_MA) + if (the_chip->usb_psy.type == POWER_SUPPLY_TYPE_USB) val->intval = is_usb_chg_plugged_in(the_chip); - else - return 0; + break; case POWER_SUPPLY_PROP_SCOPE: @@ -1366,9 +1551,11 @@ static enum power_supply_property msm_batt_power_props[] = { POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN, POWER_SUPPLY_PROP_VOLTAGE_NOW, POWER_SUPPLY_PROP_CAPACITY, + POWER_SUPPLY_PROP_CURRENT_MAX, POWER_SUPPLY_PROP_CURRENT_NOW, POWER_SUPPLY_PROP_TEMP, - POWER_SUPPLY_PROP_ENERGY_FULL, + POWER_SUPPLY_PROP_CHARGE_FULL, + POWER_SUPPLY_PROP_CHARGE_NOW, }; static int get_prop_battery_uvolts(struct pm8921_chg_chip *chip) @@ -1408,10 +1595,41 @@ static int get_prop_batt_present(struct pm8921_chg_chip *chip) return pm_chg_get_rt_status(chip, BATT_INSERTED_IRQ); } +static int get_prop_batt_status(struct pm8921_chg_chip *chip) +{ + int batt_state = POWER_SUPPLY_STATUS_DISCHARGING; + int fsm_state = pm_chg_get_fsm_state(chip); + int i; + + if (chip->ext_psy) { + if (chip->ext_charge_done) + return POWER_SUPPLY_STATUS_FULL; + if (chip->ext_charging) + return POWER_SUPPLY_STATUS_CHARGING; + } + + for (i = 0; i < ARRAY_SIZE(map); i++) + if (map[i].fsm_state == fsm_state) + batt_state = map[i].batt_state; + + if (fsm_state == FSM_STATE_ON_CHG_HIGHI_1) { + if (!pm_chg_get_rt_status(chip, BATT_INSERTED_IRQ) + || !pm_chg_get_rt_status(chip, BAT_TEMP_OK_IRQ) + || pm_chg_get_rt_status(chip, CHGHOT_IRQ) + || pm_chg_get_rt_status(chip, VBATDET_LOW_IRQ)) + + batt_state = POWER_SUPPLY_STATUS_NOT_CHARGING; + } + return batt_state; +} + static int get_prop_batt_capacity(struct pm8921_chg_chip *chip) { int percent_soc; + if (chip->battery_less_hardware) + return 100; + if (!get_prop_batt_present(chip)) percent_soc = voltage_based_capacity(chip); else @@ -1421,12 +1639,34 @@ static int get_prop_batt_capacity(struct pm8921_chg_chip *chip) percent_soc = voltage_based_capacity(chip); if (percent_soc <= 10) - pr_warn("low battery charge = %d%%\n", percent_soc); + pr_warn_ratelimited("low battery charge = %d%%\n", + percent_soc); + + if (percent_soc <= chip->resume_charge_percent + && get_prop_batt_status(chip) == POWER_SUPPLY_STATUS_FULL) { + pr_debug("soc fell below %d. charging enabled.\n", + chip->resume_charge_percent); + if (chip->is_bat_warm) + pr_warn_ratelimited("battery is warm = %d, do not resume charging at %d%%.\n", + chip->is_bat_warm, + chip->resume_charge_percent); + else if (chip->is_bat_cool) + pr_warn_ratelimited("battery is cool = %d, do not resume charging at %d%%.\n", + chip->is_bat_cool, + chip->resume_charge_percent); + else + pm_chg_vbatdet_set(the_chip, PM8921_CHG_VBATDET_MAX); + } chip->recent_reported_soc = percent_soc; return percent_soc; } +static int get_prop_batt_current_max(struct pm8921_chg_chip *chip) +{ + return pm8921_bms_get_current_max(); +} + static int get_prop_batt_current(struct pm8921_chg_chip *chip) { int result_ua, rc; @@ -1454,6 +1694,20 @@ static int get_prop_batt_fcc(struct pm8921_chg_chip *chip) return rc; } +static int get_prop_batt_charge_now(struct pm8921_chg_chip *chip) +{ + int rc; + int cc_uah; + + rc = pm8921_bms_cc_uah(&cc_uah); + + if (rc == 0) + return cc_uah; + + pr_err("unable to get batt fcc rc = %d\n", rc); + return rc; +} + static int get_prop_batt_health(struct pm8921_chg_chip *chip) { int temp; @@ -1493,40 +1747,15 @@ static int get_prop_charge_type(struct pm8921_chg_chip *chip) return POWER_SUPPLY_CHARGE_TYPE_NONE; } -static int get_prop_batt_status(struct pm8921_chg_chip *chip) -{ - int batt_state = POWER_SUPPLY_STATUS_DISCHARGING; - int fsm_state = pm_chg_get_fsm_state(chip); - int i; - - if (chip->ext_psy) { - if (chip->ext_charge_done) - return POWER_SUPPLY_STATUS_FULL; - if (chip->ext_charging) - return POWER_SUPPLY_STATUS_CHARGING; - } - - for (i = 0; i < ARRAY_SIZE(map); i++) - if (map[i].fsm_state == fsm_state) - batt_state = map[i].batt_state; - - if (fsm_state == FSM_STATE_ON_CHG_HIGHI_1) { - if (!pm_chg_get_rt_status(chip, BATT_INSERTED_IRQ) - || !pm_chg_get_rt_status(chip, BAT_TEMP_OK_IRQ) - || pm_chg_get_rt_status(chip, CHGHOT_IRQ) - || pm_chg_get_rt_status(chip, VBATDET_LOW_IRQ)) - - batt_state = POWER_SUPPLY_STATUS_NOT_CHARGING; - } - return batt_state; -} - #define MAX_TOLERABLE_BATT_TEMP_DDC 680 static int get_prop_batt_temp(struct pm8921_chg_chip *chip) { int rc; struct pm8xxx_adc_chan_result result; + if (chip->battery_less_hardware) + return 300; + rc = pm8xxx_adc_read(chip->batt_temp_channel, &result); if (rc) { pr_err("error reading adc channel = %d, rc = %d\n", @@ -1580,11 +1809,17 @@ static int pm_batt_power_get_property(struct power_supply *psy, case POWER_SUPPLY_PROP_CURRENT_NOW: val->intval = get_prop_batt_current(chip); break; + case POWER_SUPPLY_PROP_CURRENT_MAX: + val->intval = get_prop_batt_current_max(chip); + break; case POWER_SUPPLY_PROP_TEMP: val->intval = get_prop_batt_temp(chip); break; - case POWER_SUPPLY_PROP_ENERGY_FULL: - val->intval = get_prop_batt_fcc(chip) * 1000; + case POWER_SUPPLY_PROP_CHARGE_FULL: + val->intval = get_prop_batt_fcc(chip); + break; + case POWER_SUPPLY_PROP_CHARGE_NOW: + val->intval = get_prop_batt_charge_now(chip); break; default: return -EINVAL; @@ -1633,7 +1868,13 @@ static void __pm8921_charger_vbus_draw(unsigned int mA) return; } - if (mA >= 0 && mA <= 2) { + if (usb_max_current && mA > usb_max_current) { + pr_debug("restricting usb current to %d instead of %d\n", + usb_max_current, mA); + mA = usb_max_current; + } + + if (mA <= 2) { usb_chg_current = 0; rc = pm_chg_iusbmax_set(the_chip, 0); if (rc) { @@ -1651,6 +1892,12 @@ static void __pm8921_charger_vbus_draw(unsigned int mA) break; } + if (i < 0) { + pr_err("can't find %dmA in usb_ma_table. Use min.\n", + mA); + i = 0; + } + /* Check if IUSB_FINE_RES is available */ while ((usb_ma_table[i].value & PM8917_IUSB_FINE_RES) && !the_chip->iusb_fine_res) @@ -1658,9 +1905,8 @@ static void __pm8921_charger_vbus_draw(unsigned int mA) if (i < 0) i = 0; rc = pm_chg_iusbmax_set(the_chip, i); - if (rc) { + if (rc) pr_err("unable to set iusb to %d rc = %d\n", i, rc); - } } } @@ -1721,22 +1967,6 @@ void pm8921_charger_vbus_draw(unsigned int mA) } EXPORT_SYMBOL_GPL(pm8921_charger_vbus_draw); -int pm8921_charger_enable(bool enable) -{ - int rc; - - if (!the_chip) { - pr_err("called before init\n"); - return -EINVAL; - } - enable = !!enable; - rc = pm_chg_auto_enable(the_chip, enable); - if (rc) - pr_err("Failed rc=%d\n", rc); - return rc; -} -EXPORT_SYMBOL(pm8921_charger_enable); - int pm8921_is_usb_chg_plugged_in(void) { if (!the_chip) { @@ -1791,8 +2021,7 @@ int pm8921_disable_input_current_limit(bool disable) if (disable) { pr_warn("Disabling input current limit!\n"); - return pm8xxx_writeb(the_chip->dev->parent, - CHG_BUCK_CTRL_TEST3, 0xF2); + return pm_chg_write(the_chip, CHG_BUCK_CTRL_TEST3, 0xF2); } return 0; } @@ -1958,7 +2187,7 @@ int pm8921_set_usb_power_supply_type(enum power_supply_type type) return -EINVAL; } - if (type < POWER_SUPPLY_TYPE_USB) + if (type < POWER_SUPPLY_TYPE_USB && type > POWER_SUPPLY_TYPE_BATTERY) return -EINVAL; the_chip->usb_psy.type = type; @@ -1977,78 +2206,86 @@ int pm8921_batt_temperature(void) return get_prop_batt_temp(the_chip); } -static int pm8921_charger_enable_batt_alarm(struct pm8921_chg_chip *chip) +static int pm8921_apply_19p2mhz_kickstart(struct pm8921_chg_chip *chip) { - int rc = 0; + int err; + u8 temp; + unsigned long flags = 0; - rc = pm8xxx_batt_alarm_disable(PM8XXX_BATT_ALARM_UPPER_COMPARATOR); - if (!rc) - rc = pm8xxx_batt_alarm_enable( - PM8XXX_BATT_ALARM_LOWER_COMPARATOR); - if (rc) { - pr_err("unable to set batt alarm state rc=%d\n", rc); - return rc; + spin_lock_irqsave(&lpm_lock, flags); + err = pm8921_chg_set_lpm(chip, 0); + if (err) { + pr_err("Error settig LPM rc=%d\n", err); + goto kick_err; } - return rc; -} -static int pm8921_charger_configure_batt_alarm(struct pm8921_chg_chip *chip) -{ - int rc = 0; - - rc = pm8xxx_batt_alarm_disable(PM8XXX_BATT_ALARM_UPPER_COMPARATOR); - if (!rc) - rc = pm8xxx_batt_alarm_disable( - PM8XXX_BATT_ALARM_LOWER_COMPARATOR); - if (rc) { - pr_err("unable to set batt alarm state rc=%d\n", rc); - return rc; + temp = 0xD1; + err = pm8xxx_writeb(chip->dev->parent, CHG_TEST, temp); + if (err) { + pr_err("Error %d writing %d to addr %d\n", err, temp, CHG_TEST); + goto kick_err; } - /* - * The batt-alarm driver requires sane values for both min / max, - * regardless of whether they're both activated. - */ - rc = pm8xxx_batt_alarm_threshold_set( - PM8XXX_BATT_ALARM_LOWER_COMPARATOR, - chip->alarm_voltage_mv); - /* We only handle the lower limit of the battery alarm, thus - * set a high sane maximum. - */ - if (!rc) - rc = pm8xxx_batt_alarm_threshold_set( - PM8XXX_BATT_ALARM_UPPER_COMPARATOR, 5000); - if (rc) { - pr_err("unable to set batt alarm threshold rc=%d\n", rc); - return rc; + temp = 0xD3; + err = pm8xxx_writeb(chip->dev->parent, CHG_TEST, temp); + if (err) { + pr_err("Error %d writing %d to addr %d\n", err, temp, CHG_TEST); + goto kick_err; } - rc = pm8xxx_batt_alarm_hold_time_set( - PM8XXX_BATT_ALARM_HOLD_TIME_16_MS); - if (rc) { - pr_err("unable to set batt alarm hold time rc=%d\n", rc); - return rc; + temp = 0xD1; + err = pm8xxx_writeb(chip->dev->parent, CHG_TEST, temp); + if (err) { + pr_err("Error %d writing %d to addr %d\n", err, temp, CHG_TEST); + goto kick_err; } - /* PWM enabled at 2Hz */ - rc = pm8xxx_batt_alarm_pwm_rate_set(1, 7, 4); - if (rc) { - pr_err("unable to set batt alarm pwm rate rc=%d\n", rc); - return rc; + temp = 0xD5; + err = pm8xxx_writeb(chip->dev->parent, CHG_TEST, temp); + if (err) { + pr_err("Error %d writing %d to addr %d\n", err, temp, CHG_TEST); + goto kick_err; } - rc = pm8xxx_batt_alarm_register_notifier(&alarm_notifier); - if (rc) { - pr_err("unable to register alarm notifier rc=%d\n", rc); - return rc; + /* Wait a few clock cycles before re-enabling hw clock switching */ + udelay(183); + + temp = 0xD1; + err = pm8xxx_writeb(chip->dev->parent, CHG_TEST, temp); + if (err) { + pr_err("Error %d writing %d to addr %d\n", err, temp, CHG_TEST); + goto kick_err; } - return rc; + temp = 0xD0; + err = pm8xxx_writeb(chip->dev->parent, CHG_TEST, temp); + if (err) { + pr_err("Error %d writing %d to addr %d\n", err, temp, CHG_TEST); + goto kick_err; + } + + /* Wait for few clock cycles before re-enabling LPM */ + udelay(32); + +kick_err: + err = pm8921_chg_set_lpm(chip, 1); + if (err) + pr_err("Error settig LPM rc=%d\n", err); + + spin_unlock_irqrestore(&lpm_lock, flags); + + return err; } static void handle_usb_insertion_removal(struct pm8921_chg_chip *chip) { - int usb_present; + int usb_present, rc = 0; + + if (chip->lockup_lpm_wrkarnd) { + rc = pm8921_apply_19p2mhz_kickstart(chip); + if (rc) + pr_err("Failed to apply kickstart rc=%d\n", rc); + } pm_chg_failed_clear(chip, 1); usb_present = is_usb_chg_plugged_in(chip); @@ -2058,23 +2295,30 @@ static void handle_usb_insertion_removal(struct pm8921_chg_chip *chip) power_supply_changed(&chip->usb_psy); power_supply_changed(&chip->batt_psy); pm8921_bms_calibrate_hkadc(); + + /* Enable/disable bypass if charger is on battery */ + if (chip->lockup_lpm_wrkarnd) + pm8921_chg_bypass_bat_gone_debounce(chip, + is_chg_on_bat(chip)); } if (usb_present) { schedule_delayed_work(&chip->unplug_check_work, - round_jiffies_relative(msecs_to_jiffies - (UNPLUG_CHECK_WAIT_PERIOD_MS))); + msecs_to_jiffies(UNPLUG_CHECK_RAMP_MS)); pm8921_chg_enable_irq(chip, CHG_GONE_IRQ); } else { /* USB unplugged reset target current */ usb_target_ma = 0; pm8921_chg_disable_irq(chip, CHG_GONE_IRQ); } - enable_input_voltage_regulation(chip); bms_notify_check(chip); } static void handle_stop_ext_chg(struct pm8921_chg_chip *chip) { + if (chip->lockup_lpm_wrkarnd) + /* Enable bypass if charger is on battery */ + pm8921_chg_bypass_bat_gone_debounce(chip, is_chg_on_bat(chip)); + if (!chip->ext_psy) { pr_debug("external charger not registered.\n"); return; @@ -2101,10 +2345,13 @@ static void handle_start_ext_chg(struct pm8921_chg_chip *chip) int dc_present; int batt_present; int batt_temp_ok; - int vbat_ov; unsigned long delay = round_jiffies_relative(msecs_to_jiffies(EOC_CHECK_PERIOD_MS)); + /* Disable bypass if charger connected and not running on bat */ + if (chip->lockup_lpm_wrkarnd) + pm8921_chg_bypass_bat_gone_debounce(chip, is_chg_on_bat(chip)); + if (!chip->ext_psy) { pr_debug("external charger not registered.\n"); return; @@ -2115,7 +2362,7 @@ static void handle_start_ext_chg(struct pm8921_chg_chip *chip) return; } - dc_present = is_dc_chg_plugged_in(the_chip); + dc_present = is_dc_chg_plugged_in(chip); batt_present = pm_chg_get_rt_status(chip, BATT_INSERTED_IRQ); batt_temp_ok = pm_chg_get_rt_status(chip, BAT_TEMP_OK_IRQ); @@ -2131,28 +2378,29 @@ static void handle_start_ext_chg(struct pm8921_chg_chip *chip) pr_warn("%s. battery temperature not ok.\n", __func__); return; } - pm8921_disable_source_current(true); /* Force BATFET=ON */ - vbat_ov = pm_chg_get_rt_status(chip, VBAT_OV_IRQ); - if (vbat_ov) { - pr_warn("%s. battery over voltage.\n", __func__); - return; - } + + /* Force BATFET=ON */ + pm8921_disable_source_current(true); schedule_delayed_work(&chip->unplug_check_work, - round_jiffies_relative(msecs_to_jiffies - (UNPLUG_CHECK_WAIT_PERIOD_MS))); - pm8921_chg_enable_irq(chip, CHG_GONE_IRQ); + msecs_to_jiffies(UNPLUG_CHECK_RAMP_MS)); power_supply_set_online(chip->ext_psy, dc_present); power_supply_set_charge_type(chip->ext_psy, POWER_SUPPLY_CHARGE_TYPE_FAST); - power_supply_changed(&chip->dc_psy); chip->ext_charging = true; chip->ext_charge_done = false; bms_notify_check(chip); - /* Start BMS */ + /* + * since we wont get a fastchg irq from external charger + * use eoc worker to detect end of charging + */ schedule_delayed_work(&chip->eoc_work, delay); wake_lock(&chip->eoc_wake_lock); + if (chip->btc_override) + schedule_delayed_work(&chip->btc_override_work, + round_jiffies_relative(msecs_to_jiffies + (chip->btc_delay_ms))); /* Update battery charging LEDs and user space battery info */ power_supply_changed(&chip->batt_psy); } @@ -2162,72 +2410,31 @@ static void turn_off_ovp_fet(struct pm8921_chg_chip *chip, u16 ovptestreg) u8 temp; int rc; - rc = pm8xxx_writeb(chip->dev->parent, ovptestreg, 0x30); + rc = pm_chg_write(chip, ovptestreg, 0x30); if (rc) { - pr_err("Failed to write 0x30 to OVP_TEST rc = %d\n", rc); + pr_err("Failed to write 0x30 to ovptestreg rc = %d\n", rc); return; } rc = pm8xxx_readb(chip->dev->parent, ovptestreg, &temp); if (rc) { - pr_err("Failed to read from OVP_TEST rc = %d\n", rc); + pr_err("Failed to read from ovptestreg rc = %d\n", rc); return; } /* set ovp fet disable bit and the write bit */ temp |= 0x81; - rc = pm8xxx_writeb(chip->dev->parent, ovptestreg, temp); + rc = pm_chg_write(chip, ovptestreg, temp); if (rc) { - pr_err("Failed to write 0x%x OVP_TEST rc=%d\n", temp, rc); + pr_err("Failed to write 0x%x ovptestreg rc=%d\n", temp, rc); return; } } -static int pm8921_battery_gauge_alarm_notify(struct notifier_block *nb, - unsigned long status, void *unused) -{ - int rc, fsm_state; - - pr_info("status: %lu\n", status); - - /* Check if called before init */ - - switch (status) { - case 0: - pr_err("spurious interrupt\n"); - break; - /* expected case - trip of low threshold */ - case 1: - if (!the_chip) { - pr_err("not initialized\n"); - return -EINVAL; - } - - fsm_state = pm_chg_get_fsm_state(the_chip); - the_chip->disable_hw_clock_switching = 1; - - rc = pm8xxx_batt_alarm_disable( - PM8XXX_BATT_ALARM_UPPER_COMPARATOR); - if (!rc) - rc = pm8xxx_batt_alarm_disable( - PM8XXX_BATT_ALARM_LOWER_COMPARATOR); - if (rc) - pr_err("unable to set alarm state rc=%d\n", rc); - break; - case 2: - pr_err("trip of high threshold\n"); - break; - default: - pr_err("error received\n"); - }; - - return 0; -} - static void turn_on_ovp_fet(struct pm8921_chg_chip *chip, u16 ovptestreg) { u8 temp; int rc; - rc = pm8xxx_writeb(chip->dev->parent, ovptestreg, 0x30); + rc = pm_chg_write(chip, ovptestreg, 0x30); if (rc) { pr_err("Failed to write 0x30 to OVP_TEST rc = %d\n", rc); return; @@ -2240,7 +2447,7 @@ static void turn_on_ovp_fet(struct pm8921_chg_chip *chip, u16 ovptestreg) /* unset ovp fet disable bit and set the write bit */ temp &= 0xFE; temp |= 0x80; - rc = pm8xxx_writeb(chip->dev->parent, ovptestreg, temp); + rc = pm_chg_write(chip, ovptestreg, temp); if (rc) { pr_err("Failed to write 0x%x to OVP_TEST rc = %d\n", temp, rc); @@ -2308,7 +2515,11 @@ static void unplug_ovp_fet_open(struct pm8921_chg_chip *chip) break; } } - pm_chg_masked_write(chip, ovpreg, OVP_DEBOUNCE_TIME, 0x2); + if (pm8xxx_get_version(chip->dev->parent) == PM8XXX_VERSION_8917) + pm_chg_masked_write(chip, ovpreg, OVP_DEBOUNCE_TIME, 0x6); + else + pm_chg_masked_write(chip, ovpreg, OVP_DEBOUNCE_TIME, 0x2); + pr_debug("Exit count=%d chg_gone=%d, active_valid=%d\n", count, chg_gone, active_chg_plugged_in); return; @@ -2337,6 +2548,13 @@ static void decrease_usb_ma_value(int *value) while (!the_chip->iusb_fine_res && i > 0 && (usb_ma_table[i].value & PM8917_IUSB_FINE_RES)) i--; + + if (i < 0) { + pr_err("can't find %dmA in usb_ma_table. Use min.\n", + *value); + i = 0; + } + *value = usb_ma_table[i].usb_ma; } } @@ -2366,15 +2584,25 @@ static void vin_collapse_check_worker(struct work_struct *work) struct pm8921_chg_chip *chip = container_of(dwork, struct pm8921_chg_chip, vin_collapse_check_work); - /* AICL only for wall-chargers */ - if (is_usb_chg_plugged_in(chip) && - usb_target_ma > USB_WALL_THRESHOLD_MA) { + /* + * AICL only for wall-chargers. If the charger appears to be plugged + * back in now, the corresponding unplug must have been because of we + * were trying to draw more current than the charger can support. In + * such a case reset usb current to 500mA and decrease the target. + * The AICL algorithm will step up the current from 500mA to target + */ + if (is_usb_chg_plugged_in(chip) + && usb_target_ma > USB_WALL_THRESHOLD_MA) { /* decrease usb_target_ma */ decrease_usb_ma_value(&usb_target_ma); /* reset here, increase in unplug_check_worker */ __pm8921_charger_vbus_draw(USB_WALL_THRESHOLD_MA); pr_debug("usb_now=%d, usb_target = %d\n", USB_WALL_THRESHOLD_MA, usb_target_ma); + if (!delayed_work_pending(&chip->unplug_check_work)) + schedule_delayed_work(&chip->unplug_check_work, + msecs_to_jiffies + (UNPLUG_CHECK_WAIT_PERIOD_MS)); } else { handle_usb_insertion_removal(chip); } @@ -2392,12 +2620,6 @@ static irqreturn_t usbin_valid_irq_handler(int irq, void *data) return IRQ_HANDLED; } -static irqreturn_t usbin_ov_irq_handler(int irq, void *data) -{ - pr_err("USB OverVoltage\n"); - return IRQ_HANDLED; -} - static irqreturn_t batt_inserted_irq_handler(int irq, void *data) { struct pm8921_chg_chip *chip = data; @@ -2442,18 +2664,6 @@ static irqreturn_t vbatdet_low_irq_handler(int irq, void *data) return IRQ_HANDLED; } -static irqreturn_t usbin_uv_irq_handler(int irq, void *data) -{ - pr_err("USB UnderVoltage\n"); - return IRQ_HANDLED; -} - -static irqreturn_t vbat_ov_irq_handler(int irq, void *data) -{ - pr_debug("fsm_state=%d\n", pm_chg_get_fsm_state(data)); - return IRQ_HANDLED; -} - static irqreturn_t chgwdog_irq_handler(int irq, void *data) { pr_debug("fsm_state=%d\n", pm_chg_get_fsm_state(data)); @@ -2500,9 +2710,11 @@ static irqreturn_t chgfail_irq_handler(int irq, void *data) struct pm8921_chg_chip *chip = data; int ret; - ret = pm_chg_failed_clear(chip, 1); - if (ret) - pr_err("Failed to write CHG_FAILED_CLEAR bit\n"); + if (!chip->stop_chg_upon_expiry) { + ret = pm_chg_failed_clear(chip, 1); + if (ret) + pr_err("Failed to write CHG_FAILED_CLEAR bit\n"); + } pr_err("batt_present = %d, batt_temp_ok = %d, state_changed_to=%d\n", get_prop_batt_present(chip), @@ -2529,29 +2741,49 @@ static irqreturn_t chgstate_irq_handler(int irq, void *data) return IRQ_HANDLED; } -static int param_vin_disable_counter = 5; -module_param(param_vin_disable_counter, int, 0644); +enum { + PON_TIME_25NS = 0x04, + PON_TIME_50NS = 0x08, + PON_TIME_100NS = 0x0C, +}; -static void attempt_reverse_boost_fix(struct pm8921_chg_chip *chip, - int count, int usb_ma) +static void set_min_pon_time(struct pm8921_chg_chip *chip, int pon_time_ns) { - if (usb_ma) - __pm8921_charger_vbus_draw(500); - pr_debug("count = %d iusb=500mA\n", count); - disable_input_voltage_regulation(chip); - pr_debug("count = %d disable_input_regulation\n", count); + u8 temp; + int rc; - msleep(20); + rc = pm_chg_write(chip, CHG_BUCK_CTRL_TEST3, 0x40); + if (rc) { + pr_err("Failed to write 0x70 to CTRL_TEST3 rc = %d\n", rc); + return; + } + rc = pm8xxx_readb(chip->dev->parent, CHG_BUCK_CTRL_TEST3, &temp); + if (rc) { + pr_err("Failed to read CTRL_TEST3 rc = %d\n", rc); + return; + } + /* clear the min pon time select bit */ + temp &= 0xF3; + /* set the pon time */ + temp |= (u8)pon_time_ns; + /* write enable bank 4 */ + temp |= 0x80; + rc = pm_chg_write(chip, CHG_BUCK_CTRL_TEST3, temp); + if (rc) { + pr_err("Failed to write 0x%x to CTRL_TEST3 rc=%d\n", temp, rc); + return; + } +} - pr_debug("count = %d end sleep 20ms chg_gone=%d, usb_valid = %d\n", - count, - pm_chg_get_rt_status(chip, CHG_GONE_IRQ), - is_usb_chg_plugged_in(chip)); - pr_debug("count = %d restoring input regulation and usb_ma = %d\n", - count, usb_ma); - enable_input_voltage_regulation(chip); - if (usb_ma) - __pm8921_charger_vbus_draw(usb_ma); +static void attempt_reverse_boost_fix(struct pm8921_chg_chip *chip) +{ + pr_debug("Start\n"); + set_min_pon_time(chip, PON_TIME_100NS); + pm_chg_vinmin_set(chip, chip->vin_min + 200); + msleep(250); + pm_chg_vinmin_set(chip, chip->vin_min); + set_min_pon_time(chip, PON_TIME_25NS); + pr_debug("End\n"); } #define VIN_ACTIVE_BIT BIT(0) @@ -2562,19 +2794,18 @@ static void unplug_check_worker(struct work_struct *work) struct delayed_work *dwork = to_delayed_work(work); struct pm8921_chg_chip *chip = container_of(dwork, struct pm8921_chg_chip, unplug_check_work); - u8 reg_loop, active_path; + u8 reg_loop = 0, active_path; int rc, ibat, active_chg_plugged_in, usb_ma; int chg_gone = 0; - - reg_loop = 0; + bool ramp = false; rc = pm8xxx_readb(chip->dev->parent, PBL_ACCESS1, &active_path); if (rc) { pr_err("Failed to read PBL_ACCESS1 rc=%d\n", rc); return; } - chip->active_path = active_path; + chip->active_path = active_path; active_chg_plugged_in = is_active_chg_plugged_in(chip, active_path); pr_debug("active_path = 0x%x, active_chg_plugged_in = %d\n", active_path, active_chg_plugged_in); @@ -2582,11 +2813,6 @@ static void unplug_check_worker(struct work_struct *work) pr_debug("USB charger active\n"); pm_chg_iusbmax_get(chip, &usb_ma); - if (usb_ma == 500 && !usb_target_ma) { - pr_debug("Stopping Unplug Check Worker USB == 500mA\n"); - disable_input_voltage_regulation(chip); - return; - } if (usb_ma <= 100) { pr_debug( @@ -2596,10 +2822,6 @@ static void unplug_check_worker(struct work_struct *work) } } else if (active_path & DC_ACTIVE_BIT) { pr_debug("DC charger active\n"); - /* Some board designs are not prone to reverse boost on DC - * charging path */ - if (!chip->dc_unplug_check) - return; } else { /* No charger active */ if (!(is_usb_chg_plugged_in(chip) @@ -2610,15 +2832,35 @@ static void unplug_check_worker(struct work_struct *work) pm_chg_get_fsm_state(chip), get_prop_batt_current(chip) ); + if (chip->lockup_lpm_wrkarnd) { + rc = pm8921_apply_19p2mhz_kickstart(chip); + if (rc) + pr_err("Failed kickstart rc=%d\n", rc); + + /* + * Make sure kickstart happens at least 200 ms + * after charger has been removed. + */ + if (chip->final_kickstart) { + chip->final_kickstart = false; + goto check_again_later; + } + } + return; + } else { + goto check_again_later; } - return; } - if (active_path & USB_ACTIVE_BIT) { + chip->final_kickstart = true; + + /* AICL only for usb wall charger */ + if ((active_path & USB_ACTIVE_BIT) && usb_target_ma > 0) { reg_loop = pm_chg_get_regulation_loop(chip); pr_debug("reg_loop=0x%x usb_ma = %d\n", reg_loop, usb_ma); if ((reg_loop & VIN_ACTIVE_BIT) && - (usb_ma > USB_WALL_THRESHOLD_MA)) { + (usb_ma > USB_WALL_THRESHOLD_MA) + && !charging_disabled) { decrease_usb_ma_value(&usb_ma); usb_target_ma = usb_ma; /* end AICL here */ @@ -2633,28 +2875,17 @@ static void unplug_check_worker(struct work_struct *work) ibat = get_prop_batt_current(chip); if (reg_loop & VIN_ACTIVE_BIT) { - - pr_debug("ibat = %d fsm = %d reg_loop = 0x%x\n", - ibat, pm_chg_get_fsm_state(chip), reg_loop); if (ibat > 0) { - int count = 0; - - while (count++ < param_vin_disable_counter - && active_chg_plugged_in == 1) { - if (active_path & USB_ACTIVE_BIT) - attempt_reverse_boost_fix(chip, - count, usb_ma); - else - attempt_reverse_boost_fix(chip, - count, 0); - /* after reverse boost fix check if the active - * charger was detected as removed */ - active_chg_plugged_in - = is_active_chg_plugged_in(chip, - active_path); - pr_debug("active_chg_plugged_in = %d\n", - active_chg_plugged_in); - } + pr_debug("revboost ibat = %d fsm = %d loop = 0x%x\n", + ibat, pm_chg_get_fsm_state(chip), reg_loop); + attempt_reverse_boost_fix(chip); + /* after reverse boost fix check if the active + * charger was detected as removed */ + active_chg_plugged_in + = is_active_chg_plugged_in(chip, + active_path); + pr_debug("revboost post: active_chg_plugged_in = %d\n", + active_chg_plugged_in); } } @@ -2669,22 +2900,30 @@ static void unplug_check_worker(struct work_struct *work) unplug_ovp_fet_open(chip); } - if (!(reg_loop & VIN_ACTIVE_BIT) && (active_path & USB_ACTIVE_BIT)) { + /* AICL only for usb wall charger */ + if (!(reg_loop & VIN_ACTIVE_BIT) && (active_path & USB_ACTIVE_BIT) + && usb_target_ma > 0 + && !charging_disabled) { /* only increase iusb_max if vin loop not active */ if (usb_ma < usb_target_ma) { increase_usb_ma_value(&usb_ma); __pm8921_charger_vbus_draw(usb_ma); pr_debug("usb_now=%d, usb_target = %d\n", usb_ma, usb_target_ma); + ramp = true; } else { usb_target_ma = usb_ma; } } check_again_later: + pr_debug("ramp: %d\n", ramp); /* schedule to check again later */ - schedule_delayed_work(&chip->unplug_check_work, - round_jiffies_relative(msecs_to_jiffies - (UNPLUG_CHECK_WAIT_PERIOD_MS))); + if (ramp) + schedule_delayed_work(&chip->unplug_check_work, + msecs_to_jiffies(UNPLUG_CHECK_RAMP_MS)); + else + schedule_delayed_work(&chip->unplug_check_work, + msecs_to_jiffies(UNPLUG_CHECK_WAIT_PERIOD_MS)); } static irqreturn_t loop_change_irq_handler(int irq, void *data) @@ -2698,6 +2937,30 @@ static irqreturn_t loop_change_irq_handler(int irq, void *data) return IRQ_HANDLED; } +struct ibatmax_max_adj_entry { + int ibat_max_ma; + int max_adj_ma; +}; + +static struct ibatmax_max_adj_entry ibatmax_adj_table[] = { + {975, 300}, + {1475, 150}, + {1975, 200}, + {2475, 250}, +}; + +static int find_ibat_max_adj_ma(int ibat_target_ma) +{ + int i = 0; + + for (i = ARRAY_SIZE(ibatmax_adj_table) - 1; i >= 0; i--) { + if (ibat_target_ma <= ibatmax_adj_table[i].ibat_max_ma) + break; + } + + return ibatmax_adj_table[i].max_adj_ma; +} + static irqreturn_t fastchg_irq_handler(int irq, void *data) { struct pm8921_chg_chip *chip = data; @@ -2710,6 +2973,13 @@ static irqreturn_t fastchg_irq_handler(int irq, void *data) round_jiffies_relative(msecs_to_jiffies (EOC_CHECK_PERIOD_MS))); } + if (high_transition + && chip->btc_override + && !delayed_work_pending(&chip->btc_override_work)) { + schedule_delayed_work(&chip->btc_override_work, + round_jiffies_relative(msecs_to_jiffies + (chip->btc_delay_ms))); + } power_supply_changed(&chip->batt_psy); bms_notify_check(chip); return IRQ_HANDLED; @@ -2853,14 +3123,37 @@ static irqreturn_t dcin_valid_irq_handler(int irq, void *data) struct pm8921_chg_chip *chip = data; int dc_present; + pm_chg_failed_clear(chip, 1); dc_present = pm_chg_get_rt_status(chip, DCIN_VALID_IRQ); - if (chip->ext_psy) - power_supply_set_online(chip->ext_psy, dc_present); - chip->dc_present = dc_present; + + if (chip->dc_present ^ dc_present) + pm8921_bms_calibrate_hkadc(); + if (dc_present) - handle_start_ext_chg(chip); + pm8921_chg_enable_irq(chip, CHG_GONE_IRQ); else - handle_stop_ext_chg(chip); + pm8921_chg_disable_irq(chip, CHG_GONE_IRQ); + + chip->dc_present = dc_present; + + if (chip->ext_psy) { + if (dc_present) + handle_start_ext_chg(chip); + else + handle_stop_ext_chg(chip); + } else { + if (chip->lockup_lpm_wrkarnd) + /* if no external supply call bypass debounce here */ + pm8921_chg_bypass_bat_gone_debounce(chip, + is_chg_on_bat(chip)); + + if (dc_present) + schedule_delayed_work(&chip->unplug_check_work, + msecs_to_jiffies(UNPLUG_CHECK_WAIT_PERIOD_MS)); + power_supply_changed(&chip->dc_psy); + } + + power_supply_changed(&chip->batt_psy); return IRQ_HANDLED; } @@ -2923,7 +3216,6 @@ static void update_heartbeat(struct work_struct *work) struct pm8921_chg_chip *chip = container_of(dwork, struct pm8921_chg_chip, update_heartbeat_work); - pm_chg_failed_clear(chip, 1); power_supply_changed(&chip->batt_psy); if (chip->recent_reported_soc <= 20) schedule_delayed_work(&chip->update_heartbeat_work, @@ -2943,12 +3235,10 @@ static int ichg_threshold_ua = -400000; module_param(ichg_threshold_ua, int, 0644); #define PM8921_CHG_VDDMAX_RES_MV 10 -static void adjust_vdd_max_for_fastchg(struct pm8921_chg_chip *chip) +static void adjust_vdd_max_for_fastchg(struct pm8921_chg_chip *chip, + int vbat_batt_terminal_uv) { - int ichg_meas_ua, vbat_uv; - int ichg_meas_ma; int adj_vdd_max_mv, programmed_vdd_max; - int vbat_batt_terminal_uv; int vbat_batt_terminal_mv; int reg_loop; int delta_mv = 0; @@ -2970,18 +3260,6 @@ static void adjust_vdd_max_for_fastchg(struct pm8921_chg_chip *chip) reg_loop); return; } - - pm8921_bms_get_simultaneous_battery_voltage_and_current(&ichg_meas_ua, - &vbat_uv); - if (ichg_meas_ua >= 0) { - pr_debug("Exiting ichg_meas_ua = %d > 0\n", ichg_meas_ua); - return; - } - - ichg_meas_ma = ichg_meas_ua / 1000; - - /* rconn_mohm is in milliOhms */ - vbat_batt_terminal_uv = vbat_uv + ichg_meas_ma * the_chip->rconn_mohm; vbat_batt_terminal_mv = vbat_batt_terminal_uv/1000; pm_chg_vddmax_get(the_chip, &programmed_vdd_max); @@ -3009,6 +3287,95 @@ static void adjust_vdd_max_for_fastchg(struct pm8921_chg_chip *chip) pm_chg_vddmax_set(chip, adj_vdd_max_mv); } +static void set_appropriate_vbatdet(struct pm8921_chg_chip *chip) +{ + if (chip->is_bat_cool) + pm_chg_vbatdet_set(the_chip, + the_chip->cool_bat_voltage + - the_chip->resume_voltage_delta); + else if (chip->is_bat_warm) + pm_chg_vbatdet_set(the_chip, + the_chip->warm_bat_voltage + - the_chip->resume_voltage_delta); + else + pm_chg_vbatdet_set(the_chip, + the_chip->max_voltage_mv + - the_chip->resume_voltage_delta); +} + +static void set_appropriate_battery_current(struct pm8921_chg_chip *chip) +{ + unsigned int chg_current = chip->max_bat_chg_current; + + if (chip->is_bat_cool) + chg_current = min(chg_current, chip->cool_bat_chg_current); + + if (chip->is_bat_warm) + chg_current = min(chg_current, chip->warm_bat_chg_current); + + if (thermal_mitigation != 0 && chip->thermal_mitigation) + chg_current = min(chg_current, + chip->thermal_mitigation[thermal_mitigation]); + + pm_chg_ibatmax_set(the_chip, chg_current); +} + +#define TEMP_HYSTERISIS_DECIDEGC 20 +static void battery_cool(bool enter) +{ + pr_debug("enter = %d\n", enter); + if (enter == the_chip->is_bat_cool) + return; + the_chip->is_bat_cool = enter; + if (enter) + pm_chg_vddmax_set(the_chip, the_chip->cool_bat_voltage); + else + pm_chg_vddmax_set(the_chip, the_chip->max_voltage_mv); + set_appropriate_battery_current(the_chip); + set_appropriate_vbatdet(the_chip); +} + +static void battery_warm(bool enter) +{ + pr_debug("enter = %d\n", enter); + if (enter == the_chip->is_bat_warm) + return; + the_chip->is_bat_warm = enter; + if (enter) + pm_chg_vddmax_set(the_chip, the_chip->warm_bat_voltage); + else + pm_chg_vddmax_set(the_chip, the_chip->max_voltage_mv); + + set_appropriate_battery_current(the_chip); + set_appropriate_vbatdet(the_chip); +} + +static void check_temp_thresholds(struct pm8921_chg_chip *chip) +{ + int temp = 0; + + temp = get_prop_batt_temp(chip); + pr_debug("temp = %d, warm_thr_temp = %d, cool_thr_temp = %d\n", + temp, chip->warm_temp_dc, + chip->cool_temp_dc); + + if (chip->warm_temp_dc != INT_MIN) { + if (chip->is_bat_warm + && temp < chip->warm_temp_dc - TEMP_HYSTERISIS_DECIDEGC) + battery_warm(false); + else if (!chip->is_bat_warm && temp >= chip->warm_temp_dc) + battery_warm(true); + } + + if (chip->cool_temp_dc != INT_MIN) { + if (chip->is_bat_cool + && temp > chip->cool_temp_dc + TEMP_HYSTERISIS_DECIDEGC) + battery_cool(false); + else if (!chip->is_bat_cool && temp <= chip->cool_temp_dc) + battery_cool(true); + } +} + enum { CHG_IN_PROGRESS, CHG_NOT_IN_PROGRESS, @@ -3017,10 +3384,10 @@ enum { #define VBAT_TOLERANCE_MV 70 #define CHG_DISABLE_MSLEEP 100 -static int is_charging_finished(struct pm8921_chg_chip *chip) +static int is_charging_finished(struct pm8921_chg_chip *chip, + int vbat_batt_terminal_uv, int ichg_meas_ma) { - int vbat_meas_uv, vbat_meas_mv, vbat_programmed, vbatdet_low; - int ichg_meas_ma, iterm_programmed; + int vbat_programmed, iterm_programmed, vbat_intended; int regulation_loop, fast_chg, vcp; int rc; static int last_vbat_programmed = -EINVAL; @@ -3037,30 +3404,19 @@ static int is_charging_finished(struct pm8921_chg_chip *chip) if (vcp == 1) return CHG_IN_PROGRESS; - vbatdet_low = pm_chg_get_rt_status(chip, VBATDET_LOW_IRQ); - pr_debug("vbatdet_low = %d\n", vbatdet_low); - if (vbatdet_low == 1) - return CHG_IN_PROGRESS; - /* reset count if battery is hot/cold */ rc = pm_chg_get_rt_status(chip, BAT_TEMP_OK_IRQ); pr_debug("batt_temp_ok = %d\n", rc); if (rc == 0) return CHG_IN_PROGRESS; - /* reset count if battery voltage is less than vddmax */ - vbat_meas_uv = get_prop_battery_uvolts(chip); - if (vbat_meas_uv < 0) - return CHG_IN_PROGRESS; - vbat_meas_mv = vbat_meas_uv / 1000; - rc = pm_chg_vddmax_get(chip, &vbat_programmed); if (rc) { pr_err("couldnt read vddmax rc = %d\n", rc); return CHG_IN_PROGRESS; } - pr_debug("vddmax = %d vbat_meas_mv=%d\n", - vbat_programmed, vbat_meas_mv); + pr_debug("vddmax = %d vbat_batt_terminal_uv=%d\n", + vbat_programmed, vbat_batt_terminal_uv); if (last_vbat_programmed == -EINVAL) last_vbat_programmed = vbat_programmed; @@ -3072,6 +3428,20 @@ static int is_charging_finished(struct pm8921_chg_chip *chip) return CHG_IN_PROGRESS; } + if (chip->is_bat_cool) + vbat_intended = chip->cool_bat_voltage; + else if (chip->is_bat_warm) + vbat_intended = chip->warm_bat_voltage; + else + vbat_intended = chip->max_voltage_mv; + + if (vbat_batt_terminal_uv / 1000 < vbat_intended) { + pr_debug("terminal_uv:%d < vbat_intended:%d.\n", + vbat_batt_terminal_uv, + vbat_intended); + return CHG_IN_PROGRESS; + } + regulation_loop = pm_chg_get_regulation_loop(chip); if (regulation_loop < 0) { pr_err("couldnt read the regulation loop err=%d\n", @@ -3091,7 +3461,6 @@ static int is_charging_finished(struct pm8921_chg_chip *chip) return CHG_IN_PROGRESS; } - ichg_meas_ma = (get_prop_batt_current(chip)) / 1000; pr_debug("iterm_programmed = %d ichg_meas_ma=%d\n", iterm_programmed, ichg_meas_ma); /* @@ -3107,6 +3476,155 @@ static int is_charging_finished(struct pm8921_chg_chip *chip) return CHG_FINISHED; } +#define COMP_OVERRIDE_HOT_BANK 6 +#define COMP_OVERRIDE_COLD_BANK 7 +#define COMP_OVERRIDE_BIT BIT(1) +static int pm_chg_override_cold(struct pm8921_chg_chip *chip, int flag) +{ + u8 val; + int rc = 0; + + val = 0x80 | COMP_OVERRIDE_COLD_BANK << 2 | COMP_OVERRIDE_BIT; + + if (flag) + val |= 0x01; + + rc = pm_chg_write(chip, COMPARATOR_OVERRIDE, val); + if (rc < 0) + pr_err("Could not write 0x%x to override rc = %d\n", val, rc); + + pr_debug("btc cold = %d val = 0x%x\n", flag, val); + return rc; +} + +static int pm_chg_override_hot(struct pm8921_chg_chip *chip, int flag) +{ + u8 val; + int rc = 0; + + val = 0x80 | COMP_OVERRIDE_HOT_BANK << 2 | COMP_OVERRIDE_BIT; + + if (flag) + val |= 0x01; + + rc = pm_chg_write(chip, COMPARATOR_OVERRIDE, val); + if (rc < 0) + pr_err("Could not write 0x%x to override rc = %d\n", val, rc); + + pr_debug("btc hot = %d val = 0x%x\n", flag, val); + return rc; +} + +static void __devinit pm8921_chg_btc_override_init(struct pm8921_chg_chip *chip) +{ + int rc = 0; + u8 reg; + u8 val; + + val = COMP_OVERRIDE_HOT_BANK << 2; + rc = pm_chg_write(chip, COMPARATOR_OVERRIDE, val); + if (rc < 0) { + pr_err("Could not write 0x%x to override rc = %d\n", val, rc); + goto cold_init; + } + rc = pm8xxx_readb(chip->dev->parent, COMPARATOR_OVERRIDE, ®); + if (rc < 0) { + pr_err("Could not read bank %d of override rc = %d\n", + COMP_OVERRIDE_HOT_BANK, rc); + goto cold_init; + } + if ((reg & COMP_OVERRIDE_BIT) != COMP_OVERRIDE_BIT) { + /* for now override it as not hot */ + rc = pm_chg_override_hot(chip, 0); + if (rc < 0) + pr_err("Could not override hot rc = %d\n", rc); + } + +cold_init: + val = COMP_OVERRIDE_COLD_BANK << 2; + rc = pm_chg_write(chip, COMPARATOR_OVERRIDE, val); + if (rc < 0) { + pr_err("Could not write 0x%x to override rc = %d\n", val, rc); + return; + } + rc = pm8xxx_readb(chip->dev->parent, COMPARATOR_OVERRIDE, ®); + if (rc < 0) { + pr_err("Could not read bank %d of override rc = %d\n", + COMP_OVERRIDE_COLD_BANK, rc); + return; + } + if ((reg & COMP_OVERRIDE_BIT) != COMP_OVERRIDE_BIT) { + /* for now override it as not cold */ + rc = pm_chg_override_cold(chip, 0); + if (rc < 0) + pr_err("Could not override cold rc = %d\n", rc); + } +} + +static void btc_override_worker(struct work_struct *work) +{ + int decidegc; + int temp; + int rc = 0; + struct delayed_work *dwork = to_delayed_work(work); + struct pm8921_chg_chip *chip = container_of(dwork, + struct pm8921_chg_chip, btc_override_work); + + if (!chip->btc_override) { + pr_err("called when not enabled\n"); + return; + } + + decidegc = get_prop_batt_temp(chip); + + pr_debug("temp=%d\n", decidegc); + + temp = pm_chg_get_rt_status(chip, BATTTEMP_HOT_IRQ); + if (temp) { + if (decidegc < chip->btc_override_hot_decidegc) + /* stop forcing batt hot */ + rc = pm_chg_override_hot(chip, 0); + if (rc) + pr_err("Couldnt write 0 to hot comp\n"); + } else { + if (decidegc >= chip->btc_override_hot_decidegc) + /* start forcing batt hot */ + rc = pm_chg_override_hot(chip, 1); + if (rc && chip->btc_panic_if_cant_stop_chg) + panic("Couldnt override comps to stop chg\n"); + } + + temp = pm_chg_get_rt_status(chip, BATTTEMP_COLD_IRQ); + if (temp) { + if (decidegc > chip->btc_override_cold_decidegc) + /* stop forcing batt cold */ + rc = pm_chg_override_cold(chip, 0); + if (rc) + pr_err("Couldnt write 0 to cold comp\n"); + } else { + if (decidegc <= chip->btc_override_cold_decidegc) + /* start forcing batt cold */ + rc = pm_chg_override_cold(chip, 1); + if (rc && chip->btc_panic_if_cant_stop_chg) + panic("Couldnt override comps to stop chg\n"); + } + + if ((is_dc_chg_plugged_in(the_chip) || is_usb_chg_plugged_in(the_chip)) + && get_prop_batt_status(chip) != POWER_SUPPLY_STATUS_FULL) { + schedule_delayed_work(&chip->btc_override_work, + round_jiffies_relative(msecs_to_jiffies + (chip->btc_delay_ms))); + return; + } + + rc = pm_chg_override_hot(chip, 0); + if (rc) + pr_err("Couldnt write 0 to hot comp\n"); + rc = pm_chg_override_cold(chip, 0); + if (rc) + pr_err("Couldnt write 0 to cold comp\n"); +} + /** * eoc_worker - internal function to check if battery EOC * has happened @@ -3126,26 +3644,24 @@ static void eoc_worker(struct work_struct *work) struct pm8921_chg_chip, eoc_work); static int count; int end; + int vbat_meas_uv, vbat_meas_mv; + int ichg_meas_ua, ichg_meas_ma; + int vbat_batt_terminal_uv; - pm_chg_failed_clear(chip, 1); - end = is_charging_finished(chip); + pm8921_bms_get_simultaneous_battery_voltage_and_current( + &ichg_meas_ua, &vbat_meas_uv); + vbat_meas_mv = vbat_meas_uv / 1000; + /* rconn_mohm is in milliOhms */ + ichg_meas_ma = ichg_meas_ua / 1000; + vbat_batt_terminal_uv = vbat_meas_uv + + ichg_meas_ma + * the_chip->rconn_mohm; + + end = is_charging_finished(chip, vbat_batt_terminal_uv, ichg_meas_ma); if (end == CHG_NOT_IN_PROGRESS) { count = 0; - wake_unlock(&chip->eoc_wake_lock); - return; - } - - /* If the disable hw clock switching - * flag was set it can now be unset. Also, re-enable - * the battery alarm to set the flag again when needed - */ - if (chip->disable_hw_clock_switching) { - /* Unset the hw clock switching flag */ - chip->disable_hw_clock_switching = 0; - - if (pm8921_charger_enable_batt_alarm(chip)) - pr_err("couldn't set up batt alarm!\n"); + goto eoc_worker_stop; } if (end == CHG_FINISHED) { @@ -3169,120 +3685,20 @@ static void eoc_worker(struct work_struct *work) chip->bms_notify.is_battery_full = 1; /* declare end of charging by invoking chgdone interrupt */ chgdone_irq_handler(chip->pmic_chg_irq[CHGDONE_IRQ], chip); - wake_unlock(&chip->eoc_wake_lock); } else { - adjust_vdd_max_for_fastchg(chip); + check_temp_thresholds(chip); + adjust_vdd_max_for_fastchg(chip, vbat_batt_terminal_uv); pr_debug("EOC count = %d\n", count); schedule_delayed_work(&chip->eoc_work, round_jiffies_relative(msecs_to_jiffies (EOC_CHECK_PERIOD_MS))); - } -} - -static void btm_configure_work(struct work_struct *work) -{ - int rc; - - rc = pm8xxx_adc_btm_configure(&btm_config); - if (rc) - pr_err("failed to configure btm rc=%d", rc); -} - -DECLARE_WORK(btm_config_work, btm_configure_work); - -static void set_appropriate_battery_current(struct pm8921_chg_chip *chip) -{ - unsigned int chg_current = chip->max_bat_chg_current; - - if (chip->is_bat_cool) - chg_current = min(chg_current, chip->cool_bat_chg_current); - - if (chip->is_bat_warm) - chg_current = min(chg_current, chip->warm_bat_chg_current); - - if (thermal_mitigation != 0 && chip->thermal_mitigation) - chg_current = min(chg_current, - chip->thermal_mitigation[thermal_mitigation]); - - pm_chg_ibatmax_set(the_chip, chg_current); -} - -#define TEMP_HYSTERISIS_DEGC 2 -static void battery_cool(bool enter) -{ - pr_debug("enter = %d\n", enter); - if (enter == the_chip->is_bat_cool) return; - the_chip->is_bat_cool = enter; - if (enter) { - btm_config.low_thr_temp = - the_chip->cool_temp_dc + TEMP_HYSTERISIS_DEGC; - set_appropriate_battery_current(the_chip); - pm_chg_vddmax_set(the_chip, the_chip->cool_bat_voltage); - pm_chg_vbatdet_set(the_chip, - the_chip->cool_bat_voltage - - the_chip->resume_voltage_delta); - } else { - btm_config.low_thr_temp = the_chip->cool_temp_dc; - set_appropriate_battery_current(the_chip); - pm_chg_vddmax_set(the_chip, the_chip->max_voltage_mv); - pm_chg_vbatdet_set(the_chip, - the_chip->max_voltage_mv - - the_chip->resume_voltage_delta); } - schedule_work(&btm_config_work); -} -static void battery_warm(bool enter) -{ - pr_debug("enter = %d\n", enter); - if (enter == the_chip->is_bat_warm) - return; - the_chip->is_bat_warm = enter; - if (enter) { - btm_config.high_thr_temp = - the_chip->warm_temp_dc - TEMP_HYSTERISIS_DEGC; - set_appropriate_battery_current(the_chip); - pm_chg_vddmax_set(the_chip, the_chip->warm_bat_voltage); - pm_chg_vbatdet_set(the_chip, - the_chip->warm_bat_voltage - - the_chip->resume_voltage_delta); - } else { - btm_config.high_thr_temp = the_chip->warm_temp_dc; - set_appropriate_battery_current(the_chip); - pm_chg_vddmax_set(the_chip, the_chip->max_voltage_mv); - pm_chg_vbatdet_set(the_chip, - the_chip->max_voltage_mv - - the_chip->resume_voltage_delta); - } - schedule_work(&btm_config_work); -} - -static int configure_btm(struct pm8921_chg_chip *chip) -{ - int rc; - - if (chip->warm_temp_dc != INT_MIN) - btm_config.btm_warm_fn = battery_warm; - else - btm_config.btm_warm_fn = NULL; - - if (chip->cool_temp_dc != INT_MIN) - btm_config.btm_cool_fn = battery_cool; - else - btm_config.btm_cool_fn = NULL; - - btm_config.low_thr_temp = chip->cool_temp_dc; - btm_config.high_thr_temp = chip->warm_temp_dc; - btm_config.interval = chip->temp_check_period; - rc = pm8xxx_adc_btm_configure(&btm_config); - if (rc) - pr_err("failed to configure btm rc = %d\n", rc); - rc = pm8xxx_adc_btm_start(); - if (rc) - pr_err("failed to start btm rc = %d\n", rc); - - return rc; +eoc_worker_stop: + wake_unlock(&chip->eoc_wake_lock); + /* set the vbatdet back, in case it was changed to trigger charging */ + set_appropriate_vbatdet(chip); } /** @@ -3413,10 +3829,9 @@ static void __devinit determine_initial_state(struct pm8921_chg_chip *chip) chip->usb_present = !!is_usb_chg_plugged_in(chip); notify_usb_of_the_plugin_event(chip->usb_present); - if (chip->usb_present) { + if (chip->usb_present || chip->dc_present) { schedule_delayed_work(&chip->unplug_check_work, - round_jiffies_relative(msecs_to_jiffies - (UNPLUG_CHECK_WAIT_PERIOD_MS))); + msecs_to_jiffies(UNPLUG_CHECK_WAIT_PERIOD_MS)); pm8921_chg_enable_irq(chip, CHG_GONE_IRQ); } @@ -3424,8 +3839,6 @@ static void __devinit determine_initial_state(struct pm8921_chg_chip *chip) pm8921_chg_enable_irq(chip, USBIN_VALID_IRQ); pm8921_chg_enable_irq(chip, BATT_REMOVED_IRQ); pm8921_chg_enable_irq(chip, BATT_INSERTED_IRQ); - pm8921_chg_enable_irq(chip, USBIN_OV_IRQ); - pm8921_chg_enable_irq(chip, USBIN_UV_IRQ); pm8921_chg_enable_irq(chip, DCIN_OV_IRQ); pm8921_chg_enable_irq(chip, DCIN_UV_IRQ); pm8921_chg_enable_irq(chip, CHGFAIL_IRQ); @@ -3462,6 +3875,12 @@ static void __devinit determine_initial_state(struct pm8921_chg_chip *chip) chip->dc_present, get_prop_batt_present(chip), fsm_state); + + /* Determine which USB trim column to use */ + if (pm8xxx_get_version(chip->dev->parent) == PM8XXX_VERSION_8917) + chip->usb_trim_table = usb_trim_8917_table; + else if (pm8xxx_get_version(chip->dev->parent) == PM8XXX_VERSION_8038) + chip->usb_trim_table = usb_trim_8038_table; } struct pm_chg_irq_init_data { @@ -3481,14 +3900,10 @@ struct pm_chg_irq_init_data { struct pm_chg_irq_init_data chg_irq_data[] = { CHG_IRQ(USBIN_VALID_IRQ, IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, usbin_valid_irq_handler), - CHG_IRQ(USBIN_OV_IRQ, IRQF_TRIGGER_RISING, usbin_ov_irq_handler), CHG_IRQ(BATT_INSERTED_IRQ, IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, batt_inserted_irq_handler), CHG_IRQ(VBATDET_LOW_IRQ, IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, vbatdet_low_irq_handler), - CHG_IRQ(USBIN_UV_IRQ, IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, - usbin_uv_irq_handler), - CHG_IRQ(VBAT_OV_IRQ, IRQF_TRIGGER_RISING, vbat_ov_irq_handler), CHG_IRQ(CHGWDOG_IRQ, IRQF_TRIGGER_RISING, chgwdog_irq_handler), CHG_IRQ(VCP_IRQ, IRQF_TRIGGER_RISING, vcp_irq_handler), CHG_IRQ(ATCDONE_IRQ, IRQF_TRIGGER_RISING, atcdone_irq_handler), @@ -3560,91 +3975,6 @@ err_out: return -EINVAL; } -static void pm8921_chg_force_19p2mhz_clk(struct pm8921_chg_chip *chip) -{ - int err; - u8 temp; - - temp = 0xD1; - err = pm8xxx_writeb(chip->dev->parent, CHG_TEST, temp); - if (err) { - pr_err("Error %d writing %d to addr %d\n", err, temp, CHG_TEST); - return; - } - - temp = 0xD3; - err = pm8xxx_writeb(chip->dev->parent, CHG_TEST, temp); - if (err) { - pr_err("Error %d writing %d to addr %d\n", err, temp, CHG_TEST); - return; - } - - temp = 0xD1; - err = pm8xxx_writeb(chip->dev->parent, CHG_TEST, temp); - if (err) { - pr_err("Error %d writing %d to addr %d\n", err, temp, CHG_TEST); - return; - } - - temp = 0xD5; - err = pm8xxx_writeb(chip->dev->parent, CHG_TEST, temp); - if (err) { - pr_err("Error %d writing %d to addr %d\n", err, temp, CHG_TEST); - return; - } - - udelay(183); - - temp = 0xD1; - err = pm8xxx_writeb(chip->dev->parent, CHG_TEST, temp); - if (err) { - pr_err("Error %d writing %d to addr %d\n", err, temp, CHG_TEST); - return; - } - - temp = 0xD0; - err = pm8xxx_writeb(chip->dev->parent, CHG_TEST, temp); - if (err) { - pr_err("Error %d writing %d to addr %d\n", err, temp, CHG_TEST); - return; - } - udelay(32); - - temp = 0xD1; - err = pm8xxx_writeb(chip->dev->parent, CHG_TEST, temp); - if (err) { - pr_err("Error %d writing %d to addr %d\n", err, temp, CHG_TEST); - return; - } - - temp = 0xD3; - err = pm8xxx_writeb(chip->dev->parent, CHG_TEST, temp); - if (err) { - pr_err("Error %d writing %d to addr %d\n", err, temp, CHG_TEST); - return; - } -} - -static void pm8921_chg_set_hw_clk_switching(struct pm8921_chg_chip *chip) -{ - int err; - u8 temp; - - temp = 0xD1; - err = pm8xxx_writeb(chip->dev->parent, CHG_TEST, temp); - if (err) { - pr_err("Error %d writing %d to addr %d\n", err, temp, CHG_TEST); - return; - } - - temp = 0xD0; - err = pm8xxx_writeb(chip->dev->parent, CHG_TEST, temp); - if (err) { - pr_err("Error %d writing %d to addr %d\n", err, temp, CHG_TEST); - return; - } -} - #define VREF_BATT_THERM_FORCE_ON BIT(7) static void detect_battery_removal(struct pm8921_chg_chip *chip) { @@ -3668,13 +3998,20 @@ static void detect_battery_removal(struct pm8921_chg_chip *chip) #define CHG_VCP_EN BIT(0) #define CHG_BAT_TEMP_DIS_BIT BIT(2) #define SAFE_CURRENT_MA 1500 +#define PM_SUB_REV 0x001 +#define MIN_CHARGE_CURRENT_MA 350 +#define DEFAULT_SAFETY_MINUTES 500 static int __devinit pm8921_chg_hw_init(struct pm8921_chg_chip *chip) { - int rc; - int vdd_safe; + u8 subrev; + int rc, vdd_safe, fcc_uah, safety_time = DEFAULT_SAFETY_MINUTES; - /* forcing 19p2mhz before accessing any charger registers */ - pm8921_chg_force_19p2mhz_clk(chip); + spin_lock_init(&lpm_lock); + rc = pm8921_apply_19p2mhz_kickstart(chip); + if (rc) { + pr_err("Failed to apply kickstart rc=%d\n", rc); + return rc; + } detect_battery_removal(chip); @@ -3712,7 +4049,11 @@ static int __devinit pm8921_chg_hw_init(struct pm8921_chg_chip *chip) chip->max_voltage_mv, rc); return rc; } - rc = pm_chg_ibatsafe_set(chip, SAFE_CURRENT_MA); + + if (chip->safe_current_ma == 0) + chip->safe_current_ma = SAFE_CURRENT_MA; + + rc = pm_chg_ibatsafe_set(chip, chip->safe_current_ma); if (rc) { pr_err("Failed to set max voltage to %d rc=%d\n", SAFE_CURRENT_MA, rc); @@ -3740,20 +4081,30 @@ static int __devinit pm8921_chg_hw_init(struct pm8921_chg_chip *chip) return rc; } - if (chip->safety_time != 0) { - rc = pm_chg_tchg_max_set(chip, chip->safety_time); - if (rc) { - pr_err("Failed to set max time to %d minutes rc=%d\n", - chip->safety_time, rc); - return rc; - } + fcc_uah = pm8921_bms_get_fcc(); + if (fcc_uah > 0) { + safety_time = div_s64((s64)fcc_uah * 60, + 1000 * MIN_CHARGE_CURRENT_MA); + /* add 20 minutes of buffer time */ + safety_time += 20; + + /* make sure we do not exceed the maximum programmable time */ + if (safety_time > PM8921_CHG_TCHG_MAX) + safety_time = PM8921_CHG_TCHG_MAX; + } + + rc = pm_chg_tchg_max_set(chip, safety_time); + if (rc) { + pr_err("Failed to set max time to %d minutes rc=%d\n", + safety_time, rc); + return rc; } if (chip->ttrkl_time != 0) { rc = pm_chg_ttrkl_max_set(chip, chip->ttrkl_time); if (rc) { pr_err("Failed to set trkl time to %d minutes rc=%d\n", - chip->safety_time, rc); + chip->ttrkl_time, rc); return rc; } } @@ -3782,7 +4133,13 @@ static int __devinit pm8921_chg_hw_init(struct pm8921_chg_chip *chip) return rc; } /* switch to a 3.2Mhz for the buck */ - rc = pm8xxx_writeb(chip->dev->parent, CHG_BUCK_CLOCK_CTRL, 0x15); + if (pm8xxx_get_revision(chip->dev->parent) >= PM8XXX_REVISION_8038_1p0) + rc = pm_chg_write(chip, + CHG_BUCK_CLOCK_CTRL_8038, 0x15); + else + rc = pm_chg_write(chip, + CHG_BUCK_CLOCK_CTRL, 0x15); + if (rc) { pr_err("Failed to switch buck clk rc=%d\n", rc); return rc; @@ -3842,43 +4199,48 @@ static int __devinit pm8921_chg_hw_init(struct pm8921_chg_chip *chip) chip->led_src_config, rc); } - /* Workarounds for die 1.1 and 1.0 */ - if (pm8xxx_get_revision(chip->dev->parent) < PM8XXX_REVISION_8921_2p0) { - pm8xxx_writeb(chip->dev->parent, CHG_BUCK_CTRL_TEST2, 0xF1); - pm8xxx_writeb(chip->dev->parent, CHG_BUCK_CTRL_TEST3, 0xCE); - pm8xxx_writeb(chip->dev->parent, CHG_BUCK_CTRL_TEST3, 0xD8); - - /* software workaround for correct battery_id detection */ - pm8xxx_writeb(chip->dev->parent, PSI_TXRX_SAMPLE_DATA_0, 0xFF); - pm8xxx_writeb(chip->dev->parent, PSI_TXRX_SAMPLE_DATA_1, 0xFF); - pm8xxx_writeb(chip->dev->parent, PSI_TXRX_SAMPLE_DATA_2, 0xFF); - pm8xxx_writeb(chip->dev->parent, PSI_TXRX_SAMPLE_DATA_3, 0xFF); - pm8xxx_writeb(chip->dev->parent, PSI_CONFIG_STATUS, 0x0D); - udelay(100); - pm8xxx_writeb(chip->dev->parent, PSI_CONFIG_STATUS, 0x0C); + /* Workarounds for die 3.0 */ + if (pm8xxx_get_revision(chip->dev->parent) == PM8XXX_REVISION_8921_3p0 + && pm8xxx_get_version(chip->dev->parent) == PM8XXX_VERSION_8921) { + rc = pm8xxx_readb(chip->dev->parent, PM_SUB_REV, &subrev); + if (rc) { + pr_err("read failed: addr=%03X, rc=%d\n", + PM_SUB_REV, rc); + return rc; + } + /* Check if die 3.0.1 is present */ + if (subrev & 0x1) + pm_chg_write(chip, CHG_BUCK_CTRL_TEST3, 0xA4); + else + pm_chg_write(chip, CHG_BUCK_CTRL_TEST3, 0xAC); } - /* Workarounds for die 3.0 */ - if (pm8xxx_get_revision(chip->dev->parent) == PM8XXX_REVISION_8921_3p0) - pm8xxx_writeb(chip->dev->parent, CHG_BUCK_CTRL_TEST3, 0xAC); - - /* Enable isub_fine resolution AICL for PM8917 */ if (pm8xxx_get_version(chip->dev->parent) == PM8XXX_VERSION_8917) { + /* Set PM8917 USB_OVP debounce time to 15 ms */ + rc = pm_chg_masked_write(chip, USB_OVP_CONTROL, + OVP_DEBOUNCE_TIME, 0x6); + if (rc) { + pr_err("Failed to set USB OVP db rc=%d\n", rc); + return rc; + } + + /* Enable isub_fine resolution AICL for PM8917 */ chip->iusb_fine_res = true; - if (chip->uvd_voltage_mv) + if (chip->uvd_voltage_mv) { rc = pm_chg_uvd_threshold_set(chip, chip->uvd_voltage_mv); if (rc) { pr_err("Failed to set UVD threshold %drc=%d\n", chip->uvd_voltage_mv, rc); - return rc; + return rc; + } } } - pm8xxx_writeb(chip->dev->parent, CHG_BUCK_CTRL_TEST3, 0xD9); + pm_chg_write(chip, CHG_BUCK_CTRL_TEST3, 0xD9); /* Disable EOC FSM processing */ - pm8xxx_writeb(chip->dev->parent, CHG_BUCK_CTRL_TEST3, 0x91); + pm_chg_write(chip, CHG_BUCK_CTRL_TEST3, 0x91); rc = pm_chg_masked_write(chip, CHG_CNTRL, VREF_BATT_THERM_FORCE_ON, VREF_BATT_THERM_FORCE_ON); @@ -3897,6 +4259,45 @@ static int __devinit pm8921_chg_hw_init(struct pm8921_chg_chip *chip) return rc; } + if (pm8xxx_get_version(chip->dev->parent) == PM8XXX_VERSION_8921) { + /* Clear kickstart */ + rc = pm8xxx_writeb(chip->dev->parent, CHG_TEST, 0xD0); + if (rc) { + pr_err("Failed to clear kickstart rc=%d\n", rc); + return rc; + } + + /* From here the lpm_workaround will be active */ + chip->lockup_lpm_wrkarnd = true; + + /* Enable LPM */ + pm8921_chg_set_lpm(chip, 1); + } + + if (chip->lockup_lpm_wrkarnd) { + chip->vreg_xoadc = regulator_get(chip->dev, "vreg_xoadc"); + if (IS_ERR(chip->vreg_xoadc)) + return -ENODEV; + + rc = regulator_set_optimum_mode(chip->vreg_xoadc, 10000); + if (rc < 0) { + pr_err("Failed to set configure HPM rc=%d\n", rc); + return rc; + } + + rc = regulator_set_voltage(chip->vreg_xoadc, 1800000, 1800000); + if (rc) { + pr_err("Failed to set L14 voltage rc=%d\n", rc); + return rc; + } + + rc = regulator_enable(chip->vreg_xoadc); + if (rc) { + pr_err("Failed to enable L14 rc=%d\n", rc); + return rc; + } + } + return 0; } @@ -3959,9 +4360,9 @@ static int set_reg(void *data, u64 val) u8 temp; temp = (u8) val; - ret = pm8xxx_writeb(the_chip->dev->parent, addr, temp); + ret = pm_chg_write(the_chip, addr, temp); if (ret) { - pr_err("pm8xxx_writeb to %x value =%d errored = %d\n", + pr_err("pm_chg_write to %x value =%d errored = %d\n", addr, temp, ret); return -EAGAIN; } @@ -3969,6 +4370,81 @@ static int set_reg(void *data, u64 val) } DEFINE_SIMPLE_ATTRIBUTE(reg_fops, get_reg, set_reg, "0x%02llx\n"); +static int reg_loop; +#define MAX_REG_LOOP_CHAR 10 +static int get_reg_loop_param(char *buf, struct kernel_param *kp) +{ + u8 temp; + + if (!the_chip) { + pr_err("called before init\n"); + return -EINVAL; + } + temp = pm_chg_get_regulation_loop(the_chip); + return snprintf(buf, MAX_REG_LOOP_CHAR, "%d", temp); +} +module_param_call(reg_loop, NULL, get_reg_loop_param, + ®_loop, 0644); + +static int max_chg_ma; +#define MAX_MA_CHAR 10 +static int get_max_chg_ma_param(char *buf, struct kernel_param *kp) +{ + if (!the_chip) { + pr_err("called before init\n"); + return -EINVAL; + } + return snprintf(buf, MAX_MA_CHAR, "%d", the_chip->max_bat_chg_current); +} +module_param_call(max_chg_ma, NULL, get_max_chg_ma_param, + &max_chg_ma, 0644); +static int ibatmax_ma; +static int set_ibat_max(const char *val, struct kernel_param *kp) +{ + int rc; + + if (!the_chip) { + pr_err("called before init\n"); + return -EINVAL; + } + + rc = param_set_int(val, kp); + if (rc) { + pr_err("error setting value %d\n", rc); + return rc; + } + + if (abs(ibatmax_ma - the_chip->max_bat_chg_current) + <= the_chip->ibatmax_max_adj_ma) { + rc = pm_chg_ibatmax_set(the_chip, ibatmax_ma); + if (rc) { + pr_err("Failed to set ibatmax rc = %d\n", rc); + return rc; + } + } + + return 0; +} +static int get_ibat_max(char *buf, struct kernel_param *kp) +{ + int ibat_ma; + int rc; + + if (!the_chip) { + pr_err("called before init\n"); + return -EINVAL; + } + + rc = pm_chg_ibatmax_get(the_chip, &ibat_ma); + if (rc) { + pr_err("ibatmax_get error = %d\n", rc); + return rc; + } + + return snprintf(buf, MAX_MA_CHAR, "%d", ibat_ma); +} +module_param_call(ibatmax_ma, set_ibat_max, get_ibat_max, + &ibatmax_ma, 0644); enum { BAT_WARM_ZONE, BAT_COOL_ZONE, @@ -4072,11 +4548,19 @@ static int pm8921_charger_suspend_noirq(struct device *dev) int rc; struct pm8921_chg_chip *chip = dev_get_drvdata(dev); + if (chip->lockup_lpm_wrkarnd) { + rc = regulator_disable(chip->vreg_xoadc); + if (rc) + pr_err("Failed to disable L14 rc=%d\n", rc); + + rc = pm8921_apply_19p2mhz_kickstart(chip); + if (rc) + pr_err("Failed to apply kickstart rc=%d\n", rc); + } + rc = pm_chg_masked_write(chip, CHG_CNTRL, VREF_BATT_THERM_FORCE_ON, 0); if (rc) pr_err("Failed to Force Vref therm off rc=%d\n", rc); - if (!(chip->disable_hw_clock_switching)) - pm8921_chg_set_hw_clk_switching(chip); return 0; } @@ -4085,7 +4569,15 @@ static int pm8921_charger_resume_noirq(struct device *dev) int rc; struct pm8921_chg_chip *chip = dev_get_drvdata(dev); - pm8921_chg_force_19p2mhz_clk(chip); + if (chip->lockup_lpm_wrkarnd) { + rc = regulator_enable(chip->vreg_xoadc); + if (rc) + pr_err("Failed to enable L14 rc=%d\n", rc); + + rc = pm8921_apply_19p2mhz_kickstart(chip); + if (rc) + pr_err("Failed to apply kickstart rc=%d\n", rc); + } rc = pm_chg_masked_write(chip, CHG_CNTRL, VREF_BATT_THERM_FORCE_ON, VREF_BATT_THERM_FORCE_ON); @@ -4096,37 +4588,26 @@ static int pm8921_charger_resume_noirq(struct device *dev) static int pm8921_charger_resume(struct device *dev) { - int rc; struct pm8921_chg_chip *chip = dev_get_drvdata(dev); - if (!(chip->cool_temp_dc == INT_MIN && chip->warm_temp_dc == INT_MIN) - && !(chip->keep_btm_on_suspend)) { - rc = pm8xxx_adc_btm_configure(&btm_config); - if (rc) - pr_err("couldn't reconfigure btm rc=%d\n", rc); - - rc = pm8xxx_adc_btm_start(); - if (rc) - pr_err("couldn't restart btm rc=%d\n", rc); - } if (pm8921_chg_is_enabled(chip, LOOP_CHANGE_IRQ)) { disable_irq_wake(chip->pmic_chg_irq[LOOP_CHANGE_IRQ]); pm8921_chg_disable_irq(chip, LOOP_CHANGE_IRQ); } + + if (chip->btc_override && (is_dc_chg_plugged_in(the_chip) || + is_usb_chg_plugged_in(the_chip))) + schedule_delayed_work(&chip->btc_override_work, 0); + return 0; } static int pm8921_charger_suspend(struct device *dev) { - int rc; struct pm8921_chg_chip *chip = dev_get_drvdata(dev); - if (!(chip->cool_temp_dc == INT_MIN && chip->warm_temp_dc == INT_MIN) - && !(chip->keep_btm_on_suspend)) { - rc = pm8xxx_adc_btm_end(); - if (rc) - pr_err("Failed to disable BTM on suspend rc=%d\n", rc); - } + if (chip->btc_override) + cancel_delayed_work_sync(&chip->btc_override_work); if (is_usb_chg_plugged_in(chip)) { pm8921_chg_enable_irq(chip, LOOP_CHANGE_IRQ); @@ -4155,14 +4636,16 @@ static int __devinit pm8921_charger_probe(struct platform_device *pdev) } chip->dev = &pdev->dev; - chip->safety_time = pdata->safety_time; chip->ttrkl_time = pdata->ttrkl_time; chip->update_time = pdata->update_time; chip->max_voltage_mv = pdata->max_voltage; - chip->alarm_voltage_mv = pdata->alarm_voltage; + chip->alarm_low_mv = pdata->alarm_low_mv; + chip->alarm_high_mv = pdata->alarm_high_mv; chip->min_voltage_mv = pdata->min_voltage; + chip->safe_current_ma = pdata->safe_current_ma; chip->uvd_voltage_mv = pdata->uvd_thresh_voltage; chip->resume_voltage_delta = pdata->resume_voltage_delta; + chip->resume_charge_percent = pdata->resume_charge_percent; chip->term_current = pdata->term_current; chip->vbat_channel = pdata->charger_cdata.vbat_channel; chip->batt_temp_channel = pdata->charger_cdata.batt_temp_channel; @@ -4180,13 +4663,13 @@ static int __devinit pm8921_charger_probe(struct platform_device *pdev) chip->warm_temp_dc = INT_MIN; chip->temp_check_period = pdata->temp_check_period; - chip->dc_unplug_check = pdata->dc_unplug_check; chip->max_bat_chg_current = pdata->max_bat_chg_current; + /* Assign to corresponding module parameter */ + usb_max_current = pdata->usb_max_current; chip->cool_bat_chg_current = pdata->cool_bat_chg_current; chip->warm_bat_chg_current = pdata->warm_bat_chg_current; chip->cool_bat_voltage = pdata->cool_bat_voltage; chip->warm_bat_voltage = pdata->warm_bat_voltage; - chip->keep_btm_on_suspend = pdata->keep_btm_on_suspend; chip->trkl_voltage = pdata->trkl_voltage; chip->weak_voltage = pdata->weak_voltage; chip->trkl_current = pdata->trkl_current; @@ -4200,6 +4683,23 @@ static int __devinit pm8921_charger_probe(struct platform_device *pdev) chip->rconn_mohm = pdata->rconn_mohm; chip->led_src_config = pdata->led_src_config; chip->has_dc_supply = pdata->has_dc_supply; + chip->battery_less_hardware = pdata->battery_less_hardware; + chip->btc_override = pdata->btc_override; + if (chip->btc_override) { + chip->btc_delay_ms = pdata->btc_delay_ms; + chip->btc_override_cold_decidegc + = pdata->btc_override_cold_degc * 10; + chip->btc_override_hot_decidegc + = pdata->btc_override_hot_degc * 10; + chip->btc_panic_if_cant_stop_chg + = pdata->btc_panic_if_cant_stop_chg; + } + + if (chip->battery_less_hardware) + charging_disabled = 1; + + chip->ibatmax_max_adj_ma = find_ibat_max_adj_ma( + chip->max_bat_chg_current); rc = pm8921_chg_hw_init(chip); if (rc) { @@ -4207,6 +4707,11 @@ static int __devinit pm8921_charger_probe(struct platform_device *pdev) goto free_chip; } + if (chip->btc_override) + pm8921_chg_btc_override_init(chip); + + chip->stop_chg_upon_expiry = pdata->stop_chg_upon_expiry; + chip->usb_psy.name = "usb", chip->usb_psy.type = POWER_SUPPLY_TYPE_USB, chip->usb_psy.supplied_to = pm_power_supplied_to, @@ -4251,12 +4756,21 @@ static int __devinit pm8921_charger_probe(struct platform_device *pdev) platform_set_drvdata(pdev, chip); the_chip = chip; + /* set initial state of the USB charger type to UNKNOWN */ + power_supply_set_supply_type(&chip->usb_psy, POWER_SUPPLY_TYPE_UNKNOWN); + wake_lock_init(&chip->eoc_wake_lock, WAKE_LOCK_SUSPEND, "pm8921_eoc"); INIT_DELAYED_WORK(&chip->eoc_work, eoc_worker); INIT_DELAYED_WORK(&chip->vin_collapse_check_work, vin_collapse_check_worker); INIT_DELAYED_WORK(&chip->unplug_check_work, unplug_check_worker); + INIT_WORK(&chip->bms_notify.work, bms_notify); + INIT_WORK(&chip->battery_id_valid_work, battery_id_valid); + + INIT_DELAYED_WORK(&chip->update_heartbeat_work, update_heartbeat); + INIT_DELAYED_WORK(&chip->btc_override_work, btc_override_worker); + rc = request_irqs(chip, pdev); if (rc) { pr_err("couldn't register interrupts rc=%d\n", rc); @@ -4264,54 +4778,23 @@ static int __devinit pm8921_charger_probe(struct platform_device *pdev) } enable_irq_wake(chip->pmic_chg_irq[USBIN_VALID_IRQ]); - enable_irq_wake(chip->pmic_chg_irq[USBIN_OV_IRQ]); - enable_irq_wake(chip->pmic_chg_irq[USBIN_UV_IRQ]); - enable_irq_wake(chip->pmic_chg_irq[BAT_TEMP_OK_IRQ]); + enable_irq_wake(chip->pmic_chg_irq[DCIN_VALID_IRQ]); enable_irq_wake(chip->pmic_chg_irq[VBATDET_LOW_IRQ]); enable_irq_wake(chip->pmic_chg_irq[FASTCHG_IRQ]); - /* - * if both the cool_temp_dc and warm_temp_dc are invalid device doesnt - * care for jeita compliance - */ - if (!(chip->cool_temp_dc == INT_MIN && chip->warm_temp_dc == INT_MIN)) { - rc = configure_btm(chip); - if (rc) { - pr_err("couldn't register with btm rc=%d\n", rc); - goto free_irq; - } - } - rc = pm8921_charger_configure_batt_alarm(chip); - if (rc) { - pr_err("Couldn't configure battery alarm! rc=%d\n", rc); - goto free_irq; - } - - rc = pm8921_charger_enable_batt_alarm(chip); - if (rc) { - pr_err("Couldn't enable battery alarm! rc=%d\n", rc); - goto free_irq; - } create_debugfs_entries(chip); - INIT_WORK(&chip->bms_notify.work, bms_notify); - INIT_WORK(&chip->battery_id_valid_work, battery_id_valid); - /* determine what state the charger is in */ determine_initial_state(chip); - if (chip->update_time) { - INIT_DELAYED_WORK(&chip->update_heartbeat_work, - update_heartbeat); + if (chip->update_time) schedule_delayed_work(&chip->update_heartbeat_work, round_jiffies_relative(msecs_to_jiffies (chip->update_time))); - } return 0; -free_irq: - free_irqs(chip); unregister_batt: + wake_lock_destroy(&chip->eoc_wake_lock); power_supply_unregister(&chip->batt_psy); unregister_dc: power_supply_unregister(&chip->dc_psy); @@ -4326,6 +4809,7 @@ static int __devexit pm8921_charger_remove(struct platform_device *pdev) { struct pm8921_chg_chip *chip = platform_get_drvdata(pdev); + regulator_put(chip->vreg_xoadc); free_irqs(chip); platform_set_drvdata(pdev, NULL); the_chip = NULL; diff --git a/drivers/power/pm8xxx-ccadc.c b/drivers/power/pm8xxx-ccadc.c index e48257acbcb..7e37daa4ed1 100644 --- a/drivers/power/pm8xxx-ccadc.c +++ b/drivers/power/pm8xxx-ccadc.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved. +/* Copyright (c) 2011-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 @@ -17,12 +17,15 @@ #include #include #include +#include #include #include +#include #include #include #include #include +#include #define CCADC_ANA_PARAM 0x240 #define CCADC_DIG_PARAM 0x241 @@ -67,13 +70,17 @@ struct pm8xxx_ccadc_chip { struct device *dev; struct dentry *dent; + unsigned int batt_temp_channel; u16 ccadc_offset; int ccadc_gain_uv; unsigned int revision; unsigned int calib_delay_ms; + unsigned long last_calib_time; + int last_calib_temp; int eoc_irq; - int r_sense; + int r_sense_uohm; struct delayed_work calib_ccadc_work; + struct mutex calib_mutex; }; static struct pm8xxx_ccadc_chip *the_chip; @@ -312,6 +319,54 @@ static int calib_ccadc_program_trim(struct pm8xxx_ccadc_chip *chip, return 0; } +static int get_batt_temp(struct pm8xxx_ccadc_chip *chip, int *batt_temp) +{ + int rc; + struct pm8xxx_adc_chan_result result; + + rc = pm8xxx_adc_read(chip->batt_temp_channel, &result); + if (rc) { + pr_err("error reading batt_temp_channel = %d, rc = %d\n", + chip->batt_temp_channel, rc); + return rc; + } + *batt_temp = result.physical; + pr_debug("batt_temp phy = %lld meas = 0x%llx\n", result.physical, + result.measurement); + return 0; +} + +static int get_current_time(unsigned long *now_tm_sec) +{ + struct rtc_time tm; + struct rtc_device *rtc; + int rc; + + rtc = rtc_class_open(CONFIG_RTC_HCTOSYS_DEVICE); + if (rtc == NULL) { + pr_err("%s: unable to open rtc device (%s)\n", + __FILE__, CONFIG_RTC_HCTOSYS_DEVICE); + return -EINVAL; + } + + rc = rtc_read_time(rtc, &tm); + if (rc) { + pr_err("Error reading rtc device (%s) : %d\n", + CONFIG_RTC_HCTOSYS_DEVICE, rc); + return rc; + } + + rc = rtc_valid_tm(&tm); + if (rc) { + pr_err("Invalid RTC time (%s): %d\n", + CONFIG_RTC_HCTOSYS_DEVICE, rc); + return rc; + } + rtc_tm_to_time(&tm, now_tm_sec); + + return 0; +} + void pm8xxx_calib_ccadc(void) { u8 data_msb, data_lsb, sec_cntrl; @@ -324,11 +379,12 @@ void pm8xxx_calib_ccadc(void) return; } + mutex_lock(&the_chip->calib_mutex); rc = pm8xxx_readb(the_chip->dev->parent, ADC_ARB_SECP_CNTRL, &sec_cntrl); if (rc < 0) { pr_err("error = %d reading ADC_ARB_SECP_CNTRL\n", rc); - return; + goto calibration_unlock; } rc = calib_ccadc_enable_arbiter(the_chip); @@ -460,6 +516,8 @@ void pm8xxx_calib_ccadc(void) pr_debug("error = %d programming gain trim\n", rc); bail: pm8xxx_writeb(the_chip->dev->parent, ADC_ARB_SECP_CNTRL, sec_cntrl); +calibration_unlock: + mutex_unlock(&the_chip->calib_mutex); } EXPORT_SYMBOL(pm8xxx_calib_ccadc); @@ -480,6 +538,9 @@ static irqreturn_t pm8921_bms_ccadc_eoc_handler(int irq, void *data) struct pm8xxx_ccadc_chip *chip = data; int rc; + if (!the_chip) + goto out; + pr_debug("irq = %d triggered\n", irq); data_msb = chip->ccadc_offset >> 8; data_lsb = chip->ccadc_offset; @@ -488,6 +549,7 @@ static irqreturn_t pm8921_bms_ccadc_eoc_handler(int irq, void *data) data_msb, data_lsb, 0); disable_irq_nosync(chip->eoc_irq); +out: return IRQ_HANDLED; } @@ -558,7 +620,8 @@ int pm8xxx_ccadc_get_battery_current(int *bat_current_ua) return rc; } - *bat_current_ua = voltage_uv * 1000/the_chip->r_sense; + *bat_current_ua = div_s64((s64)voltage_uv * 1000000LL, + the_chip->r_sense_uohm); /* * ccadc reads +ve current when the battery is charging * We need to return -ve if the battery is charging @@ -671,12 +734,15 @@ static int __devinit pm8xxx_ccadc_probe(struct platform_device *pdev) chip->dev = &pdev->dev; chip->revision = pm8xxx_get_revision(chip->dev->parent); chip->eoc_irq = res->start; - chip->r_sense = pdata->r_sense; + chip->r_sense_uohm = pdata->r_sense_uohm; chip->calib_delay_ms = pdata->calib_delay_ms; + chip->batt_temp_channel = pdata->ccadc_cdata.batt_temp_channel; + mutex_init(&chip->calib_mutex); calib_ccadc_read_offset_and_gain(chip, &chip->ccadc_gain_uv, &chip->ccadc_offset); + irq_set_status_flags(chip->eoc_irq, IRQ_NOAUTOEN); rc = request_irq(chip->eoc_irq, pm8921_bms_ccadc_eoc_handler, IRQF_TRIGGER_RISING, "bms_eoc_ccadc", chip); @@ -685,9 +751,6 @@ static int __devinit pm8xxx_ccadc_probe(struct platform_device *pdev) goto free_chip; } - - disable_irq_nosync(chip->eoc_irq); - platform_set_drvdata(pdev, chip); the_chip = chip; INIT_DELAYED_WORK(&chip->calib_ccadc_work, calibrate_ccadc_work); @@ -698,6 +761,7 @@ static int __devinit pm8xxx_ccadc_probe(struct platform_device *pdev) return 0; free_chip: + mutex_destroy(&chip->calib_mutex); kfree(chip); return rc; } @@ -712,12 +776,50 @@ static int __devexit pm8xxx_ccadc_remove(struct platform_device *pdev) return 0; } +#define CCADC_CALIB_TEMP_THRESH 20 +static int pm8xxx_ccadc_resume(struct device *dev) +{ + int rc, batt_temp, delta_temp; + unsigned long current_time_sec; + unsigned long time_since_last_calib; + + rc = get_batt_temp(the_chip, &batt_temp); + if (rc) { + pr_err("unable to get batt_temp: %d\n", rc); + return 0; + } + rc = get_current_time(¤t_time_sec); + if (rc) { + pr_err("unable to get current time: %d\n", rc); + return 0; + } + if (current_time_sec > the_chip->last_calib_time) { + time_since_last_calib = current_time_sec - + the_chip->last_calib_time; + delta_temp = abs(batt_temp - the_chip->last_calib_temp); + pr_debug("time since last calib: %lu, delta_temp = %d\n", + time_since_last_calib, delta_temp); + if (time_since_last_calib >= the_chip->calib_delay_ms/1000 + || delta_temp > CCADC_CALIB_TEMP_THRESH) { + the_chip->last_calib_time = current_time_sec; + the_chip->last_calib_temp = batt_temp; + pm8xxx_calib_ccadc(); + } + } + return 0; +} + +static const struct dev_pm_ops pm8xxx_ccadc_pm_ops = { + .resume = pm8xxx_ccadc_resume, +}; + static struct platform_driver pm8xxx_ccadc_driver = { .probe = pm8xxx_ccadc_probe, .remove = __devexit_p(pm8xxx_ccadc_remove), .driver = { .name = PM8XXX_CCADC_DEV_NAME, .owner = THIS_MODULE, + .pm = &pm8xxx_ccadc_pm_ops, }, }; diff --git a/drivers/usb/otg/msm_otg.c b/drivers/usb/otg/msm_otg.c index 73ed93d4946..ada56e38312 100644 --- a/drivers/usb/otg/msm_otg.c +++ b/drivers/usb/otg/msm_otg.c @@ -1079,7 +1079,7 @@ static int msm_otg_notify_chg_type(struct msm_otg *motg) motg->chg_type == USB_ACA_C_CHARGER)) charger_type = POWER_SUPPLY_TYPE_USB_ACA; else - charger_type = POWER_SUPPLY_TYPE_BATTERY; + charger_type = POWER_SUPPLY_TYPE_UNKNOWN; return pm8921_set_usb_power_supply_type(charger_type); } diff --git a/include/linux/mfd/pm8xxx/batterydata-lib.h b/include/linux/mfd/pm8xxx/batterydata-lib.h new file mode 100644 index 00000000000..c55e47e0ce9 --- /dev/null +++ b/include/linux/mfd/pm8xxx/batterydata-lib.h @@ -0,0 +1,155 @@ +/* Copyright (c) 2012, 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. + */ + +#ifndef __PM8XXX_BMS_BATTERYDATA_H +#define __PM8XXX_BMS_BATTERYDATA_H + +#include + +#define FCC_CC_COLS 5 +#define FCC_TEMP_COLS 8 + +#define PC_CC_ROWS 29 +#define PC_CC_COLS 13 + +#define PC_TEMP_ROWS 29 +#define PC_TEMP_COLS 8 + +#define MAX_SINGLE_LUT_COLS 20 + +struct single_row_lut { + int x[MAX_SINGLE_LUT_COLS]; + int y[MAX_SINGLE_LUT_COLS]; + int cols; +}; + +/** + * struct sf_lut - + * @rows: number of percent charge entries should be <= PC_CC_ROWS + * @cols: number of charge cycle entries should be <= PC_CC_COLS + * @row_entries: the charge cycles/temperature at which sf data + * is available in the table. + * The charge cycles must be in increasing order from 0 to rows. + * @percent: the percent charge at which sf data is available in the table + * The percentcharge must be in decreasing order from 0 to cols. + * @sf: the scaling factor data + */ +struct sf_lut { + int rows; + int cols; + int row_entries[PC_CC_COLS]; + int percent[PC_CC_ROWS]; + int sf[PC_CC_ROWS][PC_CC_COLS]; +}; + +/** + * struct pc_temp_ocv_lut - + * @rows: number of percent charge entries should be <= PC_TEMP_ROWS + * @cols: number of temperature entries should be <= PC_TEMP_COLS + * @temp: the temperatures at which ocv data is available in the table + * The temperatures must be in increasing order from 0 to rows. + * @percent: the percent charge at which ocv data is available in the table + * The percentcharge must be in decreasing order from 0 to cols. + * @ocv: the open circuit voltage + */ +struct pc_temp_ocv_lut { + int rows; + int cols; + int temp[PC_TEMP_COLS]; + int percent[PC_TEMP_ROWS]; + int ocv[PC_TEMP_ROWS][PC_TEMP_COLS]; +}; + +enum battery_type { + BATT_UNKNOWN = 0, + BATT_PALLADIUM, + BATT_DESAY, +}; + +/** + * struct bms_battery_data - + * @fcc: full charge capacity (mAmpHour) + * @fcc_temp_lut: table to get fcc at a given temp + * @pc_temp_ocv_lut: table to get percent charge given batt temp and cycles + * @pc_sf_lut: table to get percent charge scaling factor given cycles + * and percent charge + * @rbatt_sf_lut: table to get battery resistance scaling factor given + * temperature and percent charge + * @default_rbatt_mohm: the default value of battery resistance to use when + * readings from bms are not available. + * @delta_rbatt_mohm: the resistance to be added towards lower soc to + * compensate for battery capacitance. + */ + +struct bms_battery_data { + unsigned int fcc; + struct single_row_lut *fcc_temp_lut; + struct single_row_lut *fcc_sf_lut; + struct pc_temp_ocv_lut *pc_temp_ocv_lut; + struct sf_lut *pc_sf_lut; + struct sf_lut *rbatt_sf_lut; + int default_rbatt_mohm; + int delta_rbatt_mohm; +}; + +#if defined(CONFIG_PM8921_BMS) || \ + defined(CONFIG_PM8921_BMS_MODULE) +extern struct bms_battery_data palladium_1500_data; +extern struct bms_battery_data desay_5200_data; + +int interpolate_fcc(struct single_row_lut *fcc_temp_lut, int batt_temp); +int interpolate_scalingfactor(struct sf_lut *sf_lut, int row_entry, int pc); +int interpolate_scalingfactor_fcc(struct single_row_lut *fcc_sf_lut, + int cycles); +int interpolate_pc(struct pc_temp_ocv_lut *pc_temp_ocv, + int batt_temp_degc, int ocv); +int interpolate_ocv(struct pc_temp_ocv_lut *pc_temp_ocv, + int batt_temp_degc, int pc); +int linear_interpolate(int y0, int x0, int y1, int x1, int x); +int is_between(int left, int right, int value); +#else +static inline int interpolate_fcc(struct single_row_lut *fcc_temp_lut, + int batt_temp) +{ + return -EINVAL; +} +static inline int interpolate_scalingfactor(struct sf_lut *sf_lut, + int row_entry, int pc) +{ + return -EINVAL; +} +static inline int interpolate_scalingfactor_fcc( + struct single_row_lut *fcc_sf_lut, int cycles) +{ + return -EINVAL; +} +static inline int interpolate_pc(struct pc_temp_ocv_lut *pc_temp_ocv, + int batt_temp_degc, int ocv) +{ + return -EINVAL; +} +static inline int interpolate_ocv(struct pc_temp_ocv_lut *pc_temp_ocv, + int batt_temp_degc, int pc) +{ + return -EINVAL; +} +static inline int linear_interpolate(int y0, int x0, int y1, int x1, int x) +{ + return -EINVAL; +} +static inline int is_between(int left, int right, int value) +{ + return -EINVAL; +} +#endif + +#endif diff --git a/include/linux/mfd/pm8xxx/ccadc.h b/include/linux/mfd/pm8xxx/ccadc.h index 29f7a62161c..e4d5a67f51c 100644 --- a/include/linux/mfd/pm8xxx/ccadc.h +++ b/include/linux/mfd/pm8xxx/ccadc.h @@ -17,14 +17,20 @@ #define PM8XXX_CCADC_DEV_NAME "pm8xxx-ccadc" +struct pm8xxx_ccadc_core_data { + unsigned int batt_temp_channel; +}; + /** * struct pm8xxx_ccadc_platform_data - - * @r_sense: sense resistor value in (mOhms) + * @ccadc_cdata: core data for the ccadc driver containing channel info + * @r_sense_uohm: sense resistor value in (micro Ohms) * @calib_delay_ms: how often should the adc calculate gain and offset */ struct pm8xxx_ccadc_platform_data { - int r_sense; - unsigned int calib_delay_ms; + struct pm8xxx_ccadc_core_data ccadc_cdata; + int r_sense_uohm; + unsigned int calib_delay_ms; }; #define CCADC_READING_RESOLUTION_N 542535 diff --git a/include/linux/mfd/pm8xxx/core.h b/include/linux/mfd/pm8xxx/core.h index 08e9014c7ab..38c589d80ed 100644 --- a/include/linux/mfd/pm8xxx/core.h +++ b/include/linux/mfd/pm8xxx/core.h @@ -75,6 +75,27 @@ enum pm8xxx_version { #define PM8XXX_REVISION_8917_TEST 0 #define PM8XXX_REVISION_8917_1p0 1 +#define PM8XXX_RESTART_UNKNOWN 0 +#define PM8XXX_RESTART_CBL 1 +#define PM8XXX_RESTART_KPD 2 +#define PM8XXX_RESTART_CHG 3 +#define PM8XXX_RESTART_SMPL 4 +#define PM8XXX_RESTART_RTC 5 +#define PM8XXX_RESTART_HARD_RESET 6 +#define PM8XXX_RESTART_GEN_PURPOSE 7 +#define PM8XXX_RESTART_REASON_MASK 0x07 + +static const char * const pm8xxx_restart_reason_str[] = { + [0] = "Unknown", + [1] = "Triggered from CBL (external charger)", + [2] = "Triggered from KPD (power key press)", + [3] = "Triggered from CHG (usb charger insertion)", + [4] = "Triggered from SMPL (sudden momentary power loss)", + [5] = "Triggered from RTC (real time clock)", + [6] = "Triggered by Hard Reset", + [7] = "Triggered by General Purpose Trigger", +}; + struct pm8xxx_drvdata { int (*pmic_readb) (const struct device *dev, u16 addr, u8 *val); @@ -88,6 +109,8 @@ struct pm8xxx_drvdata { int irq); enum pm8xxx_version (*pmic_get_version) (const struct device *dev); int (*pmic_get_revision) (const struct device *dev); + u8 (*pmic_restart_reason) + (const struct device *dev); void *pm_chip_data; }; @@ -156,4 +179,12 @@ static inline int pm8xxx_get_revision(const struct device *dev) return dd->pmic_get_revision(dev); } +static inline u8 pm8xxx_restart_reason(const struct device *dev) +{ + struct pm8xxx_drvdata *dd = dev_get_drvdata(dev); + + if (!dd) + return -EINVAL; + return dd->pmic_restart_reason(dev); +} #endif diff --git a/include/linux/mfd/pm8xxx/pm8921-bms.h b/include/linux/mfd/pm8xxx/pm8921-bms.h index a73a2849b6d..8ab3ac2578e 100644 --- a/include/linux/mfd/pm8xxx/pm8921-bms.h +++ b/include/linux/mfd/pm8xxx/pm8921-bms.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved. +/* Copyright (c) 2011-2013, Code Aurora Forum. 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 @@ -14,87 +14,10 @@ #define __PM8XXX_BMS_H #include +#include #define PM8921_BMS_DEV_NAME "pm8921-bms" -#define FCC_CC_COLS 5 -#define FCC_TEMP_COLS 8 - -#define PC_CC_ROWS 29 -#define PC_CC_COLS 13 - -#define PC_TEMP_ROWS 29 -#define PC_TEMP_COLS 8 - -#define MAX_SINGLE_LUT_COLS 20 - -struct single_row_lut { - int x[MAX_SINGLE_LUT_COLS]; - int y[MAX_SINGLE_LUT_COLS]; - int cols; -}; - -/** - * struct sf_lut - - * @rows: number of percent charge entries should be <= PC_CC_ROWS - * @cols: number of charge cycle entries should be <= PC_CC_COLS - * @row_entries: the charge cycles/temperature at which sf data - * is available in the table. - * The charge cycles must be in increasing order from 0 to rows. - * @percent: the percent charge at which sf data is available in the table - * The percentcharge must be in decreasing order from 0 to cols. - * @sf: the scaling factor data - */ -struct sf_lut { - int rows; - int cols; - int row_entries[PC_CC_COLS]; - int percent[PC_CC_ROWS]; - int sf[PC_CC_ROWS][PC_CC_COLS]; -}; - -/** - * struct pc_temp_ocv_lut - - * @rows: number of percent charge entries should be <= PC_TEMP_ROWS - * @cols: number of temperature entries should be <= PC_TEMP_COLS - * @temp: the temperatures at which ocv data is available in the table - * The temperatures must be in increasing order from 0 to rows. - * @percent: the percent charge at which ocv data is available in the table - * The percentcharge must be in decreasing order from 0 to cols. - * @ocv: the open circuit voltage - */ -struct pc_temp_ocv_lut { - int rows; - int cols; - int temp[PC_TEMP_COLS]; - int percent[PC_TEMP_ROWS]; - int ocv[PC_TEMP_ROWS][PC_TEMP_COLS]; -}; - -/** - * struct pm8921_bms_battery_data - - * @fcc: full charge capacity (mAmpHour) - * @fcc_temp_lut: table to get fcc at a given temp - * @pc_temp_ocv_lut: table to get percent charge given batt temp and cycles - * @pc_sf_lut: table to get percent charge scaling factor given cycles - * and percent charge - * @rbatt_sf_lut: table to get battery resistance scaling factor given - * temperature and percent charge - * @default_rbatt_mohm: the default value of battery resistance to use when - * readings from bms are not available. - * @delta_rbatt_mohm: the resistance to be added towards lower soc to - * compensate for battery capacitance. - */ -struct pm8921_bms_battery_data { - unsigned int fcc; - struct single_row_lut *fcc_temp_lut; - struct single_row_lut *fcc_sf_lut; - struct pc_temp_ocv_lut *pc_temp_ocv_lut; - struct sf_lut *pc_sf_lut; - struct sf_lut *rbatt_sf_lut; - int default_rbatt_mohm; - int delta_rbatt_mohm; -}; struct pm8xxx_bms_core_data { unsigned int batt_temp_channel; @@ -104,27 +27,25 @@ struct pm8xxx_bms_core_data { unsigned int batt_id_channel; }; -enum battery_type { - BATT_UNKNOWN = 0, - BATT_PALLADIUM, - BATT_DESAY, -}; - /** * struct pm8921_bms_platform_data - * @batt_type: allows to force chose battery calibration data - * @r_sense: sense resistor value in (mOhms) + * @r_sense_uohm: sense resistor value in (micro Ohms) * @i_test: current at which the unusable charger cutoff is to be * calculated or the peak system current (mA) * @v_cutoff: the loaded voltage at which the battery * is considered empty(mV) * @enable_fcc_learning: if set the driver will learn full charge * capacity of the battery upon end of charge + * @normal_voltage_calc_ms: The period of soc calculation in ms when battery + * voltage higher than cutoff voltage + * @low_voltage_calc_ms: The period of soc calculation in ms when battery + * voltage is near cutoff voltage */ struct pm8921_bms_platform_data { struct pm8xxx_bms_core_data bms_cdata; enum battery_type battery_type; - unsigned int r_sense; + int r_sense_uohm; unsigned int i_test; unsigned int v_cutoff; unsigned int max_voltage_uv; @@ -134,11 +55,11 @@ struct pm8921_bms_platform_data { int ignore_shutdown_soc; int adjust_soc_low_threshold; int chg_term_ua; + int normal_voltage_calc_ms; + int low_voltage_calc_ms; }; #if defined(CONFIG_PM8921_BMS) || defined(CONFIG_PM8921_BMS_MODULE) -extern struct pm8921_bms_battery_data palladium_1500_data; -extern struct pm8921_bms_battery_data desay_5200_data; /** * pm8921_bms_get_vsense_avg - return the voltage across the sense * resitor in microvolts @@ -202,9 +123,11 @@ void pm8921_bms_calibrate_hkadc(void); int pm8921_bms_get_simultaneous_battery_voltage_and_current(int *ibat_ua, int *vbat_uv); /** - * pm8921_bms_get_rbatt - function to get the battery resistance in mOhm. + * pm8921_bms_get_current_max + * - function to get the max current that can be drawn from + * the battery before it dips below the min allowed voltage */ -int pm8921_bms_get_rbatt(void); +int pm8921_bms_get_current_max(void); /** * pm8921_bms_invalidate_shutdown_soc - function to notify the bms driver that * the battery was replaced between reboot @@ -212,6 +135,15 @@ int pm8921_bms_get_rbatt(void); * soc stored in a coincell backed register */ void pm8921_bms_invalidate_shutdown_soc(void); + +/** + * pm8921_bms_cc_uah - function to get the coulomb counter based charge. Note + * that the coulomb counter are reset when the current + * consumption is low (below 8mA for more than 5 minutes), + * This will lead in a very low coulomb counter charge + * value upon wakeup from sleep. + */ +int pm8921_bms_cc_uah(int *cc_uah); #else static inline int pm8921_bms_get_vsense_avg(int *result) { @@ -250,6 +182,14 @@ static inline int pm8921_bms_get_rbatt(void) static inline void pm8921_bms_invalidate_shutdown_soc(void) { } +static inline int pm8921_bms_cc_uah(int *cc_uah) +{ + return -ENXIO; +} +static inline int pm8921_bms_get_current_max(void) +{ + return -ENXIO; +} #endif #endif diff --git a/include/linux/mfd/pm8xxx/pm8921-charger.h b/include/linux/mfd/pm8xxx/pm8921-charger.h index 7dbe98cd2a5..e5065ea8cf7 100644 --- a/include/linux/mfd/pm8xxx/pm8921-charger.h +++ b/include/linux/mfd/pm8xxx/pm8921-charger.h @@ -58,19 +58,25 @@ enum pm8921_chg_led_src_config { /** * struct pm8921_charger_platform_data - - * @safety_time: max charging time in minutes incl. fast and trkl * valid range 4 to 512 min. PON default 120 min * @ttrkl_time: max trckl charging time in minutes * valid range 1 to 64 mins. PON default 15 min * @update_time: how often the userland be updated of the charging (msec) - * @alarm_voltage: the voltage (mV) when lower battery alarm is triggered + * @alarm_low_mv: the voltage (mV) when low battery alarm is triggered + * @alarm_high_mv: the voltage (mV) when high battery alarm is triggered * @max_voltage: the max voltage (mV) the battery should be charged up to * @min_voltage: the voltage (mV) where charging method switches from * trickle to fast. This is also the minimum voltage the * system operates at * @uvd_thresh_voltage: the USB falling UVD threshold (mV) (PM8917 only) + * @safe_current_ma: The upper limit of current allowed to be pushed in + * battery. This ends up writing in a one time + * programmable register. * @resume_voltage_delta: the (mV) drop to wait for before resume charging * after the battery has been fully charged + * @resume_charge_percent: the % SOC the charger will drop to after the + * battery is fully charged before resuming + * charging. * @term_current: the charger current (mA) at which EOC happens * @cool_temp: the temperature (degC) at which the battery is * considered cool charging current and voltage is reduced. @@ -83,6 +89,7 @@ enum pm8921_chg_led_src_config { * area * @max_bat_chg_current: Max charge current of the battery in mA * Usually 70% of full charge capacity + * @usb_max_current: Maximum USB current in mA * @cool_bat_chg_current: chg current (mA) when the battery is cool * @warm_bat_chg_current: chg current (mA) when the battery is warm * @cool_bat_voltage: chg voltage (mV) when the battery is cool @@ -90,10 +97,6 @@ enum pm8921_chg_led_src_config { * @get_batt_capacity_percent: * a board specific function to return battery * capacity. If null - a default one will be used - * @dc_unplug_check: enables the reverse boosting fix for the DC_IN line - * however, this should only be enabled for devices which - * control the DC OVP FETs otherwise this option should - * remain disabled * @has_dc_supply: report DC online if this bit is set in board file * @trkl_voltage: the trkl voltage in (mV) below which hw controlled * trkl charging happens with linear charger @@ -120,22 +123,44 @@ enum pm8921_chg_led_src_config { * resistance of the pads, connectors, battery terminals * and rsense. * @led_src_config: Power source for anode of charger indicator LED. + * @btc_override: disable the comparators for conifugrations where a + * suitable voltages don't appear on vbatt therm line + * for the charger to detect battery is either cold / hot. + * @btc_override_cold_degc: Temperature in degCelcius when the battery is + * deemed cold and charging never happens. Used + * only if btc_override = 1 + * @btc_override_hot_degc: Temperature in degCelcius when the battery is + * deemed hot and charging never happens. Used + * only if btc_override = 1 + * @btc_delay_ms: Delay in milliseconds to monitor the battery temperature + * while charging when btc_override = 1 + * @btc_panic_if_cant_stop_chg: flag to instruct the driver to panic if the + * driver couldn't stop charging when battery + * temperature is out of bounds. Used only if + * btc_override = 1 + * stop_chg_upon_expiry: flag to indicate that the charger driver should + * stop charging the battery when the safety timer + * expires. If not set the charger driver will + * restart charging upon expiry. */ struct pm8921_charger_platform_data { struct pm8xxx_charger_core_data charger_cdata; - unsigned int safety_time; unsigned int ttrkl_time; unsigned int update_time; unsigned int max_voltage; unsigned int min_voltage; unsigned int uvd_thresh_voltage; - unsigned int alarm_voltage; + unsigned int safe_current_ma; + unsigned int alarm_low_mv; + unsigned int alarm_high_mv; unsigned int resume_voltage_delta; + int resume_charge_percent; unsigned int term_current; int cool_temp; int warm_temp; unsigned int temp_check_period; unsigned int max_bat_chg_current; + unsigned int usb_max_current; unsigned int cool_bat_chg_current; unsigned int warm_bat_chg_current; unsigned int cool_bat_voltage; @@ -144,7 +169,6 @@ struct pm8921_charger_platform_data { int64_t batt_id_min; int64_t batt_id_max; bool keep_btm_on_suspend; - bool dc_unplug_check; bool has_dc_supply; int trkl_voltage; int weak_voltage; @@ -157,6 +181,13 @@ struct pm8921_charger_platform_data { enum pm8921_chg_hot_thr hot_thr; int rconn_mohm; enum pm8921_chg_led_src_config led_src_config; + int battery_less_hardware; + int btc_override; + int btc_override_cold_degc; + int btc_override_hot_degc; + int btc_delay_ms; + int btc_panic_if_cant_stop_chg; + int stop_chg_upon_expiry; }; enum pm8921_charger_source { @@ -169,15 +200,6 @@ enum pm8921_charger_source { void pm8921_charger_vbus_draw(unsigned int mA); int pm8921_charger_register_vbus_sn(void (*callback)(int)); void pm8921_charger_unregister_vbus_sn(void (*callback)(int)); -/** - * pm8921_charger_enable - - * - * @enable: 1 means enable charging, 0 means disable - * - * Enable/Disable battery charging current, the device will still draw current - * from the charging source - */ -int pm8921_charger_enable(bool enable); /** * pm8921_is_usb_chg_plugged_in - is usb plugged in @@ -309,10 +331,6 @@ static inline int pm8921_charger_register_vbus_sn(void (*callback)(int)) static inline void pm8921_charger_unregister_vbus_sn(void (*callback)(int)) { } -static inline int pm8921_charger_enable(bool enable) -{ - return -ENXIO; -} static inline int pm8921_is_usb_chg_plugged_in(void) { return -ENXIO;