diff mbox series

[v4,12/20] power: supply: bq25890: Support higher charging voltages through Pump Express+ protocol

Message ID 20220130204557.15662-13-hdegoede@redhat.com
State Superseded
Headers show
Series power-suppy/i2c/extcon: Fix charger setup on Xiaomi Mi Pad 2 and Lenovo Yogabook | expand

Commit Message

Hans de Goede Jan. 30, 2022, 8:45 p.m. UTC
From: Yauhen Kharuzhy <jekhor@gmail.com>

Add a "linux,pump-express-vbus-max" property which indicates if the Pump
Express+ protocol should be used to increase the charging protocol.

If this new property is set and a DCP charger is detected then request
a higher charging voltage through the Pump Express+ protocol.

So far this new property is only used on x86/ACPI (non devicetree) devs,
IOW it is not used in actual devicetree files. The devicetree-bindings
maintainers have requested properties like these to not be added to the
devicetree-bindings, so the new property is deliberately not added
to the existing devicetree-bindings.

Changes by Hans de Goede:
- Port to my bq25890 patch-series + various cleanups
- Make behavior configurable through a new "linux,pump-express-vbus-max"
  device-property
- Sleep 1 second before re-checking the Vbus voltage after requesting
  it to be raised, to ensure that the ADC has time to sampled the new Vbus
- Add VBUSV bq25890_tables[] entry and use it in bq25890_get_vbus_voltage()
- Tweak commit message

Signed-off-by: Yauhen Kharuzhy <jekhor@gmail.com>
Co-developed-by: Hans de Goede <hdegoede@redhat.com>
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
---
Changes in v3:
- Re-indent bq25890_tables to make checkpatch happy
- Add _uV postfix to PUMP_EXPRESS_VBUS_MARGIN
- Use regmap_field_read_poll_timeout() to wait for F_PUMPX_UP bit to clear
- Don't error check device_property_read_u32() call for optional prop

Changes in v2:
- New patch in v2 of this series, also see "Changes by Hans de Goede"
---
 drivers/power/supply/bq25890_charger.c | 92 +++++++++++++++++++++++---
 1 file changed, 84 insertions(+), 8 deletions(-)

Comments

Andy Shevchenko Jan. 31, 2022, 1:48 p.m. UTC | #1
On Sun, Jan 30, 2022 at 09:45:49PM +0100, Hans de Goede wrote:
> From: Yauhen Kharuzhy <jekhor@gmail.com>
> 
> Add a "linux,pump-express-vbus-max" property which indicates if the Pump
> Express+ protocol should be used to increase the charging protocol.
> 
> If this new property is set and a DCP charger is detected then request
> a higher charging voltage through the Pump Express+ protocol.
> 
> So far this new property is only used on x86/ACPI (non devicetree) devs,
> IOW it is not used in actual devicetree files. The devicetree-bindings
> maintainers have requested properties like these to not be added to the
> devicetree-bindings, so the new property is deliberately not added
> to the existing devicetree-bindings.
> 
> Changes by Hans de Goede:
> - Port to my bq25890 patch-series + various cleanups
> - Make behavior configurable through a new "linux,pump-express-vbus-max"
>   device-property
> - Sleep 1 second before re-checking the Vbus voltage after requesting
>   it to be raised, to ensure that the ADC has time to sampled the new Vbus
> - Add VBUSV bq25890_tables[] entry and use it in bq25890_get_vbus_voltage()
> - Tweak commit message

...

> +static void bq25890_pump_express_work(struct work_struct *data)
> +{
> +	struct bq25890_device *bq =
> +		container_of(data, struct bq25890_device, pump_express_work.work);
> +	int voltage, i, ret;
> +
> +	dev_dbg(bq->dev, "Start to request input voltage increasing\n");
> +
> +	/* Enable current pulse voltage control protocol */
> +	ret = bq25890_field_write(bq, F_PUMPX_EN, 1);
> +	if (ret < 0)
> +		goto error_print;
> +
> +	for (i = 0; i < PUMP_EXPRESS_MAX_TRIES; i++) {

> +		voltage = bq25890_get_vbus_voltage(bq);
> +		if (voltage < 0)
> +			goto error_print;

It also can be (at least in align with the rest error paths)

		ret = bq25890_get_vbus_voltage(bq);
		if (ret < 0)
			goto error_print;
		voltage = ret;

followed up (but not necessarily)...

> +		dev_dbg(bq->dev, "input voltage = %d uV\n", voltage);
> +
> +		if ((voltage + PUMP_EXPRESS_VBUS_MARGIN_uV) >
> +					bq->pump_express_vbus_max)
> +			break;
> +
> +		ret = bq25890_field_write(bq, F_PUMPX_UP, 1);
> +		if (ret < 0)
> +			goto error_print;
> +
> +		/* Note a single PUMPX up pulse-sequence takes 2.1s */
> +		ret = regmap_field_read_poll_timeout(bq->rmap_fields[F_PUMPX_UP],
> +						     ret, !ret, 100000, 3000000);
> +		if (ret < 0)
> +			goto error_print;
> +
> +		/* Make sure ADC has sampled Vbus before checking again */
> +		msleep(1000);
> +	}
> +
> +	bq25890_field_write(bq, F_PUMPX_EN, 0);
> +
> +	dev_info(bq->dev, "Hi-voltage charging requested, input voltage is %d mV\n",
> +		 voltage);

> +	return;
> +error_print:

	if (ret < 0)

But it's up to you.

> +	dev_err(bq->dev, "Failed to request hi-voltage charging\n");
> +}
Hans de Goede Jan. 31, 2022, 3:18 p.m. UTC | #2
Hi,

On 1/31/22 14:48, Andy Shevchenko wrote:
> On Sun, Jan 30, 2022 at 09:45:49PM +0100, Hans de Goede wrote:
>> From: Yauhen Kharuzhy <jekhor@gmail.com>
>>
>> Add a "linux,pump-express-vbus-max" property which indicates if the Pump
>> Express+ protocol should be used to increase the charging protocol.
>>
>> If this new property is set and a DCP charger is detected then request
>> a higher charging voltage through the Pump Express+ protocol.
>>
>> So far this new property is only used on x86/ACPI (non devicetree) devs,
>> IOW it is not used in actual devicetree files. The devicetree-bindings
>> maintainers have requested properties like these to not be added to the
>> devicetree-bindings, so the new property is deliberately not added
>> to the existing devicetree-bindings.
>>
>> Changes by Hans de Goede:
>> - Port to my bq25890 patch-series + various cleanups
>> - Make behavior configurable through a new "linux,pump-express-vbus-max"
>>   device-property
>> - Sleep 1 second before re-checking the Vbus voltage after requesting
>>   it to be raised, to ensure that the ADC has time to sampled the new Vbus
>> - Add VBUSV bq25890_tables[] entry and use it in bq25890_get_vbus_voltage()
>> - Tweak commit message
> 
> ...
> 
>> +static void bq25890_pump_express_work(struct work_struct *data)
>> +{
>> +	struct bq25890_device *bq =
>> +		container_of(data, struct bq25890_device, pump_express_work.work);
>> +	int voltage, i, ret;
>> +
>> +	dev_dbg(bq->dev, "Start to request input voltage increasing\n");
>> +
>> +	/* Enable current pulse voltage control protocol */
>> +	ret = bq25890_field_write(bq, F_PUMPX_EN, 1);
>> +	if (ret < 0)
>> +		goto error_print;
>> +
>> +	for (i = 0; i < PUMP_EXPRESS_MAX_TRIES; i++) {
> 
>> +		voltage = bq25890_get_vbus_voltage(bq);
>> +		if (voltage < 0)
>> +			goto error_print;
> 
> It also can be (at least in align with the rest error paths)
> 
> 		ret = bq25890_get_vbus_voltage(bq);
> 		if (ret < 0)
> 			goto error_print;
> 		voltage = ret;
> 
> followed up (but not necessarily)...

The suggested pattern is useful when ret needs to be set on the error-exit
path, but we are not doing that here. So I prefer to just keep this as is.

Regards,

Hans



> 
>> +		dev_dbg(bq->dev, "input voltage = %d uV\n", voltage);
>> +
>> +		if ((voltage + PUMP_EXPRESS_VBUS_MARGIN_uV) >
>> +					bq->pump_express_vbus_max)
>> +			break;
>> +
>> +		ret = bq25890_field_write(bq, F_PUMPX_UP, 1);
>> +		if (ret < 0)
>> +			goto error_print;
>> +
>> +		/* Note a single PUMPX up pulse-sequence takes 2.1s */
>> +		ret = regmap_field_read_poll_timeout(bq->rmap_fields[F_PUMPX_UP],
>> +						     ret, !ret, 100000, 3000000);
>> +		if (ret < 0)
>> +			goto error_print;
>> +
>> +		/* Make sure ADC has sampled Vbus before checking again */
>> +		msleep(1000);
>> +	}
>> +
>> +	bq25890_field_write(bq, F_PUMPX_EN, 0);
>> +
>> +	dev_info(bq->dev, "Hi-voltage charging requested, input voltage is %d mV\n",
>> +		 voltage);
> 
>> +	return;
>> +error_print:
> 
> 	if (ret < 0)
> 
> But it's up to you.
> 
>> +	dev_err(bq->dev, "Failed to request hi-voltage charging\n");
>> +}
>
Andy Shevchenko Jan. 31, 2022, 4:19 p.m. UTC | #3
On Mon, Jan 31, 2022 at 04:18:23PM +0100, Hans de Goede wrote:
> On 1/31/22 14:48, Andy Shevchenko wrote:
> > On Sun, Jan 30, 2022 at 09:45:49PM +0100, Hans de Goede wrote:

...

> >> +	for (i = 0; i < PUMP_EXPRESS_MAX_TRIES; i++) {
> > 
> >> +		voltage = bq25890_get_vbus_voltage(bq);
> >> +		if (voltage < 0)
> >> +			goto error_print;
> > 
> > It also can be (at least in align with the rest error paths)
> > 
> > 		ret = bq25890_get_vbus_voltage(bq);
> > 		if (ret < 0)
> > 			goto error_print;
> > 		voltage = ret;
> > 
> > followed up (but not necessarily)...
> 
> The suggested pattern is useful when ret needs to be set on the error-exit
> path, but we are not doing that here. So I prefer to just keep this as is.

Are you talking about above proposal?

Still wouldn't be better to use it that if we want, for example, to print an
error code, it can be done easily? For the sake of consistency.

> >> +	}
> >> +
> >> +	bq25890_field_write(bq, F_PUMPX_EN, 0);
> >> +
> >> +	dev_info(bq->dev, "Hi-voltage charging requested, input voltage is %d mV\n",
> >> +		 voltage);
> > 
> >> +	return;
> >> +error_print:
> > 
> > 	if (ret < 0)
> > 
> > But it's up to you.
> > 
> >> +	dev_err(bq->dev, "Failed to request hi-voltage charging\n");
diff mbox series

Patch

diff --git a/drivers/power/supply/bq25890_charger.c b/drivers/power/supply/bq25890_charger.c
index 3de72f0fbf3e..179abed92f9b 100644
--- a/drivers/power/supply/bq25890_charger.c
+++ b/drivers/power/supply/bq25890_charger.c
@@ -27,6 +27,10 @@ 
 #define BQ25895_ID			7
 #define BQ25896_ID			0
 
+#define PUMP_EXPRESS_START_DELAY	(5 * HZ)
+#define PUMP_EXPRESS_MAX_TRIES		6
+#define PUMP_EXPRESS_VBUS_MARGIN_uV	1000000
+
 enum bq25890_chip_version {
 	BQ25890,
 	BQ25892,
@@ -107,6 +111,7 @@  struct bq25890_device {
 	struct usb_phy *usb_phy;
 	struct notifier_block usb_nb;
 	struct work_struct usb_work;
+	struct delayed_work pump_express_work;
 	unsigned long usb_event;
 
 	struct regmap *rmap;
@@ -114,6 +119,7 @@  struct bq25890_device {
 
 	bool skip_reset;
 	bool read_back_init_data;
+	u32 pump_express_vbus_max;
 	enum bq25890_chip_version chip_version;
 	struct bq25890_init_data init_data;
 	struct bq25890_state state;
@@ -265,6 +271,7 @@  enum bq25890_table_ids {
 	TBL_VREG,
 	TBL_BOOSTV,
 	TBL_SYSVMIN,
+	TBL_VBUSV,
 	TBL_VBATCOMP,
 	TBL_RBATCOMP,
 
@@ -325,14 +332,15 @@  static const union {
 } bq25890_tables[] = {
 	/* range tables */
 	/* TODO: BQ25896 has max ICHG 3008 mA */
-	[TBL_ICHG] =	{ .rt = {0,	  5056000, 64000} },	 /* uA */
-	[TBL_ITERM] =	{ .rt = {64000,   1024000, 64000} },	 /* uA */
-	[TBL_IINLIM] =  { .rt = {100000,  3250000, 50000} },	 /* uA */
-	[TBL_VREG] =	{ .rt = {3840000, 4608000, 16000} },	 /* uV */
-	[TBL_BOOSTV] =	{ .rt = {4550000, 5510000, 64000} },	 /* uV */
-	[TBL_SYSVMIN] = { .rt = {3000000, 3700000, 100000} },	 /* uV */
-	[TBL_VBATCOMP] ={ .rt = {0,        224000, 32000} },	 /* uV */
-	[TBL_RBATCOMP] ={ .rt = {0,        140000, 20000} },	 /* uOhm */
+	[TBL_ICHG] =	 { .rt = {0,        5056000, 64000} },	 /* uA */
+	[TBL_ITERM] =	 { .rt = {64000,    1024000, 64000} },	 /* uA */
+	[TBL_IINLIM] =   { .rt = {100000,   3250000, 50000} },	 /* uA */
+	[TBL_VREG] =	 { .rt = {3840000,  4608000, 16000} },	 /* uV */
+	[TBL_BOOSTV] =	 { .rt = {4550000,  5510000, 64000} },	 /* uV */
+	[TBL_SYSVMIN] =  { .rt = {3000000,  3700000, 100000} },	 /* uV */
+	[TBL_VBUSV] =	 { .rt = {2600000, 15300000, 100000} },	 /* uV */
+	[TBL_VBATCOMP] = { .rt = {0,         224000, 32000} },	 /* uV */
+	[TBL_RBATCOMP] = { .rt = {0,         140000, 20000} },	 /* uOhm */
 
 	/* lookup tables */
 	[TBL_TREG] =	{ .lt = {bq25890_treg_tbl, BQ25890_TREG_TBL_SIZE} },
@@ -435,6 +443,17 @@  static bool bq25890_is_adc_property(enum power_supply_property psp)
 
 static irqreturn_t __bq25890_handle_irq(struct bq25890_device *bq);
 
+static int bq25890_get_vbus_voltage(struct bq25890_device *bq)
+{
+	int ret;
+
+	ret = bq25890_field_read(bq, F_VBUSV);
+	if (ret < 0)
+		return ret;
+
+	return bq25890_find_val(ret, TBL_VBUSV);
+}
+
 static int bq25890_power_supply_get_property(struct power_supply *psy,
 					     enum power_supply_property psp,
 					     union power_supply_propval *val)
@@ -613,6 +632,11 @@  static void bq25890_charger_external_power_changed(struct power_supply *psy)
 	switch (val.intval) {
 	case POWER_SUPPLY_USB_TYPE_DCP:
 		input_current_limit = bq25890_find_idx(2000000, TBL_IINLIM);
+		if (bq->pump_express_vbus_max) {
+			queue_delayed_work(system_power_efficient_wq,
+					   &bq->pump_express_work,
+					   PUMP_EXPRESS_START_DELAY);
+		}
 		break;
 	case POWER_SUPPLY_USB_TYPE_CDP:
 	case POWER_SUPPLY_USB_TYPE_ACA:
@@ -878,6 +902,53 @@  static int bq25890_set_otg_cfg(struct bq25890_device *bq, u8 val)
 	return ret;
 }
 
+static void bq25890_pump_express_work(struct work_struct *data)
+{
+	struct bq25890_device *bq =
+		container_of(data, struct bq25890_device, pump_express_work.work);
+	int voltage, i, ret;
+
+	dev_dbg(bq->dev, "Start to request input voltage increasing\n");
+
+	/* Enable current pulse voltage control protocol */
+	ret = bq25890_field_write(bq, F_PUMPX_EN, 1);
+	if (ret < 0)
+		goto error_print;
+
+	for (i = 0; i < PUMP_EXPRESS_MAX_TRIES; i++) {
+		voltage = bq25890_get_vbus_voltage(bq);
+		if (voltage < 0)
+			goto error_print;
+		dev_dbg(bq->dev, "input voltage = %d uV\n", voltage);
+
+		if ((voltage + PUMP_EXPRESS_VBUS_MARGIN_uV) >
+					bq->pump_express_vbus_max)
+			break;
+
+		ret = bq25890_field_write(bq, F_PUMPX_UP, 1);
+		if (ret < 0)
+			goto error_print;
+
+		/* Note a single PUMPX up pulse-sequence takes 2.1s */
+		ret = regmap_field_read_poll_timeout(bq->rmap_fields[F_PUMPX_UP],
+						     ret, !ret, 100000, 3000000);
+		if (ret < 0)
+			goto error_print;
+
+		/* Make sure ADC has sampled Vbus before checking again */
+		msleep(1000);
+	}
+
+	bq25890_field_write(bq, F_PUMPX_EN, 0);
+
+	dev_info(bq->dev, "Hi-voltage charging requested, input voltage is %d mV\n",
+		 voltage);
+
+	return;
+error_print:
+	dev_err(bq->dev, "Failed to request hi-voltage charging\n");
+}
+
 static void bq25890_usb_work(struct work_struct *data)
 {
 	int ret;
@@ -1068,6 +1139,10 @@  static int bq25890_fw_probe(struct bq25890_device *bq)
 	int ret;
 	struct bq25890_init_data *init = &bq->init_data;
 
+	/* Optional, left at 0 if property is not present */
+	device_property_read_u32(bq->dev, "linux,pump-express-vbus-max",
+				 &bq->pump_express_vbus_max);
+
 	bq->skip_reset = device_property_read_bool(bq->dev, "linux,skip-reset");
 	bq->read_back_init_data = device_property_read_bool(bq->dev,
 						"linux,read-back-settings");
@@ -1100,6 +1175,7 @@  static int bq25890_probe(struct i2c_client *client,
 	bq->dev = dev;
 
 	mutex_init(&bq->lock);
+	INIT_DELAYED_WORK(&bq->pump_express_work, bq25890_pump_express_work);
 
 	bq->rmap = devm_regmap_init_i2c(client, &bq25890_regmap_config);
 	if (IS_ERR(bq->rmap))