339 lines
10 KiB
Diff
339 lines
10 KiB
Diff
|
From 64521fee3aff2f489a865eb1ea66b50982357a4a Mon Sep 17 00:00:00 2001
|
||
|
From: Ondrej Jirman <megi@xff.cz>
|
||
|
Date: Wed, 5 Jul 2023 02:29:29 +0200
|
||
|
Subject: [PATCH 236/464] power: supply: axp20x-usb-power: Support input
|
||
|
current limit, export extra props
|
||
|
|
||
|
Allow to set input current limit directly when autodetection fails
|
||
|
on incorrectly wired tablets, like TBS A711, that don't have
|
||
|
D+/D- pins connected, and can't detect the usb power supply type.
|
||
|
|
||
|
Rename CURRENT_MAX to USB_DCP_INPUT_CURRENT_LIMIT, since that's what
|
||
|
it is in reality. Also notify power_supply_change on BC detection
|
||
|
interrupts for the consumers to get BC results ASAP.
|
||
|
|
||
|
Signed-off-by: Ondrej Jirman <megi@xff.cz>
|
||
|
---
|
||
|
drivers/mfd/axp20x.c | 2 +
|
||
|
drivers/power/supply/axp20x_usb_power.c | 205 +++++++++++++++++++++++-
|
||
|
include/linux/mfd/axp20x.h | 1 +
|
||
|
3 files changed, 204 insertions(+), 4 deletions(-)
|
||
|
|
||
|
diff --git a/drivers/mfd/axp20x.c b/drivers/mfd/axp20x.c
|
||
|
index c5f4af152901..330943a49beb 100644
|
||
|
--- a/drivers/mfd/axp20x.c
|
||
|
+++ b/drivers/mfd/axp20x.c
|
||
|
@@ -299,6 +299,8 @@ static const struct resource axp22x_usb_power_supply_resources[] = {
|
||
|
static const struct resource axp803_usb_power_supply_resources[] = {
|
||
|
DEFINE_RES_IRQ_NAMED(AXP803_IRQ_VBUS_PLUGIN, "VBUS_PLUGIN"),
|
||
|
DEFINE_RES_IRQ_NAMED(AXP803_IRQ_VBUS_REMOVAL, "VBUS_REMOVAL"),
|
||
|
+ DEFINE_RES_IRQ_NAMED(AXP803_IRQ_BC_USB_CHNG, "BC_USB_CHNG"),
|
||
|
+ DEFINE_RES_IRQ_NAMED(AXP803_IRQ_MV_CHNG, "MV_CHNG"),
|
||
|
};
|
||
|
|
||
|
static const struct resource axp22x_pek_resources[] = {
|
||
|
diff --git a/drivers/power/supply/axp20x_usb_power.c b/drivers/power/supply/axp20x_usb_power.c
|
||
|
index eb41f1f5b6b6..fc6a07290eb6 100644
|
||
|
--- a/drivers/power/supply/axp20x_usb_power.c
|
||
|
+++ b/drivers/power/supply/axp20x_usb_power.c
|
||
|
@@ -40,6 +40,16 @@
|
||
|
#define AXP20X_ADC_EN1_VBUS_CURR BIT(2)
|
||
|
#define AXP20X_ADC_EN1_VBUS_VOLT BIT(3)
|
||
|
|
||
|
+#define AXP813_CHRG_CTRL3_VBUS_CUR_LIMIT_MASK GENMASK(7, 4)
|
||
|
+#define AXP813_CHRG_CTRL3_VBUS_CUR_LIMIT_OFFSET 4
|
||
|
+
|
||
|
+#define AXP813_BC_RESULT_MASK GENMASK(7, 5)
|
||
|
+#define AXP813_BC_RESULT_SDP (1 << 5)
|
||
|
+#define AXP813_BC_RESULT_CDP (2 << 5)
|
||
|
+#define AXP813_BC_RESULT_DCP (3 << 5)
|
||
|
+
|
||
|
+#define AXP813_BC_EN BIT(0)
|
||
|
+
|
||
|
/*
|
||
|
* Note do not raise the debounce time, we must report Vusb high within
|
||
|
* 100ms otherwise we get Vbus errors in musb.
|
||
|
@@ -57,6 +67,7 @@ struct axp_data {
|
||
|
struct reg_field usb_bc_en_bit;
|
||
|
struct reg_field vbus_disable_bit;
|
||
|
bool vbus_needs_polling: 1;
|
||
|
+ int axp20x_id;
|
||
|
};
|
||
|
|
||
|
struct axp20x_usb_power {
|
||
|
@@ -124,6 +135,125 @@ static void axp20x_usb_power_poll_vbus(struct work_struct *work)
|
||
|
mod_delayed_work(system_power_efficient_wq, &power->vbus_detect, DEBOUNCE_TIME);
|
||
|
}
|
||
|
|
||
|
+static const unsigned axp813_input_current_limits_table[] = {
|
||
|
+ 100000,
|
||
|
+ 500000,
|
||
|
+ 900000,
|
||
|
+ 1500000,
|
||
|
+ 2000000,
|
||
|
+ 2500000,
|
||
|
+ 3000000,
|
||
|
+ 3500000,
|
||
|
+ 4000000,
|
||
|
+};
|
||
|
+
|
||
|
+static int
|
||
|
+axp813_usb_power_set_input_current_limit(struct axp20x_usb_power *power,
|
||
|
+ int intval)
|
||
|
+{
|
||
|
+ unsigned int reg;
|
||
|
+
|
||
|
+ if (intval < 100000)
|
||
|
+ return -EINVAL;
|
||
|
+
|
||
|
+ for (reg = ARRAY_SIZE(axp813_input_current_limits_table) - 1; reg > 0; reg--)
|
||
|
+ if (intval >= axp813_input_current_limits_table[reg])
|
||
|
+ break;
|
||
|
+
|
||
|
+ return regmap_update_bits(power->regmap,
|
||
|
+ AXP813_CHRG_CTRL3,
|
||
|
+ AXP813_CHRG_CTRL3_VBUS_CUR_LIMIT_MASK,
|
||
|
+ reg << AXP813_CHRG_CTRL3_VBUS_CUR_LIMIT_OFFSET);
|
||
|
+}
|
||
|
+
|
||
|
+static int
|
||
|
+axp813_usb_power_get_input_current_limit(struct axp20x_usb_power *power,
|
||
|
+ int *intval)
|
||
|
+{
|
||
|
+ unsigned int v;
|
||
|
+ int ret = regmap_read(power->regmap, AXP813_CHRG_CTRL3, &v);
|
||
|
+
|
||
|
+ if (ret)
|
||
|
+ return ret;
|
||
|
+
|
||
|
+ v &= AXP813_CHRG_CTRL3_VBUS_CUR_LIMIT_MASK;
|
||
|
+ v >>= AXP813_CHRG_CTRL3_VBUS_CUR_LIMIT_OFFSET;
|
||
|
+
|
||
|
+ if (v < ARRAY_SIZE(axp813_input_current_limits_table))
|
||
|
+ *intval = axp813_input_current_limits_table[v];
|
||
|
+ else
|
||
|
+ *intval = axp813_input_current_limits_table[ARRAY_SIZE(axp813_input_current_limits_table) - 1];
|
||
|
+
|
||
|
+ return 0;
|
||
|
+}
|
||
|
+
|
||
|
+static int
|
||
|
+axp813_get_usb_bc_enabled(struct axp20x_usb_power *power, int *intval)
|
||
|
+{
|
||
|
+ unsigned int reg;
|
||
|
+ int ret;
|
||
|
+
|
||
|
+ ret = regmap_read(power->regmap, AXP288_BC_GLOBAL, ®);
|
||
|
+ if (ret)
|
||
|
+ return ret;
|
||
|
+
|
||
|
+ *intval = !!(reg & AXP813_BC_EN);
|
||
|
+ return 0;
|
||
|
+}
|
||
|
+
|
||
|
+static int
|
||
|
+axp813_set_usb_bc_enabled(struct axp20x_usb_power *power, int val)
|
||
|
+{
|
||
|
+ return regmap_update_bits(power->regmap, AXP288_BC_GLOBAL,
|
||
|
+ AXP813_BC_EN,
|
||
|
+ val ? AXP813_BC_EN : 0);
|
||
|
+}
|
||
|
+
|
||
|
+static enum power_supply_usb_type axp813_usb_types[] = {
|
||
|
+ POWER_SUPPLY_USB_TYPE_PD,
|
||
|
+ POWER_SUPPLY_USB_TYPE_SDP,
|
||
|
+ POWER_SUPPLY_USB_TYPE_DCP,
|
||
|
+ POWER_SUPPLY_USB_TYPE_CDP,
|
||
|
+ POWER_SUPPLY_USB_TYPE_UNKNOWN,
|
||
|
+};
|
||
|
+
|
||
|
+static int axp813_get_usb_type(struct axp20x_usb_power *power,
|
||
|
+ union power_supply_propval *val)
|
||
|
+{
|
||
|
+ unsigned int reg;
|
||
|
+ int ret;
|
||
|
+
|
||
|
+ ret = regmap_read(power->regmap, AXP288_BC_GLOBAL, ®);
|
||
|
+ if (ret)
|
||
|
+ return ret;
|
||
|
+
|
||
|
+ if (!(reg & AXP813_BC_EN)) {
|
||
|
+ val->intval = POWER_SUPPLY_USB_TYPE_PD;
|
||
|
+ return 0;
|
||
|
+ }
|
||
|
+
|
||
|
+ ret = regmap_read(power->regmap, AXP288_BC_DET_STAT, ®);
|
||
|
+ if (ret)
|
||
|
+ return ret;
|
||
|
+
|
||
|
+ switch (reg & AXP813_BC_RESULT_MASK) {
|
||
|
+ case AXP813_BC_RESULT_SDP:
|
||
|
+ val->intval = POWER_SUPPLY_USB_TYPE_SDP;
|
||
|
+ break;
|
||
|
+ case AXP813_BC_RESULT_CDP:
|
||
|
+ val->intval = POWER_SUPPLY_USB_TYPE_CDP;
|
||
|
+ break;
|
||
|
+ case AXP813_BC_RESULT_DCP:
|
||
|
+ val->intval = POWER_SUPPLY_USB_TYPE_DCP;
|
||
|
+ break;
|
||
|
+ default:
|
||
|
+ val->intval = POWER_SUPPLY_USB_TYPE_UNKNOWN;
|
||
|
+ break;
|
||
|
+ }
|
||
|
+
|
||
|
+ return 0;
|
||
|
+}
|
||
|
+
|
||
|
static int axp20x_usb_power_get_property(struct power_supply *psy,
|
||
|
enum power_supply_property psp, union power_supply_propval *val)
|
||
|
{
|
||
|
@@ -161,6 +291,7 @@ 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_USB_DCP_INPUT_CURRENT_LIMIT:
|
||
|
case POWER_SUPPLY_PROP_CURRENT_MAX:
|
||
|
ret = regmap_field_read(power->curr_lim_fld, &v);
|
||
|
if (ret)
|
||
|
@@ -224,6 +355,24 @@ static int axp20x_usb_power_get_property(struct power_supply *psy,
|
||
|
case POWER_SUPPLY_PROP_ONLINE:
|
||
|
val->intval = !!(input & AXP20X_PWR_STATUS_VBUS_USED);
|
||
|
break;
|
||
|
+
|
||
|
+ case POWER_SUPPLY_PROP_USB_TYPE:
|
||
|
+ if (power->axp_data->axp20x_id == AXP813_ID)
|
||
|
+ return axp813_get_usb_type(power, val);
|
||
|
+
|
||
|
+ return -EINVAL;
|
||
|
+
|
||
|
+ case POWER_SUPPLY_PROP_USB_BC_ENABLED:
|
||
|
+ if (power->axp_data->axp20x_id == AXP813_ID)
|
||
|
+ return axp813_get_usb_bc_enabled(power, &val->intval);
|
||
|
+
|
||
|
+ return -EINVAL;
|
||
|
+
|
||
|
+ case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT:
|
||
|
+ if (power->axp_data->axp20x_id == AXP813_ID)
|
||
|
+ return axp813_usb_power_get_input_current_limit(power,
|
||
|
+ &val->intval);
|
||
|
+ fallthrough;
|
||
|
default:
|
||
|
return -EINVAL;
|
||
|
}
|
||
|
@@ -288,9 +437,22 @@ static int axp20x_usb_power_set_property(struct power_supply *psy,
|
||
|
case POWER_SUPPLY_PROP_VOLTAGE_MIN:
|
||
|
return axp20x_usb_power_set_voltage_min(power, val->intval);
|
||
|
|
||
|
+ case POWER_SUPPLY_PROP_USB_DCP_INPUT_CURRENT_LIMIT:
|
||
|
case POWER_SUPPLY_PROP_CURRENT_MAX:
|
||
|
return axp20x_usb_power_set_current_max(power, val->intval);
|
||
|
|
||
|
+ case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT:
|
||
|
+ if (power->axp_data->axp20x_id == AXP813_ID)
|
||
|
+ return axp813_usb_power_set_input_current_limit(power,
|
||
|
+ val->intval);
|
||
|
+ return -EINVAL;
|
||
|
+
|
||
|
+ case POWER_SUPPLY_PROP_USB_BC_ENABLED:
|
||
|
+ if (power->axp_data->axp20x_id == AXP813_ID)
|
||
|
+ return axp813_set_usb_bc_enabled(power, val->intval);
|
||
|
+
|
||
|
+ return -EINVAL;
|
||
|
+
|
||
|
default:
|
||
|
return -EINVAL;
|
||
|
}
|
||
|
@@ -314,7 +476,10 @@ static int axp20x_usb_power_prop_writeable(struct power_supply *psy,
|
||
|
return power->vbus_disable_bit != NULL;
|
||
|
|
||
|
return psp == POWER_SUPPLY_PROP_VOLTAGE_MIN ||
|
||
|
- psp == POWER_SUPPLY_PROP_CURRENT_MAX;
|
||
|
+ psp == POWER_SUPPLY_PROP_CURRENT_MAX ||
|
||
|
+ psp == POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT ||
|
||
|
+ psp == POWER_SUPPLY_PROP_USB_BC_ENABLED ||
|
||
|
+ psp == POWER_SUPPLY_PROP_USB_DCP_INPUT_CURRENT_LIMIT;
|
||
|
}
|
||
|
|
||
|
static enum power_supply_property axp20x_usb_power_properties[] = {
|
||
|
@@ -333,6 +498,18 @@ static enum power_supply_property axp22x_usb_power_properties[] = {
|
||
|
POWER_SUPPLY_PROP_ONLINE,
|
||
|
POWER_SUPPLY_PROP_VOLTAGE_MIN,
|
||
|
POWER_SUPPLY_PROP_CURRENT_MAX,
|
||
|
+ POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT,
|
||
|
+};
|
||
|
+
|
||
|
+static enum power_supply_property axp813_usb_power_properties[] = {
|
||
|
+ POWER_SUPPLY_PROP_HEALTH,
|
||
|
+ POWER_SUPPLY_PROP_PRESENT,
|
||
|
+ POWER_SUPPLY_PROP_ONLINE,
|
||
|
+ POWER_SUPPLY_PROP_VOLTAGE_MIN,
|
||
|
+ POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT,
|
||
|
+ POWER_SUPPLY_PROP_USB_BC_ENABLED,
|
||
|
+ POWER_SUPPLY_PROP_USB_DCP_INPUT_CURRENT_LIMIT,
|
||
|
+ POWER_SUPPLY_PROP_USB_TYPE,
|
||
|
};
|
||
|
|
||
|
static const struct power_supply_desc axp20x_usb_power_desc = {
|
||
|
@@ -355,6 +532,18 @@ static const struct power_supply_desc axp22x_usb_power_desc = {
|
||
|
.set_property = axp20x_usb_power_set_property,
|
||
|
};
|
||
|
|
||
|
+static const struct power_supply_desc axp813_usb_power_desc = {
|
||
|
+ .name = "axp20x-usb",
|
||
|
+ .type = POWER_SUPPLY_TYPE_USB,
|
||
|
+ .properties = axp813_usb_power_properties,
|
||
|
+ .num_properties = ARRAY_SIZE(axp813_usb_power_properties),
|
||
|
+ .property_is_writeable = axp20x_usb_power_prop_writeable,
|
||
|
+ .get_property = axp20x_usb_power_get_property,
|
||
|
+ .set_property = axp20x_usb_power_set_property,
|
||
|
+ .usb_types = axp813_usb_types,
|
||
|
+ .num_usb_types = ARRAY_SIZE(axp813_usb_types),
|
||
|
+};
|
||
|
+
|
||
|
static const char * const axp20x_irq_names[] = {
|
||
|
"VBUS_PLUGIN",
|
||
|
"VBUS_REMOVAL",
|
||
|
@@ -367,6 +556,13 @@ static const char * const axp22x_irq_names[] = {
|
||
|
"VBUS_REMOVAL",
|
||
|
};
|
||
|
|
||
|
+static const char * const axp813_irq_names[] = {
|
||
|
+ "VBUS_PLUGIN",
|
||
|
+ "VBUS_REMOVAL",
|
||
|
+ "BC_USB_CHNG",
|
||
|
+ "MV_CHNG",
|
||
|
+};
|
||
|
+
|
||
|
static int axp192_usb_curr_lim_table[] = {
|
||
|
-1,
|
||
|
-1,
|
||
|
@@ -434,9 +630,10 @@ static const struct axp_data axp223_data = {
|
||
|
};
|
||
|
|
||
|
static const struct axp_data axp813_data = {
|
||
|
- .power_desc = &axp22x_usb_power_desc,
|
||
|
- .irq_names = axp22x_irq_names,
|
||
|
- .num_irq_names = ARRAY_SIZE(axp22x_irq_names),
|
||
|
+ .axp20x_id = AXP813_ID,
|
||
|
+ .power_desc = &axp813_usb_power_desc,
|
||
|
+ .irq_names = axp813_irq_names,
|
||
|
+ .num_irq_names = ARRAY_SIZE(axp813_irq_names),
|
||
|
.curr_lim_table = axp813_usb_curr_lim_table,
|
||
|
.curr_lim_fld = REG_FIELD(AXP20X_VBUS_IPSOUT_MGMT, 0, 1),
|
||
|
.usb_bc_en_bit = REG_FIELD(AXP288_BC_GLOBAL, 0, 0),
|
||
|
diff --git a/include/linux/mfd/axp20x.h b/include/linux/mfd/axp20x.h
|
||
|
index f1755163dd9f..3877f9576a49 100644
|
||
|
--- a/include/linux/mfd/axp20x.h
|
||
|
+++ b/include/linux/mfd/axp20x.h
|
||
|
@@ -152,6 +152,7 @@ enum axp20x_variants {
|
||
|
|
||
|
/* Other DCDC regulator control registers are the same as AXP803 */
|
||
|
#define AXP813_DCDC7_V_OUT 0x26
|
||
|
+#define AXP813_CHRG_CTRL3 0x35
|
||
|
|
||
|
#define AXP15060_STARTUP_SRC 0x00
|
||
|
#define AXP15060_PWR_OUT_CTRL1 0x10
|
||
|
--
|
||
|
2.34.1
|
||
|
|