Commit 8203d269 authored by Heikki Krogerus's avatar Heikki Krogerus Committed by Greg Kroah-Hartman

usb: typec: tcpm: Register USB Power Delivery Capabilities

Register both the port and partner USB Power Delivery
Capabilities so they are exposed to the user space.
Signed-off-by: default avatarHeikki Krogerus <heikki.krogerus@linux.intel.com>
Link: https://lore.kernel.org/r/20220502132058.86236-4-heikki.krogerus@linux.intel.comSigned-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent a7cff92f
...@@ -394,6 +394,14 @@ struct tcpm_port { ...@@ -394,6 +394,14 @@ struct tcpm_port {
bool explicit_contract; bool explicit_contract;
unsigned int rx_msgid; unsigned int rx_msgid;
/* USB PD objects */
struct usb_power_delivery *pd;
struct usb_power_delivery_capabilities *port_source_caps;
struct usb_power_delivery_capabilities *port_sink_caps;
struct usb_power_delivery *partner_pd;
struct usb_power_delivery_capabilities *partner_source_caps;
struct usb_power_delivery_capabilities *partner_sink_caps;
/* Partner capabilities/requests */ /* Partner capabilities/requests */
u32 sink_request; u32 sink_request;
u32 source_caps[PDO_MAX_OBJECTS]; u32 source_caps[PDO_MAX_OBJECTS];
...@@ -2352,6 +2360,52 @@ static void tcpm_pd_handle_msg(struct tcpm_port *port, ...@@ -2352,6 +2360,52 @@ static void tcpm_pd_handle_msg(struct tcpm_port *port,
} }
} }
static int tcpm_register_source_caps(struct tcpm_port *port)
{
struct usb_power_delivery_desc desc = { port->negotiated_rev };
struct usb_power_delivery_capabilities_desc caps = { };
struct usb_power_delivery_capabilities *cap;
if (!port->partner_pd)
port->partner_pd = usb_power_delivery_register(NULL, &desc);
if (IS_ERR(port->partner_pd))
return PTR_ERR(port->partner_pd);
memcpy(caps.pdo, port->source_caps, sizeof(u32) * port->nr_source_caps);
caps.role = TYPEC_SOURCE;
cap = usb_power_delivery_register_capabilities(port->partner_pd, &caps);
if (IS_ERR(cap))
return PTR_ERR(cap);
port->partner_source_caps = cap;
return 0;
}
static int tcpm_register_sink_caps(struct tcpm_port *port)
{
struct usb_power_delivery_desc desc = { port->negotiated_rev };
struct usb_power_delivery_capabilities_desc caps = { };
struct usb_power_delivery_capabilities *cap;
if (!port->partner_pd)
port->partner_pd = usb_power_delivery_register(NULL, &desc);
if (IS_ERR(port->partner_pd))
return PTR_ERR(port->partner_pd);
memcpy(caps.pdo, port->sink_caps, sizeof(u32) * port->nr_sink_caps);
caps.role = TYPEC_SINK;
cap = usb_power_delivery_register_capabilities(port->partner_pd, &caps);
if (IS_ERR(cap))
return PTR_ERR(cap);
port->partner_sink_caps = cap;
return 0;
}
static void tcpm_pd_data_request(struct tcpm_port *port, static void tcpm_pd_data_request(struct tcpm_port *port,
const struct pd_message *msg) const struct pd_message *msg)
{ {
...@@ -2381,6 +2435,8 @@ static void tcpm_pd_data_request(struct tcpm_port *port, ...@@ -2381,6 +2435,8 @@ static void tcpm_pd_data_request(struct tcpm_port *port,
tcpm_validate_caps(port, port->source_caps, tcpm_validate_caps(port, port->source_caps,
port->nr_source_caps); port->nr_source_caps);
tcpm_register_source_caps(port);
/* /*
* Adjust revision in subsequent message headers, as required, * Adjust revision in subsequent message headers, as required,
* to comply with 6.2.1.1.5 of the USB PD 3.0 spec. We don't * to comply with 6.2.1.1.5 of the USB PD 3.0 spec. We don't
...@@ -2488,6 +2544,8 @@ static void tcpm_pd_data_request(struct tcpm_port *port, ...@@ -2488,6 +2544,8 @@ static void tcpm_pd_data_request(struct tcpm_port *port,
port->nr_sink_caps = cnt; port->nr_sink_caps = cnt;
port->sink_cap_done = true; port->sink_cap_done = true;
tcpm_register_sink_caps(port);
if (port->ams == GET_SINK_CAPABILITIES) if (port->ams == GET_SINK_CAPABILITIES)
tcpm_set_state(port, ready_state(port), 0); tcpm_set_state(port, ready_state(port), 0);
/* Unexpected Sink Capabilities */ /* Unexpected Sink Capabilities */
...@@ -3554,6 +3612,7 @@ static void tcpm_typec_connect(struct tcpm_port *port) ...@@ -3554,6 +3612,7 @@ static void tcpm_typec_connect(struct tcpm_port *port)
port->partner = typec_register_partner(port->typec_port, port->partner = typec_register_partner(port->typec_port,
&port->partner_desc); &port->partner_desc);
port->connected = true; port->connected = true;
typec_partner_set_usb_power_delivery(port->partner, port->partner_pd);
} }
} }
...@@ -3622,6 +3681,7 @@ static int tcpm_src_attach(struct tcpm_port *port) ...@@ -3622,6 +3681,7 @@ static int tcpm_src_attach(struct tcpm_port *port)
static void tcpm_typec_disconnect(struct tcpm_port *port) static void tcpm_typec_disconnect(struct tcpm_port *port)
{ {
if (port->connected) { if (port->connected) {
typec_partner_set_usb_power_delivery(port->partner, NULL);
typec_unregister_partner(port->partner); typec_unregister_partner(port->partner);
port->partner = NULL; port->partner = NULL;
port->connected = false; port->connected = false;
...@@ -3684,6 +3744,13 @@ static void tcpm_reset_port(struct tcpm_port *port) ...@@ -3684,6 +3744,13 @@ static void tcpm_reset_port(struct tcpm_port *port)
port->sink_cap_done = false; port->sink_cap_done = false;
if (port->tcpc->enable_frs) if (port->tcpc->enable_frs)
port->tcpc->enable_frs(port->tcpc, false); port->tcpc->enable_frs(port->tcpc, false);
usb_power_delivery_unregister_capabilities(port->partner_sink_caps);
port->partner_sink_caps = NULL;
usb_power_delivery_unregister_capabilities(port->partner_source_caps);
port->partner_source_caps = NULL;
usb_power_delivery_unregister(port->partner_pd);
port->partner_pd = NULL;
} }
static void tcpm_detach(struct tcpm_port *port) static void tcpm_detach(struct tcpm_port *port)
...@@ -5924,6 +5991,68 @@ void tcpm_tcpc_reset(struct tcpm_port *port) ...@@ -5924,6 +5991,68 @@ void tcpm_tcpc_reset(struct tcpm_port *port)
} }
EXPORT_SYMBOL_GPL(tcpm_tcpc_reset); EXPORT_SYMBOL_GPL(tcpm_tcpc_reset);
static void tcpm_port_unregister_pd(struct tcpm_port *port)
{
usb_power_delivery_unregister_capabilities(port->port_sink_caps);
port->port_sink_caps = NULL;
usb_power_delivery_unregister_capabilities(port->port_source_caps);
port->port_source_caps = NULL;
usb_power_delivery_unregister(port->pd);
port->pd = NULL;
}
static int tcpm_port_register_pd(struct tcpm_port *port)
{
struct usb_power_delivery_desc desc = { port->typec_caps.pd_revision };
struct usb_power_delivery_capabilities_desc caps = { };
struct usb_power_delivery_capabilities *cap;
int ret;
if (!port->nr_src_pdo && !port->nr_snk_pdo)
return 0;
port->pd = usb_power_delivery_register(port->dev, &desc);
if (IS_ERR(port->pd)) {
ret = PTR_ERR(port->pd);
goto err_unregister;
}
if (port->nr_src_pdo) {
memcpy_and_pad(caps.pdo, sizeof(caps.pdo), port->src_pdo,
port->nr_src_pdo * sizeof(u32), 0);
caps.role = TYPEC_SOURCE;
cap = usb_power_delivery_register_capabilities(port->pd, &caps);
if (IS_ERR(cap)) {
ret = PTR_ERR(cap);
goto err_unregister;
}
port->port_source_caps = cap;
}
if (port->nr_snk_pdo) {
memcpy_and_pad(caps.pdo, sizeof(caps.pdo), port->snk_pdo,
port->nr_snk_pdo * sizeof(u32), 0);
caps.role = TYPEC_SINK;
cap = usb_power_delivery_register_capabilities(port->pd, &caps);
if (IS_ERR(cap)) {
ret = PTR_ERR(cap);
goto err_unregister;
}
port->port_sink_caps = cap;
}
return 0;
err_unregister:
tcpm_port_unregister_pd(port);
return ret;
}
static int tcpm_fw_get_caps(struct tcpm_port *port, static int tcpm_fw_get_caps(struct tcpm_port *port,
struct fwnode_handle *fwnode) struct fwnode_handle *fwnode)
{ {
...@@ -6382,10 +6511,16 @@ struct tcpm_port *tcpm_register_port(struct device *dev, struct tcpc_dev *tcpc) ...@@ -6382,10 +6511,16 @@ struct tcpm_port *tcpm_register_port(struct device *dev, struct tcpc_dev *tcpc)
goto out_role_sw_put; goto out_role_sw_put;
power_supply_changed(port->psy); power_supply_changed(port->psy);
err = tcpm_port_register_pd(port);
if (err)
goto out_role_sw_put;
port->typec_caps.pd = port->pd;
port->typec_port = typec_register_port(port->dev, &port->typec_caps); port->typec_port = typec_register_port(port->dev, &port->typec_caps);
if (IS_ERR(port->typec_port)) { if (IS_ERR(port->typec_port)) {
err = PTR_ERR(port->typec_port); err = PTR_ERR(port->typec_port);
goto out_role_sw_put; goto out_unregister_pd;
} }
typec_port_register_altmodes(port->typec_port, typec_port_register_altmodes(port->typec_port,
...@@ -6400,6 +6535,8 @@ struct tcpm_port *tcpm_register_port(struct device *dev, struct tcpc_dev *tcpc) ...@@ -6400,6 +6535,8 @@ struct tcpm_port *tcpm_register_port(struct device *dev, struct tcpc_dev *tcpc)
tcpm_log(port, "%s: registered", dev_name(dev)); tcpm_log(port, "%s: registered", dev_name(dev));
return port; return port;
out_unregister_pd:
tcpm_port_unregister_pd(port);
out_role_sw_put: out_role_sw_put:
usb_role_switch_put(port->role_sw); usb_role_switch_put(port->role_sw);
out_destroy_wq: out_destroy_wq:
...@@ -6422,6 +6559,9 @@ void tcpm_unregister_port(struct tcpm_port *port) ...@@ -6422,6 +6559,9 @@ void tcpm_unregister_port(struct tcpm_port *port)
hrtimer_cancel(&port->state_machine_timer); hrtimer_cancel(&port->state_machine_timer);
tcpm_reset_port(port); tcpm_reset_port(port);
tcpm_port_unregister_pd(port);
for (i = 0; i < ARRAY_SIZE(port->port_altmode); i++) for (i = 0; i < ARRAY_SIZE(port->port_altmode); i++)
typec_unregister_altmode(port->port_altmode[i]); typec_unregister_altmode(port->port_altmode[i]);
typec_unregister_port(port->typec_port); typec_unregister_port(port->typec_port);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment