OSDN Git Service

regulator: max8998: Add charger regulator
authorJonathan Bakker <xc-racer2@live.ca>
Sat, 16 May 2020 19:47:59 +0000 (12:47 -0700)
committerMark Brown <broonie@kernel.org>
Fri, 22 May 2020 12:11:05 +0000 (13:11 +0100)
The max8998 has a current regulator for charging control.  The
charger driver in drivers/power/supply/max8998_charger.c has a
comment in it stating that 'charger control is done by a current
regulator "CHARGER"', but this regulator was never added until
now.

The current values have been extracted from a downstream driver
for the SGH-T959V.

Signed-off-by: Jonathan Bakker <xc-racer2@live.ca>
Link: https://lore.kernel.org/r/BN6PR04MB0660E1F4A3D5A348BE88311CA3BA0@BN6PR04MB0660.namprd04.prod.outlook.com
Signed-off-by: Mark Brown <broonie@kernel.org>
drivers/regulator/max8998.c
include/linux/mfd/max8998.h

index 60599c3..668ced0 100644 (file)
@@ -33,6 +33,10 @@ struct max8998_data {
        unsigned int            buck2_idx;
 };
 
+static const unsigned int charger_current_table[] = {
+       90000, 380000, 475000, 550000, 570000, 600000, 700000, 800000,
+};
+
 static int max8998_get_enable_register(struct regulator_dev *rdev,
                                        int *reg, int *shift)
 {
@@ -63,6 +67,10 @@ static int max8998_get_enable_register(struct regulator_dev *rdev,
                *reg = MAX8998_REG_CHGR2;
                *shift = 7 - (ldo - MAX8998_ESAFEOUT1);
                break;
+       case MAX8998_CHARGER:
+               *reg = MAX8998_REG_CHGR2;
+               *shift = 0;
+               break;
        default:
                return -EINVAL;
        }
@@ -88,6 +96,11 @@ static int max8998_ldo_is_enabled(struct regulator_dev *rdev)
        return val & (1 << shift);
 }
 
+static int max8998_ldo_is_enabled_inverted(struct regulator_dev *rdev)
+{
+       return (!max8998_ldo_is_enabled(rdev));
+}
+
 static int max8998_ldo_enable(struct regulator_dev *rdev)
 {
        struct max8998_data *max8998 = rdev_get_drvdata(rdev);
@@ -358,6 +371,74 @@ static int max8998_set_voltage_buck_time_sel(struct regulator_dev *rdev,
        return 0;
 }
 
+int max8998_set_current_limit(struct regulator_dev *rdev,
+                             int min_uA, int max_uA)
+{
+       struct max8998_data *max8998 = rdev_get_drvdata(rdev);
+       struct i2c_client *i2c = max8998->iodev->i2c;
+       unsigned int n_currents = rdev->desc->n_current_limits;
+       int i, sel = -1;
+
+       if (n_currents == 0)
+               return -EINVAL;
+
+       if (rdev->desc->curr_table) {
+               const unsigned int *curr_table = rdev->desc->curr_table;
+               bool ascend = curr_table[n_currents - 1] > curr_table[0];
+
+               /* search for closest to maximum */
+               if (ascend) {
+                       for (i = n_currents - 1; i >= 0; i--) {
+                               if (min_uA <= curr_table[i] &&
+                                   curr_table[i] <= max_uA) {
+                                       sel = i;
+                                       break;
+                               }
+                       }
+               } else {
+                       for (i = 0; i < n_currents; i++) {
+                               if (min_uA <= curr_table[i] &&
+                                   curr_table[i] <= max_uA) {
+                                       sel = i;
+                                       break;
+                               }
+                       }
+               }
+       }
+
+       if (sel < 0)
+               return -EINVAL;
+
+       sel <<= ffs(rdev->desc->csel_mask) - 1;
+
+       return max8998_update_reg(i2c, rdev->desc->csel_reg,
+                                 sel, rdev->desc->csel_mask);
+}
+
+int max8998_get_current_limit(struct regulator_dev *rdev)
+{
+       struct max8998_data *max8998 = rdev_get_drvdata(rdev);
+       struct i2c_client *i2c = max8998->iodev->i2c;
+       u8 val;
+       int ret;
+
+       ret = max8998_read_reg(i2c, rdev->desc->csel_reg, &val);
+       if (ret != 0)
+               return ret;
+
+       val &= rdev->desc->csel_mask;
+       val >>= ffs(rdev->desc->csel_mask) - 1;
+
+       if (rdev->desc->curr_table) {
+               if (val >= rdev->desc->n_current_limits)
+                       return -EINVAL;
+
+               return rdev->desc->curr_table[val];
+       }
+
+       return -EINVAL;
+}
+
 static const struct regulator_ops max8998_ldo_ops = {
        .list_voltage           = regulator_list_voltage_linear,
        .map_voltage            = regulator_map_voltage_linear,
@@ -379,6 +460,15 @@ static const struct regulator_ops max8998_buck_ops = {
        .set_voltage_time_sel   = max8998_set_voltage_buck_time_sel,
 };
 
+static const struct regulator_ops max8998_charger_ops = {
+       .set_current_limit      = max8998_set_current_limit,
+       .get_current_limit      = max8998_get_current_limit,
+       .is_enabled             = max8998_ldo_is_enabled_inverted,
+       /* Swapped as register is inverted */
+       .enable                 = max8998_ldo_disable,
+       .disable                = max8998_ldo_enable,
+};
+
 static const struct regulator_ops max8998_others_ops = {
        .is_enabled             = max8998_ldo_is_enabled,
        .enable                 = max8998_ldo_enable,
@@ -397,6 +487,19 @@ static const struct regulator_ops max8998_others_ops = {
                .owner = THIS_MODULE, \
        }
 
+#define MAX8998_CURRENT_REG(_name, _ops, _table, _reg, _mask) \
+       { \
+               .name = #_name, \
+               .id = MAX8998_##_name, \
+               .ops = _ops, \
+               .curr_table = _table, \
+               .n_current_limits = ARRAY_SIZE(_table), \
+               .csel_reg = _reg, \
+               .csel_mask = _mask, \
+               .type = REGULATOR_CURRENT, \
+               .owner = THIS_MODULE, \
+       }
+
 #define MAX8998_OTHERS_REG(_name, _id) \
        { \
                .name = #_name, \
@@ -432,6 +535,8 @@ static const struct regulator_desc regulators[] = {
        MAX8998_OTHERS_REG(ENVICHG, MAX8998_ENVICHG),
        MAX8998_OTHERS_REG(ESAFEOUT1, MAX8998_ESAFEOUT1),
        MAX8998_OTHERS_REG(ESAFEOUT2, MAX8998_ESAFEOUT2),
+       MAX8998_CURRENT_REG(CHARGER, &max8998_charger_ops,
+                           charger_current_table, MAX8998_REG_CHGR1, 0x7),
 };
 
 static int max8998_pmic_dt_parse_dvs_gpio(struct max8998_dev *iodev,
index 061af22..79c020b 100644 (file)
@@ -39,6 +39,7 @@ enum {
        MAX8998_ENVICHG,
        MAX8998_ESAFEOUT1,
        MAX8998_ESAFEOUT2,
+       MAX8998_CHARGER,
 };
 
 /**