diff mbox

[RFC,v2,6/7] typec: tcpm: Represent source supply through power_supply class

Message ID 81dd8843163c9ca193f32d54adb6fece99e5f159.1510657748.git.Adam.Thomson.Opensource@diasemi.com (mailing list archive)
State RFC, archived
Headers show

Commit Message

Adam Thomson Nov. 14, 2017, 11:44 a.m. UTC
This commit adds a power_supply class instance to represent a
PD source's voltage and current properties. This provides an
interface for reading these properties from user-space or other
drivers.

For PPS enabled Sources, this also provides write access to set
the current and voltage and allows for swapping between standard
PDO and PPS APDO.

As this represents a superset of the information provided in the
fusb302 driver, the power_supply instance in that code is removed
as part of this change, so reverting the commit titled
'typec: tcpm: Represent source supply through power_supply class'

Signed-off-by: Adam Thomson <Adam.Thomson.Opensource@diasemi.com>
---
 drivers/usb/typec/Kconfig           |   1 +
 drivers/usb/typec/fusb302/Kconfig   |   2 +-
 drivers/usb/typec/fusb302/fusb302.c |  63 +---------
 drivers/usb/typec/tcpm.c            | 225 +++++++++++++++++++++++++++++++++++-
 4 files changed, 228 insertions(+), 63 deletions(-)

Comments

Heikki Krogerus Nov. 24, 2017, 12:19 p.m. UTC | #1
Hi,

On Tue, Nov 14, 2017 at 11:44:47AM +0000, Adam Thomson wrote:
> diff --git a/drivers/usb/typec/tcpm.c b/drivers/usb/typec/tcpm.c
> index 78983e1..7c26c3d 100644
> --- a/drivers/usb/typec/tcpm.c
> +++ b/drivers/usb/typec/tcpm.c
> @@ -12,6 +12,7 @@
>  #include <linux/kernel.h>
>  #include <linux/module.h>
>  #include <linux/mutex.h>
> +#include <linux/power_supply.h>
>  #include <linux/proc_fs.h>
>  #include <linux/sched/clock.h>
>  #include <linux/seq_file.h>
> @@ -277,6 +278,10 @@ struct tcpm_port {
>  	u32 current_limit;
>  	u32 supply_voltage;
>  
> +	/* Used to export TA voltage and current */
> +	struct power_supply *psy;
> +	struct power_supply_desc psy_desc;
> +
>  	u32 bist_request;
>  
>  	/* PD state for Vendor Defined Messages */
> @@ -1756,6 +1761,7 @@ static int tcpm_pd_select_pdo(struct tcpm_port *port)
>  	int ret = -EINVAL;
>  
>  	port->pps_data.supported = false;
> +	port->psy_desc.type = POWER_SUPPLY_TYPE_USB_PD;
>  
>  	/*
>  	 * Select the source PDO providing the most power while staying within
> @@ -1775,8 +1781,10 @@ static int tcpm_pd_select_pdo(struct tcpm_port *port)
>  			mv = pdo_min_voltage(pdo);
>  			break;
>  		case PDO_TYPE_APDO:
> -			if (pdo_apdo_type(pdo) == APDO_TYPE_PPS)
> +			if (pdo_apdo_type(pdo) == APDO_TYPE_PPS) {
>  				port->pps_data.supported = true;
> +				port->psy_desc.type = POWER_SUPPLY_TYPE_USB_PD_PPS;
> +			}
>  			continue;
>  		default:
>  			tcpm_log(port, "Invalid PDO type, ignoring");
> @@ -2248,6 +2256,9 @@ static void tcpm_reset_port(struct tcpm_port *port)
>  	port->try_snk_count = 0;
>  	port->supply_voltage = 0;
>  	port->current_limit = 0;
> +	port->psy_desc.type = POWER_SUPPLY_TYPE_USB_TYPE_C;

Is it OK to everybody that the type of the psy is changed like that?
Hans?!

We do have drivers that already change the type, for example
drivers/power/supply/isp1704_charger.c, but what does the user space
expect? The ABI for the power supply class was never documented I
guess.

I'm not against changing the type, but I think that we should have an
attribute file listing all supported types a psy can have if we go
forward with this. Ideally the type file would just list them as space
separated values, and show the current one with asterisk in front of
it. The output would be similar we have with some of the other files
under /sys/power, at least /sys/power/state, but that would break the
ABI.


Thanks,
Adam Thomson Nov. 24, 2017, 2:05 p.m. UTC | #2
On 24 November 2017 12:19, Heikki Krogerus wrote:

> Hi,
> 
> On Tue, Nov 14, 2017 at 11:44:47AM +0000, Adam Thomson wrote:
> > diff --git a/drivers/usb/typec/tcpm.c b/drivers/usb/typec/tcpm.c
> > index 78983e1..7c26c3d 100644
> > --- a/drivers/usb/typec/tcpm.c
> > +++ b/drivers/usb/typec/tcpm.c
> > @@ -12,6 +12,7 @@
> >  #include <linux/kernel.h>
> >  #include <linux/module.h>
> >  #include <linux/mutex.h>
> > +#include <linux/power_supply.h>
> >  #include <linux/proc_fs.h>
> >  #include <linux/sched/clock.h>
> >  #include <linux/seq_file.h>
> > @@ -277,6 +278,10 @@ struct tcpm_port {
> >  	u32 current_limit;
> >  	u32 supply_voltage;
> >
> > +	/* Used to export TA voltage and current */
> > +	struct power_supply *psy;
> > +	struct power_supply_desc psy_desc;
> > +
> >  	u32 bist_request;
> >
> >  	/* PD state for Vendor Defined Messages */
> > @@ -1756,6 +1761,7 @@ static int tcpm_pd_select_pdo(struct tcpm_port *port)
> >  	int ret = -EINVAL;
> >
> >  	port->pps_data.supported = false;
> > +	port->psy_desc.type = POWER_SUPPLY_TYPE_USB_PD;
> >
> >  	/*
> >  	 * Select the source PDO providing the most power while staying within
> > @@ -1775,8 +1781,10 @@ static int tcpm_pd_select_pdo(struct tcpm_port *port)
> >  			mv = pdo_min_voltage(pdo);
> >  			break;
> >  		case PDO_TYPE_APDO:
> > -			if (pdo_apdo_type(pdo) == APDO_TYPE_PPS)
> > +			if (pdo_apdo_type(pdo) == APDO_TYPE_PPS) {
> >  				port->pps_data.supported = true;
> > +				port->psy_desc.type =
> POWER_SUPPLY_TYPE_USB_PD_PPS;
> > +			}
> >  			continue;
> >  		default:
> >  			tcpm_log(port, "Invalid PDO type, ignoring");
> > @@ -2248,6 +2256,9 @@ static void tcpm_reset_port(struct tcpm_port *port)
> >  	port->try_snk_count = 0;
> >  	port->supply_voltage = 0;
> >  	port->current_limit = 0;
> > +	port->psy_desc.type = POWER_SUPPLY_TYPE_USB_TYPE_C;
> 
> Is it OK to everybody that the type of the psy is changed like that?
> Hans?!
> 
> We do have drivers that already change the type, for example
> drivers/power/supply/isp1704_charger.c, but what does the user space
> expect? The ABI for the power supply class was never documented I
> guess.
> 

Hi Heikki,

Appreciate your time in reviewing this.

Yes, I actually saw that as an example when I considered this approach. I didn't
see anything obvious for this in the ABI documentation. Any ideas Sebastian?
What is user-space expectation for the 'type' property of a power_supply? I
assume having this dynamic is ok given existing drivers can already do something
like this, but would be good to have clarification.

> I'm not against changing the type, but I think that we should have an
> attribute file listing all supported types a psy can have if we go
> forward with this. Ideally the type file would just list them as space
> separated values, and show the current one with asterisk in front of
> it. The output would be similar we have with some of the other files
> under /sys/power, at least /sys/power/state, but that would break the
> ABI.
> 

I added this as I wanted the user to know what was connected rather than
blindly trying to set the 'online' property to enable PPS, even if the attached
source partner didn't support this. As you say, am not sure we could change the
'TYPE' property as that to my knowledge has always been a single string.

Maybe the addition of a 'SUPPORTED_TYPES' property or something similar could
close this gap (as you eluded to), at least by providing a RO list of all
supported types? Another option would be to add a type which indicates the
supply supports multiple types, and then based on this we can read another
property which does as you suggest with multiple strings and one being
highlighted? Am certainly open to discussion on this.
Hans de Goede Nov. 25, 2017, 2:03 p.m. UTC | #3
Hi,

On 11/24/2017 01:19 PM, Heikki Krogerus wrote:
> Hi,
> 
> On Tue, Nov 14, 2017 at 11:44:47AM +0000, Adam Thomson wrote:
>> diff --git a/drivers/usb/typec/tcpm.c b/drivers/usb/typec/tcpm.c
>> index 78983e1..7c26c3d 100644
>> --- a/drivers/usb/typec/tcpm.c
>> +++ b/drivers/usb/typec/tcpm.c
>> @@ -12,6 +12,7 @@
>>   #include <linux/kernel.h>
>>   #include <linux/module.h>
>>   #include <linux/mutex.h>
>> +#include <linux/power_supply.h>
>>   #include <linux/proc_fs.h>
>>   #include <linux/sched/clock.h>
>>   #include <linux/seq_file.h>
>> @@ -277,6 +278,10 @@ struct tcpm_port {
>>   	u32 current_limit;
>>   	u32 supply_voltage;
>>   
>> +	/* Used to export TA voltage and current */
>> +	struct power_supply *psy;
>> +	struct power_supply_desc psy_desc;
>> +
>>   	u32 bist_request;
>>   
>>   	/* PD state for Vendor Defined Messages */
>> @@ -1756,6 +1761,7 @@ static int tcpm_pd_select_pdo(struct tcpm_port *port)
>>   	int ret = -EINVAL;
>>   
>>   	port->pps_data.supported = false;
>> +	port->psy_desc.type = POWER_SUPPLY_TYPE_USB_PD;
>>   
>>   	/*
>>   	 * Select the source PDO providing the most power while staying within
>> @@ -1775,8 +1781,10 @@ static int tcpm_pd_select_pdo(struct tcpm_port *port)
>>   			mv = pdo_min_voltage(pdo);
>>   			break;
>>   		case PDO_TYPE_APDO:
>> -			if (pdo_apdo_type(pdo) == APDO_TYPE_PPS)
>> +			if (pdo_apdo_type(pdo) == APDO_TYPE_PPS) {
>>   				port->pps_data.supported = true;
>> +				port->psy_desc.type = POWER_SUPPLY_TYPE_USB_PD_PPS;
>> +			}
>>   			continue;
>>   		default:
>>   			tcpm_log(port, "Invalid PDO type, ignoring");
>> @@ -2248,6 +2256,9 @@ static void tcpm_reset_port(struct tcpm_port *port)
>>   	port->try_snk_count = 0;
>>   	port->supply_voltage = 0;
>>   	port->current_limit = 0;
>> +	port->psy_desc.type = POWER_SUPPLY_TYPE_USB_TYPE_C;
> 
> Is it OK to everybody that the type of the psy is changed like that?
> Hans?!
> 
> We do have drivers that already change the type, for example
> drivers/power/supply/isp1704_charger.c, but what does the user space
> expect? The ABI for the power supply class was never documented I
> guess.

The main userspace consumer of the power_supply sysfs class is upower,
upower knows about 3 types: "Mains", "Battery" and "USB", anything
else it gives a type of UP_DEVICE_KIND_UNKNOWN. So POWER_SUPPLY_TYPE_USB_TYPE_C
vs POWER_SUPPLY_TYPE_USB_PD_PPS does matter to it.

Regards,

Hans
Heikki Krogerus Nov. 27, 2017, 1:38 p.m. UTC | #4
On Sat, Nov 25, 2017 at 03:03:02PM +0100, Hans de Goede wrote:
> > Is it OK to everybody that the type of the psy is changed like that?
> > Hans?!
> > 
> > We do have drivers that already change the type, for example
> > drivers/power/supply/isp1704_charger.c, but what does the user space
> > expect? The ABI for the power supply class was never documented I
> > guess.
> 
> The main userspace consumer of the power_supply sysfs class is upower,
> upower knows about 3 types: "Mains", "Battery" and "USB", anything
> else it gives a type of UP_DEVICE_KIND_UNKNOWN. So POWER_SUPPLY_TYPE_USB_TYPE_C
> vs POWER_SUPPLY_TYPE_USB_PD_PPS does matter to it.

OK.

Thanks Hans,
Adam Thomson Nov. 27, 2017, 1:43 p.m. UTC | #5
On 25 November 2017 14:03, Hans de Goede wrote:

> Hi,

> 

> On 11/24/2017 01:19 PM, Heikki Krogerus wrote:

> > Hi,

> >

> > On Tue, Nov 14, 2017 at 11:44:47AM +0000, Adam Thomson wrote:

> >> diff --git a/drivers/usb/typec/tcpm.c b/drivers/usb/typec/tcpm.c

> >> index 78983e1..7c26c3d 100644

> >> --- a/drivers/usb/typec/tcpm.c

> >> +++ b/drivers/usb/typec/tcpm.c

> >> @@ -12,6 +12,7 @@

> >>   #include <linux/kernel.h>

> >>   #include <linux/module.h>

> >>   #include <linux/mutex.h>

> >> +#include <linux/power_supply.h>

> >>   #include <linux/proc_fs.h>

> >>   #include <linux/sched/clock.h>

> >>   #include <linux/seq_file.h>

> >> @@ -277,6 +278,10 @@ struct tcpm_port {

> >>   	u32 current_limit;

> >>   	u32 supply_voltage;

> >>

> >> +	/* Used to export TA voltage and current */

> >> +	struct power_supply *psy;

> >> +	struct power_supply_desc psy_desc;

> >> +

> >>   	u32 bist_request;

> >>

> >>   	/* PD state for Vendor Defined Messages */

> >> @@ -1756,6 +1761,7 @@ static int tcpm_pd_select_pdo(struct tcpm_port *port)

> >>   	int ret = -EINVAL;

> >>

> >>   	port->pps_data.supported = false;

> >> +	port->psy_desc.type = POWER_SUPPLY_TYPE_USB_PD;

> >>

> >>   	/*

> >>   	 * Select the source PDO providing the most power while staying within

> >> @@ -1775,8 +1781,10 @@ static int tcpm_pd_select_pdo(struct tcpm_port

> *port)

> >>   			mv = pdo_min_voltage(pdo);

> >>   			break;

> >>   		case PDO_TYPE_APDO:

> >> -			if (pdo_apdo_type(pdo) == APDO_TYPE_PPS)

> >> +			if (pdo_apdo_type(pdo) == APDO_TYPE_PPS) {

> >>   				port->pps_data.supported = true;

> >> +				port->psy_desc.type =

> POWER_SUPPLY_TYPE_USB_PD_PPS;

> >> +			}

> >>   			continue;

> >>   		default:

> >>   			tcpm_log(port, "Invalid PDO type, ignoring");

> >> @@ -2248,6 +2256,9 @@ static void tcpm_reset_port(struct tcpm_port *port)

> >>   	port->try_snk_count = 0;

> >>   	port->supply_voltage = 0;

> >>   	port->current_limit = 0;

> >> +	port->psy_desc.type = POWER_SUPPLY_TYPE_USB_TYPE_C;

> >

> > Is it OK to everybody that the type of the psy is changed like that?

> > Hans?!

> >

> > We do have drivers that already change the type, for example

> > drivers/power/supply/isp1704_charger.c, but what does the user space

> > expect? The ABI for the power supply class was never documented I

> > guess.

> 

> The main userspace consumer of the power_supply sysfs class is upower,

> upower knows about 3 types: "Mains", "Battery" and "USB", anything

> else it gives a type of UP_DEVICE_KIND_UNKNOWN. So

> POWER_SUPPLY_TYPE_USB_TYPE_C

> vs POWER_SUPPLY_TYPE_USB_PD_PPS does matter to it.


Does or doesn't matter? I had a quick look at the code for upower and if I
understand correctly right now both USB_TYPE_C and USB_PD_PPS would be
represented as 'UP_DEVICE_KIND_UNKNOWN' based on g_ascii_strcasecmp result. Is
that correct?
Heikki Krogerus Nov. 27, 2017, 2:11 p.m. UTC | #6
Hi Adam,

On Fri, Nov 24, 2017 at 02:05:27PM +0000, Adam Thomson wrote:
> On 24 November 2017 12:19, Heikki Krogerus wrote:
> > Is it OK to everybody that the type of the psy is changed like that?
> > Hans?!
> > 
> > We do have drivers that already change the type, for example
> > drivers/power/supply/isp1704_charger.c, but what does the user space
> > expect? The ABI for the power supply class was never documented I
> > guess.
> > 
> 
> Hi Heikki,
> 
> Appreciate your time in reviewing this.
> 
> Yes, I actually saw that as an example when I considered this approach. I didn't
> see anything obvious for this in the ABI documentation. Any ideas Sebastian?
> What is user-space expectation for the 'type' property of a power_supply? I
> assume having this dynamic is ok given existing drivers can already do something
> like this, but would be good to have clarification.
> 
> > I'm not against changing the type, but I think that we should have an
> > attribute file listing all supported types a psy can have if we go
> > forward with this. Ideally the type file would just list them as space
> > separated values, and show the current one with asterisk in front of
> > it. The output would be similar we have with some of the other files
> > under /sys/power, at least /sys/power/state, but that would break the
> > ABI.
> > 
> 
> I added this as I wanted the user to know what was connected rather than
> blindly trying to set the 'online' property to enable PPS, even if the attached
> source partner didn't support this. As you say, am not sure we could change the
> 'TYPE' property as that to my knowledge has always been a single string.
> 
> Maybe the addition of a 'SUPPORTED_TYPES' property or something similar could
> close this gap (as you eluded to), at least by providing a RO list of all
> supported types? Another option would be to add a type which indicates the
> supply supports multiple types, and then based on this we can read another
> property which does as you suggest with multiple strings and one being
> highlighted? Am certainly open to discussion on this.

It looks like this is USB specific problem. I think the type should
actually be just USB, and there should be an other USB only attribute
file which should list the current and supported connection types.
Well, maybe it does not even need to be USB only.


Thanks,
Adam Thomson Nov. 27, 2017, 4:54 p.m. UTC | #7
On 27 November 2017 14:12, Heikki Krogerus wrote:

> Hi Adam,
> 
> On Fri, Nov 24, 2017 at 02:05:27PM +0000, Adam Thomson wrote:
> > On 24 November 2017 12:19, Heikki Krogerus wrote:
> > > Is it OK to everybody that the type of the psy is changed like that?
> > > Hans?!
> > >
> > > We do have drivers that already change the type, for example
> > > drivers/power/supply/isp1704_charger.c, but what does the user space
> > > expect? The ABI for the power supply class was never documented I
> > > guess.
> > >
> >
> > Hi Heikki,
> >
> > Appreciate your time in reviewing this.
> >
> > Yes, I actually saw that as an example when I considered this approach. I didn't
> > see anything obvious for this in the ABI documentation. Any ideas Sebastian?
> > What is user-space expectation for the 'type' property of a power_supply? I
> > assume having this dynamic is ok given existing drivers can already do something
> > like this, but would be good to have clarification.
> >
> > > I'm not against changing the type, but I think that we should have an
> > > attribute file listing all supported types a psy can have if we go
> > > forward with this. Ideally the type file would just list them as space
> > > separated values, and show the current one with asterisk in front of
> > > it. The output would be similar we have with some of the other files
> > > under /sys/power, at least /sys/power/state, but that would break the
> > > ABI.
> > >
> >
> > I added this as I wanted the user to know what was connected rather than
> > blindly trying to set the 'online' property to enable PPS, even if the attached
> > source partner didn't support this. As you say, am not sure we could change the
> > 'TYPE' property as that to my knowledge has always been a single string.
> >
> > Maybe the addition of a 'SUPPORTED_TYPES' property or something similar could
> > close this gap (as you eluded to), at least by providing a RO list of all
> > supported types? Another option would be to add a type which indicates the
> > supply supports multiple types, and then based on this we can read another
> > property which does as you suggest with multiple strings and one being
> > highlighted? Am certainly open to discussion on this.
> 
> It looks like this is USB specific problem. I think the type should
> actually be just USB, and there should be an other USB only attribute
> file which should list the current and supported connection types.
> Well, maybe it does not even need to be USB only.

I believe in Android (BatteryMonitor) right now it maps the various USB
types that can be reported by a power_supply driver to an internal Android 'AC'
type (or 'USB' for 'PD_DRP') for use in upper level software, so they handle the
various USB reported types but simplify reporting. That code seems like it would
also handle changes in type as well because the type is re-read when dealing
with updates. So there is a wide spread user which already 'copes' with current
driver implementations. That's not saying it's necessarily the correct way to go
in the future but wanted to highlight current usage.

Personally what you're suggesting seems reasonable to me. I would however like
to get a general consensus on this as I guess this could potentially change
future power_supply driver implementations.

Sebastian, do you have any thoughts on this topic?
Heikki Krogerus Nov. 28, 2017, 11:45 a.m. UTC | #8
On Mon, Nov 27, 2017 at 04:54:08PM +0000, Adam Thomson wrote:
> On 27 November 2017 14:12, Heikki Krogerus wrote:
> 
> > Hi Adam,
> > 
> > On Fri, Nov 24, 2017 at 02:05:27PM +0000, Adam Thomson wrote:
> > > On 24 November 2017 12:19, Heikki Krogerus wrote:
> > > > Is it OK to everybody that the type of the psy is changed like that?
> > > > Hans?!
> > > >
> > > > We do have drivers that already change the type, for example
> > > > drivers/power/supply/isp1704_charger.c, but what does the user space
> > > > expect? The ABI for the power supply class was never documented I
> > > > guess.
> > > >
> > >
> > > Hi Heikki,
> > >
> > > Appreciate your time in reviewing this.
> > >
> > > Yes, I actually saw that as an example when I considered this approach. I didn't
> > > see anything obvious for this in the ABI documentation. Any ideas Sebastian?
> > > What is user-space expectation for the 'type' property of a power_supply? I
> > > assume having this dynamic is ok given existing drivers can already do something
> > > like this, but would be good to have clarification.
> > >
> > > > I'm not against changing the type, but I think that we should have an
> > > > attribute file listing all supported types a psy can have if we go
> > > > forward with this. Ideally the type file would just list them as space
> > > > separated values, and show the current one with asterisk in front of
> > > > it. The output would be similar we have with some of the other files
> > > > under /sys/power, at least /sys/power/state, but that would break the
> > > > ABI.
> > > >
> > >
> > > I added this as I wanted the user to know what was connected rather than
> > > blindly trying to set the 'online' property to enable PPS, even if the attached
> > > source partner didn't support this. As you say, am not sure we could change the
> > > 'TYPE' property as that to my knowledge has always been a single string.
> > >
> > > Maybe the addition of a 'SUPPORTED_TYPES' property or something similar could
> > > close this gap (as you eluded to), at least by providing a RO list of all
> > > supported types? Another option would be to add a type which indicates the
> > > supply supports multiple types, and then based on this we can read another
> > > property which does as you suggest with multiple strings and one being
> > > highlighted? Am certainly open to discussion on this.
> > 
> > It looks like this is USB specific problem. I think the type should
> > actually be just USB, and there should be an other USB only attribute
> > file which should list the current and supported connection types.
> > Well, maybe it does not even need to be USB only.
> 
> I believe in Android (BatteryMonitor) right now it maps the various USB
> types that can be reported by a power_supply driver to an internal Android 'AC'
> type (or 'USB' for 'PD_DRP') for use in upper level software, so they handle the
> various USB reported types but simplify reporting. That code seems like it would
> also handle changes in type as well because the type is re-read when dealing
> with updates. So there is a wide spread user which already 'copes' with current
> driver implementations. That's not saying it's necessarily the correct way to go
> in the future but wanted to highlight current usage.
> 
> Personally what you're suggesting seems reasonable to me. I would however like
> to get a general consensus on this as I guess this could potentially change
> future power_supply driver implementations.

Agreed. What ever the final solution will be, the power supply ABI
should be documented at the same time.

> Sebastian, do you have any thoughts on this topic?


Cheers,
diff mbox

Patch

diff --git a/drivers/usb/typec/Kconfig b/drivers/usb/typec/Kconfig
index 465d7da..b9cd806 100644
--- a/drivers/usb/typec/Kconfig
+++ b/drivers/usb/typec/Kconfig
@@ -8,6 +8,7 @@  config TYPEC_TCPM
 	tristate "USB Type-C Port Controller Manager"
 	depends on USB
 	select TYPEC
+	select POWER_SUPPLY
 	help
 	  The Type-C Port Controller Manager provides a USB PD and USB Type-C
 	  state machine for use with Type-C Port Controllers.
diff --git a/drivers/usb/typec/fusb302/Kconfig b/drivers/usb/typec/fusb302/Kconfig
index 48a4f2f..fce099f 100644
--- a/drivers/usb/typec/fusb302/Kconfig
+++ b/drivers/usb/typec/fusb302/Kconfig
@@ -1,6 +1,6 @@ 
 config TYPEC_FUSB302
 	tristate "Fairchild FUSB302 Type-C chip driver"
-	depends on I2C && POWER_SUPPLY
+	depends on I2C
 	help
 	  The Fairchild FUSB302 Type-C chip driver that works with
 	  Type-C Port Controller Manager to provide USB PD and USB
diff --git a/drivers/usb/typec/fusb302/fusb302.c b/drivers/usb/typec/fusb302/fusb302.c
index 72cb060..21bd075 100644
--- a/drivers/usb/typec/fusb302/fusb302.c
+++ b/drivers/usb/typec/fusb302/fusb302.c
@@ -19,7 +19,6 @@ 
 #include <linux/of_device.h>
 #include <linux/of_gpio.h>
 #include <linux/pinctrl/consumer.h>
-#include <linux/power_supply.h>
 #include <linux/proc_fs.h>
 #include <linux/regulator/consumer.h>
 #include <linux/sched/clock.h>
@@ -100,11 +99,6 @@  struct fusb302_chip {
 	/* lock for sharing chip states */
 	struct mutex lock;
 
-	/* psy + psy status */
-	struct power_supply *psy;
-	u32 current_limit;
-	u32 supply_voltage;
-
 	/* chip status */
 	enum toggling_mode toggling_mode;
 	enum src_current_status src_current_status;
@@ -873,13 +867,11 @@  static int tcpm_set_vbus(struct tcpc_dev *dev, bool on, bool charge)
 		chip->vbus_on = on;
 		fusb302_log(chip, "vbus := %s", on ? "On" : "Off");
 	}
-	if (chip->charge_on == charge) {
+	if (chip->charge_on == charge)
 		fusb302_log(chip, "charge is already %s",
 			    charge ? "On" : "Off");
-	} else {
+	else
 		chip->charge_on = charge;
-		power_supply_changed(chip->psy);
-	}
 
 done:
 	mutex_unlock(&chip->lock);
@@ -895,11 +887,6 @@  static int tcpm_set_current_limit(struct tcpc_dev *dev, u32 max_ma, u32 mv)
 	fusb302_log(chip, "current limit: %d ma, %d mv (not implemented)",
 		    max_ma, mv);
 
-	chip->supply_voltage = mv;
-	chip->current_limit = max_ma;
-
-	power_supply_changed(chip->psy);
-
 	return 0;
 }
 
@@ -1685,43 +1672,6 @@  static irqreturn_t fusb302_irq_intn(int irq, void *dev_id)
 	return IRQ_HANDLED;
 }
 
-static int fusb302_psy_get_property(struct power_supply *psy,
-				    enum power_supply_property psp,
-				    union power_supply_propval *val)
-{
-	struct fusb302_chip *chip = power_supply_get_drvdata(psy);
-
-	switch (psp) {
-	case POWER_SUPPLY_PROP_ONLINE:
-		val->intval = chip->charge_on;
-		break;
-	case POWER_SUPPLY_PROP_VOLTAGE_NOW:
-		val->intval = chip->supply_voltage * 1000; /* mV -> µV */
-		break;
-	case POWER_SUPPLY_PROP_CURRENT_MAX:
-		val->intval = chip->current_limit * 1000; /* mA -> µA */
-		break;
-	default:
-		return -ENODATA;
-	}
-
-	return 0;
-}
-
-static enum power_supply_property fusb302_psy_properties[] = {
-	POWER_SUPPLY_PROP_ONLINE,
-	POWER_SUPPLY_PROP_VOLTAGE_NOW,
-	POWER_SUPPLY_PROP_CURRENT_MAX,
-};
-
-static const struct power_supply_desc fusb302_psy_desc = {
-	.name		= "fusb302-typec-source",
-	.type		= POWER_SUPPLY_TYPE_USB_TYPE_C,
-	.properties	= fusb302_psy_properties,
-	.num_properties	= ARRAY_SIZE(fusb302_psy_properties),
-	.get_property	= fusb302_psy_get_property,
-};
-
 static int init_gpio(struct fusb302_chip *chip)
 {
 	struct device_node *node;
@@ -1761,7 +1711,6 @@  static int fusb302_probe(struct i2c_client *client,
 	struct fusb302_chip *chip;
 	struct i2c_adapter *adapter;
 	struct device *dev = &client->dev;
-	struct power_supply_config cfg = {};
 	const char *name;
 	int ret = 0;
 	u32 v;
@@ -1808,14 +1757,6 @@  static int fusb302_probe(struct i2c_client *client,
 			return -EPROBE_DEFER;
 	}
 
-	cfg.drv_data = chip;
-	chip->psy = devm_power_supply_register(dev, &fusb302_psy_desc, &cfg);
-	if (IS_ERR(chip->psy)) {
-		ret = PTR_ERR(chip->psy);
-		dev_err(chip->dev, "Error registering power-supply: %d\n", ret);
-		return ret;
-	}
-
 	ret = fusb302_debugfs_init(chip);
 	if (ret < 0)
 		return ret;
diff --git a/drivers/usb/typec/tcpm.c b/drivers/usb/typec/tcpm.c
index 78983e1..7c26c3d 100644
--- a/drivers/usb/typec/tcpm.c
+++ b/drivers/usb/typec/tcpm.c
@@ -12,6 +12,7 @@ 
 #include <linux/kernel.h>
 #include <linux/module.h>
 #include <linux/mutex.h>
+#include <linux/power_supply.h>
 #include <linux/proc_fs.h>
 #include <linux/sched/clock.h>
 #include <linux/seq_file.h>
@@ -277,6 +278,10 @@  struct tcpm_port {
 	u32 current_limit;
 	u32 supply_voltage;
 
+	/* Used to export TA voltage and current */
+	struct power_supply *psy;
+	struct power_supply_desc psy_desc;
+
 	u32 bist_request;
 
 	/* PD state for Vendor Defined Messages */
@@ -1756,6 +1761,7 @@  static int tcpm_pd_select_pdo(struct tcpm_port *port)
 	int ret = -EINVAL;
 
 	port->pps_data.supported = false;
+	port->psy_desc.type = POWER_SUPPLY_TYPE_USB_PD;
 
 	/*
 	 * Select the source PDO providing the most power while staying within
@@ -1775,8 +1781,10 @@  static int tcpm_pd_select_pdo(struct tcpm_port *port)
 			mv = pdo_min_voltage(pdo);
 			break;
 		case PDO_TYPE_APDO:
-			if (pdo_apdo_type(pdo) == APDO_TYPE_PPS)
+			if (pdo_apdo_type(pdo) == APDO_TYPE_PPS) {
 				port->pps_data.supported = true;
+				port->psy_desc.type = POWER_SUPPLY_TYPE_USB_PD_PPS;
+			}
 			continue;
 		default:
 			tcpm_log(port, "Invalid PDO type, ignoring");
@@ -2248,6 +2256,9 @@  static void tcpm_reset_port(struct tcpm_port *port)
 	port->try_snk_count = 0;
 	port->supply_voltage = 0;
 	port->current_limit = 0;
+	port->psy_desc.type = POWER_SUPPLY_TYPE_USB_TYPE_C;
+
+	power_supply_changed(port->psy);
 }
 
 static void tcpm_detach(struct tcpm_port *port)
@@ -2780,6 +2791,8 @@  static void run_state_machine(struct tcpm_port *port)
 
 		tcpm_pps_complete(port, port->pps_status);
 
+		power_supply_changed(port->psy);
+
 		break;
 
 	/* Accessory states */
@@ -3937,6 +3950,212 @@  void tcpm_update_sink_capabilities(struct tcpm_port *port, const u32 *pdo,
 }
 EXPORT_SYMBOL_GPL(tcpm_update_sink_capabilities);
 
+/* Power Supply access to expose source power information */
+enum tcpm_psy_online_states {
+	TCPM_PSY_OFFLINE = 0,
+	TCPM_PSY_FIXED_ONLINE,
+	TCPM_PSY_PROG_ONLINE,
+};
+
+static enum power_supply_property tcpm_psy_props[] = {
+	POWER_SUPPLY_PROP_ONLINE,
+	POWER_SUPPLY_PROP_VOLTAGE_MIN,
+	POWER_SUPPLY_PROP_VOLTAGE_MAX,
+	POWER_SUPPLY_PROP_VOLTAGE_NOW,
+	POWER_SUPPLY_PROP_CURRENT_MAX,
+	POWER_SUPPLY_PROP_CURRENT_NOW,
+};
+
+static int tcpm_psy_get_online(struct tcpm_port *port,
+			       union power_supply_propval *val)
+{
+	if (port->vbus_charge) {
+		if (port->pps_data.active)
+			val->intval = TCPM_PSY_PROG_ONLINE;
+		else
+			val->intval = TCPM_PSY_FIXED_ONLINE;
+	} else {
+		val->intval = TCPM_PSY_OFFLINE;
+	}
+
+	return 0;
+}
+
+static int tcpm_psy_get_voltage_min(struct tcpm_port *port,
+				    union power_supply_propval *val)
+{
+	if (port->pps_data.active)
+		val->intval = port->pps_data.min_volt * 1000;
+	else
+		val->intval = port->supply_voltage * 1000;
+
+	return 0;
+}
+
+static int tcpm_psy_get_voltage_max(struct tcpm_port *port,
+				    union power_supply_propval *val)
+{
+	if (port->pps_data.active)
+		val->intval = port->pps_data.max_volt * 1000;
+	else
+		val->intval = port->supply_voltage * 1000;
+
+	return 0;
+}
+
+static int tcpm_psy_get_voltage_now(struct tcpm_port *port,
+				    union power_supply_propval *val)
+{
+	if (port->pps_data.active)
+		val->intval = port->pps_data.out_volt * 1000;
+	else
+		val->intval = port->supply_voltage * 1000;
+
+	return 0;
+}
+
+static int tcpm_psy_get_current_max(struct tcpm_port *port,
+				    union power_supply_propval *val)
+{
+	if (port->pps_data.active)
+		val->intval = port->pps_data.max_curr * 1000;
+	else
+		val->intval = port->current_limit * 1000;
+
+	return 0;
+}
+
+static int tcpm_psy_get_current_now(struct tcpm_port *port,
+				    union power_supply_propval *val)
+{
+	if (port->pps_data.active)
+		val->intval = port->pps_data.op_curr * 1000;
+	else
+		val->intval = port->current_limit * 1000;
+
+	return 0;
+}
+
+static int tcpm_psy_get_prop(struct power_supply *psy,
+			     enum power_supply_property psp,
+			     union power_supply_propval *val)
+{
+	struct tcpm_port *port = power_supply_get_drvdata(psy);
+	int ret;
+
+	switch (psp) {
+	case POWER_SUPPLY_PROP_ONLINE:
+		ret = tcpm_psy_get_online(port, val);
+		break;
+	case POWER_SUPPLY_PROP_VOLTAGE_MIN:
+		ret = tcpm_psy_get_voltage_min(port, val);
+		break;
+	case POWER_SUPPLY_PROP_VOLTAGE_MAX:
+		ret = tcpm_psy_get_voltage_max(port, val);
+		break;
+	case POWER_SUPPLY_PROP_VOLTAGE_NOW:
+		ret = tcpm_psy_get_voltage_now(port, val);
+		break;
+	case POWER_SUPPLY_PROP_CURRENT_MAX:
+		ret = tcpm_psy_get_current_max(port, val);
+		break;
+	case POWER_SUPPLY_PROP_CURRENT_NOW:
+		ret = tcpm_psy_get_current_now(port, val);
+		break;
+	default:
+		ret = -EINVAL;
+		break;
+	}
+
+	return ret;
+}
+
+static int tcpm_psy_set_online(struct tcpm_port *port,
+			       const union power_supply_propval *val)
+{
+	int ret;
+
+	switch (val->intval) {
+	case TCPM_PSY_FIXED_ONLINE:
+		ret = tcpm_pps_activate(port, false);
+		break;
+	case TCPM_PSY_PROG_ONLINE:
+		ret = tcpm_pps_activate(port, true);
+		break;
+	default:
+		ret = -EINVAL;
+		break;
+	}
+
+	return ret;
+}
+
+static int tcpm_psy_set_prop(struct power_supply *psy,
+			     enum power_supply_property psp,
+			     const union power_supply_propval *val)
+{
+	struct tcpm_port *port = power_supply_get_drvdata(psy);
+	int ret = 0;
+
+	switch (psp) {
+	case POWER_SUPPLY_PROP_ONLINE:
+		ret = tcpm_psy_set_online(port, val);
+		break;
+	case POWER_SUPPLY_PROP_VOLTAGE_NOW:
+		if ((val->intval < (port->pps_data.min_volt * 1000)) ||
+		    (val->intval > (port->pps_data.max_volt * 1000)))
+			ret = -EINVAL;
+		else
+			ret = tcpm_pps_set_out_volt(port, (val->intval / 1000));
+		break;
+	case POWER_SUPPLY_PROP_CURRENT_NOW:
+		if (val->intval > (port->pps_data.max_curr * 1000))
+			ret = -EINVAL;
+		else
+			ret = tcpm_pps_set_op_curr(port, (val->intval / 1000));
+		break;
+	default:
+		ret = -EINVAL;
+		break;
+	}
+
+	return ret;
+}
+
+static int tcpm_psy_prop_writeable(struct power_supply *psy,
+				   enum power_supply_property psp)
+{
+	switch (psp) {
+	case POWER_SUPPLY_PROP_ONLINE:
+	case POWER_SUPPLY_PROP_VOLTAGE_NOW:
+	case POWER_SUPPLY_PROP_CURRENT_NOW:
+		return 1;
+	default:
+		return 0;
+	}
+}
+
+int devm_tcpm_psy_register(struct tcpm_port *port)
+{
+	struct power_supply_config psy_cfg = {};
+
+	psy_cfg.drv_data = port;
+	port->psy_desc.name = "tcpm-source-psy",
+	port->psy_desc.type = POWER_SUPPLY_TYPE_USB_TYPE_C,
+	port->psy_desc.properties = tcpm_psy_props,
+	port->psy_desc.num_properties = ARRAY_SIZE(tcpm_psy_props),
+	port->psy_desc.get_property = tcpm_psy_get_prop,
+	port->psy_desc.set_property = tcpm_psy_set_prop,
+	port->psy_desc.property_is_writeable = tcpm_psy_prop_writeable,
+
+	port->psy = devm_power_supply_register(port->dev, &port->psy_desc,
+					       &psy_cfg);
+	if (IS_ERR(port->psy))
+		return PTR_ERR(port->psy);
+
+	return 0;
+}
+
 struct tcpm_port *tcpm_register_port(struct device *dev, struct tcpc_dev *tcpc)
 {
 	struct tcpm_port *port;
@@ -4000,6 +4219,10 @@  struct tcpm_port *tcpm_register_port(struct device *dev, struct tcpc_dev *tcpc)
 	port->partner_desc.identity = &port->partner_ident;
 	port->port_type = tcpc->config->type;
 
+	err = devm_tcpm_psy_register(port);
+	if (err)
+		goto out_destroy_wq;
+
 	port->typec_port = typec_register_port(port->dev, &port->typec_caps);
 	if (!port->typec_port) {
 		err = -ENOMEM;