On 1/15/21 8:33 AM, Kyle Tso wrote: > If the port partner is PD2, the PDOs of the local port should follow the > format defined in PD2 Spec. Dynamically modify the pre-defined PD3 PDOs > and transform them into PD2 format before sending them to the PD2 port > partner. > > Signed-off-by: Kyle Tso <kyletso@xxxxxxxxxx> I don't like this too much, but I don't have a better idea. Thus, Reviewed-by: Guenter Roeck <linux@xxxxxxxxxxx> Guenter > --- > drivers/usb/typec/tcpm/tcpm.c | 62 +++++++++++++++++++++++++++++------ > include/linux/usb/pd.h | 1 + > 2 files changed, 53 insertions(+), 10 deletions(-) > > diff --git a/drivers/usb/typec/tcpm/tcpm.c b/drivers/usb/typec/tcpm/tcpm.c > index 22a85b396f69..1220ab1ed47d 100644 > --- a/drivers/usb/typec/tcpm/tcpm.c > +++ b/drivers/usb/typec/tcpm/tcpm.c > @@ -911,13 +911,47 @@ static int tcpm_set_pwr_role(struct tcpm_port *port, enum typec_role role) > return 0; > } > > +/* > + * Transform the PDO to be compliant to PD rev2.0. > + * Return 0 if the PDO type is not defined in PD rev2.0. > + * Otherwise, return the converted PDO. > + */ > +static u32 tcpm_forge_legacy_pdo(struct tcpm_port *port, u32 pdo, enum typec_role role) > +{ > + switch (pdo_type(pdo)) { > + case PDO_TYPE_FIXED: > + if (role == TYPEC_SINK) > + return pdo & ~PDO_FIXED_FRS_CURR_MASK; > + else > + return pdo & ~PDO_FIXED_UNCHUNK_EXT; > + case PDO_TYPE_VAR: > + case PDO_TYPE_BATT: > + return pdo; > + case PDO_TYPE_APDO: > + default: > + return 0; > + } > +} > + > static int tcpm_pd_send_source_caps(struct tcpm_port *port) > { > struct pd_message msg; > - int i; > + u32 pdo; > + unsigned int i, nr_pdo = 0; > > memset(&msg, 0, sizeof(msg)); > - if (!port->nr_src_pdo) { > + > + for (i = 0; i < port->nr_src_pdo; i++) { > + if (port->negotiated_rev >= PD_REV30) { > + msg.payload[nr_pdo++] = cpu_to_le32(port->src_pdo[i]); > + } else { > + pdo = tcpm_forge_legacy_pdo(port, port->src_pdo[i], TYPEC_SOURCE); > + if (pdo) > + msg.payload[nr_pdo++] = cpu_to_le32(pdo); > + } > + } > + > + if (!nr_pdo) { > /* No source capabilities defined, sink only */ > msg.header = PD_HEADER_LE(PD_CTRL_REJECT, > port->pwr_role, > @@ -930,10 +964,8 @@ static int tcpm_pd_send_source_caps(struct tcpm_port *port) > port->data_role, > port->negotiated_rev, > port->message_id, > - port->nr_src_pdo); > + nr_pdo); > } > - for (i = 0; i < port->nr_src_pdo; i++) > - msg.payload[i] = cpu_to_le32(port->src_pdo[i]); > > return tcpm_pd_transmit(port, TCPC_TX_SOP, &msg); > } > @@ -941,10 +973,22 @@ static int tcpm_pd_send_source_caps(struct tcpm_port *port) > static int tcpm_pd_send_sink_caps(struct tcpm_port *port) > { > struct pd_message msg; > - int i; > + u32 pdo; > + unsigned int i, nr_pdo = 0; > > memset(&msg, 0, sizeof(msg)); > - if (!port->nr_snk_pdo) { > + > + for (i = 0; i < port->nr_snk_pdo; i++) { > + if (port->negotiated_rev >= PD_REV30) { > + msg.payload[nr_pdo++] = cpu_to_le32(port->snk_pdo[i]); > + } else { > + pdo = tcpm_forge_legacy_pdo(port, port->snk_pdo[i], TYPEC_SINK); > + if (pdo) > + msg.payload[nr_pdo++] = cpu_to_le32(pdo); > + } > + } > + > + if (!nr_pdo) { > /* No sink capabilities defined, source only */ > msg.header = PD_HEADER_LE(PD_CTRL_REJECT, > port->pwr_role, > @@ -957,10 +1001,8 @@ static int tcpm_pd_send_sink_caps(struct tcpm_port *port) > port->data_role, > port->negotiated_rev, > port->message_id, > - port->nr_snk_pdo); > + nr_pdo); > } > - for (i = 0; i < port->nr_snk_pdo; i++) > - msg.payload[i] = cpu_to_le32(port->snk_pdo[i]); > > return tcpm_pd_transmit(port, TCPC_TX_SOP, &msg); > } > diff --git a/include/linux/usb/pd.h b/include/linux/usb/pd.h > index bb9a782e1411..88f64bce5dea 100644 > --- a/include/linux/usb/pd.h > +++ b/include/linux/usb/pd.h > @@ -225,6 +225,7 @@ enum pd_pdo_type { > #define PDO_FIXED_EXTPOWER BIT(27) /* Externally powered */ > #define PDO_FIXED_USB_COMM BIT(26) /* USB communications capable */ > #define PDO_FIXED_DATA_SWAP BIT(25) /* Data role swap supported */ > +#define PDO_FIXED_UNCHUNK_EXT BIT(24) /* Unchunked Extended Message supported (Source) */ > #define PDO_FIXED_FRS_CURR_MASK (BIT(24) | BIT(23)) /* FR_Swap Current (Sink) */ > #define PDO_FIXED_FRS_CURR_SHIFT 23 > #define PDO_FIXED_VOLT_SHIFT 10 /* 50mV units */ >