usb: typec: tcpm: Create legacy PDOs for PD2 connection

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.

Reviewed-by: Guenter Roeck <linux@roeckus.net>
Signed-off-by: Kyle Tso <kyletso@google.com>
Link: https://lore.kernel.org/r/20210115163311.391332-1-kyletso@google.com
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
This commit is contained in:
Kyle Tso 2021-01-16 00:33:11 +08:00 committed by Greg Kroah-Hartman
parent 1d6a81519d
commit f75a1025c0
2 changed files with 53 additions and 10 deletions

View File

@ -1023,13 +1023,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,
@ -1042,10 +1076,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);
}
@ -1053,10 +1085,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,
@ -1069,10 +1113,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);
}

View File

@ -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 */