On Wed, Dec 16, 2020 at 10:20:47PM -0800, Jack Pham wrote: > From: Mayank Rana <mrana@xxxxxxxxxxxxxx> > > UCSI already conveys the information about a port's connection > status, whether it is operating in UFP or DFP mode, and whether the > partner supports USB data or not. This information can be used to > notify a dual-role controller to start up its host or peripheral > mode accordingly. Add optional support for this by querying each > port's fwnode to look for an associated USB role switch device. > If present, call usb_role_switch_set() with the determined data > role upon Connect Change or Connector Partner Change updates. > > Signed-off-by: Mayank Rana <mrana@xxxxxxxxxxxxxx> > Signed-off-by: Jack Pham <jackp@xxxxxxxxxxxxxx> Reviewed-by: Heikki Krogerus <heikki.krogerus@xxxxxxxxxxxxxxx> > --- > drivers/usb/typec/ucsi/ucsi.c | 55 +++++++++++++++++++++++++++++++++-- > drivers/usb/typec/ucsi/ucsi.h | 3 ++ > 2 files changed, 55 insertions(+), 3 deletions(-) > > diff --git a/drivers/usb/typec/ucsi/ucsi.c b/drivers/usb/typec/ucsi/ucsi.c > index 51a570d40a42..8d3e3518a506 100644 > --- a/drivers/usb/typec/ucsi/ucsi.c > +++ b/drivers/usb/typec/ucsi/ucsi.c > @@ -588,6 +588,7 @@ static void ucsi_unregister_partner(struct ucsi_connector *con) > > static void ucsi_partner_change(struct ucsi_connector *con) > { > + enum usb_role u_role = USB_ROLE_NONE; > int ret; > > if (!con->partner) > @@ -595,11 +596,14 @@ static void ucsi_partner_change(struct ucsi_connector *con) > > switch (UCSI_CONSTAT_PARTNER_TYPE(con->status.flags)) { > case UCSI_CONSTAT_PARTNER_TYPE_UFP: > - case UCSI_CONSTAT_PARTNER_TYPE_CABLE: > case UCSI_CONSTAT_PARTNER_TYPE_CABLE_AND_UFP: > + u_role = USB_ROLE_HOST; > + fallthrough; > + case UCSI_CONSTAT_PARTNER_TYPE_CABLE: > typec_set_data_role(con->port, TYPEC_HOST); > break; > case UCSI_CONSTAT_PARTNER_TYPE_DFP: > + u_role = USB_ROLE_DEVICE; > typec_set_data_role(con->port, TYPEC_DEVICE); > break; > default: > @@ -610,6 +614,15 @@ static void ucsi_partner_change(struct ucsi_connector *con) > if (!completion_done(&con->complete)) > complete(&con->complete); > > + /* Only notify USB controller if partner supports USB data */ > + if (!(UCSI_CONSTAT_PARTNER_FLAGS(con->status.flags) & UCSI_CONSTAT_PARTNER_FLAG_USB)) > + u_role = USB_ROLE_NONE; > + > + ret = usb_role_switch_set_role(con->usb_role_sw, u_role); > + if (ret) > + dev_err(con->ucsi->dev, "con:%d: failed to set usb role:%d\n", > + con->num, u_role); > + > /* Can't rely on Partner Flags field. Always checking the alt modes. */ > ret = ucsi_register_altmodes(con, UCSI_RECIPIENT_SOP); > if (ret) > @@ -626,6 +639,7 @@ static void ucsi_handle_connector_change(struct work_struct *work) > work); > struct ucsi *ucsi = con->ucsi; > enum typec_role role; > + enum usb_role u_role = USB_ROLE_NONE; > u64 command; > int ret; > > @@ -661,11 +675,14 @@ static void ucsi_handle_connector_change(struct work_struct *work) > > switch (UCSI_CONSTAT_PARTNER_TYPE(con->status.flags)) { > case UCSI_CONSTAT_PARTNER_TYPE_UFP: > - case UCSI_CONSTAT_PARTNER_TYPE_CABLE: > case UCSI_CONSTAT_PARTNER_TYPE_CABLE_AND_UFP: > + u_role = USB_ROLE_HOST; > + fallthrough; > + case UCSI_CONSTAT_PARTNER_TYPE_CABLE: > typec_set_data_role(con->port, TYPEC_HOST); > break; > case UCSI_CONSTAT_PARTNER_TYPE_DFP: > + u_role = USB_ROLE_DEVICE; > typec_set_data_role(con->port, TYPEC_DEVICE); > break; > default: > @@ -678,6 +695,16 @@ static void ucsi_handle_connector_change(struct work_struct *work) > ucsi_unregister_partner(con); > > ucsi_port_psy_changed(con); > + > + /* Only notify USB controller if partner supports USB data */ > + if (!(UCSI_CONSTAT_PARTNER_FLAGS(con->status.flags) & > + UCSI_CONSTAT_PARTNER_FLAG_USB)) > + u_role = USB_ROLE_NONE; > + > + ret = usb_role_switch_set_role(con->usb_role_sw, u_role); > + if (ret) > + dev_err(ucsi->dev, "con:%d: failed to set usb role:%d\n", > + con->num, u_role); > } > > if (con->status.change & UCSI_CONSTAT_CAM_CHANGE) { > @@ -903,6 +930,7 @@ static int ucsi_register_port(struct ucsi *ucsi, int index) > struct ucsi_connector *con = &ucsi->connector[index]; > struct typec_capability *cap = &con->typec_cap; > enum typec_accessory *accessory = cap->accessory; > + enum usb_role u_role = USB_ROLE_NONE; > u64 command; > int ret; > > @@ -981,11 +1009,14 @@ static int ucsi_register_port(struct ucsi *ucsi, int index) > > switch (UCSI_CONSTAT_PARTNER_TYPE(con->status.flags)) { > case UCSI_CONSTAT_PARTNER_TYPE_UFP: > - case UCSI_CONSTAT_PARTNER_TYPE_CABLE: > case UCSI_CONSTAT_PARTNER_TYPE_CABLE_AND_UFP: > + u_role = USB_ROLE_HOST; > + fallthrough; > + case UCSI_CONSTAT_PARTNER_TYPE_CABLE: > typec_set_data_role(con->port, TYPEC_HOST); > break; > case UCSI_CONSTAT_PARTNER_TYPE_DFP: > + u_role = USB_ROLE_DEVICE; > typec_set_data_role(con->port, TYPEC_DEVICE); > break; > default: > @@ -1001,6 +1032,24 @@ static int ucsi_register_port(struct ucsi *ucsi, int index) > ucsi_port_psy_changed(con); > } > > + con->usb_role_sw = fwnode_usb_role_switch_get(cap->fwnode); > + if (IS_ERR(con->usb_role_sw)) { > + dev_err(ucsi->dev, "con%d: failed to get usb role switch\n", > + con->num); > + con->usb_role_sw = NULL; > + } > + > + /* Only notify USB controller if partner supports USB data */ > + if (!(UCSI_CONSTAT_PARTNER_FLAGS(con->status.flags) & UCSI_CONSTAT_PARTNER_FLAG_USB)) > + u_role = USB_ROLE_NONE; > + > + ret = usb_role_switch_set_role(con->usb_role_sw, u_role); > + if (ret) { > + dev_err(ucsi->dev, "con:%d: failed to set usb role:%d\n", > + con->num, u_role); > + ret = 0; > + } > + > if (con->partner) { > ret = ucsi_register_altmodes(con, UCSI_RECIPIENT_SOP); > if (ret) { > diff --git a/drivers/usb/typec/ucsi/ucsi.h b/drivers/usb/typec/ucsi/ucsi.h > index b7a92f246050..8474342b79a7 100644 > --- a/drivers/usb/typec/ucsi/ucsi.h > +++ b/drivers/usb/typec/ucsi/ucsi.h > @@ -8,6 +8,7 @@ > #include <linux/power_supply.h> > #include <linux/types.h> > #include <linux/usb/typec.h> > +#include <linux/usb/role.h> > > /* -------------------------------------------------------------------------- */ > > @@ -329,6 +330,8 @@ struct ucsi_connector { > u32 rdo; > u32 src_pdos[UCSI_MAX_PDOS]; > int num_pdos; > + > + struct usb_role_switch *usb_role_sw; > }; > > int ucsi_send_command(struct ucsi *ucsi, u64 command, > -- > 2.24.0 thanks, -- heikki