mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2024-09-29 13:53:33 +00:00
power: supply: axp20x_usb_power: Simplify USB current limit handling
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>
This commit is contained in:
parent
67fce5963b
commit
28ca77c9bb
1 changed files with 57 additions and 115 deletions
|
@ -37,16 +37,6 @@
|
|||
#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)
|
||||
|
@ -61,10 +51,21 @@
|
|||
*/
|
||||
#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 @@ static void axp20x_usb_power_poll_vbus(struct work_struct *work)
|
|||
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)
|
||||
|
|
Loading…
Reference in a new issue