diff mbox

[v2,08/14] power: supply: Add power_supply_set_input_current_limit_from_supplier helper

Message ID 20170815200502.17339-9-hdegoede@redhat.com (mailing list archive)
State Deferred, archived
Headers show

Commit Message

Hans de Goede Aug. 15, 2017, 8:04 p.m. UTC
On some devices the USB Type-C port power (USB PD 2.0) negotiation is
done by a separate port-controller IC, while the current limit is
controlled through another (charger) IC.

It has been decided to model this by modelling the external Type-C
power brick (adapter/charger) as a power-supply class device which
supplies the charger-IC, with its voltage-now and current-max representing
the negotiated voltage and max current draw.

This commit adds a power_supply_set_input_current_limit_from_supplier
helper function which charger power-supply drivers can call to get
the max-current from their supplier and have this applied
through their set_property call-back to their input-current-limit.

Signed-off-by: Hans de Goede <hdegoede@redhat.com>
---
 drivers/power/supply/power_supply_core.c | 41 ++++++++++++++++++++++++++++++++
 include/linux/power_supply.h             |  2 ++
 2 files changed, 43 insertions(+)

Comments

Tony Lindgren Aug. 16, 2017, 3:54 p.m. UTC | #1
* Hans de Goede <hdegoede@redhat.com> [170815 13:06]:
> On some devices the USB Type-C port power (USB PD 2.0) negotiation is
> done by a separate port-controller IC, while the current limit is
> controlled through another (charger) IC.
> 
> It has been decided to model this by modelling the external Type-C
> power brick (adapter/charger) as a power-supply class device which
> supplies the charger-IC, with its voltage-now and current-max representing
> the negotiated voltage and max current draw.
> 
> This commit adds a power_supply_set_input_current_limit_from_supplier
> helper function which charger power-supply drivers can call to get
> the max-current from their supplier and have this applied
> through their set_property call-back to their input-current-limit.

Hmm so can this also be used for the USB gadget subsystem
to tell charge controller when it's OK to enable 500mA
charging after enumeration?

FYI, that's controlled by the bq24190 pin named OTG that should
be only set high after enumeration. Any ideas how that is wired
on your device? Does it connect to the USB PHY or to a GPIO
line?

Regards,

Tony
Hans de Goede Aug. 16, 2017, 5:38 p.m. UTC | #2
Hi,

On 16-08-17 17:54, Tony Lindgren wrote:
> * Hans de Goede <hdegoede@redhat.com> [170815 13:06]:
>> On some devices the USB Type-C port power (USB PD 2.0) negotiation is
>> done by a separate port-controller IC, while the current limit is
>> controlled through another (charger) IC.
>>
>> It has been decided to model this by modelling the external Type-C
>> power brick (adapter/charger) as a power-supply class device which
>> supplies the charger-IC, with its voltage-now and current-max representing
>> the negotiated voltage and max current draw.
>>
>> This commit adds a power_supply_set_input_current_limit_from_supplier
>> helper function which charger power-supply drivers can call to get
>> the max-current from their supplier and have this applied
>> through their set_property call-back to their input-current-limit.
> 
> Hmm so can this also be used for the USB gadget subsystem
> to tell charge controller when it's OK to enable 500mA
> charging after enumeration?

I'm not sure that that would be best modeled this way. Perhaps
the phy-driver can directly control the gpio you have for that,
that seems better then trying to solve this with cross subsystem
calls which are always tricky.

> FYI, that's controlled by the bq24190 pin named OTG that should
> be only set high after enumeration. Any ideas how that is wired
> on your device? Does it connect to the USB PHY or to a GPIO
> line?

I believe it is just hardwired to be logical high all the time
on my board.

Regards,

Hans
Tony Lindgren Aug. 16, 2017, 7:21 p.m. UTC | #3
* Hans de Goede <hdegoede@redhat.com> [170816 10:38]:
> Hi,
> 
> On 16-08-17 17:54, Tony Lindgren wrote:
> > * Hans de Goede <hdegoede@redhat.com> [170815 13:06]:
> > > On some devices the USB Type-C port power (USB PD 2.0) negotiation is
> > > done by a separate port-controller IC, while the current limit is
> > > controlled through another (charger) IC.
> > > 
> > > It has been decided to model this by modelling the external Type-C
> > > power brick (adapter/charger) as a power-supply class device which
> > > supplies the charger-IC, with its voltage-now and current-max representing
> > > the negotiated voltage and max current draw.
> > > 
> > > This commit adds a power_supply_set_input_current_limit_from_supplier
> > > helper function which charger power-supply drivers can call to get
> > > the max-current from their supplier and have this applied
> > > through their set_property call-back to their input-current-limit.
> > 
> > Hmm so can this also be used for the USB gadget subsystem
> > to tell charge controller when it's OK to enable 500mA
> > charging after enumeration?
> 
> I'm not sure that that would be best modeled this way. Perhaps
> the phy-driver can directly control the gpio you have for that,
> that seems better then trying to solve this with cross subsystem
> calls which are always tricky.

I don't think the phy driver knows either when the system
has enumerated as a gadget..

> > FYI, that's controlled by the bq24190 pin named OTG that should
> > be only set high after enumeration. Any ideas how that is wired
> > on your device? Does it connect to the USB PHY or to a GPIO
> > line?
> 
> I believe it is just hardwired to be logical high all the time
> on my board.

OK thanks for checking.

Tony
Sebastian Reichel Aug. 29, 2017, 10:54 a.m. UTC | #4
Hi,

On Tue, Aug 15, 2017 at 10:04:56PM +0200, Hans de Goede wrote:
> On some devices the USB Type-C port power (USB PD 2.0) negotiation is
> done by a separate port-controller IC, while the current limit is
> controlled through another (charger) IC.
> 
> It has been decided to model this by modelling the external Type-C
> power brick (adapter/charger) as a power-supply class device which
> supplies the charger-IC, with its voltage-now and current-max representing
> the negotiated voltage and max current draw.
> 
> This commit adds a power_supply_set_input_current_limit_from_supplier
> helper function which charger power-supply drivers can call to get
> the max-current from their supplier and have this applied
> through their set_property call-back to their input-current-limit.
> 
> Signed-off-by: Hans de Goede <hdegoede@redhat.com>

Thanks, queued.

-- Sebastian

>  drivers/power/supply/power_supply_core.c | 41 ++++++++++++++++++++++++++++++++
>  include/linux/power_supply.h             |  2 ++
>  2 files changed, 43 insertions(+)
> 
> diff --git a/drivers/power/supply/power_supply_core.c b/drivers/power/supply/power_supply_core.c
> index 0741fce..3f92574 100644
> --- a/drivers/power/supply/power_supply_core.c
> +++ b/drivers/power/supply/power_supply_core.c
> @@ -375,6 +375,47 @@ int power_supply_is_system_supplied(void)
>  }
>  EXPORT_SYMBOL_GPL(power_supply_is_system_supplied);
>  
> +static int __power_supply_get_supplier_max_current(struct device *dev,
> +						   void *data)
> +{
> +	union power_supply_propval ret = {0,};
> +	struct power_supply *epsy = dev_get_drvdata(dev);
> +	struct power_supply *psy = data;
> +
> +	if (__power_supply_is_supplied_by(epsy, psy))
> +		if (!epsy->desc->get_property(epsy,
> +					      POWER_SUPPLY_PROP_CURRENT_MAX,
> +					      &ret))
> +			return ret.intval;
> +
> +	return 0;
> +}
> +
> +int power_supply_set_input_current_limit_from_supplier(struct power_supply *psy)
> +{
> +	union power_supply_propval val = {0,};
> +	int curr;
> +
> +	if (!psy->desc->set_property)
> +		return -EINVAL;
> +
> +	/*
> +	 * This function is not intended for use with a supply with multiple
> +	 * suppliers, we simply pick the first supply to report a non 0
> +	 * max-current.
> +	 */
> +	curr = class_for_each_device(power_supply_class, NULL, psy,
> +				      __power_supply_get_supplier_max_current);
> +	if (curr <= 0)
> +		return (curr == 0) ? -ENODEV : curr;
> +
> +	val.intval = curr;
> +
> +	return psy->desc->set_property(psy,
> +				POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT, &val);
> +}
> +EXPORT_SYMBOL_GPL(power_supply_set_input_current_limit_from_supplier);
> +
>  int power_supply_set_battery_charged(struct power_supply *psy)
>  {
>  	if (atomic_read(&psy->use_cnt) >= 0 &&
> diff --git a/include/linux/power_supply.h b/include/linux/power_supply.h
> index de89066..79e90b3 100644
> --- a/include/linux/power_supply.h
> +++ b/include/linux/power_supply.h
> @@ -332,6 +332,8 @@ extern int power_supply_get_battery_info(struct power_supply *psy,
>  					 struct power_supply_battery_info *info);
>  extern void power_supply_changed(struct power_supply *psy);
>  extern int power_supply_am_i_supplied(struct power_supply *psy);
> +extern int power_supply_set_input_current_limit_from_supplier(
> +					 struct power_supply *psy);
>  extern int power_supply_set_battery_charged(struct power_supply *psy);
>  
>  #ifdef CONFIG_POWER_SUPPLY
> -- 
> 2.9.4
>
diff mbox

Patch

diff --git a/drivers/power/supply/power_supply_core.c b/drivers/power/supply/power_supply_core.c
index 0741fce..3f92574 100644
--- a/drivers/power/supply/power_supply_core.c
+++ b/drivers/power/supply/power_supply_core.c
@@ -375,6 +375,47 @@  int power_supply_is_system_supplied(void)
 }
 EXPORT_SYMBOL_GPL(power_supply_is_system_supplied);
 
+static int __power_supply_get_supplier_max_current(struct device *dev,
+						   void *data)
+{
+	union power_supply_propval ret = {0,};
+	struct power_supply *epsy = dev_get_drvdata(dev);
+	struct power_supply *psy = data;
+
+	if (__power_supply_is_supplied_by(epsy, psy))
+		if (!epsy->desc->get_property(epsy,
+					      POWER_SUPPLY_PROP_CURRENT_MAX,
+					      &ret))
+			return ret.intval;
+
+	return 0;
+}
+
+int power_supply_set_input_current_limit_from_supplier(struct power_supply *psy)
+{
+	union power_supply_propval val = {0,};
+	int curr;
+
+	if (!psy->desc->set_property)
+		return -EINVAL;
+
+	/*
+	 * This function is not intended for use with a supply with multiple
+	 * suppliers, we simply pick the first supply to report a non 0
+	 * max-current.
+	 */
+	curr = class_for_each_device(power_supply_class, NULL, psy,
+				      __power_supply_get_supplier_max_current);
+	if (curr <= 0)
+		return (curr == 0) ? -ENODEV : curr;
+
+	val.intval = curr;
+
+	return psy->desc->set_property(psy,
+				POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT, &val);
+}
+EXPORT_SYMBOL_GPL(power_supply_set_input_current_limit_from_supplier);
+
 int power_supply_set_battery_charged(struct power_supply *psy)
 {
 	if (atomic_read(&psy->use_cnt) >= 0 &&
diff --git a/include/linux/power_supply.h b/include/linux/power_supply.h
index de89066..79e90b3 100644
--- a/include/linux/power_supply.h
+++ b/include/linux/power_supply.h
@@ -332,6 +332,8 @@  extern int power_supply_get_battery_info(struct power_supply *psy,
 					 struct power_supply_battery_info *info);
 extern void power_supply_changed(struct power_supply *psy);
 extern int power_supply_am_i_supplied(struct power_supply *psy);
+extern int power_supply_set_input_current_limit_from_supplier(
+					 struct power_supply *psy);
 extern int power_supply_set_battery_charged(struct power_supply *psy);
 
 #ifdef CONFIG_POWER_SUPPLY