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.conno...@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


Reply via email to