From patchwork Wed Nov 14 09:07:04 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: "(Exiting) Baolin Wang" X-Patchwork-Id: 10682229 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 462CF139B for ; Wed, 14 Nov 2018 09:08:20 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 3A2032AF3D for ; Wed, 14 Nov 2018 09:08:20 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 2EBAF2AF46; Wed, 14 Nov 2018 09:08:20 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-8.0 required=2.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,MAILING_LIST_MULTI,RCVD_IN_DNSWL_HI autolearn=ham version=3.3.1 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 6DC802AF3D for ; Wed, 14 Nov 2018 09:08:17 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1732227AbeKNTKI (ORCPT ); Wed, 14 Nov 2018 14:10:08 -0500 Received: from mail-pl1-f193.google.com ([209.85.214.193]:38584 "EHLO mail-pl1-f193.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1732221AbeKNTKI (ORCPT ); Wed, 14 Nov 2018 14:10:08 -0500 Received: by mail-pl1-f193.google.com with SMTP id p4-v6so7477464plo.5 for ; Wed, 14 Nov 2018 01:07:47 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linaro.org; s=google; h=from:to:cc:subject:date:message-id:in-reply-to:references :in-reply-to:references; bh=wMyqG5pI89CV6L/8kIrcswRejq6Wasnn2q5PPPGYQHQ=; b=PWED4JltQZuViISB1WJMjPC5sXuvT1qCn/cpN0jSJrUClI9PUTCKikuXf9KwQmzlx0 nFlqcXoxIw0gzP5yhJd9R6e2/OuJxKEkD15JU/cKX/VS7v5ji1r/V1uV4F/fXpdUVRJO LGuOxAmAKT61wiE1W2JN/pr6rUvb3YsE06eBw= X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:in-reply-to:references; bh=wMyqG5pI89CV6L/8kIrcswRejq6Wasnn2q5PPPGYQHQ=; b=jLDtFvfXzTdGL2gMGRFsPpG8dY8eJFUGlPDfTyzJm5kJjG1egkjheU+ZUMWMwqAsNJ v8IkA/ebuhegRw9XMwFHsWhgT7hZ40riHaU1fGzOKQuYRW5W1bBQ6FwdaT2k4ODcqohC wEYUiA0hUEfy6AkkKohAPxj9HGPXQauqyRWShxISMtnah7NSI1NGsHibAo8Wm6RlnBLE f3Kw3OAZCNwff7wxbG/nXRrBKqFSBD+LsJep6Wo8oJwGuWHhY1+SRn9atWmWrFfPXuJR FNNvzAppi1pB+mVmu70LPm1uiAPEASy8Jcv63IQXWxN7nGA/IQh0KIq2OH1iMj825LuY L68A== X-Gm-Message-State: AGRZ1gLecYPMOtXijGED36yRo0XglpZ3cWJTKJqhdirqLuPi1YHWS8yA KcXhC1dHKAQQCIqVi16Ep+THHg== X-Google-Smtp-Source: AJdET5cu79RNj6Ak4SHHvr00KVWxSCFev4dOnqK6iHE4/gS4MmMQbQw/xfIt77uQTHlpaTIJWm2ykQ== X-Received: by 2002:a17:902:110a:: with SMTP id d10-v6mr1097461pla.85.1542186466971; Wed, 14 Nov 2018 01:07:46 -0800 (PST) Received: from baolinwangubtpc.spreadtrum.com ([117.18.48.102]) by smtp.gmail.com with ESMTPSA id 127-v6sm25048814pfx.91.2018.11.14.01.07.43 (version=TLS1 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Wed, 14 Nov 2018 01:07:46 -0800 (PST) From: Baolin Wang To: sre@kernel.org, robh+dt@kernel.org, mark.rutland@arm.com Cc: linux-pm@vger.kernel.org, devicetree@vger.kernel.org, linux-kernel@vger.kernel.org, yuanjiang.yu@unisoc.com, baolin.wang@linaro.org, broonie@kernel.org Subject: [PATCH 1/5] dt-bindings: power: supply: Add nvmem properties to calibrate FGU Date: Wed, 14 Nov 2018 17:07:04 +0800 Message-Id: <09143dca85a260cb6c3abc5efcf09075a8050265.1542185618.git.baolin.wang@linaro.org> X-Mailer: git-send-email 1.7.9.5 In-Reply-To: References: In-Reply-To: References: Sender: linux-pm-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-pm@vger.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP Add nvmem properties to calibrate FGU from eFuse controller. Signed-off-by: Baolin Wang Reviewed-by: Rob Herring --- .../devicetree/bindings/power/supply/sc27xx-fg.txt | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Documentation/devicetree/bindings/power/supply/sc27xx-fg.txt b/Documentation/devicetree/bindings/power/supply/sc27xx-fg.txt index 98a2400..fc35ac5 100644 --- a/Documentation/devicetree/bindings/power/supply/sc27xx-fg.txt +++ b/Documentation/devicetree/bindings/power/supply/sc27xx-fg.txt @@ -11,6 +11,8 @@ Required properties: - battery-detect-gpios: GPIO for battery detection. - io-channels: Specify the IIO ADC channel to get temperature. - io-channel-names: Should be "bat-temp". +- nvmem-cells: A phandle to the calibration cells provided by eFuse device. +- nvmem-cell-names: Should be "fgu_calib". - monitored-battery: Phandle of battery characteristics devicetree node. See Documentation/devicetree/bindings/power/supply/battery.txt @@ -47,6 +49,8 @@ Example: battery-detect-gpios = <&pmic_eic 9 GPIO_ACTIVE_HIGH>; io-channels = <&pmic_adc 5>; io-channel-names = "bat-temp"; + nvmem-cells = <&fgu_calib>; + nvmem-cell-names = "fgu_calib"; monitored-battery = <&bat>; }; }; From patchwork Wed Nov 14 09:07:05 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: "(Exiting) Baolin Wang" X-Patchwork-Id: 10682221 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 9718B14E2 for ; Wed, 14 Nov 2018 09:07:54 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 8B5732AF3D for ; Wed, 14 Nov 2018 09:07:54 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 7F38E2AF46; Wed, 14 Nov 2018 09:07:54 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-8.0 required=2.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,MAILING_LIST_MULTI,RCVD_IN_DNSWL_HI autolearn=ham version=3.3.1 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id EDC942AF3D for ; Wed, 14 Nov 2018 09:07:53 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1727966AbeKNTKN (ORCPT ); Wed, 14 Nov 2018 14:10:13 -0500 Received: from mail-pg1-f196.google.com ([209.85.215.196]:41611 "EHLO mail-pg1-f196.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1732234AbeKNTKM (ORCPT ); Wed, 14 Nov 2018 14:10:12 -0500 Received: by mail-pg1-f196.google.com with SMTP id 70so7073771pgh.8 for ; Wed, 14 Nov 2018 01:07:50 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linaro.org; s=google; h=from:to:cc:subject:date:message-id:in-reply-to:references :in-reply-to:references; bh=5jCGoWWaBFWMEfVdu5BxQTbZHcNaLmB1PTqRJRj8eG0=; b=BwnucNaAnlGs23qpoK4CECQiv24ZjCFhPKAY33oXhEsXPMffEElmigj4ZKEwQDghNK M0HzRXJuvWUjH/spyWEqUdXhjcox27pGsjpwISK3OI0ageH3R8aTYtTNmFE5g8E5parg RIrkGBfWPZ6Fccbq467RTsp8AGP401aUV0Dkc= X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:in-reply-to:references; bh=5jCGoWWaBFWMEfVdu5BxQTbZHcNaLmB1PTqRJRj8eG0=; b=Huhf6U3xtBkkSrv2V3bMIjvUzmcCuP5EmfHbLjFdIx9OUrJqTporT+ea9T7b00Zr0r N4reI3SWjbogdKH+Hz5sbpGGQeNDY/gLZIAhyNy356S6dTX8n9Pd6CPsSxm+b+XUTBr+ psehnWQ/jeWIUy97d1pv1QXDFHZYSxzlCthczSMcj7buIFsY8fhUR1Rmfh0LrwUWwcIl LfXvcR4hAiUg5HGGs575+Ij7DBLCQVf0u0F6DMHSKM+w8R1Ig7jNT/l1rSbq/o6zJQQX XbAxbSklI2BYSQBuq9ojxEwU53V7IraQQgocijzh8dFy2ZLkMUmkbZ0aBvO5wsDg6Bhk gS+A== X-Gm-Message-State: AGRZ1gIaCnlM34CtRk+EQY2kohGqKBu5TYckDQN7x1jbx2Ba/sGwXUm8 OcW8EDIYyO+lmXNtIj6Fq2OhXQ== X-Google-Smtp-Source: AJdET5f7Othf28Q7cDWUE07jr22xc/7ggewvMzcoXqYgu0XZGtQYxYMXEu9HlO+t+QgfQKKcDDKTXA== X-Received: by 2002:a63:4d0e:: with SMTP id a14mr999631pgb.408.1542186470198; Wed, 14 Nov 2018 01:07:50 -0800 (PST) Received: from baolinwangubtpc.spreadtrum.com ([117.18.48.102]) by smtp.gmail.com with ESMTPSA id 127-v6sm25048814pfx.91.2018.11.14.01.07.47 (version=TLS1 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Wed, 14 Nov 2018 01:07:49 -0800 (PST) From: Baolin Wang To: sre@kernel.org, robh+dt@kernel.org, mark.rutland@arm.com Cc: linux-pm@vger.kernel.org, devicetree@vger.kernel.org, linux-kernel@vger.kernel.org, yuanjiang.yu@unisoc.com, baolin.wang@linaro.org, broonie@kernel.org Subject: [PATCH 2/5] power: supply: sc27xx: Add fuel gauge calibration Date: Wed, 14 Nov 2018 17:07:05 +0800 Message-Id: X-Mailer: git-send-email 1.7.9.5 In-Reply-To: References: In-Reply-To: References: Sender: linux-pm-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-pm@vger.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP This patch adds support to read calibration values from the eFuse controller to calibrate the ADC values corresponding to current and voltage, which can make the current and voltage data more accurate. Signed-off-by: Baolin Wang --- drivers/power/supply/sc27xx_fuel_gauge.c | 62 ++++++++++++++++++++++++------ 1 file changed, 51 insertions(+), 11 deletions(-) diff --git a/drivers/power/supply/sc27xx_fuel_gauge.c b/drivers/power/supply/sc27xx_fuel_gauge.c index 8613584..66f4db2 100644 --- a/drivers/power/supply/sc27xx_fuel_gauge.c +++ b/drivers/power/supply/sc27xx_fuel_gauge.c @@ -6,10 +6,12 @@ #include #include #include +#include #include #include #include #include +#include /* PMIC global control registers definition */ #define SC27XX_MODULE_EN0 0xc08 @@ -39,8 +41,6 @@ #define SC27XX_FGU_CLBCNT_MASK GENMASK(15, 0) #define SC27XX_FGU_CLBCNT_SHIFT 16 -#define SC27XX_FGU_1000MV_ADC 686 -#define SC27XX_FGU_1000MA_ADC 1372 #define SC27XX_FGU_CUR_BASIC_ADC 8192 #define SC27XX_FGU_SAMPLE_HZ 2 @@ -59,6 +59,8 @@ * @init_clbcnt: the initial coulomb counter * @max_volt: the maximum constant input voltage in millivolt * @table_len: the capacity table length + * @cur_1000ma_adc: ADC value corresponding to 1000 mA + * @vol_1000mv_adc: ADC value corresponding to 1000 mV * @cap_table: capacity table with corresponding ocv */ struct sc27xx_fgu_data { @@ -76,6 +78,8 @@ struct sc27xx_fgu_data { int init_clbcnt; int max_volt; int table_len; + int cur_1000ma_adc; + int vol_1000mv_adc; struct power_supply_battery_ocv_table *cap_table; }; @@ -86,14 +90,14 @@ struct sc27xx_fgu_data { "sc2723_charger", }; -static int sc27xx_fgu_adc_to_current(int adc) +static int sc27xx_fgu_adc_to_current(struct sc27xx_fgu_data *data, int adc) { - return DIV_ROUND_CLOSEST(adc * 1000, SC27XX_FGU_1000MA_ADC); + return DIV_ROUND_CLOSEST(adc * 1000, data->cur_1000ma_adc); } -static int sc27xx_fgu_adc_to_voltage(int adc) +static int sc27xx_fgu_adc_to_voltage(struct sc27xx_fgu_data *data, int adc) { - return DIV_ROUND_CLOSEST(adc * 1000, SC27XX_FGU_1000MV_ADC); + return DIV_ROUND_CLOSEST(adc * 1000, data->vol_1000mv_adc); } /* @@ -116,7 +120,7 @@ static int sc27xx_fgu_get_boot_capacity(struct sc27xx_fgu_data *data, int *cap) return ret; cur <<= 1; - oci = sc27xx_fgu_adc_to_current(cur - SC27XX_FGU_CUR_BASIC_ADC); + oci = sc27xx_fgu_adc_to_current(data, cur - SC27XX_FGU_CUR_BASIC_ADC); /* * Should get the OCV from SC27XX_FGU_POCV register at the system @@ -127,7 +131,7 @@ static int sc27xx_fgu_get_boot_capacity(struct sc27xx_fgu_data *data, int *cap) if (ret) return ret; - volt = sc27xx_fgu_adc_to_voltage(volt); + volt = sc27xx_fgu_adc_to_voltage(data, volt); ocv = volt * 1000 - oci * data->internal_resist; /* @@ -201,7 +205,7 @@ static int sc27xx_fgu_get_capacity(struct sc27xx_fgu_data *data, int *cap) * as 100 to improve the precision. */ temp = DIV_ROUND_CLOSEST(delta_clbcnt, 360); - temp = sc27xx_fgu_adc_to_current(temp); + temp = sc27xx_fgu_adc_to_current(data, temp); /* * Convert to capacity percent of the battery total capacity, @@ -225,7 +229,7 @@ static int sc27xx_fgu_get_vbat_vol(struct sc27xx_fgu_data *data, int *val) * It is ADC values reading from registers which need to convert to * corresponding voltage values. */ - *val = sc27xx_fgu_adc_to_voltage(vol); + *val = sc27xx_fgu_adc_to_voltage(data, vol); return 0; } @@ -242,7 +246,7 @@ static int sc27xx_fgu_get_current(struct sc27xx_fgu_data *data, int *val) * It is ADC values reading from registers which need to convert to * corresponding current values. */ - *val = sc27xx_fgu_adc_to_current(cur - SC27XX_FGU_CUR_BASIC_ADC); + *val = sc27xx_fgu_adc_to_current(data, cur - SC27XX_FGU_CUR_BASIC_ADC); return 0; } @@ -469,6 +473,38 @@ static int sc27xx_fgu_cap_to_clbcnt(struct sc27xx_fgu_data *data, int capacity) return DIV_ROUND_CLOSEST(cur_cap * 36, 10); } +static int sc27xx_fgu_calibration(struct sc27xx_fgu_data *data) +{ + struct nvmem_cell *cell; + int calib_data, cal_4200mv; + void *buf; + size_t len; + + cell = nvmem_cell_get(data->dev, "fgu_calib"); + if (IS_ERR(cell)) + return PTR_ERR(cell); + + buf = nvmem_cell_read(cell, &len); + nvmem_cell_put(cell); + + if (IS_ERR(buf)) + return PTR_ERR(buf); + + memcpy(&calib_data, buf, min(len, sizeof(u32))); + + /* + * Get the ADC value corresponding to 4200 mV from eFuse controller + * according to below formula. Then convert to ADC values corresponding + * to 1000 mV and 1000 mA. + */ + cal_4200mv = (calib_data & 0x1ff) + 6963 - 4096 - 256; + data->vol_1000mv_adc = DIV_ROUND_CLOSEST(cal_4200mv * 10, 42); + data->cur_1000ma_adc = data->vol_1000mv_adc * 4; + + kfree(buf); + return 0; +} + static int sc27xx_fgu_hw_init(struct sc27xx_fgu_data *data) { struct power_supply_battery_info info = { }; @@ -503,6 +539,10 @@ static int sc27xx_fgu_hw_init(struct sc27xx_fgu_data *data) power_supply_put_battery_info(data->battery, &info); + ret = sc27xx_fgu_calibration(data); + if (ret) + return ret; + /* Enable the FGU module */ ret = regmap_update_bits(data->regmap, SC27XX_MODULE_EN0, SC27XX_FGU_EN, SC27XX_FGU_EN); From patchwork Wed Nov 14 09:07:06 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: "(Exiting) Baolin Wang" X-Patchwork-Id: 10682223 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 82003139B for ; Wed, 14 Nov 2018 09:07:57 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 71B792AF3D for ; Wed, 14 Nov 2018 09:07:57 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 634BE2AF46; Wed, 14 Nov 2018 09:07:57 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-8.0 required=2.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,MAILING_LIST_MULTI,RCVD_IN_DNSWL_HI autolearn=ham version=3.3.1 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 477ED2AF3D for ; Wed, 14 Nov 2018 09:07:56 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1732459AbeKNTKP (ORCPT ); Wed, 14 Nov 2018 14:10:15 -0500 Received: from mail-pl1-f196.google.com ([209.85.214.196]:41031 "EHLO mail-pl1-f196.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1732404AbeKNTKP (ORCPT ); Wed, 14 Nov 2018 14:10:15 -0500 Received: by mail-pl1-f196.google.com with SMTP id p16-v6so7467676plr.8 for ; Wed, 14 Nov 2018 01:07:54 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linaro.org; s=google; h=from:to:cc:subject:date:message-id:in-reply-to:references :in-reply-to:references; bh=DL1e+xmoW3uXXoV3aTSNVd0vUC31legRrah0dDPX+vY=; b=SAzSm0uPSRFcsmt3SqxLbFDf1uSUjlodnHExvb0UhH2G6UQxsjdQj6NVGWEqawuQEb PIu0aVma0XPEXwpFQqH6J2QglgLNC2qmE7mqW/rjDy//jlHylasnfG6UT04r/Dw82ywN miJnCE8zN5A3nATTvi8+mlBB3owPCfySWMiiY= X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:in-reply-to:references; bh=DL1e+xmoW3uXXoV3aTSNVd0vUC31legRrah0dDPX+vY=; b=Wrj2XtjI+QTUY49HNgMF7k+qJw854wnI8zNMtPiEip5jrSnub+1qHNAVTHnJynTWTw FgaOVzNlR5/T1Ev9XfFOLVP3vTCKW3vHtgUQ+31KNHqePwZpIRWwMNTcw3m0R6CIDykx XWZXtvkUiIc0P58H9b3Anj84ja2McWP9cZL7hRuGkEnrogq5egRwMVVYNSo0M1yfLLQz drVbCgKzOVMyHI3r1TtqI0zDT3/RhiFBmf1NB+IaZJ9XEytqc/B9oIutr4y62rn/QstB rLWNoYFj/Krhjot2GKZXb+TnL8++N8OX3n06BEc3NT7kps2200uOP4k9IK/RhMu5fwMz fDzA== X-Gm-Message-State: AGRZ1gLyZuvT6HERZkCXW2/L8T8Xwxm8j3d7Ijb0vmsckhswccebSJFF 9sNxIKH2q0NTObsm+ygbv6hMFA== X-Google-Smtp-Source: AJdET5dB6EwrR4eDn3iL/RzHyEXTi7qtxzc/s/SpKARIdE+tnIzzaRxnBYaN5A22Izs92OyPCkBa7A== X-Received: by 2002:a17:902:50ec:: with SMTP id c41-v6mr1084807plj.176.1542186473650; Wed, 14 Nov 2018 01:07:53 -0800 (PST) Received: from baolinwangubtpc.spreadtrum.com ([117.18.48.102]) by smtp.gmail.com with ESMTPSA id 127-v6sm25048814pfx.91.2018.11.14.01.07.50 (version=TLS1 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Wed, 14 Nov 2018 01:07:53 -0800 (PST) From: Baolin Wang To: sre@kernel.org, robh+dt@kernel.org, mark.rutland@arm.com Cc: linux-pm@vger.kernel.org, devicetree@vger.kernel.org, linux-kernel@vger.kernel.org, yuanjiang.yu@unisoc.com, baolin.wang@linaro.org, broonie@kernel.org Subject: [PATCH 3/5] power: supply: sc27xx: Add fuel gauge low voltage alarm Date: Wed, 14 Nov 2018 17:07:06 +0800 Message-Id: X-Mailer: git-send-email 1.7.9.5 In-Reply-To: References: In-Reply-To: References: Sender: linux-pm-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-pm@vger.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP From: Yuanjiang Yu Add low voltage alarm support to make sure the battery capacity more accurate in lower voltage stage. Signed-off-by: Yuanjiang Yu Signed-off-by: Baolin Wang --- drivers/power/supply/sc27xx_fuel_gauge.c | 171 +++++++++++++++++++++++++++++- 1 file changed, 170 insertions(+), 1 deletion(-) diff --git a/drivers/power/supply/sc27xx_fuel_gauge.c b/drivers/power/supply/sc27xx_fuel_gauge.c index 66f4db2..962d0f8 100644 --- a/drivers/power/supply/sc27xx_fuel_gauge.c +++ b/drivers/power/supply/sc27xx_fuel_gauge.c @@ -31,8 +31,11 @@ #define SC27XX_FGU_OCV 0x24 #define SC27XX_FGU_POCV 0x28 #define SC27XX_FGU_CURRENT 0x2c +#define SC27XX_FGU_LOW_OVERLOAD 0x34 #define SC27XX_FGU_CLBCNT_SETH 0x50 #define SC27XX_FGU_CLBCNT_SETL 0x54 +#define SC27XX_FGU_CLBCNT_DELTH 0x58 +#define SC27XX_FGU_CLBCNT_DELTL 0x5c #define SC27XX_FGU_CLBCNT_VALH 0x68 #define SC27XX_FGU_CLBCNT_VALL 0x6c #define SC27XX_FGU_CLBCNT_QMAXL 0x74 @@ -40,6 +43,11 @@ #define SC27XX_WRITE_SELCLB_EN BIT(0) #define SC27XX_FGU_CLBCNT_MASK GENMASK(15, 0) #define SC27XX_FGU_CLBCNT_SHIFT 16 +#define SC27XX_FGU_LOW_OVERLOAD_MASK GENMASK(12, 0) + +#define SC27XX_FGU_INT_MASK GENMASK(9, 0) +#define SC27XX_FGU_LOW_OVERLOAD_INT BIT(0) +#define SC27XX_FGU_CLBCNT_DELTA_INT BIT(2) #define SC27XX_FGU_CUR_BASIC_ADC 8192 #define SC27XX_FGU_SAMPLE_HZ 2 @@ -56,8 +64,10 @@ * @internal_resist: the battery internal resistance in mOhm * @total_cap: the total capacity of the battery in mAh * @init_cap: the initial capacity of the battery in mAh + * @alarm_cap: the alarm capacity * @init_clbcnt: the initial coulomb counter * @max_volt: the maximum constant input voltage in millivolt + * @min_volt: the minimum drained battery voltage in microvolt * @table_len: the capacity table length * @cur_1000ma_adc: ADC value corresponding to 1000 mA * @vol_1000mv_adc: ADC value corresponding to 1000 mV @@ -75,14 +85,18 @@ struct sc27xx_fgu_data { int internal_resist; int total_cap; int init_cap; + int alarm_cap; int init_clbcnt; int max_volt; + int min_volt; int table_len; int cur_1000ma_adc; int vol_1000mv_adc; struct power_supply_battery_ocv_table *cap_table; }; +static int sc27xx_fgu_cap_to_clbcnt(struct sc27xx_fgu_data *data, int capacity); + static const char * const sc27xx_charger_supply_name[] = { "sc2731_charger", "sc2720_charger", @@ -100,6 +114,11 @@ static int sc27xx_fgu_adc_to_voltage(struct sc27xx_fgu_data *data, int adc) return DIV_ROUND_CLOSEST(adc * 1000, data->vol_1000mv_adc); } +static int sc27xx_fgu_voltage_to_adc(struct sc27xx_fgu_data *data, int vol) +{ + return DIV_ROUND_CLOSEST(vol * data->vol_1000mv_adc, 1000); +} + /* * When system boots on, we can not read battery capacity from coulomb * registers, since now the coulomb registers are invalid. So we should @@ -428,6 +447,92 @@ static void sc27xx_fgu_external_power_changed(struct power_supply *psy) .external_power_changed = sc27xx_fgu_external_power_changed, }; +static void sc27xx_fgu_adjust_cap(struct sc27xx_fgu_data *data, int cap) +{ + data->init_cap = cap; + data->init_clbcnt = sc27xx_fgu_cap_to_clbcnt(data, data->init_cap); +} + +static irqreturn_t sc27xx_fgu_interrupt(int irq, void *dev_id) +{ + struct sc27xx_fgu_data *data = dev_id; + int ret, cap, ocv, adc; + u32 status; + + mutex_lock(&data->lock); + + ret = regmap_read(data->regmap, data->base + SC27XX_FGU_INT_STS, + &status); + if (ret) + goto out; + + ret = regmap_update_bits(data->regmap, data->base + SC27XX_FGU_INT_CLR, + status, status); + if (ret) + goto out; + + /* + * When low overload voltage interrupt happens, we should calibrate the + * battery capacity in lower voltage stage. + */ + if (!(status & SC27XX_FGU_LOW_OVERLOAD_INT)) + goto out; + + ret = sc27xx_fgu_get_capacity(data, &cap); + if (ret) + goto out; + + ret = sc27xx_fgu_get_vbat_ocv(data, &ocv); + if (ret) + goto out; + + /* + * If current OCV value is less than the minimum OCV value in OCV table, + * which means now battery capacity is 0%, and we should adjust the + * inititial capacity to 0. + */ + if (ocv <= data->cap_table[data->table_len - 1].ocv) { + sc27xx_fgu_adjust_cap(data, 0); + } else if (ocv <= data->min_volt) { + /* + * If current OCV value is less than the low alarm voltage, but + * current capacity is larger than the alarm capacity, we should + * adjust the inititial capacity to alarm capacity. + */ + if (cap > data->alarm_cap) { + sc27xx_fgu_adjust_cap(data, data->alarm_cap); + } else if (cap <= 0) { + int cur_cap; + + /* + * If current capacity is equal with 0 or less than 0 + * (some error occurs), we should adjust inititial + * capacity to the capacity corresponding to current OCV + * value. + */ + cur_cap = power_supply_ocv2cap_simple(data->cap_table, + data->table_len, + ocv); + sc27xx_fgu_adjust_cap(data, cur_cap); + } + + /* + * After adjusting the battery capacity, we should set the + * lowest alarm voltage instead. + */ + data->min_volt = data->cap_table[data->table_len - 1].ocv; + adc = sc27xx_fgu_voltage_to_adc(data, data->min_volt / 1000); + regmap_update_bits(data->regmap, data->base + SC27XX_FGU_LOW_OVERLOAD, + SC27XX_FGU_LOW_OVERLOAD_MASK, adc); + } + +out: + mutex_unlock(&data->lock); + + power_supply_changed(data->battery); + return IRQ_HANDLED; +} + static irqreturn_t sc27xx_fgu_bat_detection(int irq, void *dev_id) { struct sc27xx_fgu_data *data = dev_id; @@ -509,7 +614,7 @@ static int sc27xx_fgu_hw_init(struct sc27xx_fgu_data *data) { struct power_supply_battery_info info = { }; struct power_supply_battery_ocv_table *table; - int ret; + int ret, delta_clbcnt, alarm_adc; ret = power_supply_get_battery_info(data->battery, &info); if (ret) { @@ -520,6 +625,7 @@ static int sc27xx_fgu_hw_init(struct sc27xx_fgu_data *data) data->total_cap = info.charge_full_design_uah / 1000; data->max_volt = info.constant_charge_voltage_max_uv / 1000; data->internal_resist = info.factory_internal_resistance_uohm / 1000; + data->min_volt = info.voltage_min_design_uv; /* * For SC27XX fuel gauge device, we only use one ocv-capacity @@ -537,6 +643,10 @@ static int sc27xx_fgu_hw_init(struct sc27xx_fgu_data *data) return -ENOMEM; } + data->alarm_cap = power_supply_ocv2cap_simple(data->cap_table, + data->table_len, + data->min_volt); + power_supply_put_battery_info(data->battery, &info); ret = sc27xx_fgu_calibration(data); @@ -559,6 +669,50 @@ static int sc27xx_fgu_hw_init(struct sc27xx_fgu_data *data) goto disable_fgu; } + ret = regmap_update_bits(data->regmap, data->base + SC27XX_FGU_INT_CLR, + SC27XX_FGU_INT_MASK, SC27XX_FGU_INT_MASK); + if (ret) { + dev_err(data->dev, "failed to clear interrupt status\n"); + goto disable_clk; + } + + /* + * Set the voltage low overload threshold, which means when the battery + * voltage is lower than this threshold, the controller will generate + * one interrupt to notify. + */ + alarm_adc = sc27xx_fgu_voltage_to_adc(data, data->min_volt / 1000); + ret = regmap_update_bits(data->regmap, data->base + SC27XX_FGU_LOW_OVERLOAD, + SC27XX_FGU_LOW_OVERLOAD_MASK, alarm_adc); + if (ret) { + dev_err(data->dev, "failed to set fgu low overload\n"); + goto disable_clk; + } + + /* + * Set the coulomb counter delta threshold, that means when the coulomb + * counter change is multiples of the delta threshold, the controller + * will generate one interrupt to notify the users to update the battery + * capacity. Now we set the delta threshold as a counter value of 1% + * capacity. + */ + delta_clbcnt = sc27xx_fgu_cap_to_clbcnt(data, 1); + + ret = regmap_update_bits(data->regmap, data->base + SC27XX_FGU_CLBCNT_DELTL, + SC27XX_FGU_CLBCNT_MASK, delta_clbcnt); + if (ret) { + dev_err(data->dev, "failed to set low delta coulomb counter\n"); + goto disable_clk; + } + + ret = regmap_update_bits(data->regmap, data->base + SC27XX_FGU_CLBCNT_DELTH, + SC27XX_FGU_CLBCNT_MASK, + delta_clbcnt >> SC27XX_FGU_CLBCNT_SHIFT); + if (ret) { + dev_err(data->dev, "failed to set high delta coulomb counter\n"); + goto disable_clk; + } + /* * Get the boot battery capacity when system powers on, which is used to * initialize the coulomb counter. After that, we can read the coulomb @@ -658,6 +812,21 @@ static int sc27xx_fgu_probe(struct platform_device *pdev) return ret; } + irq = platform_get_irq(pdev, 0); + if (irq < 0) { + dev_err(&pdev->dev, "no irq resource specified\n"); + return irq; + } + + ret = devm_request_threaded_irq(data->dev, irq, NULL, + sc27xx_fgu_interrupt, + IRQF_NO_SUSPEND | IRQF_ONESHOT, + pdev->name, data); + if (ret) { + dev_err(data->dev, "failed to request fgu IRQ\n"); + return ret; + } + irq = gpiod_to_irq(data->gpiod); if (irq < 0) { dev_err(&pdev->dev, "failed to translate GPIO to IRQ\n"); From patchwork Wed Nov 14 09:07:07 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: "(Exiting) Baolin Wang" X-Patchwork-Id: 10682225 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id CF3D0139B for ; Wed, 14 Nov 2018 09:08:00 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id C28472AF3D for ; Wed, 14 Nov 2018 09:08:00 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id B6B4D2AF46; Wed, 14 Nov 2018 09:08:00 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-8.0 required=2.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,MAILING_LIST_MULTI,RCVD_IN_DNSWL_HI autolearn=ham version=3.3.1 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 4EE492AF3D for ; Wed, 14 Nov 2018 09:08:00 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1732234AbeKNTKT (ORCPT ); Wed, 14 Nov 2018 14:10:19 -0500 Received: from mail-pf1-f196.google.com ([209.85.210.196]:34132 "EHLO mail-pf1-f196.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1732404AbeKNTKT (ORCPT ); Wed, 14 Nov 2018 14:10:19 -0500 Received: by mail-pf1-f196.google.com with SMTP id h3so1041343pfg.1 for ; Wed, 14 Nov 2018 01:07:57 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linaro.org; s=google; h=from:to:cc:subject:date:message-id:in-reply-to:references :in-reply-to:references; bh=pM4xo8EH7+FwIopWG51Di3D79VxY65IdP6hwSYGqO0I=; b=K7MtDXrg1XRlZAecffDu2MzNpa1VDLB9fXSRB8vDJw5KyHvioYDjarzVXJlTrF9oqa veU3zPb4ithaS3sbFvKBqwAR/N27mZtvfmRGvpiHCqosUZlcDN8saJrRzX0s3aPWxGpI WE+pIAHTzb+YiCU9tHXnD1U94RjQtAN7ufFEg= X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:in-reply-to:references; bh=pM4xo8EH7+FwIopWG51Di3D79VxY65IdP6hwSYGqO0I=; b=foq56l6cyDubsIis4HXeLERzO0q/u3dMF6H0EJvBAa5TbUv19DdJVHi0TC9s8loxjS rsqcjEqkvOVBukVPr+oE+apycyZ9nhoFF78YtGVj0NTuf1rcRbVcFpLNfhfJnVLlybF0 W6Ol3bATQxSxWnQCz0V8nVT/i/2tBt3lR/0GaK/BlT8oFuzGQ6Q5ojU2dYgpsMmf2j4G mTbxxeVApflXH6kDVx6QQnKB1BuXP7S8moUzI2KeH/P7HYBUV/jz97md5i7l1FZEXFb/ CJSeFiAiDWD6MzdVHVAN1BJ7IyM+2Z/nzoz2UajPJB64gFpDFRNMUbxuE1fTf6lLy56Z 3nVg== X-Gm-Message-State: AGRZ1gIonuZL4yEzalhPTwB8AoSHdbU56VTYynzgmxMBCwCfrMCEN+pE gbEYCaL2AVI3WE1q9Pq74KFijA== X-Google-Smtp-Source: AJdET5cJNV7IPnDyhU28H6D7jCTdXdaSAi42OMoFm4jW4HpXXYxzDcchu0+/4c+LAERMwEcW2NJXVA== X-Received: by 2002:a63:1204:: with SMTP id h4mr988699pgl.51.1542186477038; Wed, 14 Nov 2018 01:07:57 -0800 (PST) Received: from baolinwangubtpc.spreadtrum.com ([117.18.48.102]) by smtp.gmail.com with ESMTPSA id 127-v6sm25048814pfx.91.2018.11.14.01.07.53 (version=TLS1 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Wed, 14 Nov 2018 01:07:56 -0800 (PST) From: Baolin Wang To: sre@kernel.org, robh+dt@kernel.org, mark.rutland@arm.com Cc: linux-pm@vger.kernel.org, devicetree@vger.kernel.org, linux-kernel@vger.kernel.org, yuanjiang.yu@unisoc.com, baolin.wang@linaro.org, broonie@kernel.org Subject: [PATCH 4/5] power: supply: sc27xx: Add suspend/resume interfaces Date: Wed, 14 Nov 2018 17:07:07 +0800 Message-Id: X-Mailer: git-send-email 1.7.9.5 In-Reply-To: References: In-Reply-To: References: Sender: linux-pm-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-pm@vger.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP From: Yuanjiang Yu Add fuel gauge platform suspend and resume interfaces. In suspend state, we should enable the low voltage and coulomb counter threshold interrupts to wake up system to calibrate the battery capacity in lower voltage stage. Signed-off-by: Yuanjiang Yu Signed-off-by: Baolin Wang --- drivers/power/supply/sc27xx_fuel_gauge.c | 77 ++++++++++++++++++++++++++++++ 1 file changed, 77 insertions(+) diff --git a/drivers/power/supply/sc27xx_fuel_gauge.c b/drivers/power/supply/sc27xx_fuel_gauge.c index 962d0f8..8c52e29 100644 --- a/drivers/power/supply/sc27xx_fuel_gauge.c +++ b/drivers/power/supply/sc27xx_fuel_gauge.c @@ -789,6 +789,7 @@ static int sc27xx_fgu_probe(struct platform_device *pdev) data->bat_present = !!ret; mutex_init(&data->lock); data->dev = &pdev->dev; + platform_set_drvdata(pdev, data); fgu_cfg.drv_data = data; fgu_cfg.of_node = np; @@ -846,6 +847,81 @@ static int sc27xx_fgu_probe(struct platform_device *pdev) return 0; } +#ifdef CONFIG_PM_SLEEP +static int sc27xx_fgu_resume(struct device *dev) +{ + struct sc27xx_fgu_data *data = dev_get_drvdata(dev); + int ret; + + ret = regmap_update_bits(data->regmap, data->base + SC27XX_FGU_INT_EN, + SC27XX_FGU_LOW_OVERLOAD_INT | + SC27XX_FGU_CLBCNT_DELTA_INT, 0); + if (ret) { + dev_err(data->dev, "failed to disable fgu interrupts\n"); + return ret; + } + + return 0; +} + +static int sc27xx_fgu_suspend(struct device *dev) +{ + struct sc27xx_fgu_data *data = dev_get_drvdata(dev); + int ret, status, ocv; + + ret = sc27xx_fgu_get_status(data, &status); + if (ret) + return ret; + + /* + * If we are charging, then no need to enable the FGU interrupts to + * adjust the battery capacity. + */ + if (status != POWER_SUPPLY_STATUS_NOT_CHARGING) + return 0; + + ret = regmap_update_bits(data->regmap, data->base + SC27XX_FGU_INT_EN, + SC27XX_FGU_LOW_OVERLOAD_INT, + SC27XX_FGU_LOW_OVERLOAD_INT); + if (ret) { + dev_err(data->dev, "failed to enable low voltage interrupt\n"); + return ret; + } + + ret = sc27xx_fgu_get_vbat_ocv(data, &ocv); + if (ret) + goto disable_int; + + /* + * If current OCV is less than the minimum voltage, we should enable the + * coulomb counter threshold interrupt to notify events to adjust the + * battery capacity. + */ + if (ocv < data->min_volt) { + ret = regmap_update_bits(data->regmap, + data->base + SC27XX_FGU_INT_EN, + SC27XX_FGU_CLBCNT_DELTA_INT, + SC27XX_FGU_CLBCNT_DELTA_INT); + if (ret) { + dev_err(data->dev, + "failed to enable coulomb threshold int\n"); + goto disable_int; + } + } + + return 0; + +disable_int: + regmap_update_bits(data->regmap, data->base + SC27XX_FGU_INT_EN, + SC27XX_FGU_LOW_OVERLOAD_INT, 0); + return ret; +} +#endif + +static const struct dev_pm_ops sc27xx_fgu_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(sc27xx_fgu_suspend, sc27xx_fgu_resume) +}; + static const struct of_device_id sc27xx_fgu_of_match[] = { { .compatible = "sprd,sc2731-fgu", }, { } @@ -856,6 +932,7 @@ static int sc27xx_fgu_probe(struct platform_device *pdev) .driver = { .name = "sc27xx-fgu", .of_match_table = sc27xx_fgu_of_match, + .pm = &sc27xx_fgu_pm_ops, } }; From patchwork Wed Nov 14 09:07:08 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: "(Exiting) Baolin Wang" X-Patchwork-Id: 10682227 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 6E4F0139B for ; Wed, 14 Nov 2018 09:08:09 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 61D0C2AF3D for ; Wed, 14 Nov 2018 09:08:09 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 55C0A2AF46; Wed, 14 Nov 2018 09:08:09 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-8.0 required=2.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,MAILING_LIST_MULTI,RCVD_IN_DNSWL_HI autolearn=ham version=3.3.1 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id B23692AF3D for ; Wed, 14 Nov 2018 09:08:08 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1732516AbeKNTKX (ORCPT ); Wed, 14 Nov 2018 14:10:23 -0500 Received: from mail-pg1-f195.google.com ([209.85.215.195]:41626 "EHLO mail-pg1-f195.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1732511AbeKNTKW (ORCPT ); Wed, 14 Nov 2018 14:10:22 -0500 Received: by mail-pg1-f195.google.com with SMTP id 70so7074005pgh.8 for ; Wed, 14 Nov 2018 01:08:00 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linaro.org; s=google; h=from:to:cc:subject:date:message-id:in-reply-to:references :in-reply-to:references; bh=IRUXU/GFwnJl002dQVlSIACjSnNwZF6k9T9koevdIfU=; b=MgP2KIyE/Z5xkWXYph0HjfOigzpkQYVms1EkRMG4ehWPeNBeBF56HsZYWpv/crCUwL vj9Nr1LnZKKUpmBLIYImVHwp0VweT4krV8Jaj8tvmvEcR9zAZ/ZbNHiS4fAZdf90X8tG D1MLf6kMvinSd5hIqHSHPvxJDQ0e4s6RFrWB0= X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:in-reply-to:references; bh=IRUXU/GFwnJl002dQVlSIACjSnNwZF6k9T9koevdIfU=; b=tEbuJyWaUsHwveIbaogH4Z4fGMuQ0Td1NbM2zB72q40Q2hpnn088xchu96TF07hTrt lgsCXmKaYQUWkV9m7N1sLbpNH2nQ5oXi/0Vj/MFFXs5bj9zE8jFsSIKrV9n6cSa9lxux xSVthj2A2rQOSkzhhojFHkyN6DRdDF2NB/cvnsJVSDix4wHq5OYRtbYPos/nPCfI2abg aU2uRSyqhJ2Ic6kvY+9r9lkzwEtTkYWKll6zRiq69arHcN2QQmNi3NpRBpG4U37Ac/wn Ne2tUU5OJI1OpDh+W00LDy/NGa5oKVWW1L8B/tNTqxVoD+8WjuTr1OgEm4x0cggxp69y Mhbg== X-Gm-Message-State: AGRZ1gKIOzroiRm+YKFzkXgsj0V60/Mf26cjsRuHJt1BPnhWjVdee2u1 FWCndKjLumvPjJiz/kFGEbtWog== X-Google-Smtp-Source: AJdET5fS2/rM84cBs7+I7ETXCiaqASRxD6WHfhPOnYcdusmmF006ASXy38lhUKPXrr84G6tp7uYrpQ== X-Received: by 2002:a63:f710:: with SMTP id x16mr1005124pgh.322.1542186480492; Wed, 14 Nov 2018 01:08:00 -0800 (PST) Received: from baolinwangubtpc.spreadtrum.com ([117.18.48.102]) by smtp.gmail.com with ESMTPSA id 127-v6sm25048814pfx.91.2018.11.14.01.07.57 (version=TLS1 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Wed, 14 Nov 2018 01:07:59 -0800 (PST) From: Baolin Wang To: sre@kernel.org, robh+dt@kernel.org, mark.rutland@arm.com Cc: linux-pm@vger.kernel.org, devicetree@vger.kernel.org, linux-kernel@vger.kernel.org, yuanjiang.yu@unisoc.com, baolin.wang@linaro.org, broonie@kernel.org Subject: [PATCH 5/5] power: supply: sc27xx: Save last battery capacity Date: Wed, 14 Nov 2018 17:07:08 +0800 Message-Id: X-Mailer: git-send-email 1.7.9.5 In-Reply-To: References: In-Reply-To: References: Sender: linux-pm-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-pm@vger.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP From: Yuanjiang Yu Our charger manager can optimize the battery capacity periodically, so we can save last battery capacity into registers. Then next system power-on, we can read the last saved battery capacity as the initial battery capacity, which can make the battery capacity more accurate. Signed-off-by: Yuanjiang Yu Signed-off-by: Baolin Wang --- drivers/power/supply/sc27xx_fuel_gauge.c | 143 +++++++++++++++++++++++++++++- 1 file changed, 142 insertions(+), 1 deletion(-) diff --git a/drivers/power/supply/sc27xx_fuel_gauge.c b/drivers/power/supply/sc27xx_fuel_gauge.c index 8c52e29..181a8f7 100644 --- a/drivers/power/supply/sc27xx_fuel_gauge.c +++ b/drivers/power/supply/sc27xx_fuel_gauge.c @@ -39,6 +39,9 @@ #define SC27XX_FGU_CLBCNT_VALH 0x68 #define SC27XX_FGU_CLBCNT_VALL 0x6c #define SC27XX_FGU_CLBCNT_QMAXL 0x74 +#define SC27XX_FGU_USER_AREA_SET 0xa0 +#define SC27XX_FGU_USER_AREA_CLEAR 0xa4 +#define SC27XX_FGU_USER_AREA_STATUS 0xa8 #define SC27XX_WRITE_SELCLB_EN BIT(0) #define SC27XX_FGU_CLBCNT_MASK GENMASK(15, 0) @@ -49,6 +52,14 @@ #define SC27XX_FGU_LOW_OVERLOAD_INT BIT(0) #define SC27XX_FGU_CLBCNT_DELTA_INT BIT(2) +#define SC27XX_FGU_MODE_AREA_MASK GENMASK(15, 12) +#define SC27XX_FGU_CAP_AREA_MASK GENMASK(11, 0) +#define SC27XX_FGU_MODE_AREA_SHIFT 12 + +#define SC27XX_FGU_FIRST_POWERTON GENMASK(3, 0) +#define SC27XX_FGU_DEFAULT_CAP GENMASK(11, 0) +#define SC27XX_FGU_NORMAIL_POWERTON 0x5 + #define SC27XX_FGU_CUR_BASIC_ADC 8192 #define SC27XX_FGU_SAMPLE_HZ 2 @@ -119,6 +130,80 @@ static int sc27xx_fgu_voltage_to_adc(struct sc27xx_fgu_data *data, int vol) return DIV_ROUND_CLOSEST(vol * data->vol_1000mv_adc, 1000); } +static bool sc27xx_fgu_is_first_poweron(struct sc27xx_fgu_data *data) +{ + int ret, status, cap, mode; + + ret = regmap_read(data->regmap, + data->base + SC27XX_FGU_USER_AREA_STATUS, &status); + if (ret) + return false; + + /* + * We use low 4 bits to save the last battery capacity and high 12 bits + * to save the system boot mode. + */ + mode = (status & SC27XX_FGU_MODE_AREA_MASK) >> SC27XX_FGU_MODE_AREA_SHIFT; + cap = status & SC27XX_FGU_CAP_AREA_MASK; + + /* + * When FGU has been powered down, the user area registers became + * default value (0xffff), which can be used to valid if the system is + * first power on or not. + */ + if (mode == SC27XX_FGU_FIRST_POWERTON || cap == SC27XX_FGU_DEFAULT_CAP) + return true; + + return false; +} + +static int sc27xx_fgu_save_boot_mode(struct sc27xx_fgu_data *data, + int boot_mode) +{ + int ret; + + ret = regmap_update_bits(data->regmap, + data->base + SC27XX_FGU_USER_AREA_CLEAR, + SC27XX_FGU_MODE_AREA_MASK, + SC27XX_FGU_MODE_AREA_MASK); + if (ret) + return ret; + + return regmap_update_bits(data->regmap, + data->base + SC27XX_FGU_USER_AREA_SET, + SC27XX_FGU_MODE_AREA_MASK, + boot_mode << SC27XX_FGU_MODE_AREA_SHIFT); +} + +static int sc27xx_fgu_save_last_cap(struct sc27xx_fgu_data *data, int cap) +{ + int ret; + + ret = regmap_update_bits(data->regmap, + data->base + SC27XX_FGU_USER_AREA_CLEAR, + SC27XX_FGU_CAP_AREA_MASK, + SC27XX_FGU_CAP_AREA_MASK); + if (ret) + return ret; + + return regmap_update_bits(data->regmap, + data->base + SC27XX_FGU_USER_AREA_SET, + SC27XX_FGU_CAP_AREA_MASK, cap); +} + +static int sc27xx_fgu_read_last_cap(struct sc27xx_fgu_data *data, int *cap) +{ + int ret, value; + + ret = regmap_read(data->regmap, + data->base + SC27XX_FGU_USER_AREA_STATUS, &value); + if (ret) + return ret; + + *cap = value & SC27XX_FGU_CAP_AREA_MASK; + return 0; +} + /* * When system boots on, we can not read battery capacity from coulomb * registers, since now the coulomb registers are invalid. So we should @@ -128,6 +213,20 @@ static int sc27xx_fgu_voltage_to_adc(struct sc27xx_fgu_data *data, int vol) static int sc27xx_fgu_get_boot_capacity(struct sc27xx_fgu_data *data, int *cap) { int volt, cur, oci, ocv, ret; + bool is_first_poweron = sc27xx_fgu_is_first_poweron(data); + + /* + * If system is not the first power on, we should use the last saved + * battery capacity as the initial battery capacity. Otherwise we should + * re-calculate the initial battery capacity. + */ + if (!is_first_poweron) { + ret = sc27xx_fgu_read_last_cap(data, cap); + if (ret) + return ret; + + return sc27xx_fgu_save_boot_mode(data, SC27XX_FGU_NORMAIL_POWERTON); + } /* * After system booting on, the SC27XX_FGU_CLBCNT_QMAXL register saved @@ -160,7 +259,11 @@ static int sc27xx_fgu_get_boot_capacity(struct sc27xx_fgu_data *data, int *cap) *cap = power_supply_ocv2cap_simple(data->cap_table, data->table_len, ocv); - return 0; + ret = sc27xx_fgu_save_last_cap(data, *cap); + if (ret) + return ret; + + return sc27xx_fgu_save_boot_mode(data, SC27XX_FGU_NORMAIL_POWERTON); } static int sc27xx_fgu_set_clbcnt(struct sc27xx_fgu_data *data, int clbcnt) @@ -418,6 +521,30 @@ static int sc27xx_fgu_get_property(struct power_supply *psy, return ret; } +static int sc27xx_fgu_set_property(struct power_supply *psy, + enum power_supply_property psp, + const union power_supply_propval *val) +{ + struct sc27xx_fgu_data *data = power_supply_get_drvdata(psy); + int ret; + + mutex_lock(&data->lock); + + switch (psp) { + case POWER_SUPPLY_PROP_CAPACITY: + ret = sc27xx_fgu_save_last_cap(data, val->intval); + if (ret < 0) + dev_err(data->dev, "failed to save battery capacity\n"); + break; + + default: + ret = -EINVAL; + } + + mutex_unlock(&data->lock); + return ret; +} + static void sc27xx_fgu_external_power_changed(struct power_supply *psy) { struct sc27xx_fgu_data *data = power_supply_get_drvdata(psy); @@ -425,6 +552,18 @@ static void sc27xx_fgu_external_power_changed(struct power_supply *psy) power_supply_changed(data->battery); } +static int sc27xx_fgu_property_is_writeable(struct power_supply *psy, + enum power_supply_property psp) +{ + switch (psp) { + case POWER_SUPPLY_PROP_CAPACITY: + return 1; + + default: + return 0; + } +} + static enum power_supply_property sc27xx_fgu_props[] = { POWER_SUPPLY_PROP_STATUS, POWER_SUPPLY_PROP_HEALTH, @@ -444,7 +583,9 @@ static void sc27xx_fgu_external_power_changed(struct power_supply *psy) .properties = sc27xx_fgu_props, .num_properties = ARRAY_SIZE(sc27xx_fgu_props), .get_property = sc27xx_fgu_get_property, + .set_property = sc27xx_fgu_set_property, .external_power_changed = sc27xx_fgu_external_power_changed, + .property_is_writeable = sc27xx_fgu_property_is_writeable, }; static void sc27xx_fgu_adjust_cap(struct sc27xx_fgu_data *data, int cap)