power: supply: axp20x_usb_power: Simplify USB current limit handling
authorAidan MacDonald <aidanmacdonald.0x0@gmail.com>
Wed, 10 May 2023 11:50:40 +0000 (12:50 +0100)
committerSebastian Reichel <sebastian.reichel@collabora.com>
Mon, 15 May 2023 21:19:52 +0000 (23:19 +0200)
Handle the USB current limit with a lookup table and regmap field,
which minimizes code duplication. Invalid or unlimited values are
denoted by -1 entries, and can't be selected from userspace.

Signed-off-by: Aidan MacDonald <aidanmacdonald.0x0@gmail.com>
Signed-off-by: Sebastian Reichel <sebastian.reichel@collabora.com>
drivers/power/supply/axp20x_usb_power.c

index a1e6d1d..67998fb 100644 (file)
 #define AXP20X_VBUS_VHOLD_uV(b)                (4000000 + (((b) >> 3) & 7) * 100000)
 #define AXP20X_VBUS_VHOLD_MASK         GENMASK(5, 3)
 #define AXP20X_VBUS_VHOLD_OFFSET       3
-#define AXP20X_VBUS_CLIMIT_MASK                3
-#define AXP20X_VBUS_CLIMIT_900mA       0
-#define AXP20X_VBUS_CLIMIT_500mA       1
-#define AXP20X_VBUS_CLIMIT_100mA       2
-#define AXP20X_VBUS_CLIMIT_NONE                3
-
-#define AXP813_VBUS_CLIMIT_900mA       0
-#define AXP813_VBUS_CLIMIT_1500mA      1
-#define AXP813_VBUS_CLIMIT_2000mA      2
-#define AXP813_VBUS_CLIMIT_2500mA      3
 
 #define AXP20X_ADC_EN1_VBUS_CURR       BIT(2)
 #define AXP20X_ADC_EN1_VBUS_VOLT       BIT(3)
  */
 #define DEBOUNCE_TIME                  msecs_to_jiffies(50)
 
+struct axp_data {
+       const struct power_supply_desc  *power_desc;
+       const char * const              *irq_names;
+       unsigned int                    num_irq_names;
+       enum axp20x_variants            axp20x_id;
+       const int                       *curr_lim_table;
+       struct reg_field                curr_lim_fld;
+};
+
 struct axp20x_usb_power {
        struct regmap *regmap;
+       struct regmap_field *curr_lim_fld;
        struct power_supply *supply;
        enum axp20x_variants axp20x_id;
+       const struct axp_data *axp_data;
        struct iio_channel *vbus_v;
        struct iio_channel *vbus_i;
        struct delayed_work vbus_detect;
@@ -121,60 +122,6 @@ out:
                mod_delayed_work(system_power_efficient_wq, &power->vbus_detect, DEBOUNCE_TIME);
 }
 
-static int axp20x_get_current_max(struct axp20x_usb_power *power, int *val)
-{
-       unsigned int v;
-       int ret = regmap_read(power->regmap, AXP20X_VBUS_IPSOUT_MGMT, &v);
-
-       if (ret)
-               return ret;
-
-       switch (v & AXP20X_VBUS_CLIMIT_MASK) {
-       case AXP20X_VBUS_CLIMIT_100mA:
-               if (power->axp20x_id == AXP221_ID)
-                       *val = -1; /* No 100mA limit */
-               else
-                       *val = 100000;
-               break;
-       case AXP20X_VBUS_CLIMIT_500mA:
-               *val = 500000;
-               break;
-       case AXP20X_VBUS_CLIMIT_900mA:
-               *val = 900000;
-               break;
-       case AXP20X_VBUS_CLIMIT_NONE:
-               *val = -1;
-               break;
-       }
-
-       return 0;
-}
-
-static int axp813_get_current_max(struct axp20x_usb_power *power, int *val)
-{
-       unsigned int v;
-       int ret = regmap_read(power->regmap, AXP20X_VBUS_IPSOUT_MGMT, &v);
-
-       if (ret)
-               return ret;
-
-       switch (v & AXP20X_VBUS_CLIMIT_MASK) {
-       case AXP813_VBUS_CLIMIT_900mA:
-               *val = 900000;
-               break;
-       case AXP813_VBUS_CLIMIT_1500mA:
-               *val = 1500000;
-               break;
-       case AXP813_VBUS_CLIMIT_2000mA:
-               *val = 2000000;
-               break;
-       case AXP813_VBUS_CLIMIT_2500mA:
-               *val = 2500000;
-               break;
-       }
-       return 0;
-}
-
 static int axp20x_usb_power_get_property(struct power_supply *psy,
        enum power_supply_property psp, union power_supply_propval *val)
 {
@@ -213,9 +160,12 @@ static int axp20x_usb_power_get_property(struct power_supply *psy,
                val->intval = ret * 1700; /* 1 step = 1.7 mV */
                return 0;
        case POWER_SUPPLY_PROP_CURRENT_MAX:
-               if (power->axp20x_id == AXP813_ID)
-                       return axp813_get_current_max(power, &val->intval);
-               return axp20x_get_current_max(power, &val->intval);
+               ret = regmap_field_read(power->curr_lim_fld, &v);
+               if (ret)
+                       return ret;
+
+               val->intval = power->axp_data->curr_lim_table[v];
+               return 0;
        case POWER_SUPPLY_PROP_CURRENT_NOW:
                if (IS_ENABLED(CONFIG_AXP20X_ADC)) {
                        ret = iio_read_channel_processed(power->vbus_i,
@@ -316,50 +266,17 @@ static int axp20x_usb_power_set_voltage_min(struct axp20x_usb_power *power,
        return -EINVAL;
 }
 
-static int axp813_usb_power_set_current_max(struct axp20x_usb_power *power,
-                                           int intval)
+static int axp20x_usb_power_set_current_max(struct axp20x_usb_power *power, int intval)
 {
-       int val;
+       const unsigned int max = GENMASK(power->axp_data->curr_lim_fld.msb,
+                                        power->axp_data->curr_lim_fld.lsb);
 
-       switch (intval) {
-       case 900000:
-               return regmap_update_bits(power->regmap,
-                                         AXP20X_VBUS_IPSOUT_MGMT,
-                                         AXP20X_VBUS_CLIMIT_MASK,
-                                         AXP813_VBUS_CLIMIT_900mA);
-       case 1500000:
-       case 2000000:
-       case 2500000:
-               val = (intval - 1000000) / 500000;
-               return regmap_update_bits(power->regmap,
-                                         AXP20X_VBUS_IPSOUT_MGMT,
-                                         AXP20X_VBUS_CLIMIT_MASK, val);
-       default:
+       if (intval == -1)
                return -EINVAL;
-       }
 
-       return -EINVAL;
-}
-
-static int axp20x_usb_power_set_current_max(struct axp20x_usb_power *power,
-                                           int intval)
-{
-       int val;
-
-       switch (intval) {
-       case 100000:
-               if (power->axp20x_id == AXP221_ID)
-                       return -EINVAL;
-               fallthrough;
-       case 500000:
-       case 900000:
-               val = (900000 - intval) / 400000;
-               return regmap_update_bits(power->regmap,
-                                         AXP20X_VBUS_IPSOUT_MGMT,
-                                         AXP20X_VBUS_CLIMIT_MASK, val);
-       default:
-               return -EINVAL;
-       }
+       for (unsigned int i = 0; i <= max; ++i)
+               if (power->axp_data->curr_lim_table[i] == intval)
+                       return regmap_field_write(power->curr_lim_fld, i);
 
        return -EINVAL;
 }
@@ -380,9 +297,6 @@ static int axp20x_usb_power_set_property(struct power_supply *psy,
                return axp20x_usb_power_set_voltage_min(power, val->intval);
 
        case POWER_SUPPLY_PROP_CURRENT_MAX:
-               if (power->axp20x_id == AXP813_ID)
-                       return axp813_usb_power_set_current_max(power,
-                                                               val->intval);
                return axp20x_usb_power_set_current_max(power, val->intval);
 
        default:
@@ -461,11 +375,25 @@ static const char * const axp22x_irq_names[] = {
        "VBUS_REMOVAL",
 };
 
-struct axp_data {
-       const struct power_supply_desc  *power_desc;
-       const char * const              *irq_names;
-       unsigned int                    num_irq_names;
-       enum axp20x_variants            axp20x_id;
+static int axp20x_usb_curr_lim_table[] = {
+       900000,
+       500000,
+       100000,
+       -1,
+};
+
+static int axp221_usb_curr_lim_table[] = {
+       900000,
+       500000,
+       -1,
+       -1,
+};
+
+static int axp813_usb_curr_lim_table[] = {
+       900000,
+       1500000,
+       2000000,
+       2500000,
 };
 
 static const struct axp_data axp202_data = {
@@ -473,6 +401,8 @@ static const struct axp_data axp202_data = {
        .irq_names      = axp20x_irq_names,
        .num_irq_names  = ARRAY_SIZE(axp20x_irq_names),
        .axp20x_id      = AXP202_ID,
+       .curr_lim_table = axp20x_usb_curr_lim_table,
+       .curr_lim_fld   = REG_FIELD(AXP20X_VBUS_IPSOUT_MGMT, 0, 1),
 };
 
 static const struct axp_data axp221_data = {
@@ -480,6 +410,8 @@ static const struct axp_data axp221_data = {
        .irq_names      = axp22x_irq_names,
        .num_irq_names  = ARRAY_SIZE(axp22x_irq_names),
        .axp20x_id      = AXP221_ID,
+       .curr_lim_table = axp221_usb_curr_lim_table,
+       .curr_lim_fld   = REG_FIELD(AXP20X_VBUS_IPSOUT_MGMT, 0, 1),
 };
 
 static const struct axp_data axp223_data = {
@@ -487,6 +419,8 @@ static const struct axp_data axp223_data = {
        .irq_names      = axp22x_irq_names,
        .num_irq_names  = ARRAY_SIZE(axp22x_irq_names),
        .axp20x_id      = AXP223_ID,
+       .curr_lim_table = axp20x_usb_curr_lim_table,
+       .curr_lim_fld   = REG_FIELD(AXP20X_VBUS_IPSOUT_MGMT, 0, 1),
 };
 
 static const struct axp_data axp813_data = {
@@ -494,6 +428,8 @@ static const struct axp_data axp813_data = {
        .irq_names      = axp22x_irq_names,
        .num_irq_names  = ARRAY_SIZE(axp22x_irq_names),
        .axp20x_id      = AXP813_ID,
+       .curr_lim_table = axp813_usb_curr_lim_table,
+       .curr_lim_fld   = REG_FIELD(AXP20X_VBUS_IPSOUT_MGMT, 0, 1),
 };
 
 #ifdef CONFIG_PM_SLEEP
@@ -592,9 +528,15 @@ static int axp20x_usb_power_probe(struct platform_device *pdev)
        platform_set_drvdata(pdev, power);
 
        power->axp20x_id = axp_data->axp20x_id;
+       power->axp_data = axp_data;
        power->regmap = axp20x->regmap;
        power->num_irqs = axp_data->num_irq_names;
 
+       power->curr_lim_fld = devm_regmap_field_alloc(&pdev->dev, power->regmap,
+                                                     axp_data->curr_lim_fld);
+       if (IS_ERR(power->curr_lim_fld))
+               return PTR_ERR(power->curr_lim_fld);
+
        ret = devm_delayed_work_autocancel(&pdev->dev, &power->vbus_detect,
                                           axp20x_usb_power_poll_vbus);
        if (ret)