diff options
Diffstat (limited to 'drivers/platform/chrome')
-rw-r--r-- | drivers/platform/chrome/cros_ec.c | 33 | ||||
-rw-r--r-- | drivers/platform/chrome/cros_ec.h | 4 | ||||
-rw-r--r-- | drivers/platform/chrome/cros_ec_ishtp.c | 6 | ||||
-rw-r--r-- | drivers/platform/chrome/cros_ec_lightbar.c | 2 | ||||
-rw-r--r-- | drivers/platform/chrome/cros_ec_proto.c | 12 | ||||
-rw-r--r-- | drivers/platform/chrome/cros_ec_rpmsg.c | 6 | ||||
-rw-r--r-- | drivers/platform/chrome/cros_ec_sysfs.c | 5 | ||||
-rw-r--r-- | drivers/platform/chrome/cros_ec_typec.c | 317 | ||||
-rw-r--r-- | drivers/platform/chrome/cros_ec_vbc.c | 2 | ||||
-rw-r--r-- | drivers/platform/chrome/wilco_ec/sysfs.c | 2 |
10 files changed, 309 insertions, 80 deletions
diff --git a/drivers/platform/chrome/cros_ec.c b/drivers/platform/chrome/cros_ec.c index 3104680b7485..fc5aa1525d13 100644 --- a/drivers/platform/chrome/cros_ec.c +++ b/drivers/platform/chrome/cros_ec.c @@ -32,7 +32,14 @@ static struct cros_ec_platform pd_p = { .cmd_offset = EC_CMD_PASSTHRU_OFFSET(CROS_EC_DEV_PD_INDEX), }; -static irqreturn_t ec_irq_handler(int irq, void *data) +/** + * cros_ec_irq_handler() - top half part of the interrupt handler + * @irq: IRQ id + * @data: (ec_dev) Device with events to process. + * + * Return: Wakeup the bottom half + */ +static irqreturn_t cros_ec_irq_handler(int irq, void *data) { struct cros_ec_device *ec_dev = data; @@ -51,7 +58,7 @@ static irqreturn_t ec_irq_handler(int irq, void *data) * Return: true if more events are still pending and this function should be * called again. */ -bool cros_ec_handle_event(struct cros_ec_device *ec_dev) +static bool cros_ec_handle_event(struct cros_ec_device *ec_dev) { bool wake_event; bool ec_has_more_events; @@ -73,9 +80,15 @@ bool cros_ec_handle_event(struct cros_ec_device *ec_dev) return ec_has_more_events; } -EXPORT_SYMBOL(cros_ec_handle_event); -static irqreturn_t ec_irq_thread(int irq, void *data) +/** + * cros_ec_irq_thread() - bottom half part of the interrupt handler + * @irq: IRQ id + * @data: (ec_dev) Device with events to process. + * + * Return: Interrupt handled. + */ +irqreturn_t cros_ec_irq_thread(int irq, void *data) { struct cros_ec_device *ec_dev = data; bool ec_has_more_events; @@ -86,6 +99,7 @@ static irqreturn_t ec_irq_thread(int irq, void *data) return IRQ_HANDLED; } +EXPORT_SYMBOL(cros_ec_irq_thread); static int cros_ec_sleep_event(struct cros_ec_device *ec_dev, u8 sleep_event) { @@ -194,8 +208,8 @@ int cros_ec_register(struct cros_ec_device *ec_dev) if (ec_dev->irq > 0) { err = devm_request_threaded_irq(dev, ec_dev->irq, - ec_irq_handler, - ec_irq_thread, + cros_ec_irq_handler, + cros_ec_irq_thread, IRQF_TRIGGER_LOW | IRQF_ONESHOT, "chromeos-ec", ec_dev); if (err) { @@ -269,6 +283,13 @@ int cros_ec_register(struct cros_ec_device *ec_dev) dev_info(dev, "Chrome EC device registered\n"); + /* + * Unlock EC that may be waiting for AP to process MKBP events. + * If the AP takes to long to answer, the EC would stop sending events. + */ + if (ec_dev->mkbp_event_supported) + cros_ec_irq_thread(0, ec_dev); + return 0; } EXPORT_SYMBOL(cros_ec_register); diff --git a/drivers/platform/chrome/cros_ec.h b/drivers/platform/chrome/cros_ec.h index e69fc1ff68b4..78363dcfdf23 100644 --- a/drivers/platform/chrome/cros_ec.h +++ b/drivers/platform/chrome/cros_ec.h @@ -8,12 +8,14 @@ #ifndef __CROS_EC_H #define __CROS_EC_H +#include <linux/interrupt.h> + int cros_ec_register(struct cros_ec_device *ec_dev); int cros_ec_unregister(struct cros_ec_device *ec_dev); int cros_ec_suspend(struct cros_ec_device *ec_dev); int cros_ec_resume(struct cros_ec_device *ec_dev); -bool cros_ec_handle_event(struct cros_ec_device *ec_dev); +irqreturn_t cros_ec_irq_thread(int irq, void *data); #endif /* __CROS_EC_H */ diff --git a/drivers/platform/chrome/cros_ec_ishtp.c b/drivers/platform/chrome/cros_ec_ishtp.c index 81364029af36..f00107017318 100644 --- a/drivers/platform/chrome/cros_ec_ishtp.c +++ b/drivers/platform/chrome/cros_ec_ishtp.c @@ -140,12 +140,8 @@ static void ish_evt_handler(struct work_struct *work) { struct ishtp_cl_data *client_data = container_of(work, struct ishtp_cl_data, work_ec_evt); - struct cros_ec_device *ec_dev = client_data->ec_dev; - bool ec_has_more_events; - do { - ec_has_more_events = cros_ec_handle_event(ec_dev); - } while (ec_has_more_events); + cros_ec_irq_thread(0, client_data->ec_dev); } /** diff --git a/drivers/platform/chrome/cros_ec_lightbar.c b/drivers/platform/chrome/cros_ec_lightbar.c index de8dfb12e486..469dfc7a4a03 100644 --- a/drivers/platform/chrome/cros_ec_lightbar.c +++ b/drivers/platform/chrome/cros_ec_lightbar.c @@ -523,7 +523,7 @@ static struct attribute *__lb_cmds_attrs[] = { NULL, }; -static struct attribute_group cros_ec_lightbar_attr_group = { +static const struct attribute_group cros_ec_lightbar_attr_group = { .name = "lightbar", .attrs = __lb_cmds_attrs, }; diff --git a/drivers/platform/chrome/cros_ec_proto.c b/drivers/platform/chrome/cros_ec_proto.c index 7c92a6e22d75..aa7f7aa77297 100644 --- a/drivers/platform/chrome/cros_ec_proto.c +++ b/drivers/platform/chrome/cros_ec_proto.c @@ -526,11 +526,13 @@ int cros_ec_query_all(struct cros_ec_device *ec_dev) * power), not wake up. */ ec_dev->host_event_wake_mask = U32_MAX & - ~(BIT(EC_HOST_EVENT_AC_DISCONNECTED) | - BIT(EC_HOST_EVENT_BATTERY_LOW) | - BIT(EC_HOST_EVENT_BATTERY_CRITICAL) | - BIT(EC_HOST_EVENT_PD_MCU) | - BIT(EC_HOST_EVENT_BATTERY_STATUS)); + ~(EC_HOST_EVENT_MASK(EC_HOST_EVENT_LID_CLOSED) | + EC_HOST_EVENT_MASK(EC_HOST_EVENT_AC_DISCONNECTED) | + EC_HOST_EVENT_MASK(EC_HOST_EVENT_BATTERY_LOW) | + EC_HOST_EVENT_MASK(EC_HOST_EVENT_BATTERY_CRITICAL) | + EC_HOST_EVENT_MASK(EC_HOST_EVENT_BATTERY) | + EC_HOST_EVENT_MASK(EC_HOST_EVENT_PD_MCU) | + EC_HOST_EVENT_MASK(EC_HOST_EVENT_BATTERY_STATUS)); /* * Old ECs may not support this command. Complain about all * other errors. diff --git a/drivers/platform/chrome/cros_ec_rpmsg.c b/drivers/platform/chrome/cros_ec_rpmsg.c index 30d0ba3b8889..d96d15b8ca94 100644 --- a/drivers/platform/chrome/cros_ec_rpmsg.c +++ b/drivers/platform/chrome/cros_ec_rpmsg.c @@ -149,12 +149,8 @@ cros_ec_rpmsg_host_event_function(struct work_struct *host_event_work) struct cros_ec_rpmsg *ec_rpmsg = container_of(host_event_work, struct cros_ec_rpmsg, host_event_work); - struct cros_ec_device *ec_dev = dev_get_drvdata(&ec_rpmsg->rpdev->dev); - bool ec_has_more_events; - do { - ec_has_more_events = cros_ec_handle_event(ec_dev); - } while (ec_has_more_events); + cros_ec_irq_thread(0, dev_get_drvdata(&ec_rpmsg->rpdev->dev)); } static int cros_ec_rpmsg_callback(struct rpmsg_device *rpdev, void *data, diff --git a/drivers/platform/chrome/cros_ec_sysfs.c b/drivers/platform/chrome/cros_ec_sysfs.c index f521a5c65091..f07eabcf9494 100644 --- a/drivers/platform/chrome/cros_ec_sysfs.c +++ b/drivers/platform/chrome/cros_ec_sysfs.c @@ -28,7 +28,7 @@ static ssize_t reboot_show(struct device *dev, int count = 0; count += scnprintf(buf + count, PAGE_SIZE - count, - "ro|rw|cancel|cold|disable-jump|hibernate"); + "ro|rw|cancel|cold|disable-jump|hibernate|cold-ap-off"); count += scnprintf(buf + count, PAGE_SIZE - count, " [at-shutdown]\n"); return count; @@ -46,6 +46,7 @@ static ssize_t reboot_store(struct device *dev, {"cancel", EC_REBOOT_CANCEL, 0}, {"ro", EC_REBOOT_JUMP_RO, 0}, {"rw", EC_REBOOT_JUMP_RW, 0}, + {"cold-ap-off", EC_REBOOT_COLD_AP_OFF, 0}, {"cold", EC_REBOOT_COLD, 0}, {"disable-jump", EC_REBOOT_DISABLE_JUMP, 0}, {"hibernate", EC_REBOOT_HIBERNATE, 0}, @@ -329,7 +330,7 @@ static umode_t cros_ec_ctrl_visible(struct kobject *kobj, return a->mode; } -static struct attribute_group cros_ec_attr_group = { +static const struct attribute_group cros_ec_attr_group = { .attrs = __ec_attrs, .is_visible = cros_ec_ctrl_visible, }; diff --git a/drivers/platform/chrome/cros_ec_typec.c b/drivers/platform/chrome/cros_ec_typec.c index c43868615790..0811562deecc 100644 --- a/drivers/platform/chrome/cros_ec_typec.c +++ b/drivers/platform/chrome/cros_ec_typec.c @@ -44,8 +44,13 @@ struct cros_typec_port { /* Initial capabilities for the port. */ struct typec_capability caps; struct typec_partner *partner; + struct typec_cable *cable; + /* SOP' plug. */ + struct typec_plug *plug; /* Port partner PD identity info. */ struct usb_pd_identity p_identity; + /* Port cable PD identity info. */ + struct usb_pd_identity c_identity; struct typec_switch *ori_sw; struct typec_mux *mux; struct usb_role_switch *role_sw; @@ -57,10 +62,12 @@ struct cros_typec_port { /* Port alt modes. */ struct typec_altmode p_altmode[CROS_EC_ALTMODE_MAX]; - /* Flag indicating that PD discovery data parsing is completed. */ - bool disc_done; - struct ec_response_typec_discovery *sop_disc; + /* Flag indicating that PD partner discovery data parsing is completed. */ + bool sop_disc_done; + bool sop_prime_disc_done; + struct ec_response_typec_discovery *disc_data; struct list_head partner_mode_list; + struct list_head plug_mode_list; }; /* Platform-specific data for the Chrome OS EC Type C controller. */ @@ -74,6 +81,7 @@ struct cros_typec_data { struct notifier_block nb; struct work_struct port_work; bool typec_cmd_supported; + bool needs_mux_ack; }; static int cros_typec_parse_port_props(struct typec_capability *cap, @@ -180,37 +188,61 @@ static int cros_typec_add_partner(struct cros_typec_data *typec, int port_num, return ret; } -static void cros_typec_unregister_altmodes(struct cros_typec_data *typec, int port_num) +static void cros_typec_unregister_altmodes(struct cros_typec_data *typec, int port_num, + bool is_partner) { struct cros_typec_port *port = typec->ports[port_num]; struct cros_typec_altmode_node *node, *tmp; + struct list_head *head; - list_for_each_entry_safe(node, tmp, &port->partner_mode_list, list) { + head = is_partner ? &port->partner_mode_list : &port->plug_mode_list; + list_for_each_entry_safe(node, tmp, head, list) { list_del(&node->list); typec_unregister_altmode(node->amode); devm_kfree(typec->dev, node); } } -static void cros_typec_remove_partner(struct cros_typec_data *typec, - int port_num) +static int cros_typec_usb_disconnect_state(struct cros_typec_port *port) { - struct cros_typec_port *port = typec->ports[port_num]; - - cros_typec_unregister_altmodes(typec, port_num); - port->state.alt = NULL; port->state.mode = TYPEC_STATE_USB; port->state.data = NULL; usb_role_switch_set_role(port->role_sw, USB_ROLE_NONE); typec_switch_set(port->ori_sw, TYPEC_ORIENTATION_NONE); - typec_mux_set(port->mux, &port->state); + + return typec_mux_set(port->mux, &port->state); +} + +static void cros_typec_remove_partner(struct cros_typec_data *typec, + int port_num) +{ + struct cros_typec_port *port = typec->ports[port_num]; + + cros_typec_unregister_altmodes(typec, port_num, true); + + cros_typec_usb_disconnect_state(port); typec_unregister_partner(port->partner); port->partner = NULL; memset(&port->p_identity, 0, sizeof(port->p_identity)); - port->disc_done = false; + port->sop_disc_done = false; +} + +static void cros_typec_remove_cable(struct cros_typec_data *typec, + int port_num) +{ + struct cros_typec_port *port = typec->ports[port_num]; + + cros_typec_unregister_altmodes(typec, port_num, false); + + typec_unregister_plug(port->plug); + port->plug = NULL; + typec_unregister_cable(port->cable); + port->cable = NULL; + memset(&port->c_identity, 0, sizeof(port->c_identity)); + port->sop_prime_disc_done = false; } static void cros_unregister_ports(struct cros_typec_data *typec) @@ -224,6 +256,9 @@ static void cros_unregister_ports(struct cros_typec_data *typec) if (typec->ports[i]->partner) cros_typec_remove_partner(typec, i); + if (typec->ports[i]->cable) + cros_typec_remove_cable(typec, i); + usb_role_switch_put(typec->ports[i]->role_sw); typec_switch_put(typec->ports[i]->ori_sw); typec_mux_put(typec->ports[i]->mux); @@ -323,13 +358,14 @@ static int cros_typec_init_ports(struct cros_typec_data *typec) cros_typec_register_port_altmodes(typec, port_num); - cros_port->sop_disc = devm_kzalloc(dev, EC_PROTO2_MAX_RESPONSE_SIZE, GFP_KERNEL); - if (!cros_port->sop_disc) { + cros_port->disc_data = devm_kzalloc(dev, EC_PROTO2_MAX_RESPONSE_SIZE, GFP_KERNEL); + if (!cros_port->disc_data) { ret = -ENOMEM; goto unregister_ports; } INIT_LIST_HEAD(&cros_port->partner_mode_list); + INIT_LIST_HEAD(&cros_port->plug_mode_list); } return 0; @@ -502,11 +538,14 @@ static int cros_typec_configure_mux(struct cros_typec_data *typec, int port_num, struct ec_response_usb_pd_control_v2 *pd_ctrl) { struct cros_typec_port *port = typec->ports[port_num]; + struct ec_params_usb_pd_mux_ack mux_ack; enum typec_orientation orientation; int ret; - if (!port->partner) - return 0; + if (mux_flags == USB_PD_MUX_NONE) { + ret = cros_typec_usb_disconnect_state(port); + goto mux_ack; + } if (mux_flags & USB_PD_MUX_POLARITY_INVERTED) orientation = TYPEC_ORIENTATION_REVERSE; @@ -541,6 +580,19 @@ static int cros_typec_configure_mux(struct cros_typec_data *typec, int port_num, mux_flags); } +mux_ack: + if (!typec->needs_mux_ack) + return ret; + + /* Sending Acknowledgment to EC */ + mux_ack.port = port_num; + + if (cros_typec_ec_command(typec, 0, EC_CMD_USB_PD_MUX_ACK, &mux_ack, + sizeof(mux_ack), NULL, 0) < 0) + dev_warn(typec->dev, + "Failed to send Mux ACK to EC for port: %d\n", + port_num); + return ret; } @@ -595,9 +647,11 @@ static void cros_typec_set_port_params_v1(struct cros_typec_data *typec, "Failed to register partner on port: %d\n", port_num); } else { - if (!typec->ports[port_num]->partner) - return; - cros_typec_remove_partner(typec, port_num); + if (typec->ports[port_num]->partner) + cros_typec_remove_partner(typec, port_num); + + if (typec->ports[port_num]->cable) + cros_typec_remove_cable(typec, port_num); } } @@ -612,13 +666,18 @@ static int cros_typec_get_mux_info(struct cros_typec_data *typec, int port_num, sizeof(req), resp, sizeof(*resp)); } -static int cros_typec_register_altmodes(struct cros_typec_data *typec, int port_num) +/* + * Helper function to register partner/plug altmodes. + */ +static int cros_typec_register_altmodes(struct cros_typec_data *typec, int port_num, + bool is_partner) { struct cros_typec_port *port = typec->ports[port_num]; - struct ec_response_typec_discovery *sop_disc = port->sop_disc; + struct ec_response_typec_discovery *sop_disc = port->disc_data; struct cros_typec_altmode_node *node; struct typec_altmode_desc desc; struct typec_altmode *amode; + int num_altmodes = 0; int ret = 0; int i, j; @@ -629,7 +688,11 @@ static int cros_typec_register_altmodes(struct cros_typec_data *typec, int port_ desc.mode = j; desc.vdo = sop_disc->svids[i].mode_vdo[j]; - amode = typec_partner_register_altmode(port->partner, &desc); + if (is_partner) + amode = typec_partner_register_altmode(port->partner, &desc); + else + amode = typec_plug_register_altmode(port->plug, &desc); + if (IS_ERR(amode)) { ret = PTR_ERR(amode); goto err_cleanup; @@ -644,27 +707,140 @@ static int cros_typec_register_altmodes(struct cros_typec_data *typec, int port_ } node->amode = amode; - list_add_tail(&node->list, &port->partner_mode_list); + + if (is_partner) + list_add_tail(&node->list, &port->partner_mode_list); + else + list_add_tail(&node->list, &port->plug_mode_list); + num_altmodes++; } } + if (is_partner) + ret = typec_partner_set_num_altmodes(port->partner, num_altmodes); + else + ret = typec_plug_set_num_altmodes(port->plug, num_altmodes); + + if (ret < 0) { + dev_err(typec->dev, "Unable to set %s num_altmodes for port: %d\n", + is_partner ? "partner" : "plug", port_num); + goto err_cleanup; + } + return 0; err_cleanup: - cros_typec_unregister_altmodes(typec, port_num); + cros_typec_unregister_altmodes(typec, port_num, is_partner); + return ret; +} + +/* + * Parse the PD identity data from the EC PD discovery responses and copy that to the supplied + * PD identity struct. + */ +static void cros_typec_parse_pd_identity(struct usb_pd_identity *id, + struct ec_response_typec_discovery *disc) +{ + int i; + + /* First, update the PD identity VDOs for the partner. */ + if (disc->identity_count > 0) + id->id_header = disc->discovery_vdo[0]; + if (disc->identity_count > 1) + id->cert_stat = disc->discovery_vdo[1]; + if (disc->identity_count > 2) + id->product = disc->discovery_vdo[2]; + + /* Copy the remaining identity VDOs till a maximum of 6. */ + for (i = 3; i < disc->identity_count && i < VDO_MAX_OBJECTS; i++) + id->vdo[i - 3] = disc->discovery_vdo[i]; +} + +static int cros_typec_handle_sop_prime_disc(struct cros_typec_data *typec, int port_num, u16 pd_revision) +{ + struct cros_typec_port *port = typec->ports[port_num]; + struct ec_response_typec_discovery *disc = port->disc_data; + struct typec_cable_desc c_desc = {}; + struct typec_plug_desc p_desc; + struct ec_params_typec_discovery req = { + .port = port_num, + .partner_type = TYPEC_PARTNER_SOP_PRIME, + }; + u32 cable_plug_type; + int ret = 0; + + memset(disc, 0, EC_PROTO2_MAX_RESPONSE_SIZE); + ret = cros_typec_ec_command(typec, 0, EC_CMD_TYPEC_DISCOVERY, &req, sizeof(req), + disc, EC_PROTO2_MAX_RESPONSE_SIZE); + if (ret < 0) { + dev_err(typec->dev, "Failed to get SOP' discovery data for port: %d\n", port_num); + goto sop_prime_disc_exit; + } + + /* Parse the PD identity data, even if only 0s were returned. */ + cros_typec_parse_pd_identity(&port->c_identity, disc); + + if (disc->identity_count != 0) { + cable_plug_type = VDO_TYPEC_CABLE_TYPE(port->c_identity.vdo[0]); + switch (cable_plug_type) { + case CABLE_ATYPE: + c_desc.type = USB_PLUG_TYPE_A; + break; + case CABLE_BTYPE: + c_desc.type = USB_PLUG_TYPE_B; + break; + case CABLE_CTYPE: + c_desc.type = USB_PLUG_TYPE_C; + break; + case CABLE_CAPTIVE: + c_desc.type = USB_PLUG_CAPTIVE; + break; + default: + c_desc.type = USB_PLUG_NONE; + } + c_desc.active = PD_IDH_PTYPE(port->c_identity.id_header) == IDH_PTYPE_ACABLE; + } + + c_desc.identity = &port->c_identity; + c_desc.pd_revision = pd_revision; + + port->cable = typec_register_cable(port->port, &c_desc); + if (IS_ERR(port->cable)) { + ret = PTR_ERR(port->cable); + port->cable = NULL; + goto sop_prime_disc_exit; + } + + p_desc.index = TYPEC_PLUG_SOP_P; + port->plug = typec_register_plug(port->cable, &p_desc); + if (IS_ERR(port->plug)) { + ret = PTR_ERR(port->plug); + port->plug = NULL; + goto sop_prime_disc_exit; + } + + ret = cros_typec_register_altmodes(typec, port_num, false); + if (ret < 0) { + dev_err(typec->dev, "Failed to register plug altmodes, port: %d\n", port_num); + goto sop_prime_disc_exit; + } + + return 0; + +sop_prime_disc_exit: + cros_typec_remove_cable(typec, port_num); return ret; } -static int cros_typec_handle_sop_disc(struct cros_typec_data *typec, int port_num) +static int cros_typec_handle_sop_disc(struct cros_typec_data *typec, int port_num, u16 pd_revision) { struct cros_typec_port *port = typec->ports[port_num]; - struct ec_response_typec_discovery *sop_disc = port->sop_disc; + struct ec_response_typec_discovery *sop_disc = port->disc_data; struct ec_params_typec_discovery req = { .port = port_num, .partner_type = TYPEC_PARTNER_SOP, }; int ret = 0; - int i; if (!port->partner) { dev_err(typec->dev, @@ -674,6 +850,8 @@ static int cros_typec_handle_sop_disc(struct cros_typec_data *typec, int port_nu goto disc_exit; } + typec_partner_set_pd_revision(port->partner, pd_revision); + memset(sop_disc, 0, EC_PROTO2_MAX_RESPONSE_SIZE); ret = cros_typec_ec_command(typec, 0, EC_CMD_TYPEC_DISCOVERY, &req, sizeof(req), sop_disc, EC_PROTO2_MAX_RESPONSE_SIZE); @@ -682,17 +860,7 @@ static int cros_typec_handle_sop_disc(struct cros_typec_data *typec, int port_nu goto disc_exit; } - /* First, update the PD identity VDOs for the partner. */ - if (sop_disc->identity_count > 0) - port->p_identity.id_header = sop_disc->discovery_vdo[0]; - if (sop_disc->identity_count > 1) - port->p_identity.cert_stat = sop_disc->discovery_vdo[1]; - if (sop_disc->identity_count > 2) - port->p_identity.product = sop_disc->discovery_vdo[2]; - - /* Copy the remaining identity VDOs till a maximum of 6. */ - for (i = 3; i < sop_disc->identity_count && i < VDO_MAX_OBJECTS; i++) - port->p_identity.vdo[i - 3] = sop_disc->discovery_vdo[i]; + cros_typec_parse_pd_identity(&port->p_identity, sop_disc); ret = typec_partner_set_identity(port->partner); if (ret < 0) { @@ -700,7 +868,7 @@ static int cros_typec_handle_sop_disc(struct cros_typec_data *typec, int port_nu goto disc_exit; } - ret = cros_typec_register_altmodes(typec, port_num); + ret = cros_typec_register_altmodes(typec, port_num, true); if (ret < 0) { dev_err(typec->dev, "Failed to register partner altmodes, port: %d\n", port_num); goto disc_exit; @@ -710,6 +878,18 @@ disc_exit: return ret; } +static int cros_typec_send_clear_event(struct cros_typec_data *typec, int port_num, u32 events_mask) +{ + struct ec_params_typec_control req = { + .port = port_num, + .command = TYPEC_CONTROL_COMMAND_CLEAR_EVENTS, + .clear_events_mask = events_mask, + }; + + return cros_typec_ec_command(typec, 0, EC_CMD_TYPEC_CONTROL, &req, + sizeof(req), NULL, 0); +} + static void cros_typec_handle_status(struct cros_typec_data *typec, int port_num) { struct ec_response_typec_status resp; @@ -725,18 +905,44 @@ static void cros_typec_handle_status(struct cros_typec_data *typec, int port_num return; } - if (typec->ports[port_num]->disc_done) - return; - /* Handle any events appropriately. */ - if (resp.events & PD_STATUS_EVENT_SOP_DISC_DONE) { - ret = cros_typec_handle_sop_disc(typec, port_num); - if (ret < 0) { + if (resp.events & PD_STATUS_EVENT_SOP_DISC_DONE && !typec->ports[port_num]->sop_disc_done) { + u16 sop_revision; + + /* Convert BCD to the format preferred by the TypeC framework */ + sop_revision = (le16_to_cpu(resp.sop_revision) & 0xff00) >> 4; + ret = cros_typec_handle_sop_disc(typec, port_num, sop_revision); + if (ret < 0) dev_err(typec->dev, "Couldn't parse SOP Disc data, port: %d\n", port_num); - return; + else { + typec->ports[port_num]->sop_disc_done = true; + ret = cros_typec_send_clear_event(typec, port_num, + PD_STATUS_EVENT_SOP_DISC_DONE); + if (ret < 0) + dev_warn(typec->dev, + "Failed SOP Disc event clear, port: %d\n", port_num); } + if (resp.sop_connected) + typec_set_pwr_opmode(typec->ports[port_num]->port, TYPEC_PWR_MODE_PD); + } - typec->ports[port_num]->disc_done = true; + if (resp.events & PD_STATUS_EVENT_SOP_PRIME_DISC_DONE && + !typec->ports[port_num]->sop_prime_disc_done) { + u16 sop_prime_revision; + + /* Convert BCD to the format preferred by the TypeC framework */ + sop_prime_revision = (le16_to_cpu(resp.sop_prime_revision) & 0xff00) >> 4; + ret = cros_typec_handle_sop_prime_disc(typec, port_num, sop_prime_revision); + if (ret < 0) + dev_err(typec->dev, "Couldn't parse SOP' Disc data, port: %d\n", port_num); + else { + typec->ports[port_num]->sop_prime_disc_done = true; + ret = cros_typec_send_clear_event(typec, port_num, + PD_STATUS_EVENT_SOP_PRIME_DISC_DONE); + if (ret < 0) + dev_warn(typec->dev, + "Failed SOP Disc event clear, port: %d\n", port_num); + } } } @@ -827,8 +1033,8 @@ static int cros_typec_get_cmd_version(struct cros_typec_data *typec) return 0; } -/* Check the EC feature flags to see if TYPEC_* commands are supported. */ -static int cros_typec_cmds_supported(struct cros_typec_data *typec) +/* Check the EC feature flags to see if TYPEC_* features are supported. */ +static int cros_typec_feature_supported(struct cros_typec_data *typec, enum ec_feature_code feature) { struct ec_response_get_features resp = {}; int ret; @@ -837,11 +1043,12 @@ static int cros_typec_cmds_supported(struct cros_typec_data *typec) &resp, sizeof(resp)); if (ret < 0) { dev_warn(typec->dev, - "Failed to get features, assuming typec commands unsupported.\n"); + "Failed to get features, assuming typec feature=%d unsupported.\n", + feature); return 0; } - return resp.flags[EC_FEATURE_TYPEC_CMD / 32] & EC_FEATURE_MASK_1(EC_FEATURE_TYPEC_CMD); + return resp.flags[feature / 32] & EC_FEATURE_MASK_1(feature); } static void cros_typec_port_work(struct work_struct *work) @@ -861,6 +1068,7 @@ static int cros_ec_typec_event(struct notifier_block *nb, { struct cros_typec_data *typec = container_of(nb, struct cros_typec_data, nb); + flush_work(&typec->port_work); schedule_work(&typec->port_work); return NOTIFY_OK; @@ -903,7 +1111,10 @@ static int cros_typec_probe(struct platform_device *pdev) return ret; } - typec->typec_cmd_supported = !!cros_typec_cmds_supported(typec); + typec->typec_cmd_supported = !!cros_typec_feature_supported(typec, + EC_FEATURE_TYPEC_CMD); + typec->needs_mux_ack = !!cros_typec_feature_supported(typec, + EC_FEATURE_TYPEC_MUX_REQUIRE_AP_ACK); ret = cros_typec_ec_command(typec, 0, EC_CMD_USB_PD_PORTS, NULL, 0, &resp, sizeof(resp)); diff --git a/drivers/platform/chrome/cros_ec_vbc.c b/drivers/platform/chrome/cros_ec_vbc.c index f3a70a312b43..c859c862d7ac 100644 --- a/drivers/platform/chrome/cros_ec_vbc.c +++ b/drivers/platform/chrome/cros_ec_vbc.c @@ -101,7 +101,7 @@ static struct bin_attribute *cros_ec_vbc_bin_attrs[] = { NULL }; -static struct attribute_group cros_ec_vbc_attr_group = { +static const struct attribute_group cros_ec_vbc_attr_group = { .name = "vbc", .bin_attrs = cros_ec_vbc_bin_attrs, }; diff --git a/drivers/platform/chrome/wilco_ec/sysfs.c b/drivers/platform/chrome/wilco_ec/sysfs.c index 3c587b4054a5..79a5e8fa680f 100644 --- a/drivers/platform/chrome/wilco_ec/sysfs.c +++ b/drivers/platform/chrome/wilco_ec/sysfs.c @@ -236,7 +236,7 @@ static struct attribute *wilco_dev_attrs[] = { NULL, }; -static struct attribute_group wilco_dev_attr_group = { +static const struct attribute_group wilco_dev_attr_group = { .attrs = wilco_dev_attrs, }; |