diff options
-rw-r--r-- | drivers/staging/typec/tcpm.c | 60 |
1 files changed, 34 insertions, 26 deletions
diff --git a/drivers/staging/typec/tcpm.c b/drivers/staging/typec/tcpm.c index 8d3a9ad118aa..a15256c190e7 100644 --- a/drivers/staging/typec/tcpm.c +++ b/drivers/staging/typec/tcpm.c | |||
@@ -115,7 +115,8 @@ | |||
115 | S(BIST_RX), \ | 115 | S(BIST_RX), \ |
116 | \ | 116 | \ |
117 | S(ERROR_RECOVERY), \ | 117 | S(ERROR_RECOVERY), \ |
118 | S(ERROR_RECOVERY_WAIT_OFF) | 118 | S(PORT_RESET), \ |
119 | S(PORT_RESET_WAIT_OFF) | ||
119 | 120 | ||
120 | #define GENERATE_ENUM(e) e | 121 | #define GENERATE_ENUM(e) e |
121 | #define GENERATE_STRING(s) #s | 122 | #define GENERATE_STRING(s) #s |
@@ -230,6 +231,7 @@ struct tcpm_port { | |||
230 | 231 | ||
231 | struct mutex swap_lock; /* swap command lock */ | 232 | struct mutex swap_lock; /* swap command lock */ |
232 | bool swap_pending; | 233 | bool swap_pending; |
234 | bool non_pd_role_swap; | ||
233 | struct completion swap_complete; | 235 | struct completion swap_complete; |
234 | int swap_status; | 236 | int swap_status; |
235 | 237 | ||
@@ -2123,6 +2125,7 @@ static void tcpm_swap_complete(struct tcpm_port *port, int result) | |||
2123 | if (port->swap_pending) { | 2125 | if (port->swap_pending) { |
2124 | port->swap_status = result; | 2126 | port->swap_status = result; |
2125 | port->swap_pending = false; | 2127 | port->swap_pending = false; |
2128 | port->non_pd_role_swap = false; | ||
2126 | complete(&port->swap_complete); | 2129 | complete(&port->swap_complete); |
2127 | } | 2130 | } |
2128 | } | 2131 | } |
@@ -2137,7 +2140,8 @@ static void run_state_machine(struct tcpm_port *port) | |||
2137 | break; | 2140 | break; |
2138 | /* SRC states */ | 2141 | /* SRC states */ |
2139 | case SRC_UNATTACHED: | 2142 | case SRC_UNATTACHED: |
2140 | tcpm_swap_complete(port, -ENOTCONN); | 2143 | if (!port->non_pd_role_swap) |
2144 | tcpm_swap_complete(port, -ENOTCONN); | ||
2141 | tcpm_src_detach(port); | 2145 | tcpm_src_detach(port); |
2142 | if (tcpm_start_drp_toggling(port)) { | 2146 | if (tcpm_start_drp_toggling(port)) { |
2143 | tcpm_set_state(port, DRP_TOGGLING, 0); | 2147 | tcpm_set_state(port, DRP_TOGGLING, 0); |
@@ -2292,7 +2296,8 @@ static void run_state_machine(struct tcpm_port *port) | |||
2292 | 2296 | ||
2293 | /* SNK states */ | 2297 | /* SNK states */ |
2294 | case SNK_UNATTACHED: | 2298 | case SNK_UNATTACHED: |
2295 | tcpm_swap_complete(port, -ENOTCONN); | 2299 | if (!port->non_pd_role_swap) |
2300 | tcpm_swap_complete(port, -ENOTCONN); | ||
2296 | tcpm_snk_detach(port); | 2301 | tcpm_snk_detach(port); |
2297 | if (tcpm_start_drp_toggling(port)) { | 2302 | if (tcpm_start_drp_toggling(port)) { |
2298 | tcpm_set_state(port, DRP_TOGGLING, 0); | 2303 | tcpm_set_state(port, DRP_TOGGLING, 0); |
@@ -2703,13 +2708,15 @@ static void run_state_machine(struct tcpm_port *port) | |||
2703 | break; | 2708 | break; |
2704 | case ERROR_RECOVERY: | 2709 | case ERROR_RECOVERY: |
2705 | tcpm_swap_complete(port, -EPROTO); | 2710 | tcpm_swap_complete(port, -EPROTO); |
2711 | tcpm_set_state(port, PORT_RESET, 0); | ||
2712 | break; | ||
2713 | case PORT_RESET: | ||
2706 | tcpm_reset_port(port); | 2714 | tcpm_reset_port(port); |
2707 | |||
2708 | tcpm_set_cc(port, TYPEC_CC_OPEN); | 2715 | tcpm_set_cc(port, TYPEC_CC_OPEN); |
2709 | tcpm_set_state(port, ERROR_RECOVERY_WAIT_OFF, | 2716 | tcpm_set_state(port, PORT_RESET_WAIT_OFF, |
2710 | PD_T_ERROR_RECOVERY); | 2717 | PD_T_ERROR_RECOVERY); |
2711 | break; | 2718 | break; |
2712 | case ERROR_RECOVERY_WAIT_OFF: | 2719 | case PORT_RESET_WAIT_OFF: |
2713 | tcpm_set_state(port, | 2720 | tcpm_set_state(port, |
2714 | tcpm_default_state(port), | 2721 | tcpm_default_state(port), |
2715 | port->vbus_present ? PD_T_PS_SOURCE_OFF : 0); | 2722 | port->vbus_present ? PD_T_PS_SOURCE_OFF : 0); |
@@ -3041,7 +3048,7 @@ static void _tcpm_pd_vbus_off(struct tcpm_port *port) | |||
3041 | /* Do nothing, expected */ | 3048 | /* Do nothing, expected */ |
3042 | break; | 3049 | break; |
3043 | 3050 | ||
3044 | case ERROR_RECOVERY_WAIT_OFF: | 3051 | case PORT_RESET_WAIT_OFF: |
3045 | tcpm_set_state(port, tcpm_default_state(port), 0); | 3052 | tcpm_set_state(port, tcpm_default_state(port), 0); |
3046 | break; | 3053 | break; |
3047 | 3054 | ||
@@ -3138,7 +3145,7 @@ static int tcpm_dr_set(const struct typec_capability *cap, | |||
3138 | mutex_lock(&port->swap_lock); | 3145 | mutex_lock(&port->swap_lock); |
3139 | mutex_lock(&port->lock); | 3146 | mutex_lock(&port->lock); |
3140 | 3147 | ||
3141 | if (port->typec_caps.type != TYPEC_PORT_DRP || !port->pd_capable) { | 3148 | if (port->typec_caps.type != TYPEC_PORT_DRP) { |
3142 | ret = -EINVAL; | 3149 | ret = -EINVAL; |
3143 | goto port_unlock; | 3150 | goto port_unlock; |
3144 | } | 3151 | } |
@@ -3159,10 +3166,26 @@ static int tcpm_dr_set(const struct typec_capability *cap, | |||
3159 | * Reject data role swap request in this case. | 3166 | * Reject data role swap request in this case. |
3160 | */ | 3167 | */ |
3161 | 3168 | ||
3169 | if (!port->pd_capable) { | ||
3170 | /* | ||
3171 | * If the partner is not PD capable, reset the port to | ||
3172 | * trigger a role change. This can only work if a preferred | ||
3173 | * role is configured, and if it matches the requested role. | ||
3174 | */ | ||
3175 | if (port->try_role == TYPEC_NO_PREFERRED_ROLE || | ||
3176 | port->try_role == port->pwr_role) { | ||
3177 | ret = -EINVAL; | ||
3178 | goto port_unlock; | ||
3179 | } | ||
3180 | port->non_pd_role_swap = true; | ||
3181 | tcpm_set_state(port, PORT_RESET, 0); | ||
3182 | } else { | ||
3183 | tcpm_set_state(port, DR_SWAP_SEND, 0); | ||
3184 | } | ||
3185 | |||
3162 | port->swap_status = 0; | 3186 | port->swap_status = 0; |
3163 | port->swap_pending = true; | 3187 | port->swap_pending = true; |
3164 | reinit_completion(&port->swap_complete); | 3188 | reinit_completion(&port->swap_complete); |
3165 | tcpm_set_state(port, DR_SWAP_SEND, 0); | ||
3166 | mutex_unlock(&port->lock); | 3189 | mutex_unlock(&port->lock); |
3167 | 3190 | ||
3168 | if (!wait_for_completion_timeout(&port->swap_complete, | 3191 | if (!wait_for_completion_timeout(&port->swap_complete, |
@@ -3171,6 +3194,7 @@ static int tcpm_dr_set(const struct typec_capability *cap, | |||
3171 | else | 3194 | else |
3172 | ret = port->swap_status; | 3195 | ret = port->swap_status; |
3173 | 3196 | ||
3197 | port->non_pd_role_swap = false; | ||
3174 | goto swap_unlock; | 3198 | goto swap_unlock; |
3175 | 3199 | ||
3176 | port_unlock: | 3200 | port_unlock: |
@@ -3203,22 +3227,6 @@ static int tcpm_pr_set(const struct typec_capability *cap, | |||
3203 | goto port_unlock; | 3227 | goto port_unlock; |
3204 | } | 3228 | } |
3205 | 3229 | ||
3206 | if (!port->pd_capable) { | ||
3207 | /* | ||
3208 | * If the partner is not PD capable, reset the port to | ||
3209 | * trigger a role change. This can only work if a preferred | ||
3210 | * role is configured, and if it matches the requested role. | ||
3211 | */ | ||
3212 | if (port->try_role == TYPEC_NO_PREFERRED_ROLE || | ||
3213 | port->try_role == port->pwr_role) { | ||
3214 | ret = -EINVAL; | ||
3215 | goto port_unlock; | ||
3216 | } | ||
3217 | tcpm_set_state(port, HARD_RESET_SEND, 0); | ||
3218 | ret = 0; | ||
3219 | goto port_unlock; | ||
3220 | } | ||
3221 | |||
3222 | port->swap_status = 0; | 3230 | port->swap_status = 0; |
3223 | port->swap_pending = true; | 3231 | port->swap_pending = true; |
3224 | reinit_completion(&port->swap_complete); | 3232 | reinit_completion(&port->swap_complete); |
@@ -3324,7 +3332,7 @@ static void tcpm_init(struct tcpm_port *port) | |||
3324 | * Some adapters need a clean slate at startup, and won't recover | 3332 | * Some adapters need a clean slate at startup, and won't recover |
3325 | * otherwise. So do not try to be fancy and force a clean disconnect. | 3333 | * otherwise. So do not try to be fancy and force a clean disconnect. |
3326 | */ | 3334 | */ |
3327 | tcpm_set_state(port, ERROR_RECOVERY, 0); | 3335 | tcpm_set_state(port, PORT_RESET, 0); |
3328 | } | 3336 | } |
3329 | 3337 | ||
3330 | void tcpm_tcpc_reset(struct tcpm_port *port) | 3338 | void tcpm_tcpc_reset(struct tcpm_port *port) |