bool explicit_contract;
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 */
u32 sink_request;
u32 source_caps[PDO_MAX_OBJECTS];
}
}
+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,
const struct pd_message *msg)
{
tcpm_validate_caps(port, port->source_caps,
port->nr_source_caps);
+ tcpm_register_source_caps(port);
+
/*
* 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
port->nr_sink_caps = cnt;
port->sink_cap_done = true;
+ tcpm_register_sink_caps(port);
+
if (port->ams == GET_SINK_CAPABILITIES)
tcpm_set_state(port, ready_state(port), 0);
/* Unexpected Sink Capabilities */
port->partner = typec_register_partner(port->typec_port,
&port->partner_desc);
port->connected = true;
+ typec_partner_set_usb_power_delivery(port->partner, port->partner_pd);
}
}
static void tcpm_typec_disconnect(struct tcpm_port *port)
{
if (port->connected) {
+ typec_partner_set_usb_power_delivery(port->partner, NULL);
typec_unregister_partner(port->partner);
port->partner = NULL;
port->connected = false;
port->sink_cap_done = false;
if (port->tcpc->enable_frs)
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)
}
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,
struct fwnode_handle *fwnode)
{
goto out_role_sw_put;
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);
if (IS_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,
tcpm_log(port, "%s: registered", dev_name(dev));
return port;
+out_unregister_pd:
+ tcpm_port_unregister_pd(port);
out_role_sw_put:
usb_role_switch_put(port->role_sw);
out_destroy_wq:
hrtimer_cancel(&port->state_machine_timer);
tcpm_reset_port(port);
+
+ tcpm_port_unregister_pd(port);
+
for (i = 0; i < ARRAY_SIZE(port->port_altmode); i++)
typec_unregister_altmode(port->port_altmode[i]);
typec_unregister_port(port->typec_port);