From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from mail-ej1-f48.google.com (mail-ej1-f48.google.com [209.85.218.48]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 1EBDF269811 for ; Fri, 5 Sep 2025 07:09:32 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.218.48 ARC-Seal:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1757056177; cv=none; b=ql8mNydlpDjfDPc4yHcXPv6WJOinCx4cur27XKpYL7PAf7zeo2C76nPr33aSutog3KbRqHRKeTcRjQRr9FZ9dXKAy/j+xtkShRuTpPVLyNaNBuHaHxpBjH9G1qi6KfevKfo2zbGkXA6OIRkW4STZXdOWs0CIMBXkzd6ZPC6BP3Q= ARC-Message-Signature:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1757056177; c=relaxed/simple; bh=guh71gia0r4Havl90xYEm9FHPV2X3Ina5OoDADl3lek=; h=Message-ID:Date:MIME-Version:From:Subject:To:Cc:References: In-Reply-To:Content-Type; b=pnXT85IXgJx2zbXHsbLwmD8YO8hudJpCrNrZuTvrLZD1V9ij0xC5iGejNJxT3+KJg2HclUVQU/zn7p3NPca9NE5pvrwJH2AHzGiMuJjdmAW9+jPYmDILmhJTM6ZkB+53pjxnuKVka3KGp1iu6V+ld9MOrXbwoMBac4wD1fNbIjU= ARC-Authentication-Results:i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linaro.org; spf=pass smtp.mailfrom=linaro.org; dkim=pass (2048-bit key) header.d=linaro.org header.i=@linaro.org header.b=G7ZE3xJW; arc=none smtp.client-ip=209.85.218.48 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linaro.org Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=linaro.org Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=linaro.org header.i=@linaro.org header.b="G7ZE3xJW" Received: by mail-ej1-f48.google.com with SMTP id a640c23a62f3a-afec5651966so363975566b.2 for ; Fri, 05 Sep 2025 00:09:32 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linaro.org; s=google; t=1757056171; x=1757660971; darn=vger.kernel.org; h=content-transfer-encoding:in-reply-to:organization:autocrypt :content-language:references:cc:to:subject:reply-to:from:user-agent :mime-version:date:message-id:from:to:cc:subject:date:message-id :reply-to; bh=w9QKVc+nKCFa2lUIO4Rrq4SKqTv7ZnawQMkLOI5coK8=; b=G7ZE3xJW93c8HKCnBM0OFDJcAjE4JOli4TCW+iM1VkbP8VqESbLF9IZN0Dsr7ZIzZi 0ypNK+HRqiTG9duvUmMr0SgpL1oXlBteo8qkbbpwCPXAv29outYTkKDT2N6hKGv6bEmg 53V6pKF45y51G68V8WGRctbfOmUT9gZu5vg7JDweBDPwY0HYmPJ4nLMmS/RTXpmxVYHp pu0Cg2l4IPzI9CJSXq88sS91ZD6+9vzULlmMmzPbSjJsG/CK9uv5TPrrCNjhajk9cIoH B3OFt5B9oA0JhwxKnI14YJS2Z3NZBk17NMXm1auPsUIK+Y7OUfjV2rb7W3AihEgmK2a+ MyZw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1757056171; x=1757660971; h=content-transfer-encoding:in-reply-to:organization:autocrypt :content-language:references:cc:to:subject:reply-to:from:user-agent :mime-version:date:message-id:x-gm-message-state:from:to:cc:subject :date:message-id:reply-to; bh=w9QKVc+nKCFa2lUIO4Rrq4SKqTv7ZnawQMkLOI5coK8=; b=a/bRzHRXD9rV8Fy7hzvPMkgFHZjnBJWSda2ht6c80n7Zo3YmBjukDhibQpP/F3ayS6 jfoe1LwcLFSG29UUHfDlH6ykZbdIfoKAKvoGZVFvvZUSR2tfjDHE3u1HHEMtsq5UnvvP UfMy1MyrHLwANqID32t0/xsxfsQ2bWaBUYPoKGdlMwQSnrr6XLXOqXQnCeyx7W9QmkA8 TkJlmbsuE1k32jGxYVz/UxETdP6VBTbSWXLr6BeXuvEFONfYLEMchRazVkXqeutLxtmN Vptpdcju61ZWzOnYQX37MS/Lfb5EO3MvMGHb0S3kXYAZRqhUFDOeqKieZDxzKMiqG+QM INog== X-Forwarded-Encrypted: i=1; AJvYcCWXxGHaiYzCG0qy91KHb+pdu7lWqAj47Pt1w7JDTrBZxWfIHXXwmx87zKYe0aiyZsSb/tV3CUmZStaqh8pP@vger.kernel.org X-Gm-Message-State: AOJu0YwyYPlLKrxtTImm7bzCTCFCeCcmwF/Cl6CQMnp5u/9p+WD/MKq8 zf8Hq43uUWwrQ1CwMf0QJ1tK+B79VfeQP9P8eAogKbeu3Q7avu0uzEInLsqnjPgnJRw= X-Gm-Gg: ASbGncu/+hH4g3A+6kE5/+JaJPRQ5NZ1oQRTrkn4i38YnH9rkH9reZzP7cnhq6LjzFW EXKO74panBX5sI6SMJU/MouUP/BDBrlmD09gvvt2KpF4GN9L/v2SYdUDEF0xpuot3z1gR1Ozg0u RSgaYyNx6lXCHjHqz93lCnh5cDgey4o2/nqko/UCK7GkFHqc8fKVatKUIgvWhoVkq+Re3lNDgAv t2WMOzJ+v5uehB8WDvwykNvop/nX1MzB+MH+I1xPYmFjwFCLGz42N9yEPjU7YxH5oh5597XwowD XrmM0W97dT4WEIxlC3pYZoLwKeZ5/ZShf+EgtsIVODxjb90X8BEWyr6rXF+jhOvKkA7jp1boAb6 Lei10AD6hvNm3KVP0tlVYa7uGQcJlX97MLQ== X-Google-Smtp-Source: AGHT+IHq6jK1abcIGWZ9lFmBlU8nNCpQKhB4LAkndBx44fVoZz3+Zdw5vpcLio+Ty19wIa4RFW/6Ug== X-Received: by 2002:a17:907:3fa8:b0:b04:54dc:3030 with SMTP id a640c23a62f3a-b0454dc336fmr1320714366b.18.1757056171314; Fri, 05 Sep 2025 00:09:31 -0700 (PDT) Received: from [192.168.7.190] ([212.114.21.58]) by smtp.gmail.com with ESMTPSA id a640c23a62f3a-b04252103f2sm1218447166b.50.2025.09.05.00.09.30 (version=TLS1_3 cipher=TLS_AES_128_GCM_SHA256 bits=128/128); Fri, 05 Sep 2025 00:09:30 -0700 (PDT) Message-ID: <3f394977-51b2-4309-ae6b-75cd1980a9c5@linaro.org> Date: Fri, 5 Sep 2025 09:09:28 +0200 Precedence: bulk X-Mailing-List: linux-arm-msm@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 User-Agent: Mozilla Thunderbird From: Neil Armstrong Reply-To: Neil Armstrong Subject: Re: [PATCH v3 7/8] power: supply: qcom_battmgr: Add charge control support To: Fenglin Wu , Sebastian Reichel , Bjorn Andersson , Konrad Dybcio , Rob Herring , Krzysztof Kozlowski , Conor Dooley , Heikki Krogerus , Greg Kroah-Hartman , Dmitry Baryshkov , Bryan O'Donoghue , Konrad Dybcio Cc: Subbaraman Narayanamurthy , David Collins , =?UTF-8?Q?Gy=C3=B6rgy_Kurucz?= , linux-pm@vger.kernel.org, linux-kernel@vger.kernel.org, linux-arm-msm@vger.kernel.org, kernel@oss.qualcomm.com, devicetree@vger.kernel.org, linux-usb@vger.kernel.org References: <20250826-qcom_battmgr_update-v3-0-74ea410ef146@oss.qualcomm.com> <20250826-qcom_battmgr_update-v3-7-74ea410ef146@oss.qualcomm.com> <76d12f47-bdb0-404a-9371-788e116c3474@oss.qualcomm.com> Content-Language: en-US, fr Autocrypt: addr=neil.armstrong@linaro.org; keydata= xsBNBE1ZBs8BCAD78xVLsXPwV/2qQx2FaO/7mhWL0Qodw8UcQJnkrWmgTFRobtTWxuRx8WWP GTjuhvbleoQ5Cxjr+v+1ARGCH46MxFP5DwauzPekwJUD5QKZlaw/bURTLmS2id5wWi3lqVH4 BVF2WzvGyyeV1o4RTCYDnZ9VLLylJ9bneEaIs/7cjCEbipGGFlfIML3sfqnIvMAxIMZrvcl9 qPV2k+KQ7q+aXavU5W+yLNn7QtXUB530Zlk/d2ETgzQ5FLYYnUDAaRl+8JUTjc0CNOTpCeik 80TZcE6f8M76Xa6yU8VcNko94Ck7iB4vj70q76P/J7kt98hklrr85/3NU3oti3nrIHmHABEB AAHNKk5laWwgQXJtc3Ryb25nIDxuZWlsLmFybXN0cm9uZ0BsaW5hcm8ub3JnPsLAkQQTAQoA OwIbIwULCQgHAwUVCgkICwUWAgMBAAIeAQIXgBYhBInsPQWERiF0UPIoSBaat7Gkz/iuBQJk Q5wSAhkBAAoJEBaat7Gkz/iuyhMIANiD94qDtUTJRfEW6GwXmtKWwl/mvqQtaTtZID2dos04 YqBbshiJbejgVJjy+HODcNUIKBB3PSLaln4ltdsV73SBcwUNdzebfKspAQunCM22Mn6FBIxQ GizsMLcP/0FX4en9NaKGfK6ZdKK6kN1GR9YffMJd2P08EO8mHowmSRe/ExAODhAs9W7XXExw UNCY4pVJyRPpEhv373vvff60bHxc1k/FF9WaPscMt7hlkbFLUs85kHtQAmr8pV5Hy9ezsSRa GzJmiVclkPc2BY592IGBXRDQ38urXeM4nfhhvqA50b/nAEXc6FzqgXqDkEIwR66/Gbp0t3+r yQzpKRyQif3OwE0ETVkGzwEIALyKDN/OGURaHBVzwjgYq+ZtifvekdrSNl8TIDH8g1xicBYp QTbPn6bbSZbdvfeQPNCcD4/EhXZuhQXMcoJsQQQnO4vwVULmPGgtGf8PVc7dxKOeta+qUh6+ SRh3vIcAUFHDT3f/Zdspz+e2E0hPV2hiSvICLk11qO6cyJE13zeNFoeY3ggrKY+IzbFomIZY 4yG6xI99NIPEVE9lNBXBKIlewIyVlkOaYvJWSV+p5gdJXOvScNN1epm5YHmf9aE2ZjnqZGoM Mtsyw18YoX9BqMFInxqYQQ3j/HpVgTSvmo5ea5qQDDUaCsaTf8UeDcwYOtgI8iL4oHcsGtUX oUk33HEAEQEAAcLAXwQYAQIACQUCTVkGzwIbDAAKCRAWmrexpM/4rrXiB/sGbkQ6itMrAIfn M7IbRuiSZS1unlySUVYu3SD6YBYnNi3G5EpbwfBNuT3H8//rVvtOFK4OD8cRYkxXRQmTvqa3 3eDIHu/zr1HMKErm+2SD6PO9umRef8V82o2oaCLvf4WeIssFjwB0b6a12opuRP7yo3E3gTCS KmbUuLv1CtxKQF+fUV1cVaTPMyT25Od+RC1K+iOR0F54oUJvJeq7fUzbn/KdlhA8XPGzwGRy 4zcsPWvwnXgfe5tk680fEKZVwOZKIEuJC3v+/yZpQzDvGYJvbyix0lHnrCzq43WefRHI5XTT QbM0WUIBIcGmq38+OgUsMYu4NzLu7uZFAcmp6h8g Organization: Linaro In-Reply-To: <76d12f47-bdb0-404a-9371-788e116c3474@oss.qualcomm.com> Content-Type: text/plain; charset=UTF-8; format=flowed Content-Transfer-Encoding: 8bit On 05/09/2025 03:19, Fenglin Wu wrote: > > On 9/3/2025 4:29 PM, Neil Armstrong wrote: >> On 26/08/2025 09:18, Fenglin Wu via B4 Relay wrote: >>> From: Fenglin Wu >>> >>> Add charge control support for SM8550 and X1E80100. It's supported >>> with below two power supply properties: >>> >>> charge_control_end_threshold: The battery SoC (State of Charge) >>> threshold at which the charging should be terminated. >>> >>> charge_control_start_threshold: The battery SoC threshold at >>> which the charging should be resumed. >>> >>> Signed-off-by: Fenglin Wu >>> --- >>>   drivers/power/supply/qcom_battmgr.c | 290 +++++++++++++++++++++++++++++++++++- >>>   1 file changed, 283 insertions(+), 7 deletions(-) >>> >>> diff --git a/drivers/power/supply/qcom_battmgr.c b/drivers/power/supply/qcom_battmgr.c >>> index 174d3f83ac2b070bb90c21a498686e91cc629ebe..23c68eb942f1b213f634e31a45420b3e113e2764 100644 >>> --- a/drivers/power/supply/qcom_battmgr.c >>> +++ b/drivers/power/supply/qcom_battmgr.c >>> @@ -7,6 +7,7 @@ >>>   #include >>>   #include >>>   #include >>> +#include >>>   #include >>>   #include >>>   #include >>> @@ -68,6 +69,9 @@ enum qcom_battmgr_variant { >>>   #define BATT_RESISTANCE            21 >>>   #define BATT_POWER_NOW            22 >>>   #define BATT_POWER_AVG            23 >>> +#define BATT_CHG_CTRL_EN        24 >>> +#define BATT_CHG_CTRL_START_THR        25 >>> +#define BATT_CHG_CTRL_END_THR        26 >>>     #define BATTMGR_USB_PROPERTY_GET    0x32 >>>   #define BATTMGR_USB_PROPERTY_SET    0x33 >>> @@ -92,6 +96,13 @@ enum qcom_battmgr_variant { >>>   #define WLS_TYPE            5 >>>   #define WLS_BOOST_EN            6 >>>   +#define BATTMGR_CHG_CTRL_LIMIT_EN    0x48 >>> +#define CHARGE_CTRL_START_THR_MIN    50 >>> +#define CHARGE_CTRL_START_THR_MAX    95 >>> +#define CHARGE_CTRL_END_THR_MIN        55 >>> +#define CHARGE_CTRL_END_THR_MAX        100 >>> +#define CHARGE_CTRL_DELTA_SOC        5 >>> + >>>   struct qcom_battmgr_enable_request { >>>       struct pmic_glink_hdr hdr; >>>       __le32 battery_id; >>> @@ -126,6 +137,13 @@ struct qcom_battmgr_discharge_time_request { >>>       __le32 reserved; >>>   }; >>>   +struct qcom_battmgr_charge_ctrl_request { >>> +    struct pmic_glink_hdr hdr; >>> +    __le32 enable; >>> +    __le32 target_soc; >>> +    __le32 delta_soc; >>> +}; >>> + >>>   struct qcom_battmgr_message { >>>       struct pmic_glink_hdr hdr; >>>       union { >>> @@ -238,6 +256,8 @@ struct qcom_battmgr_info { >>>       unsigned int capacity_warning; >>>       unsigned int cycle_count; >>>       unsigned int charge_count; >>> +    unsigned int charge_ctrl_start; >>> +    unsigned int charge_ctrl_end; >>>       char model_number[BATTMGR_STRING_LEN]; >>>       char serial_number[BATTMGR_STRING_LEN]; >>>       char oem_info[BATTMGR_STRING_LEN]; >>> @@ -426,6 +446,8 @@ static const u8 sm8350_bat_prop_map[] = { >>>       [POWER_SUPPLY_PROP_INTERNAL_RESISTANCE] = BATT_RESISTANCE, >>>       [POWER_SUPPLY_PROP_STATE_OF_HEALTH] = BATT_SOH, >>>       [POWER_SUPPLY_PROP_POWER_NOW] = BATT_POWER_NOW, >>> +    [POWER_SUPPLY_PROP_CHARGE_CONTROL_START_THRESHOLD] = BATT_CHG_CTRL_START_THR, >>> +    [POWER_SUPPLY_PROP_CHARGE_CONTROL_END_THRESHOLD] = BATT_CHG_CTRL_END_THR, >>>   }; >>>     static int qcom_battmgr_bat_sm8350_update(struct qcom_battmgr *battmgr, >>> @@ -496,7 +518,8 @@ static int qcom_battmgr_bat_get_property(struct power_supply *psy, >>>       if (!battmgr->service_up) >>>           return -EAGAIN; >>>   -    if (battmgr->variant == QCOM_BATTMGR_SC8280XP) >>> +    if (battmgr->variant == QCOM_BATTMGR_SC8280XP || >>> +        battmgr->variant == QCOM_BATTMGR_X1E80100) >>>           ret = qcom_battmgr_bat_sc8280xp_update(battmgr, psp); >>>       else >>>           ret = qcom_battmgr_bat_sm8350_update(battmgr, psp); >>> @@ -601,6 +624,12 @@ static int qcom_battmgr_bat_get_property(struct power_supply *psy, >>>       case POWER_SUPPLY_PROP_TIME_TO_FULL_AVG: >>>           val->intval = battmgr->status.charge_time; >>>           break; >>> +    case POWER_SUPPLY_PROP_CHARGE_CONTROL_START_THRESHOLD: >>> +        val->intval = battmgr->info.charge_ctrl_start; >>> +        break; >>> +    case POWER_SUPPLY_PROP_CHARGE_CONTROL_END_THRESHOLD: >>> +        val->intval = battmgr->info.charge_ctrl_end; >>> +        break; >>>       case POWER_SUPPLY_PROP_MANUFACTURE_YEAR: >>>           val->intval = battmgr->info.year; >>>           break; >>> @@ -626,6 +655,149 @@ static int qcom_battmgr_bat_get_property(struct power_supply *psy, >>>       return 0; >>>   } >>>   +static int qcom_battmgr_set_charge_control(struct qcom_battmgr *battmgr, >>> +                       u32 target_soc, u32 delta_soc) >>> +{ >>> +    struct qcom_battmgr_charge_ctrl_request request = { >>> +        .hdr.owner = cpu_to_le32(PMIC_GLINK_OWNER_BATTMGR), >>> +        .hdr.type = cpu_to_le32(PMIC_GLINK_REQ_RESP), >>> +        .hdr.opcode = cpu_to_le32(BATTMGR_CHG_CTRL_LIMIT_EN), >>> +        .enable = cpu_to_le32(1), >>> +        .target_soc = cpu_to_le32(target_soc), >>> +        .delta_soc = cpu_to_le32(delta_soc), >>> +    }; >>> + >>> +    return qcom_battmgr_request(battmgr, &request, sizeof(request)); >>> +} >>> + >>> +static int qcom_battmgr_set_charge_start_threshold(struct qcom_battmgr *battmgr, int start_soc) >>> +{ >>> +    u32 target_soc, delta_soc; >>> +    int ret; >>> + >>> +    if (start_soc < CHARGE_CTRL_START_THR_MIN || >>> +        start_soc > CHARGE_CTRL_START_THR_MAX) { >>> +        dev_err(battmgr->dev, "charge control start threshold exceed range: [%u - %u]\n", >>> +            CHARGE_CTRL_START_THR_MIN, CHARGE_CTRL_START_THR_MAX); >>> +        return -EINVAL; >>> +    } >>> + >>> +    /* >>> +     * If the new start threshold is larger than the old end threshold, >>> +     * move the end threshold one step (DELTA_SOC) after the new start >>> +     * threshold. >>> +     */ >>> +    if (start_soc > battmgr->info.charge_ctrl_end) { >>> +        target_soc = start_soc + CHARGE_CTRL_DELTA_SOC; >>> +        target_soc = min_t(u32, target_soc, CHARGE_CTRL_END_THR_MAX); >>> +        delta_soc = target_soc - start_soc; >>> +        delta_soc = min_t(u32, delta_soc, CHARGE_CTRL_DELTA_SOC); >>> +    } else { >>> +        target_soc =  battmgr->info.charge_ctrl_end; >>> +        delta_soc = battmgr->info.charge_ctrl_end - start_soc; >>> +    } >>> + >>> +    mutex_lock(&battmgr->lock); >>> +    ret = qcom_battmgr_set_charge_control(battmgr, target_soc, delta_soc); >>> +    mutex_unlock(&battmgr->lock); >>> +    if (!ret) { >>> +        battmgr->info.charge_ctrl_start = start_soc; >>> +        battmgr->info.charge_ctrl_end = target_soc; >>> +    } >>> + >>> +    return 0; >>> +} >>> + >>> +static int qcom_battmgr_set_charge_end_threshold(struct qcom_battmgr *battmgr, int end_soc) >>> +{ >>> +    u32 delta_soc = CHARGE_CTRL_DELTA_SOC; >>> +    int ret; >>> + >>> +    if (end_soc < CHARGE_CTRL_END_THR_MIN || >>> +        end_soc > CHARGE_CTRL_END_THR_MAX) { >>> +        dev_err(battmgr->dev, "charge control end threshold exceed range: [%u - %u]\n", >>> +            CHARGE_CTRL_END_THR_MIN, CHARGE_CTRL_END_THR_MAX); >>> +        return -EINVAL; >>> +    } >>> + >>> +    if (battmgr->info.charge_ctrl_start && end_soc > battmgr->info.charge_ctrl_start) >>> +        delta_soc = end_soc - battmgr->info.charge_ctrl_start; >>> + >>> +    mutex_lock(&battmgr->lock); >>> +    ret = qcom_battmgr_set_charge_control(battmgr, end_soc, delta_soc); >>> +    mutex_unlock(&battmgr->lock); >>> +    if (!ret) { >>> +        battmgr->info.charge_ctrl_start = end_soc - delta_soc; >>> +        battmgr->info.charge_ctrl_end = end_soc; >>> +    } >>> + >>> +    return 0; >>> +} >>> + >>> +static int qcom_battmgr_charge_control_thresholds_init(struct qcom_battmgr *battmgr) >>> +{ >>> +    int ret; >>> +    u8 en, end_soc, start_soc, delta_soc; >>> + >>> +    ret = nvmem_cell_read_u8(battmgr->dev->parent, "charge_limit_en", &en); >>> +    if (!ret && en != 0) { >>> +        ret = nvmem_cell_read_u8(battmgr->dev->parent, "charge_limit_end", &end_soc); >>> +        if (ret < 0) >>> +            return ret; >>> + >>> +        ret = nvmem_cell_read_u8(battmgr->dev->parent, "charge_limit_delta", &delta_soc); >>> +        if (ret < 0) >>> +            return ret; >>> + >>> +        if (delta_soc >= end_soc) >>> +            return -EINVAL; >>> + >>> +        start_soc = end_soc - delta_soc; >>> +        end_soc = clamp(end_soc, CHARGE_CTRL_END_THR_MIN, CHARGE_CTRL_END_THR_MAX); >>> +        start_soc = clamp(start_soc, CHARGE_CTRL_START_THR_MIN, CHARGE_CTRL_START_THR_MAX); >>> + >>> +        battmgr->info.charge_ctrl_start = start_soc; >>> +        battmgr->info.charge_ctrl_end = end_soc; >>> +    } >>> + >>> +    return 0; >>> +} >>> + >>> +static int qcom_battmgr_bat_is_writeable(struct power_supply *psy, >>> +                     enum power_supply_property psp) >>> +{ >>> +    switch (psp) { >>> +    case POWER_SUPPLY_PROP_CHARGE_CONTROL_START_THRESHOLD: >>> +    case POWER_SUPPLY_PROP_CHARGE_CONTROL_END_THRESHOLD: >>> +        return 1; >>> +    default: >>> +        return 0; >>> +    } >>> + >>> +    return 0; >>> +} >>> + >>> +static int qcom_battmgr_bat_set_property(struct power_supply *psy, >>> +                     enum power_supply_property psp, >>> +                     const union power_supply_propval *pval) >>> +{ >>> +    struct qcom_battmgr *battmgr = power_supply_get_drvdata(psy); >>> + >>> +    if (!battmgr->service_up) >>> +        return -EAGAIN; >>> + >>> +    switch (psp) { >>> +    case POWER_SUPPLY_PROP_CHARGE_CONTROL_START_THRESHOLD: >>> +        return qcom_battmgr_set_charge_start_threshold(battmgr, pval->intval); >>> +    case POWER_SUPPLY_PROP_CHARGE_CONTROL_END_THRESHOLD: >>> +        return qcom_battmgr_set_charge_end_threshold(battmgr, pval->intval); >>> +    default: >>> +        return -EINVAL; >>> +    } >>> + >>> +    return 0; >>> +} >>> + >>>   static const enum power_supply_property sc8280xp_bat_props[] = { >>>       POWER_SUPPLY_PROP_STATUS, >>>       POWER_SUPPLY_PROP_PRESENT, >>> @@ -659,6 +831,43 @@ static const struct power_supply_desc sc8280xp_bat_psy_desc = { >>>       .get_property = qcom_battmgr_bat_get_property, >>>   }; >>>   +static const enum power_supply_property x1e80100_bat_props[] = { >>> +    POWER_SUPPLY_PROP_STATUS, >>> +    POWER_SUPPLY_PROP_PRESENT, >>> +    POWER_SUPPLY_PROP_TECHNOLOGY, >>> +    POWER_SUPPLY_PROP_CYCLE_COUNT, >>> +    POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN, >>> +    POWER_SUPPLY_PROP_VOLTAGE_NOW, >>> +    POWER_SUPPLY_PROP_POWER_NOW, >>> +    POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN, >>> +    POWER_SUPPLY_PROP_CHARGE_FULL, >>> +    POWER_SUPPLY_PROP_CHARGE_EMPTY, >>> +    POWER_SUPPLY_PROP_CHARGE_NOW, >>> +    POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN, >>> +    POWER_SUPPLY_PROP_ENERGY_FULL, >>> +    POWER_SUPPLY_PROP_ENERGY_EMPTY, >>> +    POWER_SUPPLY_PROP_ENERGY_NOW, >>> +    POWER_SUPPLY_PROP_TEMP, >>> +    POWER_SUPPLY_PROP_MANUFACTURE_YEAR, >>> +    POWER_SUPPLY_PROP_MANUFACTURE_MONTH, >>> +    POWER_SUPPLY_PROP_MANUFACTURE_DAY, >>> +    POWER_SUPPLY_PROP_MODEL_NAME, >>> +    POWER_SUPPLY_PROP_MANUFACTURER, >>> +    POWER_SUPPLY_PROP_SERIAL_NUMBER, >>> +    POWER_SUPPLY_PROP_CHARGE_CONTROL_START_THRESHOLD, >>> +    POWER_SUPPLY_PROP_CHARGE_CONTROL_END_THRESHOLD, >> >> Is it expected to _not_ have POWER_SUPPLY_PROP_STATE_OF_HEALTH & POWER_SUPPLY_PROP_INTERNAL_RESISTANCE here ? >> >> Neil > > No, X1E80100 platform doesn't have battery resistance/SOH information exported in battery management firmware > > >> >>> +}; >>> + >>> +static const struct power_supply_desc x1e80100_bat_psy_desc = { >>> +    .name = "qcom-battmgr-bat", >>> +    .type = POWER_SUPPLY_TYPE_BATTERY, >>> +    .properties = x1e80100_bat_props, >>> +    .num_properties = ARRAY_SIZE(x1e80100_bat_props), >>> +    .get_property = qcom_battmgr_bat_get_property, >>> +    .set_property = qcom_battmgr_bat_set_property, >>> +    .property_is_writeable = qcom_battmgr_bat_is_writeable, >>> +}; >>> + >>>   static const enum power_supply_property sm8350_bat_props[] = { >>>       POWER_SUPPLY_PROP_STATUS, >>>       POWER_SUPPLY_PROP_HEALTH, >>> @@ -691,6 +900,42 @@ static const struct power_supply_desc sm8350_bat_psy_desc = { >>>       .get_property = qcom_battmgr_bat_get_property, >>>   }; >>>   +static const enum power_supply_property sm8550_bat_props[] = { >>> +    POWER_SUPPLY_PROP_STATUS, >>> +    POWER_SUPPLY_PROP_HEALTH, >>> +    POWER_SUPPLY_PROP_PRESENT, >>> +    POWER_SUPPLY_PROP_CHARGE_TYPE, >>> +    POWER_SUPPLY_PROP_CAPACITY, >>> +    POWER_SUPPLY_PROP_VOLTAGE_OCV, >>> +    POWER_SUPPLY_PROP_VOLTAGE_NOW, >>> +    POWER_SUPPLY_PROP_VOLTAGE_MAX, >>> +    POWER_SUPPLY_PROP_CURRENT_NOW, >>> +    POWER_SUPPLY_PROP_TEMP, >>> +    POWER_SUPPLY_PROP_TECHNOLOGY, >>> +    POWER_SUPPLY_PROP_CHARGE_COUNTER, >>> +    POWER_SUPPLY_PROP_CYCLE_COUNT, >>> +    POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN, >>> +    POWER_SUPPLY_PROP_CHARGE_FULL, >>> +    POWER_SUPPLY_PROP_MODEL_NAME, >>> +    POWER_SUPPLY_PROP_TIME_TO_FULL_AVG, >>> +    POWER_SUPPLY_PROP_TIME_TO_EMPTY_AVG, >>> +    POWER_SUPPLY_PROP_INTERNAL_RESISTANCE, >>> +    POWER_SUPPLY_PROP_STATE_OF_HEALTH, >>> +    POWER_SUPPLY_PROP_POWER_NOW, >>> +    POWER_SUPPLY_PROP_CHARGE_CONTROL_START_THRESHOLD, >>> +    POWER_SUPPLY_PROP_CHARGE_CONTROL_END_THRESHOLD, >>> +}; >>> + >>> +static const struct power_supply_desc sm8550_bat_psy_desc = { >>> +    .name = "qcom-battmgr-bat", >>> +    .type = POWER_SUPPLY_TYPE_BATTERY, >>> +    .properties = sm8550_bat_props, >>> +    .num_properties = ARRAY_SIZE(sm8550_bat_props), >>> +    .get_property = qcom_battmgr_bat_get_property, >>> +    .set_property = qcom_battmgr_bat_set_property, >>> +    .property_is_writeable = qcom_battmgr_bat_is_writeable, >>> +}; >>> + >>>   static int qcom_battmgr_ac_get_property(struct power_supply *psy, >>>                       enum power_supply_property psp, >>>                       union power_supply_propval *val) >>> @@ -766,7 +1011,8 @@ static int qcom_battmgr_usb_get_property(struct power_supply *psy, >>>       if (!battmgr->service_up) >>>           return -EAGAIN; >>>   -    if (battmgr->variant == QCOM_BATTMGR_SC8280XP) >>> +    if (battmgr->variant == QCOM_BATTMGR_SC8280XP || >>> +        battmgr->variant == QCOM_BATTMGR_X1E80100) >>>           ret = qcom_battmgr_bat_sc8280xp_update(battmgr, psp); >>>       else >>>           ret = qcom_battmgr_usb_sm8350_update(battmgr, psp); >>> @@ -888,7 +1134,8 @@ static int qcom_battmgr_wls_get_property(struct power_supply *psy, >>>       if (!battmgr->service_up) >>>           return -EAGAIN; >>>   -    if (battmgr->variant == QCOM_BATTMGR_SC8280XP) >>> +    if (battmgr->variant == QCOM_BATTMGR_SC8280XP || >>> +        battmgr->variant == QCOM_BATTMGR_X1E80100) >>>           ret = qcom_battmgr_bat_sc8280xp_update(battmgr, psp); >>>       else >>>           ret = qcom_battmgr_wls_sm8350_update(battmgr, psp); >>> @@ -1085,6 +1332,9 @@ static void qcom_battmgr_sc8280xp_callback(struct qcom_battmgr *battmgr, >>>       case BATTMGR_BAT_CHARGE_TIME: >>>           battmgr->status.charge_time = le32_to_cpu(resp->time); >>>           break; >>> +    case BATTMGR_CHG_CTRL_LIMIT_EN: >>> +        battmgr->error = 0; >>> +        break; >>>       default: >>>           dev_warn(battmgr->dev, "unknown message %#x\n", opcode); >>>           break; >>> @@ -1198,6 +1448,12 @@ static void qcom_battmgr_sm8350_callback(struct qcom_battmgr *battmgr, >>>           case BATT_POWER_NOW: >>>               battmgr->status.power_now = le32_to_cpu(resp->intval.value); >>>               break; >>> +        case BATT_CHG_CTRL_START_THR: >>> +            battmgr->info.charge_ctrl_start = le32_to_cpu(resp->intval.value); >>> +            break; >>> +        case BATT_CHG_CTRL_END_THR: >>> +            battmgr->info.charge_ctrl_end = le32_to_cpu(resp->intval.value); >>> +            break; >>>           default: >>>               dev_warn(battmgr->dev, "unknown property %#x\n", property); >>>               break; >>> @@ -1280,6 +1536,7 @@ static void qcom_battmgr_sm8350_callback(struct qcom_battmgr *battmgr, >>>           } >>>           break; >>>       case BATTMGR_REQUEST_NOTIFICATION: >>> +    case BATTMGR_CHG_CTRL_LIMIT_EN: >>>           battmgr->error = 0; >>>           break; >>>       default: >>> @@ -1299,7 +1556,8 @@ static void qcom_battmgr_callback(const void *data, size_t len, void *priv) >>>         if (opcode == BATTMGR_NOTIFICATION) >>>           qcom_battmgr_notification(battmgr, data, len); >>> -    else if (battmgr->variant == QCOM_BATTMGR_SC8280XP) >>> +    else if (battmgr->variant == QCOM_BATTMGR_SC8280XP || >>> +         battmgr->variant == QCOM_BATTMGR_X1E80100) >>>           qcom_battmgr_sc8280xp_callback(battmgr, data, len); >>>       else >>>           qcom_battmgr_sm8350_callback(battmgr, data, len); >>> @@ -1346,11 +1604,13 @@ static char *qcom_battmgr_battery[] = { "battery" }; >>>   static int qcom_battmgr_probe(struct auxiliary_device *adev, >>>                     const struct auxiliary_device_id *id) >>>   { >>> +    const struct power_supply_desc *psy_desc; >>>       struct power_supply_config psy_cfg_supply = {}; >>>       struct power_supply_config psy_cfg = {}; >>>       const struct of_device_id *match; >>>       struct qcom_battmgr *battmgr; >>>       struct device *dev = &adev->dev; >>> +    int ret; >>>         battmgr = devm_kzalloc(dev, sizeof(*battmgr), GFP_KERNEL); >>>       if (!battmgr) >>> @@ -1376,8 +1636,19 @@ static int qcom_battmgr_probe(struct auxiliary_device *adev, >>>       else >>>           battmgr->variant = QCOM_BATTMGR_SM8350; >>>   -    if (battmgr->variant == QCOM_BATTMGR_SC8280XP) { >>> -        battmgr->bat_psy = devm_power_supply_register(dev, &sc8280xp_bat_psy_desc, &psy_cfg); >>> +    ret = qcom_battmgr_charge_control_thresholds_init(battmgr); >>> +    if (ret < 0) >>> +        return dev_err_probe(dev, ret, >>> +                     "failed to init battery charge control thresholds\n"); >>> + >>> +    if (battmgr->variant == QCOM_BATTMGR_SC8280XP || >>> +        battmgr->variant == QCOM_BATTMGR_X1E80100) { >>> +        if (battmgr->variant == QCOM_BATTMGR_X1E80100) >>> +            psy_desc = &x1e80100_bat_psy_desc; >>> +        else >>> +            psy_desc = &sc8280xp_bat_psy_desc; >>> + >>> +        battmgr->bat_psy = devm_power_supply_register(dev, psy_desc, &psy_cfg); >>>           if (IS_ERR(battmgr->bat_psy)) >>>               return dev_err_probe(dev, PTR_ERR(battmgr->bat_psy), >>>                            "failed to register battery power supply\n"); >>> @@ -1397,7 +1668,12 @@ static int qcom_battmgr_probe(struct auxiliary_device *adev, >>>               return dev_err_probe(dev, PTR_ERR(battmgr->wls_psy), >>>                            "failed to register wireless charing power supply\n"); >>>       } else { >>> -        battmgr->bat_psy = devm_power_supply_register(dev, &sm8350_bat_psy_desc, &psy_cfg); >>> +        if (battmgr->variant == QCOM_BATTMGR_SM8550) >>> +            psy_desc = &sm8550_bat_psy_desc; >>> +        else >>> +            psy_desc = &sm8350_bat_psy_desc; >>> + >>> +        battmgr->bat_psy = devm_power_supply_register(dev, psy_desc, &psy_cfg); >>>           if (IS_ERR(battmgr->bat_psy)) >>>               return dev_err_probe(dev, PTR_ERR(battmgr->bat_psy), >>>                            "failed to register battery power supply\n"); >>> >> Ok, thanks ! Reviewed-by: Neil Armstrong