From: David Heidelberg <david@ixit.cz>
To: robin@snyders.xyz, Casey Connolly <casey.connolly@linaro.org>,
Sebastian Reichel <sre@kernel.org>, Rob Herring <robh@kernel.org>,
Krzysztof Kozlowski <krzk+dt@kernel.org>,
Conor Dooley <conor+dt@kernel.org>
Cc: linux-arm-msm@vger.kernel.org, linux-pm@vger.kernel.org,
devicetree@vger.kernel.org, linux-kernel@vger.kernel.org,
Dmitry Baryshkov <dmitry.baryshkov@oss.qualcomm.com>,
Konrad Dybcio <konrad.dybcio@oss.qualcomm.com>,
Joel Selvaraj <foss@joelselvaraj.com>
Subject: Re: [PATCH v4 2/2] power: supply: qcom_smbx: add SMB5 support
Date: Sat, 5 Sep 2026 02:17:11 +0200 [thread overview]
Message-ID: <b5f87468-20f1-437f-8610-518df679ee99@ixit.cz> (raw)
In-Reply-To: <20260820-submit-qcom-smbx-send-v1-v4-2-818dabb2771e@snyders.xyz>
On 20/08/2026 12:03, Robin Snyders via B4 Relay wrote:
> From: Casey Connolly <casey.connolly@linaro.org>
>
> Introduce support for the SMB5 charger found on PM7250B, PM8150B and
> related Qualcomm PMICs.
>
> SMB5 uses different DCDC status offsets, charger-state encodings and
> electrical ranges. Select these from per-PMIC match data, with PM7250B
> using the PM8150B compatible fallback and parameter block. Read
> overvoltage from the SMB5 status bit, use the already-prescaled IIO
> voltage reading, and convert the SMB5 current-sense voltage to microamps.
> Use battery-info property presence when selecting voltage and current
> targets.
>
> Keep Type-C power-role and VBUS control with the dedicated TCPM and
> regulator drivers. Clear the unsupported HVDCP negotiation modes so stale
> firmware settings cannot raise VBUS. Leave the firmware recharge policy
> unchanged and match the downstream default of ADC-based AICL disabled,
> while enabling periodic hardware AICL with its twelve-second SMB5 rerun
> interval. Preserve the existing three-second SMB2 interval.
>
> PM8150B places the charger and VBUS regulator in the same DCDC peripheral,
> but the SMB5 path does not write the regulator registers. qcom_smbx reads
> 0x1108 and 0x110b, while qcom_usb_vbus-regulator writes 0x1140, 0x1152
> and 0x1153. The SMB2-only OTG configuration write to 0x1153 is not part
> of the SMB5 initialization sequence. The TCPM port and PD PHY use the
> separate 0x15xx and 0x17xx peripherals. Name the USBIN BC1.2 integration
> register and SMB2-only OTG definitions accordingly to make this ownership
> boundary explicit.
>
> Program the battery limits and complete SMB5 input and charging setup
> from the power-supply registration init callback before device_add
> publishes the properties. This makes all public callbacks safe without
> driver-specific probe synchronization.
>
> Suspend USB input and charging before SMB5 initialization. On a later
> failure, restore the original charging-enable state before the
> input-suspend state; leave the input suspended if charging cannot be
> restored. Cancel status work before unregistering the power supply during
> managed teardown. Update the Kconfig description to cover both charger
> generations.
>
> On a OnePlus 7T Pro, register reads from the initial implementation
> confirmed the programmed 4.40 V, 1.50 A and 500 mA limits. A 180-second
> guarded charging trace and a subsequent 600-second runtime trace
> completed without crossing the voltage guard.
>
> Signed-off-by: Casey Connolly <casey.connolly@linaro.org>
> Co-developed-by: Joel Selvaraj <foss@joelselvaraj.com>
> Signed-off-by: Joel Selvaraj <foss@joelselvaraj.com>
> Co-developed-by: Robin Snyders <robin@snyders.xyz>
> Signed-off-by: Robin Snyders <robin@snyders.xyz>
> ---
> drivers/power/supply/Kconfig | 8 +-
> drivers/power/supply/qcom_smbx.c | 757 ++++++++++++++++++++++++++++++---------
> 2 files changed, 598 insertions(+), 167 deletions(-)
>
> diff --git a/drivers/power/supply/Kconfig b/drivers/power/supply/Kconfig
> index b89ef40df7f8..1159a7711919 100644
> --- a/drivers/power/supply/Kconfig
> +++ b/drivers/power/supply/Kconfig
> @@ -1173,13 +1173,13 @@ config BATTERY_CHARGER_SURFACE_RT
> will be called surface-rt-ec.
>
> config CHARGER_QCOM_SMB2
> - tristate "Qualcomm PMI8998 PMIC charger driver"
> + tristate "Qualcomm SMB2 and SMB5 charger driver"
> depends on MFD_SPMI_PMIC
> depends on IIO
> help
> - Say Y here to enable the Qualcomm PMIC Charger driver. This
> - adds support for the SMB2 switch mode battery charger found
> - in PMI8998 and related PMICs.
> + Say Y here to enable the Qualcomm PMIC charger driver. This adds
> + support for the SMB2 and SMB5 switch-mode battery chargers found
> + in PMI8998, PM660, PM7250B and PM8150B PMICs.
>
> config FUEL_GAUGE_MM8013
> tristate "Mitsumi MM8013 fuel gauge driver"
> diff --git a/drivers/power/supply/qcom_smbx.c b/drivers/power/supply/qcom_smbx.c
> index 71f5a4859412..bc2b1cd3ed68 100644
> --- a/drivers/power/supply/qcom_smbx.c
> +++ b/drivers/power/supply/qcom_smbx.c
> @@ -23,6 +23,13 @@
> #include <linux/types.h>
> #include <linux/workqueue.h>
>
> +enum smb_generation {
> + SMB2,
> + SMB5,
> +};
> +
> +#define SMB_REG_OFFSET(smb) ((smb)->gen == SMB2 ? 0x600 : 0x100)
> +
> /* clang-format off */
> #define BATTERY_CHARGER_STATUS_1 0x06
> #define BVR_INITIAL_RAMP_BIT BIT(7)
> @@ -34,14 +41,20 @@
> #define BATTERY_CHARGER_STATUS_2 0x07
> #define INPUT_CURRENT_LIMITED_BIT BIT(7)
> #define CHARGER_ERROR_STATUS_SFT_EXPIRE_BIT BIT(6)
> -#define CHARGER_ERROR_STATUS_BAT_OV_BIT BIT(5)
> +#define SMB2_CHARGER_ERROR_STATUS_BAT_OV_BIT BIT(5)
> #define CHARGER_ERROR_STATUS_BAT_TERM_MISSING_BIT BIT(4)
> #define BAT_TEMP_STATUS_MASK GENMASK(3, 0)
> #define BAT_TEMP_STATUS_SOFT_LIMIT_MASK GENMASK(3, 2)
> -#define BAT_TEMP_STATUS_HOT_SOFT_LIMIT_BIT BIT(3)
> -#define BAT_TEMP_STATUS_COLD_SOFT_LIMIT_BIT BIT(2)
> -#define BAT_TEMP_STATUS_TOO_HOT_BIT BIT(1)
> -#define BAT_TEMP_STATUS_TOO_COLD_BIT BIT(0)
> +#define SMB2_BAT_TEMP_STATUS_HOT_SOFT_LIMIT_BIT BIT(3)
> +#define SMB2_BAT_TEMP_STATUS_COLD_SOFT_LIMIT_BIT BIT(2)
> +#define SMB2_BAT_TEMP_STATUS_TOO_HOT_BIT BIT(1)
> +#define SMB2_BAT_TEMP_STATUS_TOO_COLD_BIT BIT(0)
> +
> +#define SMB5_CHARGER_ERROR_STATUS_BAT_OV_BIT BIT(1)
> +#define SMB5_BAT_TEMP_STATUS_HOT_SOFT_BIT BIT(5)
> +#define SMB5_BAT_TEMP_STATUS_COLD_SOFT_BIT BIT(4)
> +#define SMB5_BAT_TEMP_STATUS_TOO_HOT_BIT BIT(3)
> +#define SMB5_BAT_TEMP_STATUS_TOO_COLD_BIT BIT(2)
>
> #define BATTERY_CHARGER_STATUS_4 0x0A
> #define CHARGE_CURRENT_POST_JEITA_MASK GENMASK(7, 0)
> @@ -78,10 +91,10 @@
> #define FLOAT_VOLTAGE_CFG 0x70
> #define FLOAT_VOLTAGE_SETTING_MASK GENMASK(7, 0)
>
> -#define FG_UPDATE_CFG_2_SEL 0x7D
> +#define SMB2_FG_UPDATE_CFG_2_SEL 0x7D
> #define SOC_LT_OTG_THRESH_SEL_BIT BIT(3)
> -#define SOC_LT_CHG_RECHARGE_THRESH_SEL_BIT BIT(2)
> -#define VBT_LT_CHG_RECHARGE_THRESH_SEL_BIT BIT(1)
> +#define SMB2_SOC_LT_CHG_RECHARGE_THRESH_SEL_BIT BIT(2)
> +#define SMB2_VBT_LT_CHG_RECHARGE_THRESH_SEL_BIT BIT(1)
> #define IBT_LT_CHG_TERM_THRESH_SEL_BIT BIT(0)
>
> #define JEITA_EN_CFG 0x90
> @@ -101,13 +114,13 @@
> #define USBIN_LT_3P6V_RT_STS_BIT BIT(1)
> #define USBIN_COLLAPSE_RT_STS_BIT BIT(0)
>
> -#define OTG_CFG 0x153
> +#define SMB2_OTG_CFG 0x153
> #define OTG_RESERVED_MASK GENMASK(7, 6)
> #define DIS_OTG_ON_TLIM_BIT BIT(5)
> #define QUICKSTART_OTG_FASTROLESWAP_BIT BIT(4)
> #define INCREASE_DFP_TIME_BIT BIT(3)
> #define ENABLE_OTG_IN_DEBUG_MODE_BIT BIT(2)
> -#define OTG_EN_SRC_CFG_BIT BIT(1)
> +#define SMB2_OTG_EN_SRC_CFG_BIT BIT(1)
> #define CONCURRENT_MODE_CFG_BIT BIT(0)
>
> #define OTG_ENG_OTG_CFG 0x1C0
> @@ -189,10 +202,12 @@
> #define TYPEC_TRYSINK_DETECT_STATUS_BIT BIT(0)
>
> #define CMD_APSD 0x341
> -#define ICL_OVERRIDE_BIT BIT(1)
> #define APSD_RERUN_BIT BIT(0)
>
> -#define TYPE_C_CFG 0x358
> +#define CMD_ICL_OVERRIDE 0x342
> +#define ICL_OVERRIDE_BIT BIT(0)
> +
> +#define USBIN_TYPE_C_CFG 0x358
> #define APSD_START_ON_CC_BIT BIT(7)
> #define WAIT_FOR_APSD_BIT BIT(6)
> #define FACTORY_MODE_DETECTION_EN_BIT BIT(5)
> @@ -260,7 +275,7 @@
> #define EXIT_SNK_BASED_ON_CC_BIT BIT(7)
> #define VCONN_EN_ORIENTATION_BIT BIT(6)
> #define TYPEC_VCONN_OVERCURR_INT_EN_BIT BIT(5)
> -#define VCONN_EN_SRC_BIT BIT(4)
> +#define SMB2_VCONN_EN_SRC_BIT BIT(4)
> #define VCONN_EN_VALUE_BIT BIT(3)
> #define TYPEC_POWER_ROLE_CMD_MASK GENMASK(2, 0)
> #define UFP_EN_CMD_BIT BIT(2)
> @@ -274,7 +289,7 @@
> #define SUSPEND_ON_COLLAPSE_USBIN_BIT BIT(7)
> #define USBIN_AICL_HDC_EN_BIT BIT(6)
> #define USBIN_AICL_START_AT_MAX_BIT BIT(5)
> -#define USBIN_AICL_RERUN_EN_BIT BIT(4)
> +#define USBIN_AICL_PERIODIC_RERUN_EN_BIT BIT(4)
> #define USBIN_AICL_ADC_EN_BIT BIT(3)
> #define USBIN_AICL_EN_BIT BIT(2)
> #define USBIN_HV_COLLAPSE_RESPONSE_BIT BIT(1)
> @@ -303,10 +318,12 @@
> #define AICL_SWITCH_ENABLE_BIT BIT(1)
> #define ZIN_ICL_ENABLE_BIT BIT(0)
>
> -#define ICL_STATUS 0x607
> #define INPUT_CURRENT_LIMIT_MASK GENMASK(7, 0)
>
> -#define POWER_PATH_STATUS 0x60B
> +#define SMB2_ICL_STATUS 0x607
> +#define SMB5_AICL_ICL_STATUS 0x108
> +
> +#define POWER_PATH_STATUS(smb) (SMB_REG_OFFSET(smb) + 0x0B)
> #define P_PATH_INPUT_SS_DONE_BIT BIT(7)
> #define P_PATH_USBIN_SUSPEND_STS_BIT BIT(6)
> #define P_PATH_DCIN_SUSPEND_STS_BIT BIT(5)
> @@ -335,6 +352,8 @@
>
> #define AICL_RERUN_TIME_CFG 0x661
> #define AICL_RERUN_TIME_MASK GENMASK(1, 0)
> +#define AICL_RERUN_TIME_3_SECS 0
> +#define AICL_RERUN_TIME_12_SECS 1
>
> #define STAT_CFG 0x690
> #define STAT_SW_OVERRIDE_VALUE_BIT BIT(7)
> @@ -350,22 +369,30 @@
> #define DCP_CURRENT_UA 1500000
> #define CURRENT_MAX_UA DCP_CURRENT_UA
>
> -/* pmi8998 registers represent current in increments of 1/40th of an amp */
> -#define CURRENT_SCALE_FACTOR 25000
> -#define SMB2_FLOAT_VOLTAGE_MIN_UV 3487500
> -#define SMB2_FLOAT_VOLTAGE_MAX_UV 4920000
> -#define SMB2_FLOAT_VOLTAGE_STEP_UV 7500
> +/* PMI8998 registers represent current in increments of 1/40th of an amp. */
> +#define SMB2_CURRENT_STEP_UA 25000
> /* clang-format on */
>
> -enum charger_status {
> - TRICKLE_CHARGE = 0,
> - PRE_CHARGE,
> - FAST_CHARGE,
> - FULLON_CHARGE,
> - TAPER_CHARGE,
> - TERMINATE_CHARGE,
> - INHIBIT_CHARGE,
> - DISABLE_CHARGE,
> +enum smb2_charger_status {
> + SMB2_TRICKLE_CHARGE = 0,
> + SMB2_PRE_CHARGE,
> + SMB2_FAST_CHARGE,
> + SMB2_FULLON_CHARGE,
> + SMB2_TAPER_CHARGE,
> + SMB2_TERMINATE_CHARGE,
> + SMB2_INHIBIT_CHARGE,
> + SMB2_DISABLE_CHARGE,
> +};
> +
> +enum smb5_charger_status {
> + SMB5_INHIBIT_CHARGE = 0,
> + SMB5_TRICKLE_CHARGE,
> + SMB5_PRE_CHARGE,
> + SMB5_FULLON_CHARGE,
> + SMB5_TAPER_CHARGE,
> + SMB5_TERMINATE_CHARGE,
> + SMB5_PAUSE_CHARGE,
> + SMB5_DISABLE_CHARGE,
> };
>
> struct smb_init_register {
> @@ -374,13 +401,21 @@ struct smb_init_register {
> u8 val;
> };
>
> +struct smb_match_data;
> +
> /**
> * struct smb_chip - smb chip structure
> * @dev: Device reference for power_supply
> * @name: The platform device name
> * @base: Base address for smb registers
> * @regmap: Register map
> - * @batt_info: Battery data from DT
> + * @match_data: Per-PMIC hardware parameters
> + * @gen: Charger hardware generation
> + * @icl_max_ua: Maximum programmable USB input current
> + * @icl_step_ua: USB input current register step
> + * @icl_status: Effective input current status register offset
> + * @usbin_current_scale: USB input current-sense scale
> + * @initial_usb_suspend: USB input suspend state before SMB5 setup
> * @initial_charge_enable: Charging enable state before hardware setup
> * @status_change_work: Worker to handle plug/unplug events
> * @cable_irq: USB plugin IRQ
> @@ -394,7 +429,13 @@ struct smb_chip {
> const char *name;
> unsigned int base;
> struct regmap *regmap;
> - struct power_supply_battery_info *batt_info;
> + const struct smb_match_data *match_data;
> + enum smb_generation gen;
> + unsigned int icl_max_ua;
> + unsigned int icl_step_ua;
> + u16 icl_status;
> + unsigned int usbin_current_scale;
> + u8 initial_usb_suspend;
> u8 initial_charge_enable;
>
> struct delayed_work status_change_work;
> @@ -407,6 +448,22 @@ struct smb_chip {
> struct power_supply *chg_psy;
> };
>
> +struct smb_match_data {
> + const char *name;
> + enum smb_generation gen;
> + size_t init_seq_len;
> + unsigned int fv_min_uv;
> + unsigned int fv_max_uv;
> + unsigned int fv_step_uv;
> + unsigned int fcc_max_ua;
> + unsigned int fcc_step_ua;
> + unsigned int icl_max_ua;
> + unsigned int icl_step_ua;
> + u16 icl_status;
> + unsigned int usbin_current_scale;
> + const struct smb_init_register *init_seq;
> +};
> +
> static enum power_supply_property smb_properties[] = {
> POWER_SUPPLY_PROP_MANUFACTURER,
> POWER_SUPPLY_PROP_MODEL_NAME,
> @@ -424,7 +481,7 @@ static int smb_get_prop_usb_online(struct smb_chip *chip, int *val)
> unsigned int stat;
> int rc;
>
> - rc = regmap_read(chip->regmap, chip->base + POWER_PATH_STATUS, &stat);
> + rc = regmap_read(chip->regmap, chip->base + POWER_PATH_STATUS(chip), &stat);
> if (rc < 0) {
> dev_err(chip->dev, "Couldn't read power path status: %d\n", rc);
> return rc;
> @@ -479,9 +536,80 @@ static int smb_apsd_get_charger_type(struct smb_chip *chip, int *val)
> return 0;
> }
>
> +/* Return 1 when in overvoltage state, else 0 or -errno */
> +static int smbx_ov_status(struct smb_chip *chip)
> +{
> + u8 mask;
> + int rc;
> + u32 val;
> +
> + switch (chip->gen) {
> + case SMB2:
> + mask = SMB2_CHARGER_ERROR_STATUS_BAT_OV_BIT;
> + break;
> + case SMB5:
> + mask = SMB5_CHARGER_ERROR_STATUS_BAT_OV_BIT;
> + break;
> + default:
> + return -EINVAL;
> + }
> +
> + rc = regmap_read(chip->regmap,
> + chip->base + BATTERY_CHARGER_STATUS_2, &val);
> + if (rc)
> + return rc;
> +
> + return !!(val & mask);
> +}
> +
> +static int smb_map_charge_status(struct smb_chip *chip, u32 stat, int *val)
> +{
> + switch (chip->gen) {
> + case SMB2:
> + switch (stat) {
> + case SMB2_TRICKLE_CHARGE:
> + case SMB2_PRE_CHARGE:
> + case SMB2_FAST_CHARGE:
> + case SMB2_FULLON_CHARGE:
> + case SMB2_TAPER_CHARGE:
> + *val = POWER_SUPPLY_STATUS_CHARGING;
> + return 0;
> + case SMB2_TERMINATE_CHARGE:
> + case SMB2_INHIBIT_CHARGE:
> + *val = POWER_SUPPLY_STATUS_FULL;
> + return 0;
> + case SMB2_DISABLE_CHARGE:
> + *val = POWER_SUPPLY_STATUS_NOT_CHARGING;
> + return 0;
> + }
> + break;
> + case SMB5:
> + switch (stat) {
> + case SMB5_TRICKLE_CHARGE:
> + case SMB5_PRE_CHARGE:
> + case SMB5_FULLON_CHARGE:
> + case SMB5_TAPER_CHARGE:
> + *val = POWER_SUPPLY_STATUS_CHARGING;
> + return 0;
> + case SMB5_TERMINATE_CHARGE:
> + case SMB5_INHIBIT_CHARGE:
> + *val = POWER_SUPPLY_STATUS_FULL;
> + return 0;
> + case SMB5_PAUSE_CHARGE:
> + case SMB5_DISABLE_CHARGE:
> + *val = POWER_SUPPLY_STATUS_NOT_CHARGING;
> + return 0;
> + }
> + break;
> + }
> +
> + *val = POWER_SUPPLY_STATUS_UNKNOWN;
> + return 0;
> +}
> +
> static int smb_get_prop_status(struct smb_chip *chip, int *val)
> {
> - unsigned char stat[2];
> + u32 stat;
> int usb_online = 0;
> int rc;
>
> @@ -491,49 +619,36 @@ static int smb_get_prop_status(struct smb_chip *chip, int *val)
> return rc;
> }
>
> - rc = regmap_bulk_read(chip->regmap,
> - chip->base + BATTERY_CHARGER_STATUS_1, &stat, 2);
> + rc = regmap_read(chip->regmap,
> + chip->base + BATTERY_CHARGER_STATUS_1, &stat);
> if (rc < 0) {
> dev_err(chip->dev, "Failed to read charging status ret=%d\n",
> rc);
> return rc;
> }
>
> - if (stat[1] & CHARGER_ERROR_STATUS_BAT_OV_BIT) {
> + rc = smbx_ov_status(chip);
> + if (rc < 0)
> + return rc;
> +
> + /* In overvoltage state */
> + if (rc == 1) {
> *val = POWER_SUPPLY_STATUS_NOT_CHARGING;
> return 0;
> }
>
> - stat[0] = stat[0] & BATTERY_CHARGER_STATUS_MASK;
> + stat &= BATTERY_CHARGER_STATUS_MASK;
>
> - switch (stat[0]) {
> - case TRICKLE_CHARGE:
> - case PRE_CHARGE:
> - case FAST_CHARGE:
> - case FULLON_CHARGE:
> - case TAPER_CHARGE:
> - *val = POWER_SUPPLY_STATUS_CHARGING;
> - return rc;
> - case DISABLE_CHARGE:
> - *val = POWER_SUPPLY_STATUS_NOT_CHARGING;
> - return rc;
> - case TERMINATE_CHARGE:
> - case INHIBIT_CHARGE:
> - *val = POWER_SUPPLY_STATUS_FULL;
> - return rc;
> - default:
> - *val = POWER_SUPPLY_STATUS_UNKNOWN;
> - return rc;
> - }
> + return smb_map_charge_status(chip, stat, val);
> }
>
> static inline int smb_get_current_limit(struct smb_chip *chip,
> unsigned int *val)
> {
> - int rc = regmap_read(chip->regmap, chip->base + ICL_STATUS, val);
> + int rc = regmap_read(chip->regmap, chip->base + chip->icl_status, val);
>
> if (rc >= 0)
> - *val *= CURRENT_SCALE_FACTOR;
> + *val *= chip->icl_step_ua;
> return rc;
> }
>
> @@ -541,12 +656,13 @@ static int smb_set_current_limit(struct smb_chip *chip, unsigned int val)
> {
> unsigned char val_raw;
>
> - if (val > 4800000) {
> + if (val > chip->icl_max_ua) {
> dev_err(chip->dev,
> - "Can't set current limit higher than 4800000uA");
> + "Can't set current limit higher than %uuA",
> + chip->icl_max_ua);
> return -EINVAL;
> }
> - val_raw = val / CURRENT_SCALE_FACTOR;
> + val_raw = val / chip->icl_step_ua;
>
> return regmap_write(chip->regmap, chip->base + USBIN_CURRENT_LIMIT_CFG,
> val_raw);
> @@ -607,12 +723,10 @@ static void smb_status_change_work(struct work_struct *work)
> static int smb_get_iio_chan(struct smb_chip *chip, struct iio_channel *chan,
> int *val)
> {
> - int rc;
> - union power_supply_propval status;
> + int rc, status;
>
> - rc = power_supply_get_property(chip->chg_psy, POWER_SUPPLY_PROP_STATUS,
> - &status);
> - if (rc < 0 || status.intval != POWER_SUPPLY_STATUS_CHARGING) {
> + rc = smb_get_prop_status(chip, &status);
> + if (rc < 0 || status != POWER_SUPPLY_STATUS_CHARGING) {
> *val = 0;
> return 0;
> }
> @@ -625,7 +739,61 @@ static int smb_get_iio_chan(struct smb_chip *chip, struct iio_channel *chan,
> return iio_read_channel_processed(chan, val);
> }
>
> -static int smb_get_prop_health(struct smb_chip *chip, int *val)
> +static int smb_get_prop_current_now(struct smb_chip *chip, int *val)
> +{
> + s64 current_ua;
> + int rc;
> +
> + rc = smb_get_iio_chan(chip, chip->usb_in_i_chan, val);
> + if (rc < 0)
> + return rc;
> +
> + current_ua = (s64)*val * chip->usbin_current_scale;
> + if (current_ua < INT_MIN || current_ua > INT_MAX)
> + return -ERANGE;
> +
> + *val = (int)current_ua;
> + return 0;
> +}
> +
> +static int smb5_get_prop_health(struct smb_chip *chip, int *val)
> +{
> + int rc;
> + unsigned int stat;
> +
> + rc = smbx_ov_status(chip);
> + if (rc < 0) {
> + dev_err(chip->dev,
> + "Couldn't determine overvoltage status: %d\n", rc);
> + return rc;
> + }
> + if (rc) {
> + *val = POWER_SUPPLY_HEALTH_OVERVOLTAGE;
> + return 0;
> + }
> +
> + rc = regmap_read(chip->regmap, chip->base + BATTERY_CHARGER_STATUS_7,
> + &stat);
> + if (rc < 0) {
> + dev_err(chip->dev, "Couldn't read charger status 7 rc=%d\n", rc);
> + return rc;
> + }
> +
> + if (stat & SMB5_BAT_TEMP_STATUS_TOO_COLD_BIT)
> + *val = POWER_SUPPLY_HEALTH_COLD;
> + else if (stat & SMB5_BAT_TEMP_STATUS_TOO_HOT_BIT)
> + *val = POWER_SUPPLY_HEALTH_OVERHEAT;
> + else if (stat & SMB5_BAT_TEMP_STATUS_COLD_SOFT_BIT)
> + *val = POWER_SUPPLY_HEALTH_COOL;
> + else if (stat & SMB5_BAT_TEMP_STATUS_HOT_SOFT_BIT)
> + *val = POWER_SUPPLY_HEALTH_WARM;
> + else
> + *val = POWER_SUPPLY_HEALTH_GOOD;
> +
> + return 0;
> +}
> +
> +static int smb2_get_prop_health(struct smb_chip *chip, int *val)
> {
> int rc;
> unsigned int stat;
> @@ -637,15 +805,15 @@ static int smb_get_prop_health(struct smb_chip *chip, int *val)
> return rc;
> }
>
> - if (stat & CHARGER_ERROR_STATUS_BAT_OV_BIT)
> + if (stat & SMB2_CHARGER_ERROR_STATUS_BAT_OV_BIT)
> *val = POWER_SUPPLY_HEALTH_OVERVOLTAGE;
> - else if (stat & BAT_TEMP_STATUS_TOO_COLD_BIT)
> + else if (stat & SMB2_BAT_TEMP_STATUS_TOO_COLD_BIT)
> *val = POWER_SUPPLY_HEALTH_COLD;
> - else if (stat & BAT_TEMP_STATUS_TOO_HOT_BIT)
> + else if (stat & SMB2_BAT_TEMP_STATUS_TOO_HOT_BIT)
> *val = POWER_SUPPLY_HEALTH_OVERHEAT;
> - else if (stat & BAT_TEMP_STATUS_COLD_SOFT_LIMIT_BIT)
> + else if (stat & SMB2_BAT_TEMP_STATUS_COLD_SOFT_LIMIT_BIT)
> *val = POWER_SUPPLY_HEALTH_COOL;
> - else if (stat & BAT_TEMP_STATUS_HOT_SOFT_LIMIT_BIT)
> + else if (stat & SMB2_BAT_TEMP_STATUS_HOT_SOFT_LIMIT_BIT)
> *val = POWER_SUPPLY_HEALTH_WARM;
> else
> *val = POWER_SUPPLY_HEALTH_GOOD;
> @@ -653,6 +821,19 @@ static int smb_get_prop_health(struct smb_chip *chip, int *val)
> return 0;
> }
>
> +static int smb_get_prop_health(struct smb_chip *chip, int *val)
> +{
> + switch (chip->gen) {
> + case SMB2:
> + return smb2_get_prop_health(chip, val);
> + case SMB5:
> + return smb5_get_prop_health(chip, val);
> + default:
> + dev_err(chip->dev, "unsupported SMB chip generation\n");
> + return -EINVAL;
> + }
> +}
> +
> static int smb_get_property(struct power_supply *psy,
> enum power_supply_property psp,
> union power_supply_propval *val)
> @@ -669,8 +850,7 @@ static int smb_get_property(struct power_supply *psy,
> case POWER_SUPPLY_PROP_CURRENT_MAX:
> return smb_get_current_limit(chip, &val->intval);
> case POWER_SUPPLY_PROP_CURRENT_NOW:
> - return smb_get_iio_chan(chip, chip->usb_in_i_chan,
> - &val->intval);
> + return smb_get_prop_current_now(chip, &val->intval);
> case POWER_SUPPLY_PROP_VOLTAGE_NOW:
> return smb_get_iio_chan(chip, chip->usb_in_v_chan,
> &val->intval);
> @@ -721,17 +901,15 @@ static int smb_property_is_writable(struct power_supply *psy,
> static irqreturn_t smb_handle_batt_overvoltage(int irq, void *data)
> {
> struct smb_chip *chip = data;
> - unsigned int status;
> int rc;
>
> - rc = regmap_read(chip->regmap,
> - chip->base + BATTERY_CHARGER_STATUS_2, &status);
> + rc = smbx_ov_status(chip);
> if (rc < 0) {
> - dev_err(chip->dev, "Couldn't read charger status: %d\n", rc);
> + dev_err(chip->dev, "could not read overvoltage status: %d\n", rc);
> return IRQ_HANDLED;
> }
>
> - if (status & CHARGER_ERROR_STATUS_BAT_OV_BIT) {
> + if (rc) {
> /* The hardware stops charging automatically */
> dev_err(chip->dev, "battery overvoltage detected\n");
> }
> @@ -777,6 +955,144 @@ static irqreturn_t smb_handle_wdog_bark(int irq, void *data)
> return IRQ_HANDLED;
> }
>
> +static bool smb_batt_info_has_prop(struct power_supply_battery_info *batt_info,
> + enum power_supply_property prop)
> +{
> + return power_supply_battery_info_has_prop(batt_info, prop);
> +}
> +
> +static int smb_power_supply_init(struct power_supply *psy)
> +{
> + struct smb_chip *chip = power_supply_get_drvdata(psy);
> + const struct smb_match_data *match_data = chip->match_data;
> + struct power_supply_battery_info *batt_info;
> + unsigned int fast_charge_current_sel;
> + unsigned int float_voltage_sel;
> + bool program_float_voltage = true;
> + int fast_charge_current_ua;
> + int float_voltage_uv;
> + int rc;
> +
> + rc = power_supply_get_battery_info(psy, &batt_info);
> + if (rc)
> + return dev_err_probe(chip->dev, rc,
> + "Failed to get battery info\n");
> +
> + if (smb_batt_info_has_prop(batt_info,
> + POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE_MAX)) {
> + float_voltage_uv = batt_info->constant_charge_voltage_max_uv;
> + } else if (smb_batt_info_has_prop(batt_info,
> + POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN)) {
> + float_voltage_uv = batt_info->voltage_max_design_uv;
> + } else {
> + dev_warn(chip->dev,
> + "No battery float voltage; preserving hardware setting\n");
> + program_float_voltage = false;
> + }
> +
> + if (program_float_voltage) {
> + if (float_voltage_uv < match_data->fv_min_uv ||
> + float_voltage_uv > match_data->fv_max_uv) {
> + rc = dev_err_probe(chip->dev, -EINVAL,
> + "float voltage %d uV outside %u-%u uV\n",
> + float_voltage_uv,
> + match_data->fv_min_uv,
> + match_data->fv_max_uv);
> + goto out_put_batt_info;
> + }
> +
> + float_voltage_sel =
> + (float_voltage_uv - match_data->fv_min_uv) /
> + match_data->fv_step_uv;
> + rc = regmap_update_bits(chip->regmap,
> + chip->base + FLOAT_VOLTAGE_CFG,
> + FLOAT_VOLTAGE_SETTING_MASK,
> + float_voltage_sel);
> + if (rc < 0) {
> + rc = dev_err_probe(chip->dev, rc,
> + "could not set float voltage\n");
> + goto out_put_batt_info;
> + }
> + }
> +
> + if (chip->gen == SMB5) {
> + if (smb_batt_info_has_prop(batt_info,
> + POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX))
> + fast_charge_current_ua =
> + batt_info->constant_charge_current_max_ua;
> + else
> + fast_charge_current_ua = DCP_CURRENT_UA;
> +
> + if (fast_charge_current_ua > match_data->fcc_max_ua) {
> + rc = dev_err_probe(chip->dev, -EINVAL,
> + "fast charge current %d uA exceeds %u uA\n",
> + fast_charge_current_ua,
> + match_data->fcc_max_ua);
> + goto out_put_batt_info;
> + }
> +
> + fast_charge_current_sel = fast_charge_current_ua /
> + match_data->fcc_step_ua;
> + rc = regmap_update_bits(chip->regmap,
> + chip->base + FAST_CHARGE_CURRENT_CFG,
> + FAST_CHARGE_CURRENT_SETTING_MASK,
> + fast_charge_current_sel);
> + if (rc < 0) {
> + rc = dev_err_probe(chip->dev, rc,
> + "could not set fast charge current\n");
> + goto out_put_batt_info;
> + }
> +
> + rc = smb_set_current_limit(chip, SDP_CURRENT_UA);
> + if (rc < 0) {
> + rc = dev_err_probe(chip->dev, rc,
> + "could not set USB input current\n");
> + goto out_put_batt_info;
> + }
> +
> + if (program_float_voltage)
> + dev_info(chip->dev,
> + "charge limits: float=%u uV fast=%u uA input=%u uA\n",
> + match_data->fv_min_uv +
> + float_voltage_sel * match_data->fv_step_uv,
> + fast_charge_current_sel * match_data->fcc_step_ua,
> + SDP_CURRENT_UA);
> + else
> + dev_info(chip->dev,
> + "charge limits: float=firmware fast=%u uA input=%u uA\n",
> + fast_charge_current_sel * match_data->fcc_step_ua,
> + SDP_CURRENT_UA);
> + }
> +
> + rc = 0;
> +
> +out_put_batt_info:
> + power_supply_put_battery_info(psy, batt_info);
> +
> + if (rc)
> + return rc;
> +
> + if (chip->gen != SMB5)
> + return 0;
> +
> + rc = regmap_update_bits(chip->regmap,
> + chip->base + CHARGING_ENABLE_CMD,
> + CHARGING_ENABLE_CMD_BIT,
> + CHARGING_ENABLE_CMD_BIT);
> + if (rc < 0)
> + return dev_err_probe(chip->dev, rc,
> + "could not enable charging\n");
> +
> + rc = regmap_update_bits(chip->regmap,
> + chip->base + USBIN_CMD_IL,
> + USBIN_SUSPEND_BIT, 0);
> + if (rc < 0)
> + return dev_err_probe(chip->dev, rc,
> + "could not enable USB input\n");
> +
> + return 0;
> +}
> +
> static const struct power_supply_desc smb_psy_desc = {
> .name = "pmi8998_charger",
> .type = POWER_SUPPLY_TYPE_USB,
> @@ -789,36 +1105,85 @@ static const struct power_supply_desc smb_psy_desc = {
> .get_property = smb_get_property,
> .set_property = smb_set_property,
> .property_is_writeable = smb_property_is_writable,
> + .init = smb_power_supply_init,
> +};
> +
> +/* Init sequence derived from vendor downstream driver */
> +static const struct smb_init_register smb5_init_seq[] = {
> + /*
> + * TCPM owns the separate Type-C peripheral. This register only
> + * controls how the charger's USBIN peripheral starts BC1.2 detection.
> + */
> + { .addr = USBIN_TYPE_C_CFG,
> + .mask = APSD_START_ON_CC_BIT,
> + .val = 0 },
> + /* Enable BC1.2 source detection without unsupported HVDCP negotiation. */
> + { .addr = USBIN_OPTIONS_1_CFG,
> + .mask = HVDCP_AUTH_ALG_EN_CFG_BIT |
> + HVDCP_AUTONOMOUS_MODE_EN_CFG_BIT | AUTO_SRC_DETECT_BIT |
> + HVDCP_EN_BIT,
> + .val = AUTO_SRC_DETECT_BIT },
> + /* Set the default SDP charger type to a 500ma USB 2.0 port */
> + { .addr = USBIN_ICL_OPTIONS,
> + .mask = USBIN_MODE_CHG_BIT,
> + .val = USBIN_MODE_CHG_BIT },
> + { .addr = CMD_ICL_OVERRIDE,
> + .mask = ICL_OVERRIDE_BIT,
> + .val = 0 },
> + { .addr = USBIN_LOAD_CFG,
> + .mask = ICL_OVERRIDE_AFTER_APSD_BIT,
> + .val = 0 },
> + /* Disable watchdog */
> + { .addr = SNARL_BARK_BITE_WD_CFG, .mask = 0xff, .val = 0 },
> + { .addr = WD_CFG,
> + .mask = WATCHDOG_TRIGGER_AFP_EN_BIT | WDOG_TIMER_EN_ON_PLUGIN_BIT |
> + BARK_WDOG_INT_EN_BIT,
> + .val = 0 },
> + /*
> + * Enable Automatic Input Current Limit, this will slowly ramp up the current
> + * When connected to a wall charger, and automatically stop when it detects
> + * the charger current limit (voltage drop?) or it reaches the programmed limit.
> + */
> + { .addr = USBIN_AICL_OPTIONS_CFG,
> + .mask = USBIN_AICL_PERIODIC_RERUN_EN_BIT | USBIN_AICL_ADC_EN_BIT
> + | USBIN_AICL_EN_BIT | SUSPEND_ON_COLLAPSE_USBIN_BIT,
> + .val = USBIN_AICL_PERIODIC_RERUN_EN_BIT | USBIN_AICL_EN_BIT |
> + SUSPEND_ON_COLLAPSE_USBIN_BIT },
> + { .addr = AICL_RERUN_TIME_CFG,
> + .mask = AICL_RERUN_TIME_MASK,
> + .val = AICL_RERUN_TIME_12_SECS },
> };
>
> /* Init sequence derived from vendor downstream driver */
> -static const struct smb_init_register smb_init_seq[] = {
> - { .addr = AICL_RERUN_TIME_CFG, .mask = AICL_RERUN_TIME_MASK, .val = 0 },
> +static const struct smb_init_register smb2_init_seq[] = {
> + { .addr = AICL_RERUN_TIME_CFG,
> + .mask = AICL_RERUN_TIME_MASK,
> + .val = AICL_RERUN_TIME_3_SECS },
> /*
> * By default configure us as an upstream facing port
> * FIXME: This will be handled by the type-c driver
> */
> { .addr = TYPE_C_INTRPT_ENB_SOFTWARE_CTRL,
> - .mask = TYPEC_POWER_ROLE_CMD_MASK | VCONN_EN_SRC_BIT |
> + .mask = TYPEC_POWER_ROLE_CMD_MASK | SMB2_VCONN_EN_SRC_BIT |
> VCONN_EN_VALUE_BIT,
> - .val = VCONN_EN_SRC_BIT },
> + .val = SMB2_VCONN_EN_SRC_BIT },
> /*
> * Disable Type-C factory mode and stay in Attached.SRC state when VCONN
> * over-current happens
> */
> - { .addr = TYPE_C_CFG,
> + { .addr = USBIN_TYPE_C_CFG,
> .mask = FACTORY_MODE_DETECTION_EN_BIT | VCONN_OC_CFG_BIT,
> .val = 0 },
> /* Configure VBUS for software control */
> - { .addr = OTG_CFG, .mask = OTG_EN_SRC_CFG_BIT, .val = 0 },
> + { .addr = SMB2_OTG_CFG, .mask = SMB2_OTG_EN_SRC_CFG_BIT, .val = 0 },
> /*
> * Use VBAT to determine the recharge threshold when battery is full
> * rather than the state of charge.
> */
> - { .addr = FG_UPDATE_CFG_2_SEL,
> - .mask = SOC_LT_CHG_RECHARGE_THRESH_SEL_BIT |
> - VBT_LT_CHG_RECHARGE_THRESH_SEL_BIT,
> - .val = VBT_LT_CHG_RECHARGE_THRESH_SEL_BIT },
> + { .addr = SMB2_FG_UPDATE_CFG_2_SEL,
> + .mask = SMB2_SOC_LT_CHG_RECHARGE_THRESH_SEL_BIT |
> + SMB2_VBT_LT_CHG_RECHARGE_THRESH_SEL_BIT,
> + .val = SMB2_VBT_LT_CHG_RECHARGE_THRESH_SEL_BIT },
> /* Enable charging */
> { .addr = USBIN_OPTIONS_1_CFG, .mask = HVDCP_EN_BIT, .val = 0 },
> { .addr = CHARGING_ENABLE_CMD,
> @@ -883,7 +1248,7 @@ static const struct smb_init_register smb_init_seq[] = {
> */
> { .addr = PRE_CHARGE_CURRENT_CFG,
> .mask = PRE_CHARGE_CURRENT_SETTING_MASK,
> - .val = 500000 / CURRENT_SCALE_FACTOR },
> + .val = 500000 / SMB2_CURRENT_STEP_UA },
> /*
> * This overrides all of the current limit options exposed to userspace
> * and prevents the device from pulling more than ~1A. This is done
> @@ -891,20 +1256,69 @@ static const struct smb_init_register smb_init_seq[] = {
> */
> { .addr = FAST_CHARGE_CURRENT_CFG,
> .mask = FAST_CHARGE_CURRENT_SETTING_MASK,
> - .val = 1000000 / CURRENT_SCALE_FACTOR },
> + .val = 1000000 / SMB2_CURRENT_STEP_UA },
> +};
> +
> +static const struct smb_match_data pmi8998_match_data = {
> + .init_seq = smb2_init_seq,
> + .init_seq_len = ARRAY_SIZE(smb2_init_seq),
> + .name = "pmi8998",
> + .gen = SMB2,
> + .fv_min_uv = 3487500,
> + .fv_max_uv = 4920000,
> + .fv_step_uv = 7500,
> + .fcc_max_ua = 4500000,
> + .fcc_step_ua = 25000,
> + .icl_max_ua = 4800000,
> + .icl_step_ua = 25000,
> + .icl_status = SMB2_ICL_STATUS,
> + .usbin_current_scale = 1,
> +};
> +
> +static const struct smb_match_data pm660_match_data = {
> + .init_seq = smb2_init_seq,
> + .init_seq_len = ARRAY_SIZE(smb2_init_seq),
> + .name = "pm660",
> + .gen = SMB2,
> + .fv_min_uv = 3487500,
> + .fv_max_uv = 4920000,
> + .fv_step_uv = 7500,
> + .fcc_max_ua = 4500000,
> + .fcc_step_ua = 25000,
> + .icl_max_ua = 4800000,
> + .icl_step_ua = 25000,
> + .icl_status = SMB2_ICL_STATUS,
> + .usbin_current_scale = 1,
> };
>
> -static int smb_init_hw(struct smb_chip *chip)
> +static const struct smb_match_data pm8150b_match_data = {
> + .init_seq = smb5_init_seq,
> + .init_seq_len = ARRAY_SIZE(smb5_init_seq),
> + .name = "pm8150b",
> + .gen = SMB5,
> + .fv_min_uv = 3600000,
> + .fv_max_uv = 4790000,
> + .fv_step_uv = 10000,
> + .fcc_max_ua = 8000000,
> + .fcc_step_ua = 50000,
> + .icl_max_ua = 5000000,
> + .icl_step_ua = 50000,
> + .icl_status = SMB5_AICL_ICL_STATUS,
> + .usbin_current_scale = 5,
> +};
> +
> +static int smb_init_hw(struct smb_chip *chip,
> + const struct smb_init_register *init_seq, size_t len)
> {
> int rc, i;
>
> - for (i = 0; i < ARRAY_SIZE(smb_init_seq); i++) {
> + for (i = 0; i < len; i++) {
> dev_dbg(chip->dev, "%d: Writing 0x%02x to 0x%02x\n", i,
> - smb_init_seq[i].val, smb_init_seq[i].addr);
> + init_seq[i].val, init_seq[i].addr);
> rc = regmap_update_bits(chip->regmap,
> - chip->base + smb_init_seq[i].addr,
> - smb_init_seq[i].mask,
> - smb_init_seq[i].val);
> + chip->base + init_seq[i].addr,
> + init_seq[i].mask,
> + init_seq[i].val);
> if (rc < 0)
> return dev_err_probe(chip->dev, rc,
> "%s: init command %d failed\n",
> @@ -914,19 +1328,6 @@ static int smb_init_hw(struct smb_chip *chip)
> return 0;
> }
>
> -static void smb_restore_charge_enable(void *data)
> -{
> - struct smb_chip *chip = data;
> - int rc;
> -
> - rc = regmap_update_bits(chip->regmap,
> - chip->base + CHARGING_ENABLE_CMD,
> - CHARGING_ENABLE_CMD_BIT,
> - chip->initial_charge_enable);
> - if (rc < 0)
> - dev_err(chip->dev, "Couldn't restore charging state: %d\n", rc);
> -}
> -
> static int smb_init_irq(struct smb_chip *chip, int *irq, const char *name,
> irqreturn_t (*handler)(int irq, void *data))
> {
> @@ -948,14 +1349,38 @@ static int smb_init_irq(struct smb_chip *chip, int *irq, const char *name,
> return 0;
> }
>
> +static void smb_restore_charge_state(void *data)
> +{
> + struct smb_chip *chip = data;
> + int rc;
> +
> + rc = regmap_update_bits(chip->regmap,
> + chip->base + CHARGING_ENABLE_CMD,
> + CHARGING_ENABLE_CMD_BIT,
> + chip->initial_charge_enable);
> + if (rc < 0) {
> + dev_err(chip->dev, "could not restore charging state: %d\n", rc);
> + return;
> + }
> +
> + if (chip->gen == SMB5) {
> + rc = regmap_update_bits(chip->regmap,
> + chip->base + USBIN_CMD_IL,
> + USBIN_SUSPEND_BIT,
> + chip->initial_usb_suspend);
> + if (rc < 0)
> + dev_err(chip->dev,
> + "could not restore USB input state: %d\n", rc);
> + }
> +}
> +
> static int smb_probe(struct platform_device *pdev)
> {
> struct power_supply_config supply_config = {};
> struct power_supply_desc *desc;
> struct smb_chip *chip;
> - unsigned int charge_enable;
> - unsigned int float_voltage_sel;
> - int float_voltage_uv;
> + const struct smb_match_data *match_data;
> + unsigned int reg;
> int rc, irq;
>
> chip = devm_kzalloc(&pdev->dev, sizeof(*chip), GFP_KERNEL);
> @@ -986,19 +1411,57 @@ static int smb_probe(struct platform_device *pdev)
> "Couldn't get usbin_i IIO channel\n");
> }
>
> - rc = regmap_read(chip->regmap, chip->base + CHARGING_ENABLE_CMD,
> - &charge_enable);
> + match_data = device_get_match_data(chip->dev);
> + if (!match_data)
> + return dev_err_probe(chip->dev, -ENODEV,
> + "missing match data\n");
> +
> + chip->gen = match_data->gen;
> + chip->match_data = match_data;
> + chip->icl_max_ua = match_data->icl_max_ua;
> + chip->icl_step_ua = match_data->icl_step_ua;
> + chip->icl_status = match_data->icl_status;
except mapping the match_data to the chip->match_data, this seems to be a bit
duplication of existing structure.
I would just do chip->data->gen (I would likely call `match_data` just `data`)
to access the values.
David
> + chip->usbin_current_scale = match_data->usbin_current_scale;
> +
> + dev_info(chip->dev, "Generation %s\n", chip->gen == SMB2 ? "SMB2" : "SMB5");
> + if (chip->gen == SMB5) {
> + rc = regmap_read(chip->regmap, chip->base + USBIN_CMD_IL, ®);
> + if (rc < 0)
> + return dev_err_probe(chip->dev, rc,
> + "could not read USB input state\n");
> + chip->initial_usb_suspend = reg & USBIN_SUSPEND_BIT;
> + }
> +
> + rc = regmap_read(chip->regmap,
> + chip->base + CHARGING_ENABLE_CMD, ®);
> if (rc < 0)
> return dev_err_probe(chip->dev, rc,
> - "Couldn't read charging state\n");
> + "could not read charging state\n");
> + chip->initial_charge_enable = reg & CHARGING_ENABLE_CMD_BIT;
>
> - chip->initial_charge_enable = charge_enable & CHARGING_ENABLE_CMD_BIT;
> - rc = devm_add_action_or_reset(chip->dev, smb_restore_charge_enable, chip);
> - if (rc)
> - return dev_err_probe(chip->dev, rc,
> - "Couldn't register charging state rollback\n");
> + rc = devm_add_action_or_reset(chip->dev,
> + smb_restore_charge_state, chip);
> + if (rc < 0)
> + return rc;
> +
> + if (chip->gen == SMB5) {
> + /* Do not use bootloader charge limits while configuring SMB5. */
> + rc = regmap_update_bits(chip->regmap,
> + chip->base + USBIN_CMD_IL,
> + USBIN_SUSPEND_BIT, USBIN_SUSPEND_BIT);
> + if (rc < 0)
> + return dev_err_probe(chip->dev, rc,
> + "could not suspend USB input\n");
>
> - rc = smb_init_hw(chip);
> + rc = regmap_update_bits(chip->regmap,
> + chip->base + CHARGING_ENABLE_CMD,
> + CHARGING_ENABLE_CMD_BIT, 0);
> + if (rc < 0)
> + return dev_err_probe(chip->dev, rc,
> + "could not disable charging\n");
> + }
> +
> + rc = smb_init_hw(chip, match_data->init_seq, match_data->init_seq_len);
> if (rc < 0)
> return rc;
>
> @@ -1011,7 +1474,7 @@ static int smb_probe(struct platform_device *pdev)
> memcpy(desc, &smb_psy_desc, sizeof(smb_psy_desc));
> desc->name =
> devm_kasprintf(chip->dev, GFP_KERNEL, "%s-charger",
> - (const char *)device_get_match_data(chip->dev));
> + match_data->name);
> if (!desc->name)
> return -ENOMEM;
>
> @@ -1021,46 +1484,13 @@ static int smb_probe(struct platform_device *pdev)
> return dev_err_probe(chip->dev, PTR_ERR(chip->chg_psy),
> "failed to register power supply\n");
>
> - rc = power_supply_get_battery_info(chip->chg_psy, &chip->batt_info);
> - if (rc)
> - return dev_err_probe(chip->dev, rc,
> - "Failed to get battery info\n");
> -
> - rc = devm_delayed_work_autocancel(chip->dev, &chip->status_change_work,
> + rc = devm_delayed_work_autocancel(chip->dev,
> + &chip->status_change_work,
> smb_status_change_work);
> if (rc)
> return dev_err_probe(chip->dev, rc,
> "Failed to init status change work\n");
>
> - if (power_supply_battery_info_has_prop(chip->batt_info,
> - POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE_MAX)) {
> - float_voltage_uv =
> - chip->batt_info->constant_charge_voltage_max_uv;
> - } else if (power_supply_battery_info_has_prop(chip->batt_info,
> - POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN)) {
> - float_voltage_uv = chip->batt_info->voltage_max_design_uv;
> - } else {
> - dev_warn(chip->dev, "No battery float voltage; preserving hardware setting\n");
> - goto skip_float_voltage;
> - }
> -
> - if (float_voltage_uv < SMB2_FLOAT_VOLTAGE_MIN_UV ||
> - float_voltage_uv > SMB2_FLOAT_VOLTAGE_MAX_UV)
> - return dev_err_probe(chip->dev, -EINVAL,
> - "float voltage %d uV outside %d-%d uV\n",
> - float_voltage_uv,
> - SMB2_FLOAT_VOLTAGE_MIN_UV,
> - SMB2_FLOAT_VOLTAGE_MAX_UV);
> -
> - float_voltage_sel =
> - (float_voltage_uv - SMB2_FLOAT_VOLTAGE_MIN_UV) /
> - SMB2_FLOAT_VOLTAGE_STEP_UV;
> - rc = regmap_update_bits(chip->regmap, chip->base + FLOAT_VOLTAGE_CFG,
> - FLOAT_VOLTAGE_SETTING_MASK, float_voltage_sel);
> - if (rc < 0)
> - return dev_err_probe(chip->dev, rc, "Couldn't set vbat max\n");
> -
> -skip_float_voltage:
> rc = smb_init_irq(chip, &irq, "bat-ov", smb_handle_batt_overvoltage);
> if (rc < 0)
> return rc;
> @@ -1084,7 +1514,7 @@ static int smb_probe(struct platform_device *pdev)
> if (rc < 0)
> return dev_err_probe(chip->dev, rc, "Couldn't set wake irq\n");
>
> - devm_remove_action(chip->dev, smb_restore_charge_enable, chip);
> + devm_remove_action(chip->dev, smb_restore_charge_state, chip);
>
> platform_set_drvdata(pdev, chip);
>
> @@ -1095,8 +1525,9 @@ static int smb_probe(struct platform_device *pdev)
> }
>
> static const struct of_device_id smb_match_id_table[] = {
> - { .compatible = "qcom,pmi8998-charger", .data = "pmi8998" },
> - { .compatible = "qcom,pm660-charger", .data = "pm660" },
> + { .compatible = "qcom,pmi8998-charger", .data = &pmi8998_match_data },
> + { .compatible = "qcom,pm660-charger", .data = &pm660_match_data },
> + { .compatible = "qcom,pm8150b-charger", .data = &pm8150b_match_data },
> { /* sentinal */ }
> };
> MODULE_DEVICE_TABLE(of, smb_match_id_table);
> @@ -1112,5 +1543,5 @@ static struct platform_driver qcom_spmi_smb = {
> module_platform_driver(qcom_spmi_smb);
>
> MODULE_AUTHOR("Casey Connolly <casey.connolly@linaro.org>");
> -MODULE_DESCRIPTION("Qualcomm SMB2 Charger Driver");
> +MODULE_DESCRIPTION("Qualcomm SMB2 and SMB5 Charger Driver");
> MODULE_LICENSE("GPL");
>
next prev parent reply other threads:[~2026-09-05 0:17 UTC|newest]
Thread overview: 6+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-08-20 10:03 [PATCH v4 0/2] power: supply: qcom_smbx: add SMB5 support Robin Snyders via B4 Relay
2026-08-20 10:03 ` [PATCH v4 1/2] dt-bindings: power: supply: qcom,pmi8998-charger: add SMB5 PMICs Robin Snyders via B4 Relay
2026-08-20 10:03 ` [PATCH v4 2/2] power: supply: qcom_smbx: add SMB5 support Robin Snyders via B4 Relay
2026-09-04 20:37 ` David Heidelberg
2026-09-05 0:17 ` David Heidelberg [this message]
2026-09-02 20:09 ` [PATCH v4 0/2] " David Heidelberg
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=b5f87468-20f1-437f-8610-518df679ee99@ixit.cz \
--to=david@ixit.cz \
--cc=casey.connolly@linaro.org \
--cc=conor+dt@kernel.org \
--cc=devicetree@vger.kernel.org \
--cc=dmitry.baryshkov@oss.qualcomm.com \
--cc=foss@joelselvaraj.com \
--cc=konrad.dybcio@oss.qualcomm.com \
--cc=krzk+dt@kernel.org \
--cc=linux-arm-msm@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-pm@vger.kernel.org \
--cc=robh@kernel.org \
--cc=robin@snyders.xyz \
--cc=sre@kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox