diff options
author | Maxime Ripard <maxime@cerno.tech> | 2021-09-14 09:25:30 +0200 |
---|---|---|
committer | Maxime Ripard <maxime@cerno.tech> | 2021-09-14 09:25:30 +0200 |
commit | 2f76520561d01a5f37e6d6ed2c2e441b6a355a96 (patch) | |
tree | 692f210145f35ec2621e2015d788267f25e673cb /drivers/usb/typec | |
parent | 617d5b34f22c66fe9fc9f150be27f5de1b87ca15 (diff) | |
parent | 6880fa6c56601bb8ed59df6c30fd390cc5f6dd8f (diff) |
Merge drm/drm-next into drm-misc-next
Kickstart new drm-misc-next cycle.
Signed-off-by: Maxime Ripard <maxime@cerno.tech>
Diffstat (limited to 'drivers/usb/typec')
-rw-r--r-- | drivers/usb/typec/bus.c | 4 | ||||
-rw-r--r-- | drivers/usb/typec/tcpm/tcpm.c | 189 |
2 files changed, 118 insertions, 75 deletions
diff --git a/drivers/usb/typec/bus.c b/drivers/usb/typec/bus.c index 7f3c9a8e2bf0..78e0e78954f2 100644 --- a/drivers/usb/typec/bus.c +++ b/drivers/usb/typec/bus.c @@ -382,7 +382,7 @@ static int typec_probe(struct device *dev) return ret; } -static int typec_remove(struct device *dev) +static void typec_remove(struct device *dev) { struct typec_altmode_driver *drv = to_altmode_driver(dev->driver); struct typec_altmode *adev = to_typec_altmode(dev); @@ -400,8 +400,6 @@ static int typec_remove(struct device *dev) adev->desc = NULL; adev->ops = NULL; - - return 0; } struct bus_type typec_bus = { diff --git a/drivers/usb/typec/tcpm/tcpm.c b/drivers/usb/typec/tcpm/tcpm.c index 5b22a1c931a9..a4d37205df54 100644 --- a/drivers/usb/typec/tcpm/tcpm.c +++ b/drivers/usb/typec/tcpm/tcpm.c @@ -278,7 +278,7 @@ struct pd_mode_data { * @req_max_curr: Requested max current of the port partner * @req_out_volt: Requested output voltage to the port partner * @req_op_curr: Requested operating current to the port partner - * @supported: Parter has atleast one APDO hence supports PPS + * @supported: Parter has at least one APDO hence supports PPS * @active: PPS mode is active */ struct pd_pps_data { @@ -316,6 +316,7 @@ struct tcpm_port { struct typec_partner *partner; enum typec_cc_status cc_req; + enum typec_cc_status src_rp; /* work only if pd_supported == false */ enum typec_cc_status cc1; enum typec_cc_status cc2; @@ -323,6 +324,7 @@ struct tcpm_port { bool attached; bool connected; + bool pd_supported; enum typec_port_type port_type; /* @@ -341,6 +343,7 @@ struct tcpm_port { bool vbus_source; bool vbus_charge; + /* Set to true when Discover_Identity Command is expected to be sent in Ready states. */ bool send_discover; bool op_vsafe5v; @@ -370,6 +373,7 @@ struct tcpm_port { struct hrtimer send_discover_timer; struct kthread_work send_discover_work; bool state_machine_running; + /* Set to true when VDM State Machine has following actions. */ bool vdm_sm_running; struct completion tx_complete; @@ -815,6 +819,9 @@ static enum typec_cc_status tcpm_rp_cc(struct tcpm_port *port) int nr_pdo = port->nr_src_pdo; int i; + if (!port->pd_supported) + return port->src_rp; + /* * Search for first entry with matching voltage. * It should report the maximum supported current. @@ -1431,6 +1438,7 @@ static void tcpm_queue_vdm(struct tcpm_port *port, const u32 header, /* Set ready, vdm state machine will actually send */ port->vdm_retries = 0; port->vdm_state = VDM_STATE_READY; + port->vdm_sm_running = true; mod_vdm_delayed_work(port, 0); } @@ -1673,7 +1681,6 @@ static int tcpm_pd_svdm(struct tcpm_port *port, struct typec_altmode *adev, rlen = 1; } else { tcpm_register_partner_altmodes(port); - port->vdm_sm_running = false; } break; case CMD_ENTER_MODE: @@ -1721,14 +1728,12 @@ static int tcpm_pd_svdm(struct tcpm_port *port, struct typec_altmode *adev, (VDO_SVDM_VERS(svdm_version)); break; } - port->vdm_sm_running = false; break; default: response[0] = p[0] | VDO_CMDT(CMDT_RSP_NAK); rlen = 1; response[0] = (response[0] & ~VDO_SVDM_VERS_MASK) | (VDO_SVDM_VERS(svdm_version)); - port->vdm_sm_running = false; break; } @@ -1737,6 +1742,10 @@ static int tcpm_pd_svdm(struct tcpm_port *port, struct typec_altmode *adev, return rlen; } +static void tcpm_pd_handle_msg(struct tcpm_port *port, + enum pd_msg_request message, + enum tcpm_ams ams); + static void tcpm_handle_vdm_request(struct tcpm_port *port, const __le32 *payload, int cnt) { @@ -1764,11 +1773,25 @@ static void tcpm_handle_vdm_request(struct tcpm_port *port, port->vdm_state = VDM_STATE_DONE; } - if (PD_VDO_SVDM(p[0])) { + if (PD_VDO_SVDM(p[0]) && (adev || tcpm_vdm_ams(port) || port->nr_snk_vdo)) { + /* + * Here a SVDM is received (INIT or RSP or unknown). Set the vdm_sm_running in + * advance because we are dropping the lock but may send VDMs soon. + * For the cases of INIT received: + * - If no response to send, it will be cleared later in this function. + * - If there are responses to send, it will be cleared in the state machine. + * For the cases of RSP received: + * - If no further INIT to send, it will be cleared later in this function. + * - Otherwise, it will be cleared in the state machine if timeout or it will go + * back here until no further INIT to send. + * For the cases of unknown type received: + * - We will send NAK and the flag will be cleared in the state machine. + */ + port->vdm_sm_running = true; rlen = tcpm_pd_svdm(port, adev, p, cnt, response, &adev_action); } else { if (port->negotiated_rev >= PD_REV30) - tcpm_queue_message(port, PD_MSG_CTRL_NOT_SUPP); + tcpm_pd_handle_msg(port, PD_MSG_CTRL_NOT_SUPP, NONE_AMS); } /* @@ -1833,6 +1856,8 @@ static void tcpm_handle_vdm_request(struct tcpm_port *port, if (rlen > 0) tcpm_queue_vdm(port, response[0], &response[1], rlen - 1); + else + port->vdm_sm_running = false; } static void tcpm_send_vdm(struct tcpm_port *port, u32 vid, int cmd, @@ -1898,8 +1923,10 @@ static void vdm_run_state_machine(struct tcpm_port *port) * if there's traffic or we're not in PDO ready state don't send * a VDM. */ - if (port->state != SRC_READY && port->state != SNK_READY) + if (port->state != SRC_READY && port->state != SNK_READY) { + port->vdm_sm_running = false; break; + } /* TODO: AMS operation for Unstructured VDM */ if (PD_VDO_SVDM(vdo_hdr) && PD_VDO_CMDT(vdo_hdr) == CMDT_INIT) { @@ -2041,7 +2068,7 @@ enum pdo_err { static const char * const pdo_err_msg[] = { [PDO_ERR_NO_VSAFE5V] = - " err: source/sink caps should atleast have vSafe5V", + " err: source/sink caps should at least have vSafe5V", [PDO_ERR_VSAFE5V_NOT_FIRST] = " err: vSafe5V Fixed Supply Object Shall always be the first object", [PDO_ERR_PDO_TYPE_NOT_IN_ORDER] = @@ -2471,10 +2498,7 @@ static void tcpm_pd_data_request(struct tcpm_port *port, NONE_AMS); break; case PD_DATA_VENDOR_DEF: - if (tcpm_vdm_ams(port) || port->nr_snk_vdo) - tcpm_handle_vdm_request(port, msg->payload, cnt); - else if (port->negotiated_rev > PD_REV20) - tcpm_pd_handle_msg(port, PD_MSG_CTRL_NOT_SUPP, NONE_AMS); + tcpm_handle_vdm_request(port, msg->payload, cnt); break; case PD_DATA_BIST: port->bist_request = le32_to_cpu(msg->payload[0]); @@ -2555,10 +2579,6 @@ static void tcpm_pd_ctrl_request(struct tcpm_port *port, TYPEC_PWR_MODE_PD, port->pps_data.active, port->supply_voltage); - /* Set VDM running flag ASAP */ - if (port->data_role == TYPEC_HOST && - port->send_discover) - port->vdm_sm_running = true; tcpm_set_state(port, SNK_READY, 0); } else { /* @@ -2596,14 +2616,10 @@ static void tcpm_pd_ctrl_request(struct tcpm_port *port, switch (port->state) { case SNK_NEGOTIATE_CAPABILITIES: /* USB PD specification, Figure 8-43 */ - if (port->explicit_contract) { + if (port->explicit_contract) next_state = SNK_READY; - if (port->data_role == TYPEC_HOST && - port->send_discover) - port->vdm_sm_running = true; - } else { + else next_state = SNK_WAIT_CAPABILITIES; - } /* Threshold was relaxed before sending Request. Restore it back. */ tcpm_set_auto_vbus_discharge_threshold(port, TYPEC_PWR_MODE_PD, @@ -2618,10 +2634,6 @@ static void tcpm_pd_ctrl_request(struct tcpm_port *port, port->pps_status = (type == PD_CTRL_WAIT ? -EAGAIN : -EOPNOTSUPP); - if (port->data_role == TYPEC_HOST && - port->send_discover) - port->vdm_sm_running = true; - /* Threshold was relaxed before sending Request. Restore it back. */ tcpm_set_auto_vbus_discharge_threshold(port, TYPEC_PWR_MODE_PD, port->pps_data.active, @@ -2697,10 +2709,6 @@ static void tcpm_pd_ctrl_request(struct tcpm_port *port, } break; case DR_SWAP_SEND: - if (port->data_role == TYPEC_DEVICE && - port->send_discover) - port->vdm_sm_running = true; - tcpm_set_state(port, DR_SWAP_CHANGE_DR, 0); break; case PR_SWAP_SEND: @@ -2738,7 +2746,7 @@ static void tcpm_pd_ctrl_request(struct tcpm_port *port, PD_MSG_CTRL_NOT_SUPP, NONE_AMS); } else { - if (port->vdm_sm_running) { + if (port->send_discover) { tcpm_queue_message(port, PD_MSG_CTRL_WAIT); break; } @@ -2754,7 +2762,7 @@ static void tcpm_pd_ctrl_request(struct tcpm_port *port, PD_MSG_CTRL_NOT_SUPP, NONE_AMS); } else { - if (port->vdm_sm_running) { + if (port->send_discover) { tcpm_queue_message(port, PD_MSG_CTRL_WAIT); break; } @@ -2763,7 +2771,7 @@ static void tcpm_pd_ctrl_request(struct tcpm_port *port, } break; case PD_CTRL_VCONN_SWAP: - if (port->vdm_sm_running) { + if (port->send_discover) { tcpm_queue_message(port, PD_MSG_CTRL_WAIT); break; } @@ -3568,9 +3576,11 @@ static int tcpm_src_attach(struct tcpm_port *port) if (ret < 0) return ret; - ret = port->tcpc->set_pd_rx(port->tcpc, true); - if (ret < 0) - goto out_disable_mux; + if (port->pd_supported) { + ret = port->tcpc->set_pd_rx(port->tcpc, true); + if (ret < 0) + goto out_disable_mux; + } /* * USB Type-C specification, version 1.2, @@ -3600,7 +3610,8 @@ static int tcpm_src_attach(struct tcpm_port *port) out_disable_vconn: tcpm_set_vconn(port, false); out_disable_pd: - port->tcpc->set_pd_rx(port->tcpc, false); + if (port->pd_supported) + port->tcpc->set_pd_rx(port->tcpc, false); out_disable_mux: tcpm_mux_set(port, TYPEC_STATE_SAFE, USB_ROLE_NONE, TYPEC_ORIENTATION_NONE); @@ -3804,6 +3815,20 @@ static enum typec_pwr_opmode tcpm_get_pwr_opmode(enum typec_cc_status cc) } } +static enum typec_cc_status tcpm_pwr_opmode_to_rp(enum typec_pwr_opmode opmode) +{ + switch (opmode) { + case TYPEC_PWR_MODE_USB: + return TYPEC_CC_RP_DEF; + case TYPEC_PWR_MODE_1_5A: + return TYPEC_CC_RP_1_5; + case TYPEC_PWR_MODE_3_0A: + case TYPEC_PWR_MODE_PD: + default: + return TYPEC_CC_RP_3_0; + } +} + static void run_state_machine(struct tcpm_port *port) { int ret; @@ -3914,6 +3939,10 @@ static void run_state_machine(struct tcpm_port *port) if (port->ams == POWER_ROLE_SWAP || port->ams == FAST_ROLE_SWAP) tcpm_ams_finish(port); + if (!port->pd_supported) { + tcpm_set_state(port, SRC_READY, 0); + break; + } port->upcoming_state = SRC_SEND_CAPABILITIES; tcpm_ams_start(port, POWER_NEGOTIATION); break; @@ -4161,7 +4190,10 @@ static void run_state_machine(struct tcpm_port *port) current_lim = PD_P_SNK_STDBY_MW / 5; tcpm_set_current_limit(port, current_lim, 5000); tcpm_set_charge(port, true); - tcpm_set_state(port, SNK_WAIT_CAPABILITIES, 0); + if (!port->pd_supported) + tcpm_set_state(port, SNK_READY, 0); + else + tcpm_set_state(port, SNK_WAIT_CAPABILITIES, 0); break; } /* @@ -4389,7 +4421,8 @@ static void run_state_machine(struct tcpm_port *port) tcpm_set_vbus(port, true); if (port->ams == HARD_RESET) tcpm_ams_finish(port); - port->tcpc->set_pd_rx(port->tcpc, true); + if (port->pd_supported) + port->tcpc->set_pd_rx(port->tcpc, true); tcpm_set_attached_state(port, true); tcpm_set_state(port, SRC_UNATTACHED, PD_T_PS_SOURCE_ON); break; @@ -4479,18 +4512,20 @@ static void run_state_machine(struct tcpm_port *port) /* DR_Swap states */ case DR_SWAP_SEND: tcpm_pd_send_control(port, PD_CTRL_DR_SWAP); + if (port->data_role == TYPEC_DEVICE || port->negotiated_rev > PD_REV20) + port->send_discover = true; tcpm_set_state_cond(port, DR_SWAP_SEND_TIMEOUT, PD_T_SENDER_RESPONSE); break; case DR_SWAP_ACCEPT: tcpm_pd_send_control(port, PD_CTRL_ACCEPT); - /* Set VDM state machine running flag ASAP */ - if (port->data_role == TYPEC_DEVICE && port->send_discover) - port->vdm_sm_running = true; + if (port->data_role == TYPEC_DEVICE || port->negotiated_rev > PD_REV20) + port->send_discover = true; tcpm_set_state_cond(port, DR_SWAP_CHANGE_DR, 0); break; case DR_SWAP_SEND_TIMEOUT: tcpm_swap_complete(port, -ETIMEDOUT); + port->send_discover = false; tcpm_ams_finish(port); tcpm_set_state(port, ready_state(port), 0); break; @@ -4502,7 +4537,6 @@ static void run_state_machine(struct tcpm_port *port) } else { tcpm_set_roles(port, true, port->pwr_role, TYPEC_HOST); - port->send_discover = true; } tcpm_ams_finish(port); tcpm_set_state(port, ready_state(port), 0); @@ -4645,8 +4679,6 @@ static void run_state_machine(struct tcpm_port *port) break; case VCONN_SWAP_SEND_TIMEOUT: tcpm_swap_complete(port, -ETIMEDOUT); - if (port->data_role == TYPEC_HOST && port->send_discover) - port->vdm_sm_running = true; tcpm_set_state(port, ready_state(port), 0); break; case VCONN_SWAP_START: @@ -4662,14 +4694,10 @@ static void run_state_machine(struct tcpm_port *port) case VCONN_SWAP_TURN_ON_VCONN: tcpm_set_vconn(port, true); tcpm_pd_send_control(port, PD_CTRL_PS_RDY); - if (port->data_role == TYPEC_HOST && port->send_discover) - port->vdm_sm_running = true; tcpm_set_state(port, ready_state(port), 0); break; case VCONN_SWAP_TURN_OFF_VCONN: tcpm_set_vconn(port, false); - if (port->data_role == TYPEC_HOST && port->send_discover) - port->vdm_sm_running = true; tcpm_set_state(port, ready_state(port), 0); break; @@ -4677,8 +4705,6 @@ static void run_state_machine(struct tcpm_port *port) case PR_SWAP_CANCEL: case VCONN_SWAP_CANCEL: tcpm_swap_complete(port, port->swap_status); - if (port->data_role == TYPEC_HOST && port->send_discover) - port->vdm_sm_running = true; if (port->pwr_role == TYPEC_SOURCE) tcpm_set_state(port, SRC_READY, 0); else @@ -5028,9 +5054,6 @@ static void _tcpm_pd_vbus_on(struct tcpm_port *port) switch (port->state) { case SNK_TRANSITION_SINK_VBUS: port->explicit_contract = true; - /* Set the VDM flag ASAP */ - if (port->data_role == TYPEC_HOST && port->send_discover) - port->vdm_sm_running = true; tcpm_set_state(port, SNK_READY, 0); break; case SNK_DISCOVERY: @@ -5369,7 +5392,7 @@ EXPORT_SYMBOL_GPL(tcpm_pd_hard_reset); void tcpm_sink_frs(struct tcpm_port *port) { spin_lock(&port->pd_event_lock); - port->pd_events = TCPM_FRS_EVENT; + port->pd_events |= TCPM_FRS_EVENT; spin_unlock(&port->pd_event_lock); kthread_queue_work(port->wq, &port->event_work); } @@ -5378,7 +5401,7 @@ EXPORT_SYMBOL_GPL(tcpm_sink_frs); void tcpm_sourcing_vbus(struct tcpm_port *port) { spin_lock(&port->pd_event_lock); - port->pd_events = TCPM_SOURCING_VBUS; + port->pd_events |= TCPM_SOURCING_VBUS; spin_unlock(&port->pd_event_lock); kthread_queue_work(port->wq, &port->event_work); } @@ -5425,15 +5448,18 @@ static void tcpm_send_discover_work(struct kthread_work *work) if (!port->send_discover) goto unlock; + if (port->data_role == TYPEC_DEVICE && port->negotiated_rev < PD_REV30) { + port->send_discover = false; + goto unlock; + } + /* Retry if the port is not idle */ if ((port->state != SRC_READY && port->state != SNK_READY) || port->vdm_sm_running) { mod_send_discover_delayed_work(port, SEND_DISCOVER_RETRY_MS); goto unlock; } - /* Only send the Message if the port is host for PD rev2.0 */ - if (port->data_role == TYPEC_HOST || port->negotiated_rev > PD_REV20) - tcpm_send_vdm(port, USB_SID_PD, CMD_DISCOVER_IDENT, NULL, 0); + tcpm_send_vdm(port, USB_SID_PD, CMD_DISCOVER_IDENT, NULL, 0); unlock: mutex_unlock(&port->lock); @@ -5898,6 +5924,7 @@ EXPORT_SYMBOL_GPL(tcpm_tcpc_reset); static int tcpm_fw_get_caps(struct tcpm_port *port, struct fwnode_handle *fwnode) { + const char *opmode_str; const char *cap_str; int ret; u32 mw, frs_current; @@ -5932,22 +5959,37 @@ static int tcpm_fw_get_caps(struct tcpm_port *port, return ret; port->typec_caps.type = ret; port->port_type = port->typec_caps.type; + port->pd_supported = !fwnode_property_read_bool(fwnode, "pd-disable"); port->slow_charger_loop = fwnode_property_read_bool(fwnode, "slow-charger-loop"); if (port->port_type == TYPEC_PORT_SNK) goto sink; - /* Get source pdos */ - ret = fwnode_property_count_u32(fwnode, "source-pdos"); - if (ret <= 0) - return -EINVAL; + /* Get Source PDOs for the PD port or Source Rp value for the non-PD port */ + if (port->pd_supported) { + ret = fwnode_property_count_u32(fwnode, "source-pdos"); + if (ret == 0) + return -EINVAL; + else if (ret < 0) + return ret; - port->nr_src_pdo = min(ret, PDO_MAX_OBJECTS); - ret = fwnode_property_read_u32_array(fwnode, "source-pdos", - port->src_pdo, port->nr_src_pdo); - if ((ret < 0) || tcpm_validate_caps(port, port->src_pdo, - port->nr_src_pdo)) - return -EINVAL; + port->nr_src_pdo = min(ret, PDO_MAX_OBJECTS); + ret = fwnode_property_read_u32_array(fwnode, "source-pdos", + port->src_pdo, port->nr_src_pdo); + if (ret) + return ret; + ret = tcpm_validate_caps(port, port->src_pdo, port->nr_src_pdo); + if (ret) + return ret; + } else { + ret = fwnode_property_read_string(fwnode, "typec-power-opmode", &opmode_str); + if (ret) + return ret; + ret = typec_find_pwr_opmode(opmode_str); + if (ret < 0) + return ret; + port->src_rp = tcpm_pwr_opmode_to_rp(ret); + } if (port->port_type == TYPEC_PORT_SRC) return 0; @@ -5961,6 +6003,11 @@ static int tcpm_fw_get_caps(struct tcpm_port *port, if (port->typec_caps.prefer_role < 0) return -EINVAL; sink: + port->self_powered = fwnode_property_read_bool(fwnode, "self-powered"); + + if (!port->pd_supported) + return 0; + /* Get sink pdos */ ret = fwnode_property_count_u32(fwnode, "sink-pdos"); if (ret <= 0) @@ -5977,9 +6024,7 @@ sink: return -EINVAL; port->operating_snk_mw = mw / 1000; - port->self_powered = fwnode_property_read_bool(fwnode, "self-powered"); - - /* FRS can only be supported byb DRP ports */ + /* FRS can only be supported by DRP ports */ if (port->port_type == TYPEC_PORT_DRP) { ret = fwnode_property_read_u32(fwnode, "new-source-frs-typec-current", &frs_current); |