build/patch/kernel/archive/sunxi-6.5/patches.megous/power-supply-axp20x-usb-power-Support-input-current-limit-expor.patch

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, &reg);
+ 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, &reg);
+ 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, &reg);
+ 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