Introduce support for the SMB5 charger found on pm8150b and other more
modern Qualcomm SoCs.
SMB5 is largely similar to SMB2, with a few register differences. The
main difference is the new Type-C hardware block which some registers
are moved to.
Signed-off-by: Casey Connolly <casey.connolly@linaro.org>
---
drivers/power/supply/qcom_smbx.c | 367 +++++++++++++++++++++++++++++++++------
1 file changed, 314 insertions(+), 53 deletions(-)
diff --git a/drivers/power/supply/qcom_smbx.c b/drivers/power/supply/qcom_smbx.c
index 10ddd33a09599decb23c0f1ccd02fa9b56602543..d902f3f43548191d3d0310ce90e699918ed0f16f 100644
--- a/drivers/power/supply/qcom_smbx.c
+++ b/drivers/power/supply/qcom_smbx.c
@@ -22,18 +22,32 @@
#include <linux/regmap.h>
#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 BATTERY_CHARGER_STATUS_MASK GENMASK(2, 0)
#define BATTERY_CHARGER_STATUS_2 0x07
-#define CHARGER_ERROR_STATUS_BAT_OV_BIT BIT(5)
-#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_CHARGER_ERROR_STATUS_BAT_OV_BIT BIT(5)
+#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 SMB5_CHARGER_ERROR_STATUS_BAT_OV_BIT BIT(1)
+#define SMB2_BAT_TEMP_STATUS_TOO_COLD_BIT BIT(0)
+
+#define BATTERY_CHARGER_STATUS_7 0x0D
+#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 CHARGING_ENABLE_CMD 0x42
#define CHARGING_ENABLE_CMD_BIT BIT(0)
@@ -55,11 +69,14 @@
#define FLOAT_VOLTAGE_CFG 0x70
#define FLOAT_VOLTAGE_SETTING_MASK GENMASK(7, 0)
-#define FG_UPDATE_CFG_2_SEL 0x7D
-#define SOC_LT_CHG_RECHARGE_THRESH_SEL_BIT BIT(2)
-#define VBT_LT_CHG_RECHARGE_THRESH_SEL_BIT BIT(1)
+#define SMB2_FG_UPDATE_CFG_2_SEL 0x7D
+#define SMB2_SOC_LT_CHG_RECHARGE_THRESH_SEL_BIT BIT(2)
+#define SMB2_VBT_LT_CHG_RECHARGE_THRESH_SEL_BIT BIT(1)
+
+#define SMB5_CHARGE_RCHG_SOC_THRESHOLD_CFG_REG 0x7D
+#define SMB5_CHARGE_RCHG_SOC_THRESHOLD_CFG_MASK GENMASK(7, 0)
#define OTG_CFG 0x153
#define OTG_EN_SRC_CFG_BIT BIT(1)
@@ -73,36 +90,46 @@
#define CDP_CHARGER_BIT BIT(2)
#define OCP_CHARGER_BIT BIT(1)
#define SDP_CHARGER_BIT BIT(0)
+#define USBIN_CMD_IL 0x340
+#define USBIN_SUSPEND_BIT BIT(0)
+
#define CMD_APSD 0x341
#define APSD_RERUN_BIT BIT(0)
+#define CMD_ICL_OVERRIDE 0x342
+#define ICL_OVERRIDE_BIT BIT(0)
+
#define TYPE_C_CFG 0x358
+#define APSD_START_ON_CC_BIT BIT(7)
#define FACTORY_MODE_DETECTION_EN_BIT BIT(5)
#define VCONN_OC_CFG_BIT BIT(1)
#define USBIN_OPTIONS_1_CFG 0x362
+#define AUTO_SRC_DETECT_BIT BIT(3)
#define HVDCP_EN_BIT BIT(2)
+#define USBIN_LOAD_CFG 0x65
+#define ICL_OVERRIDE_AFTER_APSD_BIT BIT(4)
+
#define USBIN_ICL_OPTIONS 0x366
#define USB51_MODE_BIT BIT(1)
#define USBIN_MODE_CHG_BIT BIT(0)
/* PMI8998 only */
#define TYPE_C_INTRPT_ENB_SOFTWARE_CTRL 0x368
-#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)
-#define DFP_EN_CMD_BIT BIT(1)
-#define TYPEC_DISABLE_CMD_BIT BIT(0)
+#define SMB5_EN_SNK_ONLY_BIT BIT(1)
#define USBIN_CURRENT_LIMIT_CFG 0x370
#define USBIN_AICL_OPTIONS_CFG 0x380
#define SUSPEND_ON_COLLAPSE_USBIN_BIT BIT(7)
#define USBIN_AICL_START_AT_MAX_BIT BIT(5)
+#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)
#define USBIN_LV_COLLAPSE_RESPONSE_BIT BIT(0)
@@ -113,15 +140,41 @@
#define USBIN_CONT_AICL_THRESHOLD_CFG 0x384
#define USBIN_CONT_AICL_THRESHOLD_CFG_MASK GENMASK(5, 0)
-#define ICL_STATUS 0x607
+#define ICL_STATUS(smb) (SMB_REG_OFFSET(smb) + 0x07)
#define INPUT_CURRENT_LIMIT_MASK GENMASK(7, 0)
-#define POWER_PATH_STATUS 0x60B
+#define POWER_PATH_STATUS(smb) (SMB_REG_OFFSET(smb) + 0x0B)
#define P_PATH_USE_USBIN_BIT BIT(4)
#define P_PATH_VALID_INPUT_POWER_SOURCE_STS_BIT BIT(0)
+/* 0x5xx region is PM8150b only Type-C registers */
+
+/* Bits 2:0 match PMI8998 TYPE_C_INTRPT_ENB_SOFTWARE_CTRL */
+#define SMB5_TYPE_C_MODE_CFG 0x544
+#define SMB5_EN_TRY_SNK_BIT BIT(4)
+#define SMB5_EN_SNK_ONLY_BIT BIT(1)
+
+#define SMB5_TYPEC_TYPE_C_VCONN_CONTROL 0x546
+#define SMB5_VCONN_EN_ORIENTATION_BIT BIT(2)
+#define SMB5_VCONN_EN_VALUE_BIT BIT(1)
+#define SMB5_VCONN_EN_SRC_BIT BIT(0)
+
+
+#define SMB5_TYPE_C_DEBUG_ACCESS_SINK 0x54a
+#define SMB5_TYPEC_DEBUG_ACCESS_SINK_MASK GENMASK(4, 0)
+
+#define SMB5_DEBUG_ACCESS_SRC_CFG 0x54C
+#define SMB5_EN_UNORIENTED_DEBUG_ACCESS_SRC_BIT BIT(0)
+
+#define SMB5_TYPE_C_EXIT_STATE_CFG 0x550
+#define SMB5_BYPASS_VSAFE0V_DURING_ROLE_SWAP_BIT BIT(3)
+#define SMB5_SEL_SRC_UPPER_REF_BIT BIT(2)
+#define SMB5_EXIT_SNK_BASED_ON_CC_BIT BIT(0)
+
+/* Common */
+
#define BARK_BITE_WDOG_PET 0x643
#define BARK_BITE_WDOG_PET_BIT BIT(0)
#define WD_CFG 0x651
@@ -184,8 +237,9 @@ struct smb_chip {
const char *name;
unsigned int base;
struct regmap *regmap;
struct power_supply_battery_info *batt_info;
+ enum smb_generation gen;
struct delayed_work status_change_work;
int cable_irq;
bool wakeup_enabled;
@@ -195,8 +249,15 @@ struct smb_chip {
struct power_supply *chg_psy;
};
+struct smb_match_data {
+ const char *name;
+ enum smb_generation gen;
+ size_t init_seq_len;
+ const struct smb_init_register __counted_by(init_seq_len) *init_seq;
+};
+
static enum power_supply_property smb_properties[] = {
POWER_SUPPLY_PROP_MANUFACTURER,
POWER_SUPPLY_PROP_MODEL_NAME,
POWER_SUPPLY_PROP_CURRENT_MAX,
@@ -212,9 +273,9 @@ 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;
}
@@ -267,11 +328,37 @@ 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)
+{
+ u16 reg;
+ u8 mask;
+ int rc;
+ u32 val;
+
+ switch (chip->gen) {
+ case SMB2:
+ reg = BATTERY_CHARGER_STATUS_2;
+ mask = SMB2_CHARGER_ERROR_STATUS_BAT_OV_BIT;
+ break;
+ case SMB5:
+ reg = BATTERY_CHARGER_STATUS_7;
+ mask = SMB5_CHARGER_ERROR_STATUS_BAT_OV_BIT;
+ break;
+ }
+
+ rc = regmap_read(chip->regmap, chip->base + reg, &val);
+ if (rc)
+ return rc;
+
+ return !!(reg & mask);
+}
+
static int smb_get_prop_status(struct smb_chip *chip, int *val)
{
- unsigned char stat[2];
+ u32 stat;
int usb_online = 0;
int rc;
rc = smb_get_prop_usb_online(chip, &usb_online);
@@ -279,24 +366,29 @@ static int smb_get_prop_status(struct smb_chip *chip, int *val)
*val = POWER_SUPPLY_STATUS_DISCHARGING;
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 = stat & BATTERY_CHARGER_STATUS_MASK;
- switch (stat[0]) {
+ switch (stat) {
case TRICKLE_CHARGE:
case PRE_CHARGE:
case FAST_CHARGE:
case FULLON_CHARGE:
@@ -318,9 +410,9 @@ static int smb_get_prop_status(struct smb_chip *chip, int *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 + ICL_STATUS(chip), val);
if (rc >= 0)
*val *= CURRENT_SCALE_FACTOR;
return rc;
@@ -413,9 +505,46 @@ 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 smb5_get_prop_health(struct smb_chip *chip, int *val)
+{
+ int rc;
+ unsigned int stat;
+
+ rc = smbx_ov_status(chip);
+
+ /* Treat any error as if we are in the overvoltage state */
+ if (rc < 0)
+ dev_err(chip->dev, "Couldn't determine overvoltage status!");
+ if (rc) {
+ dev_err(chip->dev, "battery over-voltage");
+ *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;
@@ -426,34 +555,45 @@ static int smb_get_prop_health(struct smb_chip *chip, int *val)
return rc;
}
switch (stat) {
- case CHARGER_ERROR_STATUS_BAT_OV_BIT:
+ case SMB2_CHARGER_ERROR_STATUS_BAT_OV_BIT:
*val = POWER_SUPPLY_HEALTH_OVERVOLTAGE;
return 0;
- case BAT_TEMP_STATUS_TOO_COLD_BIT:
+ case SMB2_BAT_TEMP_STATUS_TOO_COLD_BIT:
*val = POWER_SUPPLY_HEALTH_COLD;
return 0;
- case BAT_TEMP_STATUS_TOO_HOT_BIT:
+ case SMB2_BAT_TEMP_STATUS_TOO_HOT_BIT:
*val = POWER_SUPPLY_HEALTH_OVERHEAT;
return 0;
- case BAT_TEMP_STATUS_COLD_SOFT_LIMIT_BIT:
+ case SMB2_BAT_TEMP_STATUS_COLD_SOFT_LIMIT_BIT:
*val = POWER_SUPPLY_HEALTH_COOL;
return 0;
- case BAT_TEMP_STATUS_HOT_SOFT_LIMIT_BIT:
+ case SMB2_BAT_TEMP_STATUS_HOT_SOFT_LIMIT_BIT:
*val = POWER_SUPPLY_HEALTH_WARM;
return 0;
default:
*val = POWER_SUPPLY_HEALTH_GOOD;
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);
+ }
+}
+
static int smb_get_property(struct power_supply *psy,
enum power_supply_property psp,
union power_supply_propval *val)
{
struct smb_chip *chip = power_supply_get_drvdata(psy);
+ int ret;
switch (psp) {
case POWER_SUPPLY_PROP_MANUFACTURER:
val->strval = "Qualcomm";
@@ -466,10 +606,15 @@ static int smb_get_property(struct power_supply *psy,
case POWER_SUPPLY_PROP_CURRENT_NOW:
return smb_get_iio_chan(chip, chip->usb_in_i_chan,
&val->intval);
case POWER_SUPPLY_PROP_VOLTAGE_NOW:
- return smb_get_iio_chan(chip, chip->usb_in_v_chan,
+ ret = smb_get_iio_chan(chip, chip->usb_in_v_chan,
&val->intval);
+ if (!ret) {
+ if (chip->gen == SMB5)
+ val->intval *= 16;
+ }
+ return ret;
case POWER_SUPPLY_PROP_ONLINE:
return smb_get_prop_usb_online(chip, &val->intval);
case POWER_SUPPLY_PROP_STATUS:
return smb_get_prop_status(chip, &val->intval);
@@ -515,14 +660,10 @@ 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;
- regmap_read(chip->regmap, chip->base + BATTERY_CHARGER_STATUS_2,
- &status);
-
- if (status & CHARGER_ERROR_STATUS_BAT_OV_BIT) {
+ if (smbx_ov_status(chip) == 1) {
/* The hardware stops charging automatically */
dev_err(chip->dev, "battery overvoltage detected\n");
power_supply_changed(chip->chg_psy);
}
@@ -566,9 +707,9 @@ static irqreturn_t smb_handle_wdog_bark(int irq, void *data)
return IRQ_HANDLED;
}
static const struct power_supply_desc smb_psy_desc = {
- .name = "pmi8998_charger",
+ .name = "SMB2_charger",
.type = POWER_SUPPLY_TYPE_USB,
.usb_types = BIT(POWER_SUPPLY_USB_TYPE_SDP) |
BIT(POWER_SUPPLY_USB_TYPE_CDP) |
BIT(POWER_SUPPLY_USB_TYPE_DCP) |
@@ -580,18 +721,100 @@ static const struct power_supply_desc smb_psy_desc = {
.property_is_writeable = smb_property_is_writable,
};
/* 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 smb5_init_seq[] = {
+ { .addr = USBIN_CMD_IL, .mask = USBIN_SUSPEND_BIT, .val = 0 },
+ /*
+ * By default configure us as an upstream facing port
+ * FIXME: This will be handled by the type-c driver
+ */
+ { .addr = SMB5_TYPE_C_MODE_CFG,
+ .mask = SMB5_EN_TRY_SNK_BIT | SMB5_EN_SNK_ONLY_BIT,
+ .val = SMB5_EN_TRY_SNK_BIT },
+ { .addr = SMB5_TYPEC_TYPE_C_VCONN_CONTROL,
+ .mask = SMB5_VCONN_EN_ORIENTATION_BIT | SMB5_VCONN_EN_SRC_BIT |
+ SMB5_VCONN_EN_VALUE_BIT,
+ .val = SMB2_VCONN_EN_SRC_BIT },
+ { .addr = SMB5_DEBUG_ACCESS_SRC_CFG,
+ .mask = SMB5_EN_UNORIENTED_DEBUG_ACCESS_SRC_BIT,
+ .val = SMB5_EN_UNORIENTED_DEBUG_ACCESS_SRC_BIT },
+ { .addr = SMB5_TYPE_C_EXIT_STATE_CFG,
+ .mask = SMB5_SEL_SRC_UPPER_REF_BIT,
+ .val = SMB5_SEL_SRC_UPPER_REF_BIT },
+ /*
+ * Disable Type-C factory mode and stay in Attached.SRC state when VCONN
+ * over-current happens
+ */
+ { .addr = TYPE_C_CFG,
+ .mask = APSD_START_ON_CC_BIT,
+ .val = 0 },
+ { .addr = SMB5_TYPE_C_DEBUG_ACCESS_SINK,
+ .mask = SMB5_TYPEC_DEBUG_ACCESS_SINK_MASK,
+ .val = 0x17 },
+ /* Configure VBUS for software control */
+ { .addr = OTG_CFG, .mask = OTG_EN_SRC_CFG_BIT, .val = 0 },
+ /*
+ * Recharge when State Of Charge drops below 98%.
+ */
+ { .addr = SMB5_CHARGE_RCHG_SOC_THRESHOLD_CFG_REG,
+ .mask = SMB5_CHARGE_RCHG_SOC_THRESHOLD_CFG_MASK,
+ .val = 250 },
+ /* Enable charging */
+ { .addr = CHARGING_ENABLE_CMD,
+ .mask = CHARGING_ENABLE_CMD_BIT,
+ .val = CHARGING_ENABLE_CMD_BIT },
+ /* Enable BC1P2 auto Src detect */
+ { .addr = USBIN_OPTIONS_1_CFG,
+ .mask = AUTO_SRC_DETECT_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_ADC_EN_BIT
+ | USBIN_AICL_EN_BIT | SUSPEND_ON_COLLAPSE_USBIN_BIT },
+ /*
+ * This overrides all of the other current limit configs and is
+ * expected to be used for setting limits based on temperature.
+ * We set some relatively safe default value while still allowing
+ * a comfortably fast charging rate.
+ */
+ { .addr = FAST_CHARGE_CURRENT_CFG,
+ .mask = FAST_CHARGE_CURRENT_SETTING_MASK,
+ .val = 1950000 / CURRENT_SCALE_FACTOR },
+};
+
+/* Init sequence derived from vendor downstream driver */
+static const struct smb_init_register smb2_init_seq[] = {
/*
* 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
*/
@@ -603,12 +826,12 @@ static const struct smb_init_register smb_init_seq[] = {
/*
* 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,
.mask = CHARGING_ENABLE_CMD_BIT,
@@ -674,19 +897,48 @@ static const struct smb_init_register smb_init_seq[] = {
.mask = PRE_CHARGE_CURRENT_SETTING_MASK,
.val = 500000 / CURRENT_SCALE_FACTOR },
};
-static int smb_init_hw(struct smb_chip *chip)
+struct smb_match_data pmi8998_match_data = {
+ .init_seq = smb2_init_seq,
+ .init_seq_len = ARRAY_SIZE(smb2_init_seq),
+ .name = "pmi8998",
+ .gen = SMB2,
+};
+
+struct smb_match_data pm660_match_data = {
+ .init_seq = smb2_init_seq,
+ .init_seq_len = ARRAY_SIZE(smb2_init_seq),
+ .name = "pm660",
+ .gen = SMB2,
+};
+
+struct smb_match_data pm8150b_match_data = {
+ .init_seq = smb5_init_seq,
+ .init_seq_len = ARRAY_SIZE(smb5_init_seq),
+ .name = "pm8150b",
+ .gen = SMB5,
+};
+
+struct smb_match_data pm7250b_match_data = {
+ .init_seq = smb5_init_seq,
+ .init_seq_len = ARRAY_SIZE(smb5_init_seq),
+ .name = "pm7250b",
+ .gen = SMB5,
+};
+
+
+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",
__func__, i);
@@ -721,8 +973,9 @@ static int smb_probe(struct platform_device *pdev)
{
struct power_supply_config supply_config = {};
struct power_supply_desc *desc;
struct smb_chip *chip;
+ const struct smb_match_data *match_data;
int rc, irq;
chip = devm_kzalloc(&pdev->dev, sizeof(*chip), GFP_KERNEL);
if (!chip)
@@ -751,9 +1004,15 @@ static int smb_probe(struct platform_device *pdev)
return dev_err_probe(chip->dev, PTR_ERR(chip->usb_in_i_chan),
"Couldn't get usbin_i IIO channel\n");
}
- rc = smb_init_hw(chip);
+ match_data = (const struct smb_match_data *)device_get_match_data(chip->dev);
+
+ chip->gen = match_data->gen;
+
+ dev_info(chip->dev, "Generation %s\n", chip->gen == SMB2 ? "SMB2" : "SMB5");
+
+ rc = smb_init_hw(chip, match_data->init_seq, match_data->init_seq_len);
if (rc < 0)
return rc;
supply_config.drv_data = chip;
@@ -764,9 +1023,9 @@ static int smb_probe(struct platform_device *pdev)
return -ENOMEM;
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;
chip->chg_psy =
@@ -839,10 +1098,12 @@ static int smb_probe(struct platform_device *pdev)
return 0;
}
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,pm7250b-charger", .data = &pm7250b_match_data },
+ { .compatible = "qcom,pm8150b-charger", .data = &pm8150b_match_data },
{ /* sentinal */ }
};
MODULE_DEVICE_TABLE(of, smb_match_id_table);
--
2.49.0
On Thu, Jun 19, 2025 at 04:55:17PM +0200, Casey Connolly wrote: > Introduce support for the SMB5 charger found on pm8150b and other more > modern Qualcomm SoCs. > > SMB5 is largely similar to SMB2, with a few register differences. The > main difference is the new Type-C hardware block which some registers > are moved to. > > Signed-off-by: Casey Connolly <casey.connolly@linaro.org> > --- > drivers/power/supply/qcom_smbx.c | 367 +++++++++++++++++++++++++++++++++------ > 1 file changed, 314 insertions(+), 53 deletions(-) > > diff --git a/drivers/power/supply/qcom_smbx.c b/drivers/power/supply/qcom_smbx.c > index 10ddd33a09599decb23c0f1ccd02fa9b56602543..d902f3f43548191d3d0310ce90e699918ed0f16f 100644 > --- a/drivers/power/supply/qcom_smbx.c > +++ b/drivers/power/supply/qcom_smbx.c > @@ -22,18 +22,32 @@ > #include <linux/regmap.h> > #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 BATTERY_CHARGER_STATUS_MASK GENMASK(2, 0) > > #define BATTERY_CHARGER_STATUS_2 0x07 > -#define CHARGER_ERROR_STATUS_BAT_OV_BIT BIT(5) > -#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_CHARGER_ERROR_STATUS_BAT_OV_BIT BIT(5) > +#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 SMB5_CHARGER_ERROR_STATUS_BAT_OV_BIT BIT(1) > +#define SMB2_BAT_TEMP_STATUS_TOO_COLD_BIT BIT(0) > + > +#define BATTERY_CHARGER_STATUS_7 0x0D > +#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 CHARGING_ENABLE_CMD 0x42 > #define CHARGING_ENABLE_CMD_BIT BIT(0) > > @@ -55,11 +69,14 @@ > > #define FLOAT_VOLTAGE_CFG 0x70 > #define FLOAT_VOLTAGE_SETTING_MASK GENMASK(7, 0) > > -#define FG_UPDATE_CFG_2_SEL 0x7D > -#define SOC_LT_CHG_RECHARGE_THRESH_SEL_BIT BIT(2) > -#define VBT_LT_CHG_RECHARGE_THRESH_SEL_BIT BIT(1) > +#define SMB2_FG_UPDATE_CFG_2_SEL 0x7D > +#define SMB2_SOC_LT_CHG_RECHARGE_THRESH_SEL_BIT BIT(2) > +#define SMB2_VBT_LT_CHG_RECHARGE_THRESH_SEL_BIT BIT(1) > + > +#define SMB5_CHARGE_RCHG_SOC_THRESHOLD_CFG_REG 0x7D > +#define SMB5_CHARGE_RCHG_SOC_THRESHOLD_CFG_MASK GENMASK(7, 0) > > #define OTG_CFG 0x153 > #define OTG_EN_SRC_CFG_BIT BIT(1) > > @@ -73,36 +90,46 @@ > #define CDP_CHARGER_BIT BIT(2) > #define OCP_CHARGER_BIT BIT(1) > #define SDP_CHARGER_BIT BIT(0) > > +#define USBIN_CMD_IL 0x340 > +#define USBIN_SUSPEND_BIT BIT(0) > + > #define CMD_APSD 0x341 > #define APSD_RERUN_BIT BIT(0) > > +#define CMD_ICL_OVERRIDE 0x342 > +#define ICL_OVERRIDE_BIT BIT(0) > + > #define TYPE_C_CFG 0x358 > +#define APSD_START_ON_CC_BIT BIT(7) > #define FACTORY_MODE_DETECTION_EN_BIT BIT(5) > #define VCONN_OC_CFG_BIT BIT(1) > > #define USBIN_OPTIONS_1_CFG 0x362 > +#define AUTO_SRC_DETECT_BIT BIT(3) > #define HVDCP_EN_BIT BIT(2) > > +#define USBIN_LOAD_CFG 0x65 > +#define ICL_OVERRIDE_AFTER_APSD_BIT BIT(4) > + > #define USBIN_ICL_OPTIONS 0x366 > #define USB51_MODE_BIT BIT(1) > #define USBIN_MODE_CHG_BIT BIT(0) > > /* PMI8998 only */ > #define TYPE_C_INTRPT_ENB_SOFTWARE_CTRL 0x368 > -#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) > -#define DFP_EN_CMD_BIT BIT(1) > -#define TYPEC_DISABLE_CMD_BIT BIT(0) > +#define SMB5_EN_SNK_ONLY_BIT BIT(1) > > #define USBIN_CURRENT_LIMIT_CFG 0x370 > > #define USBIN_AICL_OPTIONS_CFG 0x380 > #define SUSPEND_ON_COLLAPSE_USBIN_BIT BIT(7) > #define USBIN_AICL_START_AT_MAX_BIT BIT(5) > +#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) > #define USBIN_LV_COLLAPSE_RESPONSE_BIT BIT(0) > @@ -113,15 +140,41 @@ > > #define USBIN_CONT_AICL_THRESHOLD_CFG 0x384 > #define USBIN_CONT_AICL_THRESHOLD_CFG_MASK GENMASK(5, 0) > > -#define ICL_STATUS 0x607 > +#define ICL_STATUS(smb) (SMB_REG_OFFSET(smb) + 0x07) > #define INPUT_CURRENT_LIMIT_MASK GENMASK(7, 0) > > -#define POWER_PATH_STATUS 0x60B > +#define POWER_PATH_STATUS(smb) (SMB_REG_OFFSET(smb) + 0x0B) > #define P_PATH_USE_USBIN_BIT BIT(4) > #define P_PATH_VALID_INPUT_POWER_SOURCE_STS_BIT BIT(0) > > +/* 0x5xx region is PM8150b only Type-C registers */ > + > +/* Bits 2:0 match PMI8998 TYPE_C_INTRPT_ENB_SOFTWARE_CTRL */ > +#define SMB5_TYPE_C_MODE_CFG 0x544 > +#define SMB5_EN_TRY_SNK_BIT BIT(4) > +#define SMB5_EN_SNK_ONLY_BIT BIT(1) > + > +#define SMB5_TYPEC_TYPE_C_VCONN_CONTROL 0x546 > +#define SMB5_VCONN_EN_ORIENTATION_BIT BIT(2) > +#define SMB5_VCONN_EN_VALUE_BIT BIT(1) > +#define SMB5_VCONN_EN_SRC_BIT BIT(0) > + > + > +#define SMB5_TYPE_C_DEBUG_ACCESS_SINK 0x54a > +#define SMB5_TYPEC_DEBUG_ACCESS_SINK_MASK GENMASK(4, 0) > + > +#define SMB5_DEBUG_ACCESS_SRC_CFG 0x54C > +#define SMB5_EN_UNORIENTED_DEBUG_ACCESS_SRC_BIT BIT(0) > + > +#define SMB5_TYPE_C_EXIT_STATE_CFG 0x550 > +#define SMB5_BYPASS_VSAFE0V_DURING_ROLE_SWAP_BIT BIT(3) > +#define SMB5_SEL_SRC_UPPER_REF_BIT BIT(2) > +#define SMB5_EXIT_SNK_BASED_ON_CC_BIT BIT(0) > + > +/* Common */ > + > #define BARK_BITE_WDOG_PET 0x643 > #define BARK_BITE_WDOG_PET_BIT BIT(0) > > #define WD_CFG 0x651 > @@ -184,8 +237,9 @@ struct smb_chip { > const char *name; > unsigned int base; > struct regmap *regmap; > struct power_supply_battery_info *batt_info; > + enum smb_generation gen; > > struct delayed_work status_change_work; > int cable_irq; > bool wakeup_enabled; > @@ -195,8 +249,15 @@ struct smb_chip { > > struct power_supply *chg_psy; > }; > > +struct smb_match_data { > + const char *name; > + enum smb_generation gen; > + size_t init_seq_len; > + const struct smb_init_register __counted_by(init_seq_len) *init_seq; > +}; > + > static enum power_supply_property smb_properties[] = { > POWER_SUPPLY_PROP_MANUFACTURER, > POWER_SUPPLY_PROP_MODEL_NAME, > POWER_SUPPLY_PROP_CURRENT_MAX, > @@ -212,9 +273,9 @@ 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; > } > @@ -267,11 +328,37 @@ 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) > +{ > + u16 reg; > + u8 mask; > + int rc; > + u32 val; > + > + switch (chip->gen) { > + case SMB2: > + reg = BATTERY_CHARGER_STATUS_2; > + mask = SMB2_CHARGER_ERROR_STATUS_BAT_OV_BIT; > + break; > + case SMB5: > + reg = BATTERY_CHARGER_STATUS_7; > + mask = SMB5_CHARGER_ERROR_STATUS_BAT_OV_BIT; > + break; > + } > + > + rc = regmap_read(chip->regmap, chip->base + reg, &val); > + if (rc) > + return rc; > + > + return !!(reg & mask); > +} > + > static int smb_get_prop_status(struct smb_chip *chip, int *val) > { > - unsigned char stat[2]; > + u32 stat; > int usb_online = 0; > int rc; > > rc = smb_get_prop_usb_online(chip, &usb_online); > @@ -279,24 +366,29 @@ static int smb_get_prop_status(struct smb_chip *chip, int *val) > *val = POWER_SUPPLY_STATUS_DISCHARGING; > 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 = stat & BATTERY_CHARGER_STATUS_MASK; > > - switch (stat[0]) { > + switch (stat) { > case TRICKLE_CHARGE: > case PRE_CHARGE: > case FAST_CHARGE: > case FULLON_CHARGE: > @@ -318,9 +410,9 @@ static int smb_get_prop_status(struct smb_chip *chip, int *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 + ICL_STATUS(chip), val); > > if (rc >= 0) > *val *= CURRENT_SCALE_FACTOR; > return rc; > @@ -413,9 +505,46 @@ 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 smb5_get_prop_health(struct smb_chip *chip, int *val) > +{ > + int rc; > + unsigned int stat; > + > + rc = smbx_ov_status(chip); > + > + /* Treat any error as if we are in the overvoltage state */ > + if (rc < 0) > + dev_err(chip->dev, "Couldn't determine overvoltage status!"); > + if (rc) { > + dev_err(chip->dev, "battery over-voltage"); > + *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; > > @@ -426,34 +555,45 @@ static int smb_get_prop_health(struct smb_chip *chip, int *val) > return rc; > } > > switch (stat) { > - case CHARGER_ERROR_STATUS_BAT_OV_BIT: > + case SMB2_CHARGER_ERROR_STATUS_BAT_OV_BIT: > *val = POWER_SUPPLY_HEALTH_OVERVOLTAGE; > return 0; > - case BAT_TEMP_STATUS_TOO_COLD_BIT: > + case SMB2_BAT_TEMP_STATUS_TOO_COLD_BIT: > *val = POWER_SUPPLY_HEALTH_COLD; > return 0; > - case BAT_TEMP_STATUS_TOO_HOT_BIT: > + case SMB2_BAT_TEMP_STATUS_TOO_HOT_BIT: > *val = POWER_SUPPLY_HEALTH_OVERHEAT; > return 0; > - case BAT_TEMP_STATUS_COLD_SOFT_LIMIT_BIT: > + case SMB2_BAT_TEMP_STATUS_COLD_SOFT_LIMIT_BIT: > *val = POWER_SUPPLY_HEALTH_COOL; > return 0; > - case BAT_TEMP_STATUS_HOT_SOFT_LIMIT_BIT: > + case SMB2_BAT_TEMP_STATUS_HOT_SOFT_LIMIT_BIT: > *val = POWER_SUPPLY_HEALTH_WARM; > return 0; > default: > *val = POWER_SUPPLY_HEALTH_GOOD; > 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); > + } > +} > + > static int smb_get_property(struct power_supply *psy, > enum power_supply_property psp, > union power_supply_propval *val) > { > struct smb_chip *chip = power_supply_get_drvdata(psy); > + int ret; > > switch (psp) { > case POWER_SUPPLY_PROP_MANUFACTURER: > val->strval = "Qualcomm"; > @@ -466,10 +606,15 @@ static int smb_get_property(struct power_supply *psy, > case POWER_SUPPLY_PROP_CURRENT_NOW: > return smb_get_iio_chan(chip, chip->usb_in_i_chan, > &val->intval); > case POWER_SUPPLY_PROP_VOLTAGE_NOW: > - return smb_get_iio_chan(chip, chip->usb_in_v_chan, > + ret = smb_get_iio_chan(chip, chip->usb_in_v_chan, > &val->intval); > + if (!ret) { > + if (chip->gen == SMB5) > + val->intval *= 16; > + } > + return ret; > case POWER_SUPPLY_PROP_ONLINE: > return smb_get_prop_usb_online(chip, &val->intval); > case POWER_SUPPLY_PROP_STATUS: > return smb_get_prop_status(chip, &val->intval); > @@ -515,14 +660,10 @@ 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; > > - regmap_read(chip->regmap, chip->base + BATTERY_CHARGER_STATUS_2, > - &status); > - > - if (status & CHARGER_ERROR_STATUS_BAT_OV_BIT) { > + if (smbx_ov_status(chip) == 1) { > /* The hardware stops charging automatically */ > dev_err(chip->dev, "battery overvoltage detected\n"); > power_supply_changed(chip->chg_psy); > } > @@ -566,9 +707,9 @@ static irqreturn_t smb_handle_wdog_bark(int irq, void *data) > return IRQ_HANDLED; > } > > static const struct power_supply_desc smb_psy_desc = { > - .name = "pmi8998_charger", > + .name = "SMB2_charger", > .type = POWER_SUPPLY_TYPE_USB, > .usb_types = BIT(POWER_SUPPLY_USB_TYPE_SDP) | > BIT(POWER_SUPPLY_USB_TYPE_CDP) | > BIT(POWER_SUPPLY_USB_TYPE_DCP) | > @@ -580,18 +721,100 @@ static const struct power_supply_desc smb_psy_desc = { > .property_is_writeable = smb_property_is_writable, > }; > > /* 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 smb5_init_seq[] = { > + { .addr = USBIN_CMD_IL, .mask = USBIN_SUSPEND_BIT, .val = 0 }, > + /* > + * By default configure us as an upstream facing port > + * FIXME: This will be handled by the type-c driver > + */ All of this needs to be reworked to cooperate with the type-c driver. Otherwise it might try to reconfigure the Type-C mode _after_ the TCPM has negotiated some configuration. So, it can't go in in this way. > + { .addr = SMB5_TYPE_C_MODE_CFG, > + .mask = SMB5_EN_TRY_SNK_BIT | SMB5_EN_SNK_ONLY_BIT, > + .val = SMB5_EN_TRY_SNK_BIT }, > -- With best wishes Dmitry
On 24/06/2025 03:06, Dmitry Baryshkov wrote: > On Thu, Jun 19, 2025 at 04:55:17PM +0200, Casey Connolly wrote: >> Introduce support for the SMB5 charger found on pm8150b and other more >> modern Qualcomm SoCs. >> >> SMB5 is largely similar to SMB2, with a few register differences. The >> main difference is the new Type-C hardware block which some registers >> are moved to. >> >> Signed-off-by: Casey Connolly <casey.connolly@linaro.org> >> --- >> drivers/power/supply/qcom_smbx.c | 367 +++++++++++++++++++++++++++++++++------ >> 1 file changed, 314 insertions(+), 53 deletions(-) >> >> diff --git a/drivers/power/supply/qcom_smbx.c b/drivers/power/supply/qcom_smbx.c >> index 10ddd33a09599decb23c0f1ccd02fa9b56602543..d902f3f43548191d3d0310ce90e699918ed0f16f 100644 >> --- a/drivers/power/supply/qcom_smbx.c >> +++ b/drivers/power/supply/qcom_smbx.c >> @@ -22,18 +22,32 @@ >> #include <linux/regmap.h> >> #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 BATTERY_CHARGER_STATUS_MASK GENMASK(2, 0) >> >> #define BATTERY_CHARGER_STATUS_2 0x07 >> -#define CHARGER_ERROR_STATUS_BAT_OV_BIT BIT(5) >> -#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_CHARGER_ERROR_STATUS_BAT_OV_BIT BIT(5) >> +#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 SMB5_CHARGER_ERROR_STATUS_BAT_OV_BIT BIT(1) >> +#define SMB2_BAT_TEMP_STATUS_TOO_COLD_BIT BIT(0) >> + >> +#define BATTERY_CHARGER_STATUS_7 0x0D >> +#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 CHARGING_ENABLE_CMD 0x42 >> #define CHARGING_ENABLE_CMD_BIT BIT(0) >> >> @@ -55,11 +69,14 @@ >> >> #define FLOAT_VOLTAGE_CFG 0x70 >> #define FLOAT_VOLTAGE_SETTING_MASK GENMASK(7, 0) >> >> -#define FG_UPDATE_CFG_2_SEL 0x7D >> -#define SOC_LT_CHG_RECHARGE_THRESH_SEL_BIT BIT(2) >> -#define VBT_LT_CHG_RECHARGE_THRESH_SEL_BIT BIT(1) >> +#define SMB2_FG_UPDATE_CFG_2_SEL 0x7D >> +#define SMB2_SOC_LT_CHG_RECHARGE_THRESH_SEL_BIT BIT(2) >> +#define SMB2_VBT_LT_CHG_RECHARGE_THRESH_SEL_BIT BIT(1) >> + >> +#define SMB5_CHARGE_RCHG_SOC_THRESHOLD_CFG_REG 0x7D >> +#define SMB5_CHARGE_RCHG_SOC_THRESHOLD_CFG_MASK GENMASK(7, 0) >> >> #define OTG_CFG 0x153 >> #define OTG_EN_SRC_CFG_BIT BIT(1) >> >> @@ -73,36 +90,46 @@ >> #define CDP_CHARGER_BIT BIT(2) >> #define OCP_CHARGER_BIT BIT(1) >> #define SDP_CHARGER_BIT BIT(0) >> >> +#define USBIN_CMD_IL 0x340 >> +#define USBIN_SUSPEND_BIT BIT(0) >> + >> #define CMD_APSD 0x341 >> #define APSD_RERUN_BIT BIT(0) >> >> +#define CMD_ICL_OVERRIDE 0x342 >> +#define ICL_OVERRIDE_BIT BIT(0) >> + >> #define TYPE_C_CFG 0x358 >> +#define APSD_START_ON_CC_BIT BIT(7) >> #define FACTORY_MODE_DETECTION_EN_BIT BIT(5) >> #define VCONN_OC_CFG_BIT BIT(1) >> >> #define USBIN_OPTIONS_1_CFG 0x362 >> +#define AUTO_SRC_DETECT_BIT BIT(3) >> #define HVDCP_EN_BIT BIT(2) >> >> +#define USBIN_LOAD_CFG 0x65 >> +#define ICL_OVERRIDE_AFTER_APSD_BIT BIT(4) >> + >> #define USBIN_ICL_OPTIONS 0x366 >> #define USB51_MODE_BIT BIT(1) >> #define USBIN_MODE_CHG_BIT BIT(0) >> >> /* PMI8998 only */ >> #define TYPE_C_INTRPT_ENB_SOFTWARE_CTRL 0x368 >> -#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) >> -#define DFP_EN_CMD_BIT BIT(1) >> -#define TYPEC_DISABLE_CMD_BIT BIT(0) >> +#define SMB5_EN_SNK_ONLY_BIT BIT(1) >> >> #define USBIN_CURRENT_LIMIT_CFG 0x370 >> >> #define USBIN_AICL_OPTIONS_CFG 0x380 >> #define SUSPEND_ON_COLLAPSE_USBIN_BIT BIT(7) >> #define USBIN_AICL_START_AT_MAX_BIT BIT(5) >> +#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) >> #define USBIN_LV_COLLAPSE_RESPONSE_BIT BIT(0) >> @@ -113,15 +140,41 @@ >> >> #define USBIN_CONT_AICL_THRESHOLD_CFG 0x384 >> #define USBIN_CONT_AICL_THRESHOLD_CFG_MASK GENMASK(5, 0) >> >> -#define ICL_STATUS 0x607 >> +#define ICL_STATUS(smb) (SMB_REG_OFFSET(smb) + 0x07) >> #define INPUT_CURRENT_LIMIT_MASK GENMASK(7, 0) >> >> -#define POWER_PATH_STATUS 0x60B >> +#define POWER_PATH_STATUS(smb) (SMB_REG_OFFSET(smb) + 0x0B) >> #define P_PATH_USE_USBIN_BIT BIT(4) >> #define P_PATH_VALID_INPUT_POWER_SOURCE_STS_BIT BIT(0) >> >> +/* 0x5xx region is PM8150b only Type-C registers */ >> + >> +/* Bits 2:0 match PMI8998 TYPE_C_INTRPT_ENB_SOFTWARE_CTRL */ >> +#define SMB5_TYPE_C_MODE_CFG 0x544 >> +#define SMB5_EN_TRY_SNK_BIT BIT(4) >> +#define SMB5_EN_SNK_ONLY_BIT BIT(1) >> + >> +#define SMB5_TYPEC_TYPE_C_VCONN_CONTROL 0x546 >> +#define SMB5_VCONN_EN_ORIENTATION_BIT BIT(2) >> +#define SMB5_VCONN_EN_VALUE_BIT BIT(1) >> +#define SMB5_VCONN_EN_SRC_BIT BIT(0) >> + >> + >> +#define SMB5_TYPE_C_DEBUG_ACCESS_SINK 0x54a >> +#define SMB5_TYPEC_DEBUG_ACCESS_SINK_MASK GENMASK(4, 0) >> + >> +#define SMB5_DEBUG_ACCESS_SRC_CFG 0x54C >> +#define SMB5_EN_UNORIENTED_DEBUG_ACCESS_SRC_BIT BIT(0) >> + >> +#define SMB5_TYPE_C_EXIT_STATE_CFG 0x550 >> +#define SMB5_BYPASS_VSAFE0V_DURING_ROLE_SWAP_BIT BIT(3) >> +#define SMB5_SEL_SRC_UPPER_REF_BIT BIT(2) >> +#define SMB5_EXIT_SNK_BASED_ON_CC_BIT BIT(0) >> + >> +/* Common */ >> + >> #define BARK_BITE_WDOG_PET 0x643 >> #define BARK_BITE_WDOG_PET_BIT BIT(0) >> >> #define WD_CFG 0x651 >> @@ -184,8 +237,9 @@ struct smb_chip { >> const char *name; >> unsigned int base; >> struct regmap *regmap; >> struct power_supply_battery_info *batt_info; >> + enum smb_generation gen; >> >> struct delayed_work status_change_work; >> int cable_irq; >> bool wakeup_enabled; >> @@ -195,8 +249,15 @@ struct smb_chip { >> >> struct power_supply *chg_psy; >> }; >> >> +struct smb_match_data { >> + const char *name; >> + enum smb_generation gen; >> + size_t init_seq_len; >> + const struct smb_init_register __counted_by(init_seq_len) *init_seq; >> +}; >> + >> static enum power_supply_property smb_properties[] = { >> POWER_SUPPLY_PROP_MANUFACTURER, >> POWER_SUPPLY_PROP_MODEL_NAME, >> POWER_SUPPLY_PROP_CURRENT_MAX, >> @@ -212,9 +273,9 @@ 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; >> } >> @@ -267,11 +328,37 @@ 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) >> +{ >> + u16 reg; >> + u8 mask; >> + int rc; >> + u32 val; >> + >> + switch (chip->gen) { >> + case SMB2: >> + reg = BATTERY_CHARGER_STATUS_2; >> + mask = SMB2_CHARGER_ERROR_STATUS_BAT_OV_BIT; >> + break; >> + case SMB5: >> + reg = BATTERY_CHARGER_STATUS_7; >> + mask = SMB5_CHARGER_ERROR_STATUS_BAT_OV_BIT; >> + break; >> + } >> + >> + rc = regmap_read(chip->regmap, chip->base + reg, &val); >> + if (rc) >> + return rc; >> + >> + return !!(reg & mask); >> +} >> + >> static int smb_get_prop_status(struct smb_chip *chip, int *val) >> { >> - unsigned char stat[2]; >> + u32 stat; >> int usb_online = 0; >> int rc; >> >> rc = smb_get_prop_usb_online(chip, &usb_online); >> @@ -279,24 +366,29 @@ static int smb_get_prop_status(struct smb_chip *chip, int *val) >> *val = POWER_SUPPLY_STATUS_DISCHARGING; >> 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 = stat & BATTERY_CHARGER_STATUS_MASK; >> >> - switch (stat[0]) { >> + switch (stat) { >> case TRICKLE_CHARGE: >> case PRE_CHARGE: >> case FAST_CHARGE: >> case FULLON_CHARGE: >> @@ -318,9 +410,9 @@ static int smb_get_prop_status(struct smb_chip *chip, int *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 + ICL_STATUS(chip), val); >> >> if (rc >= 0) >> *val *= CURRENT_SCALE_FACTOR; >> return rc; >> @@ -413,9 +505,46 @@ 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 smb5_get_prop_health(struct smb_chip *chip, int *val) >> +{ >> + int rc; >> + unsigned int stat; >> + >> + rc = smbx_ov_status(chip); >> + >> + /* Treat any error as if we are in the overvoltage state */ >> + if (rc < 0) >> + dev_err(chip->dev, "Couldn't determine overvoltage status!"); >> + if (rc) { >> + dev_err(chip->dev, "battery over-voltage"); >> + *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; >> >> @@ -426,34 +555,45 @@ static int smb_get_prop_health(struct smb_chip *chip, int *val) >> return rc; >> } >> >> switch (stat) { >> - case CHARGER_ERROR_STATUS_BAT_OV_BIT: >> + case SMB2_CHARGER_ERROR_STATUS_BAT_OV_BIT: >> *val = POWER_SUPPLY_HEALTH_OVERVOLTAGE; >> return 0; >> - case BAT_TEMP_STATUS_TOO_COLD_BIT: >> + case SMB2_BAT_TEMP_STATUS_TOO_COLD_BIT: >> *val = POWER_SUPPLY_HEALTH_COLD; >> return 0; >> - case BAT_TEMP_STATUS_TOO_HOT_BIT: >> + case SMB2_BAT_TEMP_STATUS_TOO_HOT_BIT: >> *val = POWER_SUPPLY_HEALTH_OVERHEAT; >> return 0; >> - case BAT_TEMP_STATUS_COLD_SOFT_LIMIT_BIT: >> + case SMB2_BAT_TEMP_STATUS_COLD_SOFT_LIMIT_BIT: >> *val = POWER_SUPPLY_HEALTH_COOL; >> return 0; >> - case BAT_TEMP_STATUS_HOT_SOFT_LIMIT_BIT: >> + case SMB2_BAT_TEMP_STATUS_HOT_SOFT_LIMIT_BIT: >> *val = POWER_SUPPLY_HEALTH_WARM; >> return 0; >> default: >> *val = POWER_SUPPLY_HEALTH_GOOD; >> 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); >> + } >> +} >> + >> static int smb_get_property(struct power_supply *psy, >> enum power_supply_property psp, >> union power_supply_propval *val) >> { >> struct smb_chip *chip = power_supply_get_drvdata(psy); >> + int ret; >> >> switch (psp) { >> case POWER_SUPPLY_PROP_MANUFACTURER: >> val->strval = "Qualcomm"; >> @@ -466,10 +606,15 @@ static int smb_get_property(struct power_supply *psy, >> case POWER_SUPPLY_PROP_CURRENT_NOW: >> return smb_get_iio_chan(chip, chip->usb_in_i_chan, >> &val->intval); >> case POWER_SUPPLY_PROP_VOLTAGE_NOW: >> - return smb_get_iio_chan(chip, chip->usb_in_v_chan, >> + ret = smb_get_iio_chan(chip, chip->usb_in_v_chan, >> &val->intval); >> + if (!ret) { >> + if (chip->gen == SMB5) >> + val->intval *= 16; >> + } >> + return ret; >> case POWER_SUPPLY_PROP_ONLINE: >> return smb_get_prop_usb_online(chip, &val->intval); >> case POWER_SUPPLY_PROP_STATUS: >> return smb_get_prop_status(chip, &val->intval); >> @@ -515,14 +660,10 @@ 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; >> >> - regmap_read(chip->regmap, chip->base + BATTERY_CHARGER_STATUS_2, >> - &status); >> - >> - if (status & CHARGER_ERROR_STATUS_BAT_OV_BIT) { >> + if (smbx_ov_status(chip) == 1) { >> /* The hardware stops charging automatically */ >> dev_err(chip->dev, "battery overvoltage detected\n"); >> power_supply_changed(chip->chg_psy); >> } >> @@ -566,9 +707,9 @@ static irqreturn_t smb_handle_wdog_bark(int irq, void *data) >> return IRQ_HANDLED; >> } >> >> static const struct power_supply_desc smb_psy_desc = { >> - .name = "pmi8998_charger", >> + .name = "SMB2_charger", >> .type = POWER_SUPPLY_TYPE_USB, >> .usb_types = BIT(POWER_SUPPLY_USB_TYPE_SDP) | >> BIT(POWER_SUPPLY_USB_TYPE_CDP) | >> BIT(POWER_SUPPLY_USB_TYPE_DCP) | >> @@ -580,18 +721,100 @@ static const struct power_supply_desc smb_psy_desc = { >> .property_is_writeable = smb_property_is_writable, >> }; >> >> /* 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 smb5_init_seq[] = { >> + { .addr = USBIN_CMD_IL, .mask = USBIN_SUSPEND_BIT, .val = 0 }, >> + /* >> + * By default configure us as an upstream facing port >> + * FIXME: This will be handled by the type-c driver >> + */ > > All of this needs to be reworked to cooperate with the type-c driver. > Otherwise it might try to reconfigure the Type-C mode _after_ the TCPM > has negotiated some configuration. So, it can't go in in this way. Hi Dmitry, this is the smb2/pmi8998 init sequence, we don't have any type-c support for this yet hence this. When we add role switching/type-c support it will make sense to rework this but until then we need this. Kind regards, > >> + { .addr = SMB5_TYPE_C_MODE_CFG, >> + .mask = SMB5_EN_TRY_SNK_BIT | SMB5_EN_SNK_ONLY_BIT, >> + .val = SMB5_EN_TRY_SNK_BIT }, >> > -- // Casey (she/her)
On Mon, Jun 30, 2025 at 01:06:24AM +0200, Caleb Connolly wrote: > > > On 24/06/2025 03:06, Dmitry Baryshkov wrote: > > On Thu, Jun 19, 2025 at 04:55:17PM +0200, Casey Connolly wrote: > >> Introduce support for the SMB5 charger found on pm8150b and other more > >> modern Qualcomm SoCs. > >> > >> SMB5 is largely similar to SMB2, with a few register differences. The > >> main difference is the new Type-C hardware block which some registers > >> are moved to. > >> > >> Signed-off-by: Casey Connolly <casey.connolly@linaro.org> > >> --- > >> drivers/power/supply/qcom_smbx.c | 367 +++++++++++++++++++++++++++++++++------ > >> 1 file changed, 314 insertions(+), 53 deletions(-) > >> > >> diff --git a/drivers/power/supply/qcom_smbx.c b/drivers/power/supply/qcom_smbx.c > >> index 10ddd33a09599decb23c0f1ccd02fa9b56602543..d902f3f43548191d3d0310ce90e699918ed0f16f 100644 > >> --- a/drivers/power/supply/qcom_smbx.c > >> +++ b/drivers/power/supply/qcom_smbx.c > >> @@ -580,18 +721,100 @@ static const struct power_supply_desc smb_psy_desc = { > >> .property_is_writeable = smb_property_is_writable, > >> }; > >> > >> /* 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 smb5_init_seq[] = { > >> + { .addr = USBIN_CMD_IL, .mask = USBIN_SUSPEND_BIT, .val = 0 }, > >> + /* > >> + * By default configure us as an upstream facing port > >> + * FIXME: This will be handled by the type-c driver > >> + */ > > > > All of this needs to be reworked to cooperate with the type-c driver. > > Otherwise it might try to reconfigure the Type-C mode _after_ the TCPM > > has negotiated some configuration. So, it can't go in in this way. > > Hi Dmitry, > > this is the smb2/pmi8998 init sequence, we don't have any type-c support > for this yet hence this. When we add role switching/type-c support it > will make sense to rework this but until then we need this. No, it is not. It is smb5_init_seq, which is then being used for PM8150B and PM7250. > > Kind regards, > > > > >> + { .addr = SMB5_TYPE_C_MODE_CFG, > >> + .mask = SMB5_EN_TRY_SNK_BIT | SMB5_EN_SNK_ONLY_BIT, > >> + .val = SMB5_EN_TRY_SNK_BIT }, > >> > > > > -- > // Casey (she/her) > -- With best wishes Dmitry
On Thu Jun 19, 2025 at 4:55 PM CEST, Casey Connolly wrote: > Introduce support for the SMB5 charger found on pm8150b and other more > modern Qualcomm SoCs. > > SMB5 is largely similar to SMB2, with a few register differences. The > main difference is the new Type-C hardware block which some registers > are moved to. > > Signed-off-by: Casey Connolly <casey.connolly@linaro.org> > --- <snip> > +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); > + } > +} This doesn't compile for me: drivers/power/supply/qcom_smbx.c: In function 'smb_get_prop_health': drivers/power/supply/qcom_smbx.c:588:1: error: control reaches end of non-void function [-Werror=return-type] 588 | } | ^ Regards Luca
Hi Casey! Adding a note here, I also plan to look into what (if any) changes are necessary for this to work on PMI632 (which is the PMIC for sdm632/msm8953 Fairphone 3) since that's also SMB5. On Thu Jun 19, 2025 at 4:55 PM CEST, Casey Connolly wrote: > Introduce support for the SMB5 charger found on pm8150b and other more > modern Qualcomm SoCs. > > SMB5 is largely similar to SMB2, with a few register differences. The > main difference is the new Type-C hardware block which some registers > are moved to. > > Signed-off-by: Casey Connolly <casey.connolly@linaro.org> > --- > drivers/power/supply/qcom_smbx.c | 367 +++++++++++++++++++++++++++++++++------ > 1 file changed, 314 insertions(+), 53 deletions(-) <snip> > +/* Bits 2:0 match PMI8998 TYPE_C_INTRPT_ENB_SOFTWARE_CTRL */ > +#define SMB5_TYPE_C_MODE_CFG 0x544 > +#define SMB5_EN_TRY_SNK_BIT BIT(4) > +#define SMB5_EN_SNK_ONLY_BIT BIT(1) > + > +#define SMB5_TYPEC_TYPE_C_VCONN_CONTROL 0x546 > +#define SMB5_VCONN_EN_ORIENTATION_BIT BIT(2) > +#define SMB5_VCONN_EN_VALUE_BIT BIT(1) > +#define SMB5_VCONN_EN_SRC_BIT BIT(0) > + > + > +#define SMB5_TYPE_C_DEBUG_ACCESS_SINK 0x54a > +#define SMB5_TYPEC_DEBUG_ACCESS_SINK_MASK GENMASK(4, 0) > + > +#define SMB5_DEBUG_ACCESS_SRC_CFG 0x54C > +#define SMB5_EN_UNORIENTED_DEBUG_ACCESS_SRC_BIT BIT(0) > + > +#define SMB5_TYPE_C_EXIT_STATE_CFG 0x550 > +#define SMB5_BYPASS_VSAFE0V_DURING_ROLE_SWAP_BIT BIT(3) > +#define SMB5_SEL_SRC_UPPER_REF_BIT BIT(2) > +#define SMB5_EXIT_SNK_BASED_ON_CC_BIT BIT(0) <snip> > /* 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 smb5_init_seq[] = { > + { .addr = USBIN_CMD_IL, .mask = USBIN_SUSPEND_BIT, .val = 0 }, > + /* > + * By default configure us as an upstream facing port > + * FIXME: This will be handled by the type-c driver > + */ > + { .addr = SMB5_TYPE_C_MODE_CFG, > + .mask = SMB5_EN_TRY_SNK_BIT | SMB5_EN_SNK_ONLY_BIT, > + .val = SMB5_EN_TRY_SNK_BIT }, Since there's already a driver for the Type-C in pm8150b and pm7250b, can we remove this? Or is additional plumbing between the two drivers necessary to make this work? Maybe Bryan can also jump in here. Regards Luca > + { .addr = SMB5_TYPEC_TYPE_C_VCONN_CONTROL, > + .mask = SMB5_VCONN_EN_ORIENTATION_BIT | SMB5_VCONN_EN_SRC_BIT | > + SMB5_VCONN_EN_VALUE_BIT, > + .val = SMB2_VCONN_EN_SRC_BIT }, > + { .addr = SMB5_DEBUG_ACCESS_SRC_CFG, > + .mask = SMB5_EN_UNORIENTED_DEBUG_ACCESS_SRC_BIT, > + .val = SMB5_EN_UNORIENTED_DEBUG_ACCESS_SRC_BIT }, > + { .addr = SMB5_TYPE_C_EXIT_STATE_CFG, > + .mask = SMB5_SEL_SRC_UPPER_REF_BIT, > + .val = SMB5_SEL_SRC_UPPER_REF_BIT }, > + /* > + * Disable Type-C factory mode and stay in Attached.SRC state when VCONN > + * over-current happens > + */ > + { .addr = TYPE_C_CFG, > + .mask = APSD_START_ON_CC_BIT, > + .val = 0 }, > + { .addr = SMB5_TYPE_C_DEBUG_ACCESS_SINK, > + .mask = SMB5_TYPEC_DEBUG_ACCESS_SINK_MASK, > + .val = 0x17 },
Hi Casey, kernel test robot noticed the following build warnings: [auto build test WARNING on bc6e0ba6c9bafa6241b05524b9829808056ac4ad] url: https://github.com/intel-lab-lkp/linux/commits/Casey-Connolly/dt-bindings-power-supply-qcom-pmi89980-charger-add-pm8150b-and-7250b/20250619-230137 base: bc6e0ba6c9bafa6241b05524b9829808056ac4ad patch link: https://lore.kernel.org/r/20250619-smb2-smb5-support-v1-9-ac5dec51b6e1%40linaro.org patch subject: [PATCH 09/11] power: supply: qcom_smbx: add smb5 support config: x86_64-buildonly-randconfig-006-20250620 (https://download.01.org/0day-ci/archive/20250620/202506201101.9HMIR1fb-lkp@intel.com/config) compiler: clang version 20.1.2 (https://github.com/llvm/llvm-project 58df0ef89dd64126512e4ee27b4ac3fd8ddf6247) reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20250620/202506201101.9HMIR1fb-lkp@intel.com/reproduce) If you fix the issue in a separate patch/commit (i.e. not just a new version of the same patch/commit), kindly add following tags | Reported-by: kernel test robot <lkp@intel.com> | Closes: https://lore.kernel.org/oe-kbuild-all/202506201101.9HMIR1fb-lkp@intel.com/ All warnings (new ones prefixed by >>): >> Warning: drivers/power/supply/qcom_smbx.c:250 struct member 'gen' not described in 'smb_chip' -- 0-DAY CI Kernel Test Service https://github.com/intel/lkp-tests/wiki
© 2016 - 2025 Red Hat, Inc.