Re: [PATCH v0 2/2] typec: Add Type-C charger

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On Tue, Apr 21, 2020 at 6:41 AM Heikki Krogerus
<heikki.krogerus@xxxxxxxxxxxxxxx> wrote:
>
> On Tue, Apr 21, 2020 at 08:44:38AM +0000, Adam Thomson wrote:
> > On 20 April 2020 17:37, Mathew King wrote:
> >
> > > Add an option to expose USB Type-C ports that can charge system
> > > batteries as a power_supply class. This implementation only exposes
> > > three properties of the power supply.
> > >
> > > POWER_SUPPLY_PROP_ONLINE - Set to true if the Type-C port is configured
> > >                            as a sink and is connected to a partner
> > > POWER_SUPPLY_PROP_STATUS - Set to CHARGING if a partner is connected and
> > >                            the port is a sink and set to NOT_CHARGING
> > >                            otherwise
> > > POWER_SUPPLY_PROP_USB_TYPE - When a partner is conneced set to TYPE_C,
> > >                              TYPE_PD, or TYPE_PD_DRP depending on the
> > >                              partner capibilities and set to
> > >                              TYPE_UNKNOWN otherwise
> > >
> > > This implementation can be expanded as the typec class is expaneded. In
> > > particular the STATUS property should show more than CHARGING and
> > > NOT_CHARGING. Also properties like VOLTAGE and CURRENT can be added
> > > when
> > > the typec class supports getting PDOs.
> >
> > Hmm, this functionally looks almost exactly like code already available in TCPM,
> > except a much smaller subset. This looks like it would duplicate that work so as
> > it stands doesn't feel sensible to me. It may be that the work in TCPM needs
> > refactoring, but I don't believe the two should coexist.
>
> I agree. We can't register a psy for every port and partner
> unconditionally like that.
>
> I do think that for the sake of uniformity it would make sense to have
> the Type-C subsystem supply API that the Type-C drivers can use for
> registering the psy instead of every Type-C driver doing that directly
> with the psy API. So this patch should first introduce that API
> without doing anything automatically.
>
> Once things settle down, we can consider taking care of the psy
> registration for the drivers as well.
>
> Ideally the psy(s) registered here would supply the same information
> and functionality as the psy registered in TCPM. Then a separate patch
> that follows (that is part of the series) could simply convert TCPM to
> use this new API for registering the psy.
>
> thanks,

Thank you for the feedback Adam and Heikki. I will work on improving
the port API so that the psy is not created unconditionally and I will
work on getting to parity with the TCPM psy so that it can be switched
to this new method.

>
> > >
> > > Signed-off-by: Mathew King <mathewk@xxxxxxxxxxxx>
> > > ---
> > >  drivers/usb/typec/Kconfig   |  11 ++
> > >  drivers/usb/typec/Makefile  |   1 +
> > >  drivers/usb/typec/charger.c | 204
> > > ++++++++++++++++++++++++++++++++++++
> > >  drivers/usb/typec/charger.h |  33 ++++++
> > >  drivers/usb/typec/class.c   |  48 +++++++--
> > >  drivers/usb/typec/class.h   |   2 +
> > >  6 files changed, 290 insertions(+), 9 deletions(-)
> > >  create mode 100644 drivers/usb/typec/charger.c
> > >  create mode 100644 drivers/usb/typec/charger.h
> > >
> > > diff --git a/drivers/usb/typec/Kconfig b/drivers/usb/typec/Kconfig
> > > index b4f2aac7ae8a..1040c990cb7e 100644
> > > --- a/drivers/usb/typec/Kconfig
> > > +++ b/drivers/usb/typec/Kconfig
> > > @@ -46,6 +46,17 @@ menuconfig TYPEC
> > >
> > >  if TYPEC
> > >
> > > +config TYPEC_CHARGER
> > > +   bool "Type-C Power Supply support"
> > > +   depends on POWER_SUPPLY
> > > +   help
> > > +     Say Y here to enable Type-C charging ports to be exposed as a power
> > > +     supply class.
> > > +
> > > +     If you choose this option Type-C charger support will be built into
> > > +     the typec driver. This will expose all Type-C ports as a power_supply
> > > +     class.
> > > +
> > >  source "drivers/usb/typec/tcpm/Kconfig"
> > >
> > >  source "drivers/usb/typec/ucsi/Kconfig"
> > > diff --git a/drivers/usb/typec/Makefile b/drivers/usb/typec/Makefile
> > > index 7753a5c3cd46..6fc5424761a1 100644
> > > --- a/drivers/usb/typec/Makefile
> > > +++ b/drivers/usb/typec/Makefile
> > > @@ -1,6 +1,7 @@
> > >  # SPDX-License-Identifier: GPL-2.0
> > >  obj-$(CONFIG_TYPEC)                += typec.o
> > >  typec-y                            := class.o mux.o bus.o
> > > +typec-$(CONFIG_TYPEC_CHARGER)      += charger.o
> > >  obj-$(CONFIG_TYPEC)                += altmodes/
> > >  obj-$(CONFIG_TYPEC_TCPM)   += tcpm/
> > >  obj-$(CONFIG_TYPEC_UCSI)   += ucsi/
> > > diff --git a/drivers/usb/typec/charger.c b/drivers/usb/typec/charger.c
> > > new file mode 100644
> > > index 000000000000..07c3cd065be8
> > > --- /dev/null
> > > +++ b/drivers/usb/typec/charger.c
> > > @@ -0,0 +1,204 @@
> > > +// SPDX-License-Identifier: GPL-2.0
> > > +/*
> > > + * USB Type-C Charger Class
> > > + *
> > > + * Copyright (C) 2020, Google LLC
> > > + * Author: Mathew King <mathewk@xxxxxxxxxx>
> > > + */
> > > +
> > > +#include <linux/slab.h>
> > > +
> > > +#include "charger.h"
> > > +#include "class.h"
> > > +
> > > +static enum power_supply_property typec_charger_props[] = {
> > > +   POWER_SUPPLY_PROP_ONLINE,
> > > +   POWER_SUPPLY_PROP_STATUS,
> > > +   POWER_SUPPLY_PROP_USB_TYPE
> > > +};
> > > +
> > > +static enum power_supply_usb_type typec_charger_usb_types[] = {
> > > +   POWER_SUPPLY_USB_TYPE_UNKNOWN,
> > > +   POWER_SUPPLY_USB_TYPE_C,
> > > +   POWER_SUPPLY_USB_TYPE_PD,
> > > +   POWER_SUPPLY_USB_TYPE_PD_DRP,
> > > +};
> > > +
> > > +static int typec_charger_get_prop(struct power_supply *psy,
> > > +                             enum power_supply_property psp,
> > > +                             union power_supply_propval *val)
> > > +{
> > > +   struct typec_charger *charger = power_supply_get_drvdata(psy);
> > > +
> > > +   switch (psp) {
> > > +   case POWER_SUPPLY_PROP_ONLINE:
> > > +           val->intval = charger->psy_online;
> > > +           break;
> > > +   case POWER_SUPPLY_PROP_STATUS:
> > > +           val->intval = charger->psy_status;
> > > +           break;
> > > +   case POWER_SUPPLY_PROP_USB_TYPE:
> > > +           val->intval = charger->psy_usb_type;
> > > +           break;
> > > +   default:
> > > +           return -EINVAL;
> > > +   }
> > > +
> > > +   return 0;
> > > +}
> > > +
> > > +static int typec_charger_set_prop(struct power_supply *psy,
> > > +                             enum power_supply_property psp,
> > > +                             const union power_supply_propval *val)
> > > +{
> > > +   return -EINVAL;
> > > +}
> > > +
> > > +static int typec_charger_is_writeable(struct power_supply *psy,
> > > +                                 enum power_supply_property psp)
> > > +{
> > > +   return 0;
> > > +}
> > > +
> > > +/**
> > > + * typec_charger_changed - Notify of a Type-C charger change
> > > + * @charger: Type-C charger that changed
> > > + *
> > > + * Notifies the Type-C charger that one or more of its attributes may have
> > > + * changed.
> > > + */
> > > +void typec_charger_changed(struct typec_charger *charger)
> > > +{
> > > +   int last_psy_status, last_psy_usb_type, last_psy_online;
> > > +
> > > +   last_psy_online = charger->psy_online;
> > > +   last_psy_status = charger->psy_status;
> > > +   last_psy_usb_type = charger->psy_usb_type;
> > > +
> > > +   if (!charger->partner) {
> > > +           charger->psy_usb_type =
> > > POWER_SUPPLY_USB_TYPE_UNKNOWN;
> > > +           charger->psy_online = 0;
> > > +           charger->psy_status =
> > > POWER_SUPPLY_STATUS_NOT_CHARGING;
> > > +           goto out_notify;
> > > +   }
> > > +
> > > +   if (charger->port->pwr_role == TYPEC_SOURCE) {
> > > +           charger->psy_online = 0;
> > > +           charger->psy_status =
> > > POWER_SUPPLY_STATUS_NOT_CHARGING;
> > > +           if (charger->partner->usb_pd)
> > > +                   charger->psy_usb_type =
> > > POWER_SUPPLY_USB_TYPE_PD_DRP;
> > > +           else
> > > +                   charger->psy_usb_type =
> > > POWER_SUPPLY_USB_TYPE_UNKNOWN;
> > > +
> > > +           goto out_notify;
> > > +   }
> > > +
> > > +   charger->psy_online = 1;
> > > +   charger->psy_status = POWER_SUPPLY_STATUS_CHARGING;
> > > +
> > > +   if (charger->partner->usb_pd)
> > > +           charger->psy_usb_type = POWER_SUPPLY_USB_TYPE_PD;
> > > +   else
> > > +           charger->psy_usb_type = POWER_SUPPLY_USB_TYPE_C;
> > > +
> > > +out_notify:
> > > +   if (last_psy_usb_type != charger->psy_usb_type ||
> > > +       last_psy_status != charger->psy_status ||
> > > +       last_psy_online != charger->psy_online)
> > > +           power_supply_changed(charger->psy);
> > > +}
> > > +EXPORT_SYMBOL_GPL(typec_charger_changed);
> > > +
> > > +/**
> > > + * typec_register_charger - Register a USB Type-C Charger
> > > + * @port: Type-C port to register as a charger
> > > + *
> > > + * Registers a Type-C port as a charger.
> > > + *
> > > + * Returns handle to the charger on success or ERR_PTR on failure.
> > > + */
> > > +struct typec_charger *typec_register_charger(struct typec_port *port)
> > > +{
> > > +   struct power_supply_config psy_cfg = {};
> > > +   struct typec_charger *charger;
> > > +   struct power_supply *psy;
> > > +
> > > +   charger = kzalloc(sizeof(struct typec_charger), GFP_KERNEL);
> > > +   if (!port)
> > > +           return ERR_PTR(-ENOMEM);
> > > +
> > > +   charger->port = port;
> > > +   sprintf(charger->name, TYPEC_CHARGER_DIR_NAME, port->id);
> > > +   charger->psy_usb_type = POWER_SUPPLY_USB_TYPE_UNKNOWN;
> > > +   charger->psy_online = 0;
> > > +   charger->psy_status = POWER_SUPPLY_STATUS_NOT_CHARGING;
> > > +
> > > +   charger->psy_desc.name = charger->name;
> > > +   charger->psy_desc.type = POWER_SUPPLY_TYPE_USB;
> > > +   charger->psy_desc.get_property = typec_charger_get_prop;
> > > +   charger->psy_desc.set_property = typec_charger_set_prop;
> > > +   charger->psy_desc.property_is_writeable =
> > > +           typec_charger_is_writeable;
> > > +   charger->psy_desc.properties = typec_charger_props;
> > > +   charger->psy_desc.num_properties =
> > > +                           ARRAY_SIZE(typec_charger_props);
> > > +   charger->psy_desc.usb_types = typec_charger_usb_types;
> > > +   charger->psy_desc.num_usb_types =
> > > +                   ARRAY_SIZE(typec_charger_usb_types);
> > > +   psy_cfg.drv_data = charger;
> > > +
> > > +   psy = devm_power_supply_register_no_ws(&port->dev, &charger-
> > > >psy_desc,
> > > +                                          &psy_cfg);
> > > +   if (IS_ERR(psy)) {
> > > +           dev_err(&port->dev, "Failed to register Type-C power
> > > supply\n");
> > > +           return ERR_CAST(psy);
> > > +   }
> > > +   charger->psy = psy;
> > > +
> > > +   return charger;
> > > +}
> > > +EXPORT_SYMBOL_GPL(typec_register_charger);
> > > +
> > > +/**
> > > + * typec_unregister_charger - Unregister a USB Type-C Charger
> > > + * @charger: The charger to unregister
> > > + *
> > > + * Unregisters a charger created with typec_register_charger().
> > > + */
> > > +void typec_unregister_charger(struct typec_charger *charger)
> > > +{
> > > +   if (!IS_ERR_OR_NULL(charger))
> > > +           kfree(charger);
> > > +}
> > > +EXPORT_SYMBOL_GPL(typec_unregister_charger);
> > > +
> > > +/**
> > > + * typec_charger_register_partner - Register a partner with a USB Type-C
> > > Charger
> > > + * @charger: The charger to add the partner too
> > > + * @partner: The partner to add
> > > + *
> > > + * Add a partner to a Type-C charger to indicate that the partner is connected
> > > + * and may be charging.
> > > + */
> > > +void typec_charger_register_partner(struct typec_charger *charger,
> > > +                               struct typec_partner *partner)
> > > +{
> > > +   charger->partner = partner;
> > > +   typec_charger_changed(charger);
> > > +}
> > > +EXPORT_SYMBOL_GPL(typec_charger_register_partner);
> > > +
> > > +/**
> > > + * typec_charger_unregister_partner - Unregister a USB Type-C Charger partner
> > > + * @charger: The charger to remove the partner from
> > > + *
> > > + * Remove partner added with typec_charger_register_partner().
> > > + */
> > > +void typec_charger_unregister_partner(struct typec_charger *charger)
> > > +{
> > > +   if (!IS_ERR_OR_NULL(charger))
> > > +           charger->partner = NULL;
> > > +
> > > +   typec_charger_changed(charger);
> > > +}
> > > +EXPORT_SYMBOL_GPL(typec_charger_unregister_partner);
> > > diff --git a/drivers/usb/typec/charger.h b/drivers/usb/typec/charger.h
> > > new file mode 100644
> > > index 000000000000..32cdaa7c1a83
> > > --- /dev/null
> > > +++ b/drivers/usb/typec/charger.h
> > > @@ -0,0 +1,33 @@
> > > +/* SPDX-License-Identifier: GPL-2.0 */
> > > +
> > > +#ifndef __USB_TYPEC_CHARGER_H__
> > > +#define __USB_TYPEC_CHARGER_H__
> > > +
> > > +#include <linux/power_supply.h>
> > > +#include <linux/usb/typec.h>
> > > +
> > > +#include "class.h"
> > > +
> > > +#define TYPEC_CHARGER_DIR_NAME
> > >     "TYPEC_CHARGER%d"
> > > +#define TYPEC_CHARGER_DIR_NAME_LENGTH
> > >     sizeof(TYPEC_CHARGER_DIR_NAME)
> > > +
> > > +struct typec_charger {
> > > +   struct typec_port *port;
> > > +   struct typec_partner *partner;
> > > +   char name[TYPEC_CHARGER_DIR_NAME_LENGTH];
> > > +   struct power_supply *psy;
> > > +   struct power_supply_desc psy_desc;
> > > +   int psy_usb_type;
> > > +   int psy_online;
> > > +   int psy_status;
> > > +};
> > > +
> > > +struct typec_charger *typec_register_charger(struct typec_port *port);
> > > +void typec_unregister_charger(struct typec_charger *charger);
> > > +
> > > +void typec_charger_register_partner(struct typec_charger *charger,
> > > +                               struct typec_partner *partner);
> > > +void typec_charger_unregister_partner(struct typec_charger *charger);
> > > +void typec_charger_changed(struct typec_charger *charger);
> > > +
> > > +#endif /* __USB_TYPEC_CHARGER_H__ */
> > > diff --git a/drivers/usb/typec/class.c b/drivers/usb/typec/class.c
> > > index 9a1fdce137b9..1542d3af342c 100644
> > > --- a/drivers/usb/typec/class.c
> > > +++ b/drivers/usb/typec/class.c
> > > @@ -13,6 +13,7 @@
> > >  #include <linux/slab.h>
> > >
> > >  #include "bus.h"
> > > +#include "charger.h"
> > >  #include "class.h"
> > >
> > >  static DEFINE_IDA(typec_index_ida);
> > > @@ -489,6 +490,12 @@ static void typec_partner_release(struct device *dev)
> > >  {
> > >     struct typec_partner *partner = to_typec_partner(dev);
> > >
> > > +   if (IS_ENABLED(CONFIG_TYPEC_CHARGER)) {
> > > +           struct typec_port *port = to_typec_port(dev->parent);
> > > +
> > > +           typec_charger_unregister_partner(port->charger);
> > > +   }
> > > +
> > >     ida_destroy(&partner->mode_ids);
> > >     kfree(partner);
> > >  }
> > > @@ -580,6 +587,10 @@ struct typec_partner *typec_register_partner(struct
> > > typec_port *port,
> > >             return ERR_PTR(ret);
> > >     }
> > >
> > > +   if (IS_ENABLED(CONFIG_TYPEC_CHARGER) && port->charger) {
> > > +           typec_charger_register_partner(port->charger, partner);
> > > +   }
> > > +
> > >     return partner;
> > >  }
> > >  EXPORT_SYMBOL_GPL(typec_register_partner);
> > > @@ -1283,6 +1294,9 @@ static void typec_release(struct device *dev)
> > >  {
> > >     struct typec_port *port = to_typec_port(dev);
> > >
> > > +   if (IS_ENABLED(CONFIG_TYPEC_CHARGER))
> > > +           typec_unregister_charger(port->charger);
> > > +
> > >     ida_simple_remove(&typec_index_ida, port->id);
> > >     ida_destroy(&port->mode_ids);
> > >     typec_switch_put(port->sw);
> > > @@ -1564,7 +1578,8 @@ struct typec_port *typec_register_port(struct device
> > > *parent,
> > >     id = ida_simple_get(&typec_index_ida, 0, 0, GFP_KERNEL);
> > >     if (id < 0) {
> > >             kfree(port);
> > > -           return ERR_PTR(id);
> > > +           ret = id;
> > > +           goto err_return;
> > >     }
> > >
> > >     switch (cap->type) {
> > > @@ -1617,32 +1632,47 @@ struct typec_port *typec_register_port(struct device
> > > *parent,
> > >
> > >     port->cap = kmemdup(cap, sizeof(*cap), GFP_KERNEL);
> > >     if (!port->cap) {
> > > -           put_device(&port->dev);
> > > -           return ERR_PTR(-ENOMEM);
> > > +           ret = -ENOMEM;
> > > +           goto err_put_device;
> > >     }
> > >
> > >     port->sw = typec_switch_get(&port->dev);
> > >     if (IS_ERR(port->sw)) {
> > >             ret = PTR_ERR(port->sw);
> > > -           put_device(&port->dev);
> > > -           return ERR_PTR(ret);
> > > +           goto err_put_device;
> > >     }
> > >
> > >     port->mux = typec_mux_get(&port->dev, NULL);
> > >     if (IS_ERR(port->mux)) {
> > >             ret = PTR_ERR(port->mux);
> > > -           put_device(&port->dev);
> > > -           return ERR_PTR(ret);
> > > +           goto err_put_device;
> > >     }
> > >
> > >     ret = device_add(&port->dev);
> > >     if (ret) {
> > >             dev_err(parent, "failed to register port (%d)\n", ret);
> > > -           put_device(&port->dev);
> > > -           return ERR_PTR(ret);
> > > +           goto err_put_device;
> > > +   }
> > > +
> > > +   if (IS_ENABLED(CONFIG_TYPEC_CHARGER)) {
> > > +           port->charger = typec_register_charger(port);
> > > +
> > > +           if (IS_ERR(port->charger)) {
> > > +                   ret = PTR_ERR(port->charger);
> > > +                   goto err_device_del;
> > > +           }
> > >     }
> > >
> > >     return port;
> > > +
> > > +err_device_del:
> > > +   device_del(&port->dev);
> > > +
> > > +err_put_device:
> > > +   put_device(&port->dev);
> > > +
> > > +err_return:
> > > +   return ERR_PTR(ret);
> > >  }
> > >  EXPORT_SYMBOL_GPL(typec_register_port);
> > >
> > > diff --git a/drivers/usb/typec/class.h b/drivers/usb/typec/class.h
> > > index ec933dfe1323..0ff0a590d316 100644
> > > --- a/drivers/usb/typec/class.h
> > > +++ b/drivers/usb/typec/class.h
> > > @@ -41,6 +41,8 @@ struct typec_port {
> > >     struct typec_switch             *sw;
> > >     struct typec_mux                *mux;
> > >
> > > +   struct typec_charger            *charger;
> > > +
> > >     const struct typec_capability   *cap;
> > >     const struct typec_operations   *ops;
> > >  };
> > > --
> > > 2.26.1.301.g55bc3eb7cb9-goog
>
> --
> heikki



[Index of Archives]     [Linux Media]     [Linux Input]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]     [Old Linux USB Devel Archive]

  Powered by Linux