Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

staging: typec: tcpm: Improve role swap with non PD capable partners

If the partner is not PD capable, we can not use a power role set request
to swap roles. Use the data role set request instead.

Also, if a partner is not PD capable, it does not really make sense to send
a PD message to trigger a role swap. On top of that, we should really wait
for the attempted role change to complete. Otherwise, it may well be that
user space requests another role change immediately afterwards which will
fail because the port is not yet in ready state.

Trigger the role swap from data role change requests and introduce new
state PORT_RESET and use it to solve the problem. This new state is
mostly identical to ERROR_RECOVERY, only it does not cause a pending
role change to fail. Use this new state also when initializing the driver.
Rename ERROR_RECOVERY_WAIT_OFF to PORT_RESET_WAIT_OFF to better reflect
its new meaning.

Signed-off-by: Guenter Roeck <linux@roeck-us.net>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>

authored by

Guenter Roeck and committed by
Greg Kroah-Hartman
b17dd571 9adf9f9e

+34 -26
+34 -26
drivers/staging/typec/tcpm.c
··· 115 115 S(BIST_RX), \ 116 116 \ 117 117 S(ERROR_RECOVERY), \ 118 - S(ERROR_RECOVERY_WAIT_OFF) 118 + S(PORT_RESET), \ 119 + S(PORT_RESET_WAIT_OFF) 119 120 120 121 #define GENERATE_ENUM(e) e 121 122 #define GENERATE_STRING(s) #s ··· 231 230 232 231 struct mutex swap_lock; /* swap command lock */ 233 232 bool swap_pending; 233 + bool non_pd_role_swap; 234 234 struct completion swap_complete; 235 235 int swap_status; 236 236 ··· 2125 2123 if (port->swap_pending) { 2126 2124 port->swap_status = result; 2127 2125 port->swap_pending = false; 2126 + port->non_pd_role_swap = false; 2128 2127 complete(&port->swap_complete); 2129 2128 } 2130 2129 } ··· 2140 2137 break; 2141 2138 /* SRC states */ 2142 2139 case SRC_UNATTACHED: 2143 - tcpm_swap_complete(port, -ENOTCONN); 2140 + if (!port->non_pd_role_swap) 2141 + tcpm_swap_complete(port, -ENOTCONN); 2144 2142 tcpm_src_detach(port); 2145 2143 if (tcpm_start_drp_toggling(port)) { 2146 2144 tcpm_set_state(port, DRP_TOGGLING, 0); ··· 2296 2292 2297 2293 /* SNK states */ 2298 2294 case SNK_UNATTACHED: 2299 - tcpm_swap_complete(port, -ENOTCONN); 2295 + if (!port->non_pd_role_swap) 2296 + tcpm_swap_complete(port, -ENOTCONN); 2300 2297 tcpm_snk_detach(port); 2301 2298 if (tcpm_start_drp_toggling(port)) { 2302 2299 tcpm_set_state(port, DRP_TOGGLING, 0); ··· 2708 2703 break; 2709 2704 case ERROR_RECOVERY: 2710 2705 tcpm_swap_complete(port, -EPROTO); 2706 + tcpm_set_state(port, PORT_RESET, 0); 2707 + break; 2708 + case PORT_RESET: 2711 2709 tcpm_reset_port(port); 2712 - 2713 2710 tcpm_set_cc(port, TYPEC_CC_OPEN); 2714 - tcpm_set_state(port, ERROR_RECOVERY_WAIT_OFF, 2711 + tcpm_set_state(port, PORT_RESET_WAIT_OFF, 2715 2712 PD_T_ERROR_RECOVERY); 2716 2713 break; 2717 - case ERROR_RECOVERY_WAIT_OFF: 2714 + case PORT_RESET_WAIT_OFF: 2718 2715 tcpm_set_state(port, 2719 2716 tcpm_default_state(port), 2720 2717 port->vbus_present ? PD_T_PS_SOURCE_OFF : 0); ··· 3048 3041 /* Do nothing, expected */ 3049 3042 break; 3050 3043 3051 - case ERROR_RECOVERY_WAIT_OFF: 3044 + case PORT_RESET_WAIT_OFF: 3052 3045 tcpm_set_state(port, tcpm_default_state(port), 0); 3053 3046 break; 3054 3047 ··· 3145 3138 mutex_lock(&port->swap_lock); 3146 3139 mutex_lock(&port->lock); 3147 3140 3148 - if (port->typec_caps.type != TYPEC_PORT_DRP || !port->pd_capable) { 3141 + if (port->typec_caps.type != TYPEC_PORT_DRP) { 3149 3142 ret = -EINVAL; 3150 3143 goto port_unlock; 3151 3144 } ··· 3166 3159 * Reject data role swap request in this case. 3167 3160 */ 3168 3161 3162 + if (!port->pd_capable) { 3163 + /* 3164 + * If the partner is not PD capable, reset the port to 3165 + * trigger a role change. This can only work if a preferred 3166 + * role is configured, and if it matches the requested role. 3167 + */ 3168 + if (port->try_role == TYPEC_NO_PREFERRED_ROLE || 3169 + port->try_role == port->pwr_role) { 3170 + ret = -EINVAL; 3171 + goto port_unlock; 3172 + } 3173 + port->non_pd_role_swap = true; 3174 + tcpm_set_state(port, PORT_RESET, 0); 3175 + } else { 3176 + tcpm_set_state(port, DR_SWAP_SEND, 0); 3177 + } 3178 + 3169 3179 port->swap_status = 0; 3170 3180 port->swap_pending = true; 3171 3181 reinit_completion(&port->swap_complete); 3172 - tcpm_set_state(port, DR_SWAP_SEND, 0); 3173 3182 mutex_unlock(&port->lock); 3174 3183 3175 3184 if (!wait_for_completion_timeout(&port->swap_complete, ··· 3194 3171 else 3195 3172 ret = port->swap_status; 3196 3173 3174 + port->non_pd_role_swap = false; 3197 3175 goto swap_unlock; 3198 3176 3199 3177 port_unlock: ··· 3223 3199 } 3224 3200 3225 3201 if (role == port->pwr_role) { 3226 - ret = 0; 3227 - goto port_unlock; 3228 - } 3229 - 3230 - if (!port->pd_capable) { 3231 - /* 3232 - * If the partner is not PD capable, reset the port to 3233 - * trigger a role change. This can only work if a preferred 3234 - * role is configured, and if it matches the requested role. 3235 - */ 3236 - if (port->try_role == TYPEC_NO_PREFERRED_ROLE || 3237 - port->try_role == port->pwr_role) { 3238 - ret = -EINVAL; 3239 - goto port_unlock; 3240 - } 3241 - tcpm_set_state(port, HARD_RESET_SEND, 0); 3242 3202 ret = 0; 3243 3203 goto port_unlock; 3244 3204 } ··· 3332 3324 * Some adapters need a clean slate at startup, and won't recover 3333 3325 * otherwise. So do not try to be fancy and force a clean disconnect. 3334 3326 */ 3335 - tcpm_set_state(port, ERROR_RECOVERY, 0); 3327 + tcpm_set_state(port, PORT_RESET, 0); 3336 3328 } 3337 3329 3338 3330 void tcpm_tcpc_reset(struct tcpm_port *port)