.. | .. |
---|
12 | 12 | #include <linux/module.h> |
---|
13 | 13 | #include <linux/delay.h> |
---|
14 | 14 | #include <linux/slab.h> |
---|
15 | | -#include <linux/usb/typec.h> |
---|
| 15 | +#include <linux/usb/typec_dp.h> |
---|
16 | 16 | |
---|
17 | 17 | #include "ucsi.h" |
---|
18 | 18 | #include "trace.h" |
---|
19 | | - |
---|
20 | | -#define to_ucsi_connector(_cap_) container_of(_cap_, struct ucsi_connector, \ |
---|
21 | | - typec_cap) |
---|
22 | 19 | |
---|
23 | 20 | /* |
---|
24 | 21 | * UCSI_TIMEOUT_MS - PPM communication timeout |
---|
.. | .. |
---|
39 | 36 | */ |
---|
40 | 37 | #define UCSI_SWAP_TIMEOUT_MS 5000 |
---|
41 | 38 | |
---|
42 | | -enum ucsi_status { |
---|
43 | | - UCSI_IDLE = 0, |
---|
44 | | - UCSI_BUSY, |
---|
45 | | - UCSI_ERROR, |
---|
46 | | -}; |
---|
47 | | - |
---|
48 | | -struct ucsi_connector { |
---|
49 | | - int num; |
---|
50 | | - |
---|
51 | | - struct ucsi *ucsi; |
---|
52 | | - struct work_struct work; |
---|
53 | | - struct completion complete; |
---|
54 | | - |
---|
55 | | - struct typec_port *port; |
---|
56 | | - struct typec_partner *partner; |
---|
57 | | - |
---|
58 | | - struct typec_capability typec_cap; |
---|
59 | | - |
---|
60 | | - struct ucsi_connector_status status; |
---|
61 | | - struct ucsi_connector_capability cap; |
---|
62 | | -}; |
---|
63 | | - |
---|
64 | | -struct ucsi { |
---|
65 | | - struct device *dev; |
---|
66 | | - struct ucsi_ppm *ppm; |
---|
67 | | - |
---|
68 | | - enum ucsi_status status; |
---|
69 | | - struct completion complete; |
---|
70 | | - struct ucsi_capability cap; |
---|
71 | | - struct ucsi_connector *connector; |
---|
72 | | - |
---|
73 | | - struct work_struct work; |
---|
74 | | - |
---|
75 | | - /* PPM Communication lock */ |
---|
76 | | - struct mutex ppm_lock; |
---|
77 | | - |
---|
78 | | - /* PPM communication flags */ |
---|
79 | | - unsigned long flags; |
---|
80 | | -#define EVENT_PENDING 0 |
---|
81 | | -#define COMMAND_PENDING 1 |
---|
82 | | -#define ACK_PENDING 2 |
---|
83 | | -}; |
---|
84 | | - |
---|
85 | | -static inline int ucsi_sync(struct ucsi *ucsi) |
---|
| 39 | +static int ucsi_acknowledge_command(struct ucsi *ucsi) |
---|
86 | 40 | { |
---|
87 | | - if (ucsi->ppm && ucsi->ppm->sync) |
---|
88 | | - return ucsi->ppm->sync(ucsi->ppm); |
---|
89 | | - return 0; |
---|
| 41 | + u64 ctrl; |
---|
| 42 | + |
---|
| 43 | + ctrl = UCSI_ACK_CC_CI; |
---|
| 44 | + ctrl |= UCSI_ACK_COMMAND_COMPLETE; |
---|
| 45 | + |
---|
| 46 | + return ucsi->ops->sync_write(ucsi, UCSI_CONTROL, &ctrl, sizeof(ctrl)); |
---|
90 | 47 | } |
---|
91 | 48 | |
---|
92 | | -static int ucsi_command(struct ucsi *ucsi, struct ucsi_control *ctrl) |
---|
| 49 | +static int ucsi_acknowledge_connector_change(struct ucsi *ucsi) |
---|
93 | 50 | { |
---|
94 | | - int ret; |
---|
| 51 | + u64 ctrl; |
---|
95 | 52 | |
---|
96 | | - trace_ucsi_command(ctrl); |
---|
| 53 | + ctrl = UCSI_ACK_CC_CI; |
---|
| 54 | + ctrl |= UCSI_ACK_CONNECTOR_CHANGE; |
---|
97 | 55 | |
---|
98 | | - set_bit(COMMAND_PENDING, &ucsi->flags); |
---|
99 | | - |
---|
100 | | - ret = ucsi->ppm->cmd(ucsi->ppm, ctrl); |
---|
101 | | - if (ret) |
---|
102 | | - goto err_clear_flag; |
---|
103 | | - |
---|
104 | | - if (!wait_for_completion_timeout(&ucsi->complete, |
---|
105 | | - msecs_to_jiffies(UCSI_TIMEOUT_MS))) { |
---|
106 | | - dev_warn(ucsi->dev, "PPM NOT RESPONDING\n"); |
---|
107 | | - ret = -ETIMEDOUT; |
---|
108 | | - } |
---|
109 | | - |
---|
110 | | -err_clear_flag: |
---|
111 | | - clear_bit(COMMAND_PENDING, &ucsi->flags); |
---|
112 | | - |
---|
113 | | - return ret; |
---|
| 56 | + return ucsi->ops->sync_write(ucsi, UCSI_CONTROL, &ctrl, sizeof(ctrl)); |
---|
114 | 57 | } |
---|
115 | 58 | |
---|
116 | | -static int ucsi_ack(struct ucsi *ucsi, u8 ack) |
---|
| 59 | +static int ucsi_exec_command(struct ucsi *ucsi, u64 command); |
---|
| 60 | + |
---|
| 61 | +static int ucsi_read_error(struct ucsi *ucsi) |
---|
117 | 62 | { |
---|
118 | | - struct ucsi_control ctrl; |
---|
119 | | - int ret; |
---|
120 | | - |
---|
121 | | - trace_ucsi_ack(ack); |
---|
122 | | - |
---|
123 | | - set_bit(ACK_PENDING, &ucsi->flags); |
---|
124 | | - |
---|
125 | | - UCSI_CMD_ACK(ctrl, ack); |
---|
126 | | - ret = ucsi->ppm->cmd(ucsi->ppm, &ctrl); |
---|
127 | | - if (ret) |
---|
128 | | - goto out_clear_bit; |
---|
129 | | - |
---|
130 | | - /* Waiting for ACK with ACK CMD, but not with EVENT for now */ |
---|
131 | | - if (ack == UCSI_ACK_EVENT) |
---|
132 | | - goto out_clear_bit; |
---|
133 | | - |
---|
134 | | - if (!wait_for_completion_timeout(&ucsi->complete, |
---|
135 | | - msecs_to_jiffies(UCSI_TIMEOUT_MS))) |
---|
136 | | - ret = -ETIMEDOUT; |
---|
137 | | - |
---|
138 | | -out_clear_bit: |
---|
139 | | - clear_bit(ACK_PENDING, &ucsi->flags); |
---|
140 | | - |
---|
141 | | - if (ret) |
---|
142 | | - dev_err(ucsi->dev, "%s: failed\n", __func__); |
---|
143 | | - |
---|
144 | | - return ret; |
---|
145 | | -} |
---|
146 | | - |
---|
147 | | -static int ucsi_run_command(struct ucsi *ucsi, struct ucsi_control *ctrl, |
---|
148 | | - void *data, size_t size) |
---|
149 | | -{ |
---|
150 | | - struct ucsi_control _ctrl; |
---|
151 | | - u8 data_length; |
---|
152 | 63 | u16 error; |
---|
153 | 64 | int ret; |
---|
154 | 65 | |
---|
155 | | - ret = ucsi_command(ucsi, ctrl); |
---|
| 66 | + /* Acknowlege the command that failed */ |
---|
| 67 | + ret = ucsi_acknowledge_command(ucsi); |
---|
156 | 68 | if (ret) |
---|
157 | | - goto err; |
---|
| 69 | + return ret; |
---|
158 | 70 | |
---|
159 | | - switch (ucsi->status) { |
---|
160 | | - case UCSI_IDLE: |
---|
161 | | - ret = ucsi_sync(ucsi); |
---|
162 | | - if (ret) |
---|
163 | | - dev_warn(ucsi->dev, "%s: sync failed\n", __func__); |
---|
| 71 | + ret = ucsi_exec_command(ucsi, UCSI_GET_ERROR_STATUS); |
---|
| 72 | + if (ret < 0) |
---|
| 73 | + return ret; |
---|
164 | 74 | |
---|
165 | | - if (data) |
---|
166 | | - memcpy(data, ucsi->ppm->data->message_in, size); |
---|
| 75 | + ret = ucsi->ops->read(ucsi, UCSI_MESSAGE_IN, &error, sizeof(error)); |
---|
| 76 | + if (ret) |
---|
| 77 | + return ret; |
---|
167 | 78 | |
---|
168 | | - data_length = ucsi->ppm->data->cci.data_length; |
---|
| 79 | + ret = ucsi_acknowledge_command(ucsi); |
---|
| 80 | + if (ret) |
---|
| 81 | + return ret; |
---|
169 | 82 | |
---|
170 | | - ret = ucsi_ack(ucsi, UCSI_ACK_CMD); |
---|
171 | | - if (!ret) |
---|
172 | | - ret = data_length; |
---|
| 83 | + switch (error) { |
---|
| 84 | + case UCSI_ERROR_INCOMPATIBLE_PARTNER: |
---|
| 85 | + return -EOPNOTSUPP; |
---|
| 86 | + case UCSI_ERROR_CC_COMMUNICATION_ERR: |
---|
| 87 | + return -ECOMM; |
---|
| 88 | + case UCSI_ERROR_CONTRACT_NEGOTIATION_FAIL: |
---|
| 89 | + return -EPROTO; |
---|
| 90 | + case UCSI_ERROR_DEAD_BATTERY: |
---|
| 91 | + dev_warn(ucsi->dev, "Dead battery condition!\n"); |
---|
| 92 | + return -EPERM; |
---|
| 93 | + case UCSI_ERROR_INVALID_CON_NUM: |
---|
| 94 | + case UCSI_ERROR_UNREGONIZED_CMD: |
---|
| 95 | + case UCSI_ERROR_INVALID_CMD_ARGUMENT: |
---|
| 96 | + dev_err(ucsi->dev, "possible UCSI driver bug %u\n", error); |
---|
| 97 | + return -EINVAL; |
---|
| 98 | + case UCSI_ERROR_OVERCURRENT: |
---|
| 99 | + dev_warn(ucsi->dev, "Overcurrent condition\n"); |
---|
173 | 100 | break; |
---|
174 | | - case UCSI_BUSY: |
---|
175 | | - /* The caller decides whether to cancel or not */ |
---|
176 | | - ret = -EBUSY; |
---|
| 101 | + case UCSI_ERROR_PARTNER_REJECTED_SWAP: |
---|
| 102 | + dev_warn(ucsi->dev, "Partner rejected swap\n"); |
---|
177 | 103 | break; |
---|
178 | | - case UCSI_ERROR: |
---|
179 | | - ret = ucsi_ack(ucsi, UCSI_ACK_CMD); |
---|
180 | | - if (ret) |
---|
181 | | - break; |
---|
182 | | - |
---|
183 | | - _ctrl.raw_cmd = 0; |
---|
184 | | - _ctrl.cmd.cmd = UCSI_GET_ERROR_STATUS; |
---|
185 | | - ret = ucsi_command(ucsi, &_ctrl); |
---|
186 | | - if (ret) { |
---|
187 | | - dev_err(ucsi->dev, "reading error failed!\n"); |
---|
188 | | - break; |
---|
189 | | - } |
---|
190 | | - |
---|
191 | | - memcpy(&error, ucsi->ppm->data->message_in, sizeof(error)); |
---|
192 | | - |
---|
193 | | - /* Something has really gone wrong */ |
---|
194 | | - if (WARN_ON(ucsi->status == UCSI_ERROR)) { |
---|
195 | | - ret = -ENODEV; |
---|
196 | | - break; |
---|
197 | | - } |
---|
198 | | - |
---|
199 | | - ret = ucsi_ack(ucsi, UCSI_ACK_CMD); |
---|
200 | | - if (ret) |
---|
201 | | - break; |
---|
202 | | - |
---|
203 | | - switch (error) { |
---|
204 | | - case UCSI_ERROR_INCOMPATIBLE_PARTNER: |
---|
205 | | - ret = -EOPNOTSUPP; |
---|
206 | | - break; |
---|
207 | | - case UCSI_ERROR_CC_COMMUNICATION_ERR: |
---|
208 | | - ret = -ECOMM; |
---|
209 | | - break; |
---|
210 | | - case UCSI_ERROR_CONTRACT_NEGOTIATION_FAIL: |
---|
211 | | - ret = -EPROTO; |
---|
212 | | - break; |
---|
213 | | - case UCSI_ERROR_DEAD_BATTERY: |
---|
214 | | - dev_warn(ucsi->dev, "Dead battery condition!\n"); |
---|
215 | | - ret = -EPERM; |
---|
216 | | - break; |
---|
217 | | - /* The following mean a bug in this driver */ |
---|
218 | | - case UCSI_ERROR_INVALID_CON_NUM: |
---|
219 | | - case UCSI_ERROR_UNREGONIZED_CMD: |
---|
220 | | - case UCSI_ERROR_INVALID_CMD_ARGUMENT: |
---|
221 | | - dev_warn(ucsi->dev, |
---|
222 | | - "%s: possible UCSI driver bug - error 0x%x\n", |
---|
223 | | - __func__, error); |
---|
224 | | - ret = -EINVAL; |
---|
225 | | - break; |
---|
226 | | - default: |
---|
227 | | - dev_warn(ucsi->dev, |
---|
228 | | - "%s: error without status\n", __func__); |
---|
229 | | - ret = -EIO; |
---|
230 | | - break; |
---|
231 | | - } |
---|
| 104 | + case UCSI_ERROR_HARD_RESET: |
---|
| 105 | + dev_warn(ucsi->dev, "Hard reset occurred\n"); |
---|
| 106 | + break; |
---|
| 107 | + case UCSI_ERROR_PPM_POLICY_CONFLICT: |
---|
| 108 | + dev_warn(ucsi->dev, "PPM Policy conflict\n"); |
---|
| 109 | + break; |
---|
| 110 | + case UCSI_ERROR_SWAP_REJECTED: |
---|
| 111 | + dev_warn(ucsi->dev, "Swap rejected\n"); |
---|
| 112 | + break; |
---|
| 113 | + case UCSI_ERROR_UNDEFINED: |
---|
| 114 | + default: |
---|
| 115 | + dev_err(ucsi->dev, "unknown error %u\n", error); |
---|
232 | 116 | break; |
---|
233 | 117 | } |
---|
234 | 118 | |
---|
| 119 | + return -EIO; |
---|
| 120 | +} |
---|
| 121 | + |
---|
| 122 | +static int ucsi_exec_command(struct ucsi *ucsi, u64 cmd) |
---|
| 123 | +{ |
---|
| 124 | + u32 cci; |
---|
| 125 | + int ret; |
---|
| 126 | + |
---|
| 127 | + ret = ucsi->ops->sync_write(ucsi, UCSI_CONTROL, &cmd, sizeof(cmd)); |
---|
| 128 | + if (ret) |
---|
| 129 | + return ret; |
---|
| 130 | + |
---|
| 131 | + ret = ucsi->ops->read(ucsi, UCSI_CCI, &cci, sizeof(cci)); |
---|
| 132 | + if (ret) |
---|
| 133 | + return ret; |
---|
| 134 | + |
---|
| 135 | + if (cci & UCSI_CCI_BUSY) |
---|
| 136 | + return -EBUSY; |
---|
| 137 | + |
---|
| 138 | + if (!(cci & UCSI_CCI_COMMAND_COMPLETE)) |
---|
| 139 | + return -EIO; |
---|
| 140 | + |
---|
| 141 | + if (cci & UCSI_CCI_NOT_SUPPORTED) |
---|
| 142 | + return -EOPNOTSUPP; |
---|
| 143 | + |
---|
| 144 | + if (cci & UCSI_CCI_ERROR) { |
---|
| 145 | + if (cmd == UCSI_GET_ERROR_STATUS) |
---|
| 146 | + return -EIO; |
---|
| 147 | + return ucsi_read_error(ucsi); |
---|
| 148 | + } |
---|
| 149 | + |
---|
| 150 | + return UCSI_CCI_LENGTH(cci); |
---|
| 151 | +} |
---|
| 152 | + |
---|
| 153 | +int ucsi_send_command(struct ucsi *ucsi, u64 command, |
---|
| 154 | + void *data, size_t size) |
---|
| 155 | +{ |
---|
| 156 | + u8 length; |
---|
| 157 | + int ret; |
---|
| 158 | + |
---|
| 159 | + mutex_lock(&ucsi->ppm_lock); |
---|
| 160 | + |
---|
| 161 | + ret = ucsi_exec_command(ucsi, command); |
---|
| 162 | + if (ret < 0) |
---|
| 163 | + goto out; |
---|
| 164 | + |
---|
| 165 | + length = ret; |
---|
| 166 | + |
---|
| 167 | + if (data) { |
---|
| 168 | + ret = ucsi->ops->read(ucsi, UCSI_MESSAGE_IN, data, size); |
---|
| 169 | + if (ret) |
---|
| 170 | + goto out; |
---|
| 171 | + } |
---|
| 172 | + |
---|
| 173 | + ret = ucsi_acknowledge_command(ucsi); |
---|
| 174 | + if (ret) |
---|
| 175 | + goto out; |
---|
| 176 | + |
---|
| 177 | + ret = length; |
---|
| 178 | +out: |
---|
| 179 | + mutex_unlock(&ucsi->ppm_lock); |
---|
| 180 | + return ret; |
---|
| 181 | +} |
---|
| 182 | +EXPORT_SYMBOL_GPL(ucsi_send_command); |
---|
| 183 | + |
---|
| 184 | +int ucsi_resume(struct ucsi *ucsi) |
---|
| 185 | +{ |
---|
| 186 | + u64 command; |
---|
| 187 | + |
---|
| 188 | + /* Restore UCSI notification enable mask after system resume */ |
---|
| 189 | + command = UCSI_SET_NOTIFICATION_ENABLE | ucsi->ntfy; |
---|
| 190 | + |
---|
| 191 | + return ucsi_send_command(ucsi, command, NULL, 0); |
---|
| 192 | +} |
---|
| 193 | +EXPORT_SYMBOL_GPL(ucsi_resume); |
---|
| 194 | +/* -------------------------------------------------------------------------- */ |
---|
| 195 | + |
---|
| 196 | +void ucsi_altmode_update_active(struct ucsi_connector *con) |
---|
| 197 | +{ |
---|
| 198 | + const struct typec_altmode *altmode = NULL; |
---|
| 199 | + u64 command; |
---|
| 200 | + int ret; |
---|
| 201 | + u8 cur; |
---|
| 202 | + int i; |
---|
| 203 | + |
---|
| 204 | + command = UCSI_GET_CURRENT_CAM | UCSI_CONNECTOR_NUMBER(con->num); |
---|
| 205 | + ret = ucsi_send_command(con->ucsi, command, &cur, sizeof(cur)); |
---|
| 206 | + if (ret < 0) { |
---|
| 207 | + if (con->ucsi->version > 0x0100) { |
---|
| 208 | + dev_err(con->ucsi->dev, |
---|
| 209 | + "GET_CURRENT_CAM command failed\n"); |
---|
| 210 | + return; |
---|
| 211 | + } |
---|
| 212 | + cur = 0xff; |
---|
| 213 | + } |
---|
| 214 | + |
---|
| 215 | + if (cur < UCSI_MAX_ALTMODES) |
---|
| 216 | + altmode = typec_altmode_get_partner(con->port_altmode[cur]); |
---|
| 217 | + |
---|
| 218 | + for (i = 0; con->partner_altmode[i]; i++) |
---|
| 219 | + typec_altmode_update_active(con->partner_altmode[i], |
---|
| 220 | + con->partner_altmode[i] == altmode); |
---|
| 221 | +} |
---|
| 222 | + |
---|
| 223 | +static int ucsi_altmode_next_mode(struct typec_altmode **alt, u16 svid) |
---|
| 224 | +{ |
---|
| 225 | + u8 mode = 1; |
---|
| 226 | + int i; |
---|
| 227 | + |
---|
| 228 | + for (i = 0; alt[i]; i++) { |
---|
| 229 | + if (i > MODE_DISCOVERY_MAX) |
---|
| 230 | + return -ERANGE; |
---|
| 231 | + |
---|
| 232 | + if (alt[i]->svid == svid) |
---|
| 233 | + mode++; |
---|
| 234 | + } |
---|
| 235 | + |
---|
| 236 | + return mode; |
---|
| 237 | +} |
---|
| 238 | + |
---|
| 239 | +static int ucsi_next_altmode(struct typec_altmode **alt) |
---|
| 240 | +{ |
---|
| 241 | + int i = 0; |
---|
| 242 | + |
---|
| 243 | + for (i = 0; i < UCSI_MAX_ALTMODES; i++) |
---|
| 244 | + if (!alt[i]) |
---|
| 245 | + return i; |
---|
| 246 | + |
---|
| 247 | + return -ENOENT; |
---|
| 248 | +} |
---|
| 249 | + |
---|
| 250 | +static int ucsi_register_altmode(struct ucsi_connector *con, |
---|
| 251 | + struct typec_altmode_desc *desc, |
---|
| 252 | + u8 recipient) |
---|
| 253 | +{ |
---|
| 254 | + struct typec_altmode *alt; |
---|
| 255 | + bool override; |
---|
| 256 | + int ret; |
---|
| 257 | + int i; |
---|
| 258 | + |
---|
| 259 | + override = !!(con->ucsi->cap.features & UCSI_CAP_ALT_MODE_OVERRIDE); |
---|
| 260 | + |
---|
| 261 | + switch (recipient) { |
---|
| 262 | + case UCSI_RECIPIENT_CON: |
---|
| 263 | + i = ucsi_next_altmode(con->port_altmode); |
---|
| 264 | + if (i < 0) { |
---|
| 265 | + ret = i; |
---|
| 266 | + goto err; |
---|
| 267 | + } |
---|
| 268 | + |
---|
| 269 | + ret = ucsi_altmode_next_mode(con->port_altmode, desc->svid); |
---|
| 270 | + if (ret < 0) |
---|
| 271 | + return ret; |
---|
| 272 | + |
---|
| 273 | + desc->mode = ret; |
---|
| 274 | + |
---|
| 275 | + switch (desc->svid) { |
---|
| 276 | + case USB_TYPEC_DP_SID: |
---|
| 277 | + alt = ucsi_register_displayport(con, override, i, desc); |
---|
| 278 | + break; |
---|
| 279 | + case USB_TYPEC_NVIDIA_VLINK_SID: |
---|
| 280 | + if (desc->vdo == USB_TYPEC_NVIDIA_VLINK_DBG_VDO) |
---|
| 281 | + alt = typec_port_register_altmode(con->port, |
---|
| 282 | + desc); |
---|
| 283 | + else |
---|
| 284 | + alt = ucsi_register_displayport(con, override, |
---|
| 285 | + i, desc); |
---|
| 286 | + break; |
---|
| 287 | + default: |
---|
| 288 | + alt = typec_port_register_altmode(con->port, desc); |
---|
| 289 | + break; |
---|
| 290 | + } |
---|
| 291 | + |
---|
| 292 | + if (IS_ERR(alt)) { |
---|
| 293 | + ret = PTR_ERR(alt); |
---|
| 294 | + goto err; |
---|
| 295 | + } |
---|
| 296 | + |
---|
| 297 | + con->port_altmode[i] = alt; |
---|
| 298 | + break; |
---|
| 299 | + case UCSI_RECIPIENT_SOP: |
---|
| 300 | + i = ucsi_next_altmode(con->partner_altmode); |
---|
| 301 | + if (i < 0) { |
---|
| 302 | + ret = i; |
---|
| 303 | + goto err; |
---|
| 304 | + } |
---|
| 305 | + |
---|
| 306 | + ret = ucsi_altmode_next_mode(con->partner_altmode, desc->svid); |
---|
| 307 | + if (ret < 0) |
---|
| 308 | + return ret; |
---|
| 309 | + |
---|
| 310 | + desc->mode = ret; |
---|
| 311 | + |
---|
| 312 | + alt = typec_partner_register_altmode(con->partner, desc); |
---|
| 313 | + if (IS_ERR(alt)) { |
---|
| 314 | + ret = PTR_ERR(alt); |
---|
| 315 | + goto err; |
---|
| 316 | + } |
---|
| 317 | + |
---|
| 318 | + con->partner_altmode[i] = alt; |
---|
| 319 | + break; |
---|
| 320 | + default: |
---|
| 321 | + return -EINVAL; |
---|
| 322 | + } |
---|
| 323 | + |
---|
| 324 | + trace_ucsi_register_altmode(recipient, alt); |
---|
| 325 | + |
---|
| 326 | + return 0; |
---|
| 327 | + |
---|
235 | 328 | err: |
---|
236 | | - trace_ucsi_run_command(ctrl, ret); |
---|
| 329 | + dev_err(con->ucsi->dev, "failed to registers svid 0x%04x mode %d\n", |
---|
| 330 | + desc->svid, desc->mode); |
---|
237 | 331 | |
---|
238 | 332 | return ret; |
---|
239 | 333 | } |
---|
240 | 334 | |
---|
241 | | -/* -------------------------------------------------------------------------- */ |
---|
| 335 | +static int |
---|
| 336 | +ucsi_register_altmodes_nvidia(struct ucsi_connector *con, u8 recipient) |
---|
| 337 | +{ |
---|
| 338 | + int max_altmodes = UCSI_MAX_ALTMODES; |
---|
| 339 | + struct typec_altmode_desc desc; |
---|
| 340 | + struct ucsi_altmode alt; |
---|
| 341 | + struct ucsi_altmode orig[UCSI_MAX_ALTMODES]; |
---|
| 342 | + struct ucsi_altmode updated[UCSI_MAX_ALTMODES]; |
---|
| 343 | + struct ucsi *ucsi = con->ucsi; |
---|
| 344 | + bool multi_dp = false; |
---|
| 345 | + u64 command; |
---|
| 346 | + int ret; |
---|
| 347 | + int len; |
---|
| 348 | + int i; |
---|
| 349 | + int k = 0; |
---|
| 350 | + |
---|
| 351 | + if (recipient == UCSI_RECIPIENT_CON) |
---|
| 352 | + max_altmodes = con->ucsi->cap.num_alt_modes; |
---|
| 353 | + |
---|
| 354 | + memset(orig, 0, sizeof(orig)); |
---|
| 355 | + memset(updated, 0, sizeof(updated)); |
---|
| 356 | + |
---|
| 357 | + /* First get all the alternate modes */ |
---|
| 358 | + for (i = 0; i < max_altmodes; i++) { |
---|
| 359 | + memset(&alt, 0, sizeof(alt)); |
---|
| 360 | + command = UCSI_GET_ALTERNATE_MODES; |
---|
| 361 | + command |= UCSI_GET_ALTMODE_RECIPIENT(recipient); |
---|
| 362 | + command |= UCSI_GET_ALTMODE_CONNECTOR_NUMBER(con->num); |
---|
| 363 | + command |= UCSI_GET_ALTMODE_OFFSET(i); |
---|
| 364 | + len = ucsi_send_command(con->ucsi, command, &alt, sizeof(alt)); |
---|
| 365 | + /* |
---|
| 366 | + * We are collecting all altmodes first and then registering. |
---|
| 367 | + * Some type-C device will return zero length data beyond last |
---|
| 368 | + * alternate modes. We should not return if length is zero. |
---|
| 369 | + */ |
---|
| 370 | + if (len < 0) |
---|
| 371 | + return len; |
---|
| 372 | + |
---|
| 373 | + /* We got all altmodes, now break out and register them */ |
---|
| 374 | + if (!len || !alt.svid) |
---|
| 375 | + break; |
---|
| 376 | + |
---|
| 377 | + orig[k].mid = alt.mid; |
---|
| 378 | + orig[k].svid = alt.svid; |
---|
| 379 | + k++; |
---|
| 380 | + } |
---|
| 381 | + /* |
---|
| 382 | + * Update the original altmode table as some ppms may report |
---|
| 383 | + * multiple DP altmodes. |
---|
| 384 | + */ |
---|
| 385 | + if (recipient == UCSI_RECIPIENT_CON) |
---|
| 386 | + multi_dp = ucsi->ops->update_altmodes(ucsi, orig, updated); |
---|
| 387 | + |
---|
| 388 | + /* now register altmodes */ |
---|
| 389 | + for (i = 0; i < max_altmodes; i++) { |
---|
| 390 | + memset(&desc, 0, sizeof(desc)); |
---|
| 391 | + if (multi_dp && recipient == UCSI_RECIPIENT_CON) { |
---|
| 392 | + desc.svid = updated[i].svid; |
---|
| 393 | + desc.vdo = updated[i].mid; |
---|
| 394 | + } else { |
---|
| 395 | + desc.svid = orig[i].svid; |
---|
| 396 | + desc.vdo = orig[i].mid; |
---|
| 397 | + } |
---|
| 398 | + desc.roles = TYPEC_PORT_DRD; |
---|
| 399 | + |
---|
| 400 | + if (!desc.svid) |
---|
| 401 | + return 0; |
---|
| 402 | + |
---|
| 403 | + ret = ucsi_register_altmode(con, &desc, recipient); |
---|
| 404 | + if (ret) |
---|
| 405 | + return ret; |
---|
| 406 | + } |
---|
| 407 | + |
---|
| 408 | + return 0; |
---|
| 409 | +} |
---|
| 410 | + |
---|
| 411 | +static int ucsi_register_altmodes(struct ucsi_connector *con, u8 recipient) |
---|
| 412 | +{ |
---|
| 413 | + int max_altmodes = UCSI_MAX_ALTMODES; |
---|
| 414 | + struct typec_altmode_desc desc; |
---|
| 415 | + struct ucsi_altmode alt[2]; |
---|
| 416 | + u64 command; |
---|
| 417 | + int num; |
---|
| 418 | + int ret; |
---|
| 419 | + int len; |
---|
| 420 | + int j; |
---|
| 421 | + int i; |
---|
| 422 | + |
---|
| 423 | + if (!(con->ucsi->cap.features & UCSI_CAP_ALT_MODE_DETAILS)) |
---|
| 424 | + return 0; |
---|
| 425 | + |
---|
| 426 | + if (recipient == UCSI_RECIPIENT_SOP && con->partner_altmode[0]) |
---|
| 427 | + return 0; |
---|
| 428 | + |
---|
| 429 | + if (con->ucsi->ops->update_altmodes) |
---|
| 430 | + return ucsi_register_altmodes_nvidia(con, recipient); |
---|
| 431 | + |
---|
| 432 | + if (recipient == UCSI_RECIPIENT_CON) |
---|
| 433 | + max_altmodes = con->ucsi->cap.num_alt_modes; |
---|
| 434 | + |
---|
| 435 | + for (i = 0; i < max_altmodes;) { |
---|
| 436 | + memset(alt, 0, sizeof(alt)); |
---|
| 437 | + command = UCSI_GET_ALTERNATE_MODES; |
---|
| 438 | + command |= UCSI_GET_ALTMODE_RECIPIENT(recipient); |
---|
| 439 | + command |= UCSI_GET_ALTMODE_CONNECTOR_NUMBER(con->num); |
---|
| 440 | + command |= UCSI_GET_ALTMODE_OFFSET(i); |
---|
| 441 | + len = ucsi_send_command(con->ucsi, command, alt, sizeof(alt)); |
---|
| 442 | + if (len <= 0) |
---|
| 443 | + return len; |
---|
| 444 | + |
---|
| 445 | + /* |
---|
| 446 | + * This code is requesting one alt mode at a time, but some PPMs |
---|
| 447 | + * may still return two. If that happens both alt modes need be |
---|
| 448 | + * registered and the offset for the next alt mode has to be |
---|
| 449 | + * incremented. |
---|
| 450 | + */ |
---|
| 451 | + num = len / sizeof(alt[0]); |
---|
| 452 | + i += num; |
---|
| 453 | + |
---|
| 454 | + for (j = 0; j < num; j++) { |
---|
| 455 | + if (!alt[j].svid) |
---|
| 456 | + return 0; |
---|
| 457 | + |
---|
| 458 | + memset(&desc, 0, sizeof(desc)); |
---|
| 459 | + desc.vdo = alt[j].mid; |
---|
| 460 | + desc.svid = alt[j].svid; |
---|
| 461 | + desc.roles = TYPEC_PORT_DRD; |
---|
| 462 | + |
---|
| 463 | + ret = ucsi_register_altmode(con, &desc, recipient); |
---|
| 464 | + if (ret) |
---|
| 465 | + return ret; |
---|
| 466 | + } |
---|
| 467 | + } |
---|
| 468 | + |
---|
| 469 | + return 0; |
---|
| 470 | +} |
---|
| 471 | + |
---|
| 472 | +static void ucsi_unregister_altmodes(struct ucsi_connector *con, u8 recipient) |
---|
| 473 | +{ |
---|
| 474 | + const struct typec_altmode *pdev; |
---|
| 475 | + struct typec_altmode **adev; |
---|
| 476 | + int i = 0; |
---|
| 477 | + |
---|
| 478 | + switch (recipient) { |
---|
| 479 | + case UCSI_RECIPIENT_CON: |
---|
| 480 | + adev = con->port_altmode; |
---|
| 481 | + break; |
---|
| 482 | + case UCSI_RECIPIENT_SOP: |
---|
| 483 | + adev = con->partner_altmode; |
---|
| 484 | + break; |
---|
| 485 | + default: |
---|
| 486 | + return; |
---|
| 487 | + } |
---|
| 488 | + |
---|
| 489 | + while (adev[i]) { |
---|
| 490 | + if (recipient == UCSI_RECIPIENT_SOP && |
---|
| 491 | + (adev[i]->svid == USB_TYPEC_DP_SID || |
---|
| 492 | + (adev[i]->svid == USB_TYPEC_NVIDIA_VLINK_SID && |
---|
| 493 | + adev[i]->vdo != USB_TYPEC_NVIDIA_VLINK_DBG_VDO))) { |
---|
| 494 | + pdev = typec_altmode_get_partner(adev[i]); |
---|
| 495 | + ucsi_displayport_remove_partner((void *)pdev); |
---|
| 496 | + } |
---|
| 497 | + typec_unregister_altmode(adev[i]); |
---|
| 498 | + adev[i++] = NULL; |
---|
| 499 | + } |
---|
| 500 | +} |
---|
| 501 | + |
---|
| 502 | +static int ucsi_get_pdos(struct ucsi_connector *con, int is_partner, |
---|
| 503 | + u32 *pdos, int offset, int num_pdos) |
---|
| 504 | +{ |
---|
| 505 | + struct ucsi *ucsi = con->ucsi; |
---|
| 506 | + u64 command; |
---|
| 507 | + int ret; |
---|
| 508 | + |
---|
| 509 | + command = UCSI_COMMAND(UCSI_GET_PDOS) | UCSI_CONNECTOR_NUMBER(con->num); |
---|
| 510 | + command |= UCSI_GET_PDOS_PARTNER_PDO(is_partner); |
---|
| 511 | + command |= UCSI_GET_PDOS_PDO_OFFSET(offset); |
---|
| 512 | + command |= UCSI_GET_PDOS_NUM_PDOS(num_pdos - 1); |
---|
| 513 | + command |= UCSI_GET_PDOS_SRC_PDOS; |
---|
| 514 | + ret = ucsi_send_command(ucsi, command, pdos + offset, |
---|
| 515 | + num_pdos * sizeof(u32)); |
---|
| 516 | + if (ret < 0) |
---|
| 517 | + dev_err(ucsi->dev, "UCSI_GET_PDOS failed (%d)\n", ret); |
---|
| 518 | + |
---|
| 519 | + return ret; |
---|
| 520 | +} |
---|
| 521 | + |
---|
| 522 | +static void ucsi_get_src_pdos(struct ucsi_connector *con, int is_partner) |
---|
| 523 | +{ |
---|
| 524 | + int ret; |
---|
| 525 | + |
---|
| 526 | + /* UCSI max payload means only getting at most 4 PDOs at a time */ |
---|
| 527 | + ret = ucsi_get_pdos(con, 1, con->src_pdos, 0, UCSI_MAX_PDOS); |
---|
| 528 | + if (ret < 0) |
---|
| 529 | + return; |
---|
| 530 | + |
---|
| 531 | + con->num_pdos = ret / sizeof(u32); /* number of bytes to 32-bit PDOs */ |
---|
| 532 | + if (con->num_pdos < UCSI_MAX_PDOS) |
---|
| 533 | + return; |
---|
| 534 | + |
---|
| 535 | + /* get the remaining PDOs, if any */ |
---|
| 536 | + ret = ucsi_get_pdos(con, 1, con->src_pdos, UCSI_MAX_PDOS, |
---|
| 537 | + PDO_MAX_OBJECTS - UCSI_MAX_PDOS); |
---|
| 538 | + if (ret < 0) |
---|
| 539 | + return; |
---|
| 540 | + |
---|
| 541 | + con->num_pdos += ret / sizeof(u32); |
---|
| 542 | +} |
---|
242 | 543 | |
---|
243 | 544 | static void ucsi_pwr_opmode_change(struct ucsi_connector *con) |
---|
244 | 545 | { |
---|
245 | | - switch (con->status.pwr_op_mode) { |
---|
| 546 | + switch (UCSI_CONSTAT_PWR_OPMODE(con->status.flags)) { |
---|
246 | 547 | case UCSI_CONSTAT_PWR_OPMODE_PD: |
---|
| 548 | + con->rdo = con->status.request_data_obj; |
---|
247 | 549 | typec_set_pwr_opmode(con->port, TYPEC_PWR_MODE_PD); |
---|
| 550 | + ucsi_get_src_pdos(con, 1); |
---|
248 | 551 | break; |
---|
249 | 552 | case UCSI_CONSTAT_PWR_OPMODE_TYPEC1_5: |
---|
| 553 | + con->rdo = 0; |
---|
250 | 554 | typec_set_pwr_opmode(con->port, TYPEC_PWR_MODE_1_5A); |
---|
251 | 555 | break; |
---|
252 | 556 | case UCSI_CONSTAT_PWR_OPMODE_TYPEC3_0: |
---|
| 557 | + con->rdo = 0; |
---|
253 | 558 | typec_set_pwr_opmode(con->port, TYPEC_PWR_MODE_3_0A); |
---|
254 | 559 | break; |
---|
255 | 560 | default: |
---|
| 561 | + con->rdo = 0; |
---|
256 | 562 | typec_set_pwr_opmode(con->port, TYPEC_PWR_MODE_USB); |
---|
257 | 563 | break; |
---|
258 | 564 | } |
---|
.. | .. |
---|
260 | 566 | |
---|
261 | 567 | static int ucsi_register_partner(struct ucsi_connector *con) |
---|
262 | 568 | { |
---|
| 569 | + u8 pwr_opmode = UCSI_CONSTAT_PWR_OPMODE(con->status.flags); |
---|
263 | 570 | struct typec_partner_desc desc; |
---|
264 | 571 | struct typec_partner *partner; |
---|
265 | 572 | |
---|
.. | .. |
---|
268 | 575 | |
---|
269 | 576 | memset(&desc, 0, sizeof(desc)); |
---|
270 | 577 | |
---|
271 | | - switch (con->status.partner_type) { |
---|
| 578 | + switch (UCSI_CONSTAT_PARTNER_TYPE(con->status.flags)) { |
---|
272 | 579 | case UCSI_CONSTAT_PARTNER_TYPE_DEBUG: |
---|
273 | 580 | desc.accessory = TYPEC_ACCESSORY_DEBUG; |
---|
274 | 581 | break; |
---|
.. | .. |
---|
279 | 586 | break; |
---|
280 | 587 | } |
---|
281 | 588 | |
---|
282 | | - desc.usb_pd = con->status.pwr_op_mode == UCSI_CONSTAT_PWR_OPMODE_PD; |
---|
| 589 | + desc.usb_pd = pwr_opmode == UCSI_CONSTAT_PWR_OPMODE_PD; |
---|
283 | 590 | |
---|
284 | 591 | partner = typec_register_partner(con->port, &desc); |
---|
285 | 592 | if (IS_ERR(partner)) { |
---|
.. | .. |
---|
299 | 606 | if (!con->partner) |
---|
300 | 607 | return; |
---|
301 | 608 | |
---|
| 609 | + ucsi_unregister_altmodes(con, UCSI_RECIPIENT_SOP); |
---|
302 | 610 | typec_unregister_partner(con->partner); |
---|
303 | 611 | con->partner = NULL; |
---|
304 | 612 | } |
---|
305 | 613 | |
---|
306 | | -static void ucsi_connector_change(struct work_struct *work) |
---|
| 614 | +static void ucsi_partner_change(struct ucsi_connector *con) |
---|
| 615 | +{ |
---|
| 616 | + enum usb_role u_role = USB_ROLE_NONE; |
---|
| 617 | + int ret; |
---|
| 618 | + |
---|
| 619 | + if (!con->partner) |
---|
| 620 | + return; |
---|
| 621 | + |
---|
| 622 | + switch (UCSI_CONSTAT_PARTNER_TYPE(con->status.flags)) { |
---|
| 623 | + case UCSI_CONSTAT_PARTNER_TYPE_UFP: |
---|
| 624 | + case UCSI_CONSTAT_PARTNER_TYPE_CABLE_AND_UFP: |
---|
| 625 | + u_role = USB_ROLE_HOST; |
---|
| 626 | + fallthrough; |
---|
| 627 | + case UCSI_CONSTAT_PARTNER_TYPE_CABLE: |
---|
| 628 | + typec_set_data_role(con->port, TYPEC_HOST); |
---|
| 629 | + break; |
---|
| 630 | + case UCSI_CONSTAT_PARTNER_TYPE_DFP: |
---|
| 631 | + u_role = USB_ROLE_DEVICE; |
---|
| 632 | + typec_set_data_role(con->port, TYPEC_DEVICE); |
---|
| 633 | + break; |
---|
| 634 | + default: |
---|
| 635 | + break; |
---|
| 636 | + } |
---|
| 637 | + |
---|
| 638 | + /* Complete pending data role swap */ |
---|
| 639 | + if (!completion_done(&con->complete)) |
---|
| 640 | + complete(&con->complete); |
---|
| 641 | + |
---|
| 642 | + /* Only notify USB controller if partner supports USB data */ |
---|
| 643 | + if (!(UCSI_CONSTAT_PARTNER_FLAGS(con->status.flags) & UCSI_CONSTAT_PARTNER_FLAG_USB)) |
---|
| 644 | + u_role = USB_ROLE_NONE; |
---|
| 645 | + |
---|
| 646 | + ret = usb_role_switch_set_role(con->usb_role_sw, u_role); |
---|
| 647 | + if (ret) |
---|
| 648 | + dev_err(con->ucsi->dev, "con:%d: failed to set usb role:%d\n", |
---|
| 649 | + con->num, u_role); |
---|
| 650 | + |
---|
| 651 | + /* Can't rely on Partner Flags field. Always checking the alt modes. */ |
---|
| 652 | + ret = ucsi_register_altmodes(con, UCSI_RECIPIENT_SOP); |
---|
| 653 | + if (ret) |
---|
| 654 | + dev_err(con->ucsi->dev, |
---|
| 655 | + "con%d: failed to register partner alternate modes\n", |
---|
| 656 | + con->num); |
---|
| 657 | + else |
---|
| 658 | + ucsi_altmode_update_active(con); |
---|
| 659 | +} |
---|
| 660 | + |
---|
| 661 | +static void ucsi_handle_connector_change(struct work_struct *work) |
---|
307 | 662 | { |
---|
308 | 663 | struct ucsi_connector *con = container_of(work, struct ucsi_connector, |
---|
309 | 664 | work); |
---|
310 | 665 | struct ucsi *ucsi = con->ucsi; |
---|
311 | | - struct ucsi_control ctrl; |
---|
| 666 | + struct ucsi_connector_status pre_ack_status; |
---|
| 667 | + struct ucsi_connector_status post_ack_status; |
---|
| 668 | + enum typec_role role; |
---|
| 669 | + enum usb_role u_role = USB_ROLE_NONE; |
---|
| 670 | + u16 inferred_changes; |
---|
| 671 | + u16 changed_flags; |
---|
| 672 | + u64 command; |
---|
312 | 673 | int ret; |
---|
313 | 674 | |
---|
314 | | - mutex_lock(&ucsi->ppm_lock); |
---|
| 675 | + mutex_lock(&con->lock); |
---|
315 | 676 | |
---|
316 | | - UCSI_CMD_GET_CONNECTOR_STATUS(ctrl, con->num); |
---|
317 | | - ret = ucsi_run_command(ucsi, &ctrl, &con->status, sizeof(con->status)); |
---|
| 677 | + /* |
---|
| 678 | + * Some/many PPMs have an issue where all fields in the change bitfield |
---|
| 679 | + * are cleared when an ACK is send. This will causes any change |
---|
| 680 | + * between GET_CONNECTOR_STATUS and ACK to be lost. |
---|
| 681 | + * |
---|
| 682 | + * We work around this by re-fetching the connector status afterwards. |
---|
| 683 | + * We then infer any changes that we see have happened but that may not |
---|
| 684 | + * be represented in the change bitfield. |
---|
| 685 | + * |
---|
| 686 | + * Also, even though we don't need to know the currently supported alt |
---|
| 687 | + * modes, we run the GET_CAM_SUPPORTED command to ensure the PPM does |
---|
| 688 | + * not get stuck in case it assumes we do. |
---|
| 689 | + * Always do this, rather than relying on UCSI_CONSTAT_CAM_CHANGE to be |
---|
| 690 | + * set in the change bitfield. |
---|
| 691 | + * |
---|
| 692 | + * We end up with the following actions: |
---|
| 693 | + * 1. UCSI_GET_CONNECTOR_STATUS, store result, update unprocessed_changes |
---|
| 694 | + * 2. UCSI_GET_CAM_SUPPORTED, discard result |
---|
| 695 | + * 3. ACK connector change |
---|
| 696 | + * 4. UCSI_GET_CONNECTOR_STATUS, store result |
---|
| 697 | + * 5. Infere lost changes by comparing UCSI_GET_CONNECTOR_STATUS results |
---|
| 698 | + * 6. If PPM reported a new change, then restart in order to ACK |
---|
| 699 | + * 7. Process everything as usual. |
---|
| 700 | + * |
---|
| 701 | + * We may end up seeing a change twice, but we can only miss extremely |
---|
| 702 | + * short transitional changes. |
---|
| 703 | + */ |
---|
| 704 | + |
---|
| 705 | + /* 1. First UCSI_GET_CONNECTOR_STATUS */ |
---|
| 706 | + command = UCSI_GET_CONNECTOR_STATUS | UCSI_CONNECTOR_NUMBER(con->num); |
---|
| 707 | + ret = ucsi_send_command(ucsi, command, &pre_ack_status, |
---|
| 708 | + sizeof(pre_ack_status)); |
---|
| 709 | + if (ret < 0) { |
---|
| 710 | + dev_err(ucsi->dev, "%s: GET_CONNECTOR_STATUS failed (%d)\n", |
---|
| 711 | + __func__, ret); |
---|
| 712 | + goto out_unlock; |
---|
| 713 | + } |
---|
| 714 | + con->unprocessed_changes |= pre_ack_status.change; |
---|
| 715 | + |
---|
| 716 | + /* 2. Run UCSI_GET_CAM_SUPPORTED and discard the result. */ |
---|
| 717 | + command = UCSI_GET_CAM_SUPPORTED; |
---|
| 718 | + command |= UCSI_CONNECTOR_NUMBER(con->num); |
---|
| 719 | + ucsi_send_command(con->ucsi, command, NULL, 0); |
---|
| 720 | + |
---|
| 721 | + /* 3. ACK connector change */ |
---|
| 722 | + ret = ucsi_acknowledge_connector_change(ucsi); |
---|
| 723 | + clear_bit(EVENT_PENDING, &ucsi->flags); |
---|
| 724 | + if (ret) { |
---|
| 725 | + dev_err(ucsi->dev, "%s: ACK failed (%d)", __func__, ret); |
---|
| 726 | + goto out_unlock; |
---|
| 727 | + } |
---|
| 728 | + |
---|
| 729 | + /* 4. Second UCSI_GET_CONNECTOR_STATUS */ |
---|
| 730 | + command = UCSI_GET_CONNECTOR_STATUS | UCSI_CONNECTOR_NUMBER(con->num); |
---|
| 731 | + ret = ucsi_send_command(ucsi, command, &post_ack_status, |
---|
| 732 | + sizeof(post_ack_status)); |
---|
318 | 733 | if (ret < 0) { |
---|
319 | 734 | dev_err(ucsi->dev, "%s: GET_CONNECTOR_STATUS failed (%d)\n", |
---|
320 | 735 | __func__, ret); |
---|
321 | 736 | goto out_unlock; |
---|
322 | 737 | } |
---|
323 | 738 | |
---|
324 | | - if (con->status.change & UCSI_CONSTAT_POWER_OPMODE_CHANGE) |
---|
| 739 | + /* 5. Inferre any missing changes */ |
---|
| 740 | + changed_flags = pre_ack_status.flags ^ post_ack_status.flags; |
---|
| 741 | + inferred_changes = 0; |
---|
| 742 | + if (UCSI_CONSTAT_PWR_OPMODE(changed_flags) != 0) |
---|
| 743 | + inferred_changes |= UCSI_CONSTAT_POWER_OPMODE_CHANGE; |
---|
| 744 | + |
---|
| 745 | + if (changed_flags & UCSI_CONSTAT_CONNECTED) |
---|
| 746 | + inferred_changes |= UCSI_CONSTAT_CONNECT_CHANGE; |
---|
| 747 | + |
---|
| 748 | + if (changed_flags & UCSI_CONSTAT_PWR_DIR) |
---|
| 749 | + inferred_changes |= UCSI_CONSTAT_POWER_DIR_CHANGE; |
---|
| 750 | + |
---|
| 751 | + if (UCSI_CONSTAT_PARTNER_FLAGS(changed_flags) != 0) |
---|
| 752 | + inferred_changes |= UCSI_CONSTAT_PARTNER_CHANGE; |
---|
| 753 | + |
---|
| 754 | + if (UCSI_CONSTAT_PARTNER_TYPE(changed_flags) != 0) |
---|
| 755 | + inferred_changes |= UCSI_CONSTAT_PARTNER_CHANGE; |
---|
| 756 | + |
---|
| 757 | + /* Mask out anything that was correctly notified in the later call. */ |
---|
| 758 | + inferred_changes &= ~post_ack_status.change; |
---|
| 759 | + if (inferred_changes) |
---|
| 760 | + dev_dbg(ucsi->dev, "%s: Inferred changes that would have been lost: 0x%04x\n", |
---|
| 761 | + __func__, inferred_changes); |
---|
| 762 | + |
---|
| 763 | + con->unprocessed_changes |= inferred_changes; |
---|
| 764 | + |
---|
| 765 | + /* 6. If PPM reported a new change, then restart in order to ACK */ |
---|
| 766 | + if (post_ack_status.change) |
---|
| 767 | + goto out_unlock; |
---|
| 768 | + |
---|
| 769 | + /* 7. Continue as if nothing happened */ |
---|
| 770 | + con->status = post_ack_status; |
---|
| 771 | + con->status.change = con->unprocessed_changes; |
---|
| 772 | + con->unprocessed_changes = 0; |
---|
| 773 | + |
---|
| 774 | + role = !!(con->status.flags & UCSI_CONSTAT_PWR_DIR); |
---|
| 775 | + |
---|
| 776 | + if (con->status.change & UCSI_CONSTAT_POWER_OPMODE_CHANGE || |
---|
| 777 | + con->status.change & UCSI_CONSTAT_POWER_LEVEL_CHANGE) { |
---|
325 | 778 | ucsi_pwr_opmode_change(con); |
---|
| 779 | + ucsi_port_psy_changed(con); |
---|
| 780 | + } |
---|
326 | 781 | |
---|
327 | 782 | if (con->status.change & UCSI_CONSTAT_POWER_DIR_CHANGE) { |
---|
328 | | - typec_set_pwr_role(con->port, con->status.pwr_dir); |
---|
| 783 | + typec_set_pwr_role(con->port, role); |
---|
329 | 784 | |
---|
330 | 785 | /* Complete pending power role swap */ |
---|
331 | 786 | if (!completion_done(&con->complete)) |
---|
332 | 787 | complete(&con->complete); |
---|
333 | 788 | } |
---|
334 | 789 | |
---|
335 | | - if (con->status.change & UCSI_CONSTAT_PARTNER_CHANGE) { |
---|
336 | | - switch (con->status.partner_type) { |
---|
337 | | - case UCSI_CONSTAT_PARTNER_TYPE_UFP: |
---|
338 | | - typec_set_data_role(con->port, TYPEC_HOST); |
---|
339 | | - break; |
---|
340 | | - case UCSI_CONSTAT_PARTNER_TYPE_DFP: |
---|
341 | | - typec_set_data_role(con->port, TYPEC_DEVICE); |
---|
342 | | - break; |
---|
343 | | - default: |
---|
344 | | - break; |
---|
345 | | - } |
---|
346 | | - |
---|
347 | | - /* Complete pending data role swap */ |
---|
348 | | - if (!completion_done(&con->complete)) |
---|
349 | | - complete(&con->complete); |
---|
350 | | - } |
---|
351 | | - |
---|
352 | 790 | if (con->status.change & UCSI_CONSTAT_CONNECT_CHANGE) { |
---|
353 | | - typec_set_pwr_role(con->port, con->status.pwr_dir); |
---|
| 791 | + typec_set_pwr_role(con->port, role); |
---|
354 | 792 | |
---|
355 | | - switch (con->status.partner_type) { |
---|
| 793 | + switch (UCSI_CONSTAT_PARTNER_TYPE(con->status.flags)) { |
---|
356 | 794 | case UCSI_CONSTAT_PARTNER_TYPE_UFP: |
---|
| 795 | + case UCSI_CONSTAT_PARTNER_TYPE_CABLE_AND_UFP: |
---|
| 796 | + u_role = USB_ROLE_HOST; |
---|
| 797 | + fallthrough; |
---|
| 798 | + case UCSI_CONSTAT_PARTNER_TYPE_CABLE: |
---|
357 | 799 | typec_set_data_role(con->port, TYPEC_HOST); |
---|
358 | 800 | break; |
---|
359 | 801 | case UCSI_CONSTAT_PARTNER_TYPE_DFP: |
---|
| 802 | + u_role = USB_ROLE_DEVICE; |
---|
360 | 803 | typec_set_data_role(con->port, TYPEC_DEVICE); |
---|
361 | 804 | break; |
---|
362 | 805 | default: |
---|
363 | 806 | break; |
---|
364 | 807 | } |
---|
365 | 808 | |
---|
366 | | - if (con->status.connected) |
---|
| 809 | + if (con->status.flags & UCSI_CONSTAT_CONNECTED) |
---|
367 | 810 | ucsi_register_partner(con); |
---|
368 | 811 | else |
---|
369 | 812 | ucsi_unregister_partner(con); |
---|
| 813 | + |
---|
| 814 | + ucsi_port_psy_changed(con); |
---|
| 815 | + |
---|
| 816 | + /* Only notify USB controller if partner supports USB data */ |
---|
| 817 | + if (!(UCSI_CONSTAT_PARTNER_FLAGS(con->status.flags) & |
---|
| 818 | + UCSI_CONSTAT_PARTNER_FLAG_USB)) |
---|
| 819 | + u_role = USB_ROLE_NONE; |
---|
| 820 | + |
---|
| 821 | + ret = usb_role_switch_set_role(con->usb_role_sw, u_role); |
---|
| 822 | + if (ret) |
---|
| 823 | + dev_err(ucsi->dev, "con:%d: failed to set usb role:%d\n", |
---|
| 824 | + con->num, u_role); |
---|
370 | 825 | } |
---|
371 | 826 | |
---|
372 | | - ret = ucsi_ack(ucsi, UCSI_ACK_EVENT); |
---|
373 | | - if (ret) |
---|
374 | | - dev_err(ucsi->dev, "%s: ACK failed (%d)", __func__, ret); |
---|
| 827 | + if (con->status.change & UCSI_CONSTAT_PARTNER_CHANGE) |
---|
| 828 | + ucsi_partner_change(con); |
---|
375 | 829 | |
---|
376 | 830 | trace_ucsi_connector_change(con->num, &con->status); |
---|
377 | 831 | |
---|
378 | 832 | out_unlock: |
---|
379 | | - clear_bit(EVENT_PENDING, &ucsi->flags); |
---|
380 | | - mutex_unlock(&ucsi->ppm_lock); |
---|
| 833 | + if (test_and_clear_bit(EVENT_PENDING, &ucsi->flags)) { |
---|
| 834 | + schedule_work(&con->work); |
---|
| 835 | + mutex_unlock(&con->lock); |
---|
| 836 | + return; |
---|
| 837 | + } |
---|
| 838 | + |
---|
| 839 | + clear_bit(EVENT_PROCESSING, &ucsi->flags); |
---|
| 840 | + mutex_unlock(&con->lock); |
---|
381 | 841 | } |
---|
382 | 842 | |
---|
383 | 843 | /** |
---|
384 | | - * ucsi_notify - PPM notification handler |
---|
385 | | - * @ucsi: Source UCSI Interface for the notifications |
---|
386 | | - * |
---|
387 | | - * Handle notifications from PPM of @ucsi. |
---|
| 844 | + * ucsi_connector_change - Process Connector Change Event |
---|
| 845 | + * @ucsi: UCSI Interface |
---|
| 846 | + * @num: Connector number |
---|
388 | 847 | */ |
---|
389 | | -void ucsi_notify(struct ucsi *ucsi) |
---|
| 848 | +void ucsi_connector_change(struct ucsi *ucsi, u8 num) |
---|
390 | 849 | { |
---|
391 | | - struct ucsi_cci *cci; |
---|
| 850 | + struct ucsi_connector *con = &ucsi->connector[num - 1]; |
---|
392 | 851 | |
---|
393 | | - /* There is no requirement to sync here, but no harm either. */ |
---|
394 | | - ucsi_sync(ucsi); |
---|
395 | | - |
---|
396 | | - cci = &ucsi->ppm->data->cci; |
---|
397 | | - |
---|
398 | | - if (cci->error) |
---|
399 | | - ucsi->status = UCSI_ERROR; |
---|
400 | | - else if (cci->busy) |
---|
401 | | - ucsi->status = UCSI_BUSY; |
---|
402 | | - else |
---|
403 | | - ucsi->status = UCSI_IDLE; |
---|
404 | | - |
---|
405 | | - if (cci->cmd_complete && test_bit(COMMAND_PENDING, &ucsi->flags)) { |
---|
406 | | - complete(&ucsi->complete); |
---|
407 | | - } else if (cci->ack_complete && test_bit(ACK_PENDING, &ucsi->flags)) { |
---|
408 | | - complete(&ucsi->complete); |
---|
409 | | - } else if (cci->connector_change) { |
---|
410 | | - struct ucsi_connector *con; |
---|
411 | | - |
---|
412 | | - con = &ucsi->connector[cci->connector_change - 1]; |
---|
413 | | - |
---|
414 | | - if (!test_and_set_bit(EVENT_PENDING, &ucsi->flags)) |
---|
415 | | - schedule_work(&con->work); |
---|
| 852 | + if (!(ucsi->ntfy & UCSI_ENABLE_NTFY_CONNECTOR_CHANGE)) { |
---|
| 853 | + dev_dbg(ucsi->dev, "Bogus connector change event\n"); |
---|
| 854 | + return; |
---|
416 | 855 | } |
---|
417 | 856 | |
---|
418 | | - trace_ucsi_notify(ucsi->ppm->data->raw_cci); |
---|
| 857 | + set_bit(EVENT_PENDING, &ucsi->flags); |
---|
| 858 | + |
---|
| 859 | + if (!test_and_set_bit(EVENT_PROCESSING, &ucsi->flags)) |
---|
| 860 | + schedule_work(&con->work); |
---|
419 | 861 | } |
---|
420 | | -EXPORT_SYMBOL_GPL(ucsi_notify); |
---|
| 862 | +EXPORT_SYMBOL_GPL(ucsi_connector_change); |
---|
421 | 863 | |
---|
422 | 864 | /* -------------------------------------------------------------------------- */ |
---|
423 | 865 | |
---|
424 | 866 | static int ucsi_reset_connector(struct ucsi_connector *con, bool hard) |
---|
425 | 867 | { |
---|
426 | | - struct ucsi_control ctrl; |
---|
| 868 | + u64 command; |
---|
427 | 869 | |
---|
428 | | - UCSI_CMD_CONNECTOR_RESET(ctrl, con, hard); |
---|
| 870 | + command = UCSI_CONNECTOR_RESET | UCSI_CONNECTOR_NUMBER(con->num); |
---|
| 871 | + command |= hard ? UCSI_CONNECTOR_RESET_HARD : 0; |
---|
429 | 872 | |
---|
430 | | - return ucsi_run_command(con->ucsi, &ctrl, NULL, 0); |
---|
| 873 | + return ucsi_send_command(con->ucsi, command, NULL, 0); |
---|
431 | 874 | } |
---|
432 | 875 | |
---|
433 | 876 | static int ucsi_reset_ppm(struct ucsi *ucsi) |
---|
434 | 877 | { |
---|
435 | | - struct ucsi_control ctrl; |
---|
| 878 | + u64 command = UCSI_PPM_RESET; |
---|
436 | 879 | unsigned long tmo; |
---|
| 880 | + u32 cci; |
---|
437 | 881 | int ret; |
---|
438 | 882 | |
---|
439 | | - ctrl.raw_cmd = 0; |
---|
440 | | - ctrl.cmd.cmd = UCSI_PPM_RESET; |
---|
441 | | - trace_ucsi_command(&ctrl); |
---|
442 | | - ret = ucsi->ppm->cmd(ucsi->ppm, &ctrl); |
---|
443 | | - if (ret) |
---|
444 | | - goto err; |
---|
| 883 | + mutex_lock(&ucsi->ppm_lock); |
---|
| 884 | + |
---|
| 885 | + ret = ucsi->ops->async_write(ucsi, UCSI_CONTROL, &command, |
---|
| 886 | + sizeof(command)); |
---|
| 887 | + if (ret < 0) |
---|
| 888 | + goto out; |
---|
445 | 889 | |
---|
446 | 890 | tmo = jiffies + msecs_to_jiffies(UCSI_TIMEOUT_MS); |
---|
447 | 891 | |
---|
448 | 892 | do { |
---|
449 | | - /* Here sync is critical. */ |
---|
450 | | - ret = ucsi_sync(ucsi); |
---|
451 | | - if (ret) |
---|
452 | | - goto err; |
---|
453 | | - |
---|
454 | | - if (ucsi->ppm->data->cci.reset_complete) |
---|
455 | | - break; |
---|
456 | | - |
---|
457 | | - /* If the PPM is still doing something else, reset it again. */ |
---|
458 | | - if (ucsi->ppm->data->raw_cci) { |
---|
459 | | - dev_warn_ratelimited(ucsi->dev, |
---|
460 | | - "Failed to reset PPM! Trying again..\n"); |
---|
461 | | - |
---|
462 | | - trace_ucsi_command(&ctrl); |
---|
463 | | - ret = ucsi->ppm->cmd(ucsi->ppm, &ctrl); |
---|
464 | | - if (ret) |
---|
465 | | - goto err; |
---|
| 893 | + if (time_is_before_jiffies(tmo)) { |
---|
| 894 | + ret = -ETIMEDOUT; |
---|
| 895 | + goto out; |
---|
466 | 896 | } |
---|
467 | 897 | |
---|
468 | | - /* Letting the PPM settle down. */ |
---|
| 898 | + ret = ucsi->ops->read(ucsi, UCSI_CCI, &cci, sizeof(cci)); |
---|
| 899 | + if (ret) |
---|
| 900 | + goto out; |
---|
| 901 | + |
---|
| 902 | + /* If the PPM is still doing something else, reset it again. */ |
---|
| 903 | + if (cci & ~UCSI_CCI_RESET_COMPLETE) { |
---|
| 904 | + ret = ucsi->ops->async_write(ucsi, UCSI_CONTROL, |
---|
| 905 | + &command, |
---|
| 906 | + sizeof(command)); |
---|
| 907 | + if (ret < 0) |
---|
| 908 | + goto out; |
---|
| 909 | + } |
---|
| 910 | + |
---|
469 | 911 | msleep(20); |
---|
| 912 | + } while (!(cci & UCSI_CCI_RESET_COMPLETE)); |
---|
470 | 913 | |
---|
471 | | - ret = -ETIMEDOUT; |
---|
472 | | - } while (time_is_after_jiffies(tmo)); |
---|
473 | | - |
---|
474 | | -err: |
---|
475 | | - trace_ucsi_reset_ppm(&ctrl, ret); |
---|
476 | | - |
---|
| 914 | +out: |
---|
| 915 | + mutex_unlock(&ucsi->ppm_lock); |
---|
477 | 916 | return ret; |
---|
478 | 917 | } |
---|
479 | 918 | |
---|
480 | | -static int ucsi_role_cmd(struct ucsi_connector *con, struct ucsi_control *ctrl) |
---|
| 919 | +static int ucsi_role_cmd(struct ucsi_connector *con, u64 command) |
---|
481 | 920 | { |
---|
482 | 921 | int ret; |
---|
483 | 922 | |
---|
484 | | - ret = ucsi_run_command(con->ucsi, ctrl, NULL, 0); |
---|
| 923 | + ret = ucsi_send_command(con->ucsi, command, NULL, 0); |
---|
485 | 924 | if (ret == -ETIMEDOUT) { |
---|
486 | | - struct ucsi_control c; |
---|
| 925 | + u64 c; |
---|
487 | 926 | |
---|
488 | 927 | /* PPM most likely stopped responding. Resetting everything. */ |
---|
489 | 928 | ucsi_reset_ppm(con->ucsi); |
---|
490 | 929 | |
---|
491 | | - UCSI_CMD_SET_NTFY_ENABLE(c, UCSI_ENABLE_NTFY_ALL); |
---|
492 | | - ucsi_run_command(con->ucsi, &c, NULL, 0); |
---|
| 930 | + c = UCSI_SET_NOTIFICATION_ENABLE | con->ucsi->ntfy; |
---|
| 931 | + ucsi_send_command(con->ucsi, c, NULL, 0); |
---|
493 | 932 | |
---|
494 | 933 | ucsi_reset_connector(con, true); |
---|
495 | 934 | } |
---|
.. | .. |
---|
497 | 936 | return ret; |
---|
498 | 937 | } |
---|
499 | 938 | |
---|
500 | | -static int |
---|
501 | | -ucsi_dr_swap(const struct typec_capability *cap, enum typec_data_role role) |
---|
| 939 | +static int ucsi_dr_swap(struct typec_port *port, enum typec_data_role role) |
---|
502 | 940 | { |
---|
503 | | - struct ucsi_connector *con = to_ucsi_connector(cap); |
---|
504 | | - struct ucsi_control ctrl; |
---|
| 941 | + struct ucsi_connector *con = typec_get_drvdata(port); |
---|
| 942 | + u8 partner_type; |
---|
| 943 | + u64 command; |
---|
505 | 944 | int ret = 0; |
---|
506 | 945 | |
---|
507 | | - if (!con->partner) |
---|
508 | | - return -ENOTCONN; |
---|
| 946 | + mutex_lock(&con->lock); |
---|
509 | 947 | |
---|
510 | | - mutex_lock(&con->ucsi->ppm_lock); |
---|
| 948 | + if (!con->partner) { |
---|
| 949 | + ret = -ENOTCONN; |
---|
| 950 | + goto out_unlock; |
---|
| 951 | + } |
---|
511 | 952 | |
---|
512 | | - if ((con->status.partner_type == UCSI_CONSTAT_PARTNER_TYPE_DFP && |
---|
| 953 | + partner_type = UCSI_CONSTAT_PARTNER_TYPE(con->status.flags); |
---|
| 954 | + if ((partner_type == UCSI_CONSTAT_PARTNER_TYPE_DFP && |
---|
513 | 955 | role == TYPEC_DEVICE) || |
---|
514 | | - (con->status.partner_type == UCSI_CONSTAT_PARTNER_TYPE_UFP && |
---|
| 956 | + (partner_type == UCSI_CONSTAT_PARTNER_TYPE_UFP && |
---|
515 | 957 | role == TYPEC_HOST)) |
---|
516 | 958 | goto out_unlock; |
---|
517 | 959 | |
---|
518 | | - UCSI_CMD_SET_UOR(ctrl, con, role); |
---|
519 | | - ret = ucsi_role_cmd(con, &ctrl); |
---|
| 960 | + reinit_completion(&con->complete); |
---|
| 961 | + |
---|
| 962 | + command = UCSI_SET_UOR | UCSI_CONNECTOR_NUMBER(con->num); |
---|
| 963 | + command |= UCSI_SET_UOR_ROLE(role); |
---|
| 964 | + command |= UCSI_SET_UOR_ACCEPT_ROLE_SWAPS; |
---|
| 965 | + ret = ucsi_role_cmd(con, command); |
---|
520 | 966 | if (ret < 0) |
---|
521 | 967 | goto out_unlock; |
---|
522 | 968 | |
---|
523 | | - mutex_unlock(&con->ucsi->ppm_lock); |
---|
| 969 | + mutex_unlock(&con->lock); |
---|
524 | 970 | |
---|
525 | 971 | if (!wait_for_completion_timeout(&con->complete, |
---|
526 | | - msecs_to_jiffies(UCSI_SWAP_TIMEOUT_MS))) |
---|
| 972 | + msecs_to_jiffies(UCSI_SWAP_TIMEOUT_MS))) |
---|
527 | 973 | return -ETIMEDOUT; |
---|
528 | 974 | |
---|
529 | 975 | return 0; |
---|
530 | 976 | |
---|
531 | 977 | out_unlock: |
---|
532 | | - mutex_unlock(&con->ucsi->ppm_lock); |
---|
| 978 | + mutex_unlock(&con->lock); |
---|
533 | 979 | |
---|
534 | 980 | return ret; |
---|
535 | 981 | } |
---|
536 | 982 | |
---|
537 | | -static int |
---|
538 | | -ucsi_pr_swap(const struct typec_capability *cap, enum typec_role role) |
---|
| 983 | +static int ucsi_pr_swap(struct typec_port *port, enum typec_role role) |
---|
539 | 984 | { |
---|
540 | | - struct ucsi_connector *con = to_ucsi_connector(cap); |
---|
541 | | - struct ucsi_control ctrl; |
---|
| 985 | + struct ucsi_connector *con = typec_get_drvdata(port); |
---|
| 986 | + enum typec_role cur_role; |
---|
| 987 | + u64 command; |
---|
542 | 988 | int ret = 0; |
---|
543 | 989 | |
---|
544 | | - if (!con->partner) |
---|
545 | | - return -ENOTCONN; |
---|
| 990 | + mutex_lock(&con->lock); |
---|
546 | 991 | |
---|
547 | | - mutex_lock(&con->ucsi->ppm_lock); |
---|
| 992 | + if (!con->partner) { |
---|
| 993 | + ret = -ENOTCONN; |
---|
| 994 | + goto out_unlock; |
---|
| 995 | + } |
---|
548 | 996 | |
---|
549 | | - if (con->status.pwr_dir == role) |
---|
| 997 | + cur_role = !!(con->status.flags & UCSI_CONSTAT_PWR_DIR); |
---|
| 998 | + |
---|
| 999 | + if (cur_role == role) |
---|
550 | 1000 | goto out_unlock; |
---|
551 | 1001 | |
---|
552 | | - UCSI_CMD_SET_PDR(ctrl, con, role); |
---|
553 | | - ret = ucsi_role_cmd(con, &ctrl); |
---|
| 1002 | + reinit_completion(&con->complete); |
---|
| 1003 | + |
---|
| 1004 | + command = UCSI_SET_PDR | UCSI_CONNECTOR_NUMBER(con->num); |
---|
| 1005 | + command |= UCSI_SET_PDR_ROLE(role); |
---|
| 1006 | + command |= UCSI_SET_PDR_ACCEPT_ROLE_SWAPS; |
---|
| 1007 | + ret = ucsi_role_cmd(con, command); |
---|
554 | 1008 | if (ret < 0) |
---|
555 | 1009 | goto out_unlock; |
---|
556 | 1010 | |
---|
557 | | - mutex_unlock(&con->ucsi->ppm_lock); |
---|
| 1011 | + mutex_unlock(&con->lock); |
---|
558 | 1012 | |
---|
559 | 1013 | if (!wait_for_completion_timeout(&con->complete, |
---|
560 | | - msecs_to_jiffies(UCSI_SWAP_TIMEOUT_MS))) |
---|
| 1014 | + msecs_to_jiffies(UCSI_SWAP_TIMEOUT_MS))) |
---|
561 | 1015 | return -ETIMEDOUT; |
---|
562 | 1016 | |
---|
563 | | - mutex_lock(&con->ucsi->ppm_lock); |
---|
| 1017 | + mutex_lock(&con->lock); |
---|
564 | 1018 | |
---|
565 | 1019 | /* Something has gone wrong while swapping the role */ |
---|
566 | | - if (con->status.pwr_op_mode != UCSI_CONSTAT_PWR_OPMODE_PD) { |
---|
| 1020 | + if (UCSI_CONSTAT_PWR_OPMODE(con->status.flags) != |
---|
| 1021 | + UCSI_CONSTAT_PWR_OPMODE_PD) { |
---|
567 | 1022 | ucsi_reset_connector(con, true); |
---|
568 | 1023 | ret = -EPROTO; |
---|
569 | 1024 | } |
---|
570 | 1025 | |
---|
571 | 1026 | out_unlock: |
---|
572 | | - mutex_unlock(&con->ucsi->ppm_lock); |
---|
| 1027 | + mutex_unlock(&con->lock); |
---|
573 | 1028 | |
---|
574 | 1029 | return ret; |
---|
575 | 1030 | } |
---|
576 | 1031 | |
---|
| 1032 | +static const struct typec_operations ucsi_ops = { |
---|
| 1033 | + .dr_set = ucsi_dr_swap, |
---|
| 1034 | + .pr_set = ucsi_pr_swap |
---|
| 1035 | +}; |
---|
| 1036 | + |
---|
| 1037 | +/* Caller must call fwnode_handle_put() after use */ |
---|
577 | 1038 | static struct fwnode_handle *ucsi_find_fwnode(struct ucsi_connector *con) |
---|
578 | 1039 | { |
---|
579 | 1040 | struct fwnode_handle *fwnode; |
---|
.. | .. |
---|
590 | 1051 | struct ucsi_connector *con = &ucsi->connector[index]; |
---|
591 | 1052 | struct typec_capability *cap = &con->typec_cap; |
---|
592 | 1053 | enum typec_accessory *accessory = cap->accessory; |
---|
593 | | - struct ucsi_control ctrl; |
---|
| 1054 | + enum usb_role u_role = USB_ROLE_NONE; |
---|
| 1055 | + u64 command; |
---|
594 | 1056 | int ret; |
---|
595 | 1057 | |
---|
596 | | - INIT_WORK(&con->work, ucsi_connector_change); |
---|
| 1058 | + INIT_WORK(&con->work, ucsi_handle_connector_change); |
---|
597 | 1059 | init_completion(&con->complete); |
---|
| 1060 | + mutex_init(&con->lock); |
---|
598 | 1061 | con->num = index + 1; |
---|
599 | 1062 | con->ucsi = ucsi; |
---|
600 | 1063 | |
---|
| 1064 | + cap->fwnode = ucsi_find_fwnode(con); |
---|
| 1065 | + con->usb_role_sw = fwnode_usb_role_switch_get(cap->fwnode); |
---|
| 1066 | + if (IS_ERR(con->usb_role_sw)) { |
---|
| 1067 | + dev_err(ucsi->dev, "con%d: failed to get usb role switch\n", |
---|
| 1068 | + con->num); |
---|
| 1069 | + return PTR_ERR(con->usb_role_sw); |
---|
| 1070 | + } |
---|
| 1071 | + |
---|
| 1072 | + |
---|
| 1073 | + /* Delay other interactions with the con until registration is complete */ |
---|
| 1074 | + mutex_lock(&con->lock); |
---|
| 1075 | + |
---|
601 | 1076 | /* Get connector capability */ |
---|
602 | | - UCSI_CMD_GET_CONNECTOR_CAPABILITY(ctrl, con->num); |
---|
603 | | - ret = ucsi_run_command(ucsi, &ctrl, &con->cap, sizeof(con->cap)); |
---|
| 1077 | + command = UCSI_GET_CONNECTOR_CAPABILITY; |
---|
| 1078 | + command |= UCSI_CONNECTOR_NUMBER(con->num); |
---|
| 1079 | + ret = ucsi_send_command(ucsi, command, &con->cap, sizeof(con->cap)); |
---|
604 | 1080 | if (ret < 0) |
---|
605 | | - return ret; |
---|
| 1081 | + goto out_unlock; |
---|
606 | 1082 | |
---|
607 | 1083 | if (con->cap.op_mode & UCSI_CONCAP_OPMODE_DRP) |
---|
608 | 1084 | cap->data = TYPEC_PORT_DRD; |
---|
.. | .. |
---|
611 | 1087 | else if (con->cap.op_mode & UCSI_CONCAP_OPMODE_UFP) |
---|
612 | 1088 | cap->data = TYPEC_PORT_UFP; |
---|
613 | 1089 | |
---|
614 | | - if (con->cap.provider && con->cap.consumer) |
---|
| 1090 | + if ((con->cap.flags & UCSI_CONCAP_FLAG_PROVIDER) && |
---|
| 1091 | + (con->cap.flags & UCSI_CONCAP_FLAG_CONSUMER)) |
---|
615 | 1092 | cap->type = TYPEC_PORT_DRP; |
---|
616 | | - else if (con->cap.provider) |
---|
| 1093 | + else if (con->cap.flags & UCSI_CONCAP_FLAG_PROVIDER) |
---|
617 | 1094 | cap->type = TYPEC_PORT_SRC; |
---|
618 | | - else if (con->cap.consumer) |
---|
| 1095 | + else if (con->cap.flags & UCSI_CONCAP_FLAG_CONSUMER) |
---|
619 | 1096 | cap->type = TYPEC_PORT_SNK; |
---|
620 | 1097 | |
---|
621 | 1098 | cap->revision = ucsi->cap.typec_version; |
---|
622 | 1099 | cap->pd_revision = ucsi->cap.pd_version; |
---|
| 1100 | + cap->svdm_version = SVDM_VER_2_0; |
---|
623 | 1101 | cap->prefer_role = TYPEC_NO_PREFERRED_ROLE; |
---|
624 | 1102 | |
---|
625 | 1103 | if (con->cap.op_mode & UCSI_CONCAP_OPMODE_AUDIO_ACCESSORY) |
---|
.. | .. |
---|
627 | 1105 | if (con->cap.op_mode & UCSI_CONCAP_OPMODE_DEBUG_ACCESSORY) |
---|
628 | 1106 | *accessory = TYPEC_ACCESSORY_DEBUG; |
---|
629 | 1107 | |
---|
630 | | - cap->fwnode = ucsi_find_fwnode(con); |
---|
631 | | - cap->dr_set = ucsi_dr_swap; |
---|
632 | | - cap->pr_set = ucsi_pr_swap; |
---|
| 1108 | + cap->driver_data = con; |
---|
| 1109 | + cap->ops = &ucsi_ops; |
---|
| 1110 | + |
---|
| 1111 | + ret = ucsi_register_port_psy(con); |
---|
| 1112 | + if (ret) |
---|
| 1113 | + goto out; |
---|
633 | 1114 | |
---|
634 | 1115 | /* Register the connector */ |
---|
635 | 1116 | con->port = typec_register_port(ucsi->dev, cap); |
---|
636 | | - if (IS_ERR(con->port)) |
---|
637 | | - return PTR_ERR(con->port); |
---|
638 | | - |
---|
639 | | - /* Get the status */ |
---|
640 | | - UCSI_CMD_GET_CONNECTOR_STATUS(ctrl, con->num); |
---|
641 | | - ret = ucsi_run_command(ucsi, &ctrl, &con->status, sizeof(con->status)); |
---|
642 | | - if (ret < 0) { |
---|
643 | | - dev_err(ucsi->dev, "con%d: failed to get status\n", con->num); |
---|
644 | | - return 0; |
---|
| 1117 | + if (IS_ERR(con->port)) { |
---|
| 1118 | + ret = PTR_ERR(con->port); |
---|
| 1119 | + goto out; |
---|
645 | 1120 | } |
---|
646 | 1121 | |
---|
647 | | - ucsi_pwr_opmode_change(con); |
---|
648 | | - typec_set_pwr_role(con->port, con->status.pwr_dir); |
---|
| 1122 | + /* Alternate modes */ |
---|
| 1123 | + ret = ucsi_register_altmodes(con, UCSI_RECIPIENT_CON); |
---|
| 1124 | + if (ret) { |
---|
| 1125 | + dev_err(ucsi->dev, "con%d: failed to register alt modes\n", |
---|
| 1126 | + con->num); |
---|
| 1127 | + goto out; |
---|
| 1128 | + } |
---|
649 | 1129 | |
---|
650 | | - switch (con->status.partner_type) { |
---|
| 1130 | + /* Get the status */ |
---|
| 1131 | + command = UCSI_GET_CONNECTOR_STATUS | UCSI_CONNECTOR_NUMBER(con->num); |
---|
| 1132 | + ret = ucsi_send_command(ucsi, command, &con->status, sizeof(con->status)); |
---|
| 1133 | + if (ret < 0) { |
---|
| 1134 | + dev_err(ucsi->dev, "con%d: failed to get status\n", con->num); |
---|
| 1135 | + ret = 0; |
---|
| 1136 | + goto out; |
---|
| 1137 | + } |
---|
| 1138 | + ret = 0; /* ucsi_send_command() returns length on success */ |
---|
| 1139 | + |
---|
| 1140 | + switch (UCSI_CONSTAT_PARTNER_TYPE(con->status.flags)) { |
---|
651 | 1141 | case UCSI_CONSTAT_PARTNER_TYPE_UFP: |
---|
| 1142 | + case UCSI_CONSTAT_PARTNER_TYPE_CABLE_AND_UFP: |
---|
| 1143 | + u_role = USB_ROLE_HOST; |
---|
| 1144 | + fallthrough; |
---|
| 1145 | + case UCSI_CONSTAT_PARTNER_TYPE_CABLE: |
---|
652 | 1146 | typec_set_data_role(con->port, TYPEC_HOST); |
---|
653 | 1147 | break; |
---|
654 | 1148 | case UCSI_CONSTAT_PARTNER_TYPE_DFP: |
---|
| 1149 | + u_role = USB_ROLE_DEVICE; |
---|
655 | 1150 | typec_set_data_role(con->port, TYPEC_DEVICE); |
---|
656 | 1151 | break; |
---|
657 | 1152 | default: |
---|
.. | .. |
---|
659 | 1154 | } |
---|
660 | 1155 | |
---|
661 | 1156 | /* Check if there is already something connected */ |
---|
662 | | - if (con->status.connected) |
---|
| 1157 | + if (con->status.flags & UCSI_CONSTAT_CONNECTED) { |
---|
| 1158 | + typec_set_pwr_role(con->port, |
---|
| 1159 | + !!(con->status.flags & UCSI_CONSTAT_PWR_DIR)); |
---|
| 1160 | + ucsi_pwr_opmode_change(con); |
---|
663 | 1161 | ucsi_register_partner(con); |
---|
| 1162 | + ucsi_port_psy_changed(con); |
---|
| 1163 | + } |
---|
| 1164 | + |
---|
| 1165 | + /* Only notify USB controller if partner supports USB data */ |
---|
| 1166 | + if (!(UCSI_CONSTAT_PARTNER_FLAGS(con->status.flags) & UCSI_CONSTAT_PARTNER_FLAG_USB)) |
---|
| 1167 | + u_role = USB_ROLE_NONE; |
---|
| 1168 | + |
---|
| 1169 | + ret = usb_role_switch_set_role(con->usb_role_sw, u_role); |
---|
| 1170 | + if (ret) { |
---|
| 1171 | + dev_err(ucsi->dev, "con:%d: failed to set usb role:%d\n", |
---|
| 1172 | + con->num, u_role); |
---|
| 1173 | + ret = 0; |
---|
| 1174 | + } |
---|
| 1175 | + |
---|
| 1176 | + if (con->partner) { |
---|
| 1177 | + ret = ucsi_register_altmodes(con, UCSI_RECIPIENT_SOP); |
---|
| 1178 | + if (ret) { |
---|
| 1179 | + dev_err(ucsi->dev, |
---|
| 1180 | + "con%d: failed to register alternate modes\n", |
---|
| 1181 | + con->num); |
---|
| 1182 | + ret = 0; |
---|
| 1183 | + } else { |
---|
| 1184 | + ucsi_altmode_update_active(con); |
---|
| 1185 | + } |
---|
| 1186 | + } |
---|
664 | 1187 | |
---|
665 | 1188 | trace_ucsi_register_port(con->num, &con->status); |
---|
666 | 1189 | |
---|
667 | | - return 0; |
---|
| 1190 | +out: |
---|
| 1191 | + fwnode_handle_put(cap->fwnode); |
---|
| 1192 | +out_unlock: |
---|
| 1193 | + mutex_unlock(&con->lock); |
---|
| 1194 | + return ret; |
---|
668 | 1195 | } |
---|
669 | 1196 | |
---|
670 | | -static void ucsi_init(struct work_struct *work) |
---|
| 1197 | +/** |
---|
| 1198 | + * ucsi_init - Initialize UCSI interface |
---|
| 1199 | + * @ucsi: UCSI to be initialized |
---|
| 1200 | + * |
---|
| 1201 | + * Registers all ports @ucsi has and enables all notification events. |
---|
| 1202 | + */ |
---|
| 1203 | +static int ucsi_init(struct ucsi *ucsi) |
---|
671 | 1204 | { |
---|
672 | | - struct ucsi *ucsi = container_of(work, struct ucsi, work); |
---|
673 | 1205 | struct ucsi_connector *con; |
---|
674 | | - struct ucsi_control ctrl; |
---|
| 1206 | + u64 command, ntfy; |
---|
675 | 1207 | int ret; |
---|
676 | 1208 | int i; |
---|
677 | | - |
---|
678 | | - mutex_lock(&ucsi->ppm_lock); |
---|
679 | 1209 | |
---|
680 | 1210 | /* Reset the PPM */ |
---|
681 | 1211 | ret = ucsi_reset_ppm(ucsi); |
---|
.. | .. |
---|
685 | 1215 | } |
---|
686 | 1216 | |
---|
687 | 1217 | /* Enable basic notifications */ |
---|
688 | | - UCSI_CMD_SET_NTFY_ENABLE(ctrl, UCSI_ENABLE_NTFY_CMD_COMPLETE | |
---|
689 | | - UCSI_ENABLE_NTFY_ERROR); |
---|
690 | | - ret = ucsi_run_command(ucsi, &ctrl, NULL, 0); |
---|
| 1218 | + ntfy = UCSI_ENABLE_NTFY_CMD_COMPLETE | UCSI_ENABLE_NTFY_ERROR; |
---|
| 1219 | + command = UCSI_SET_NOTIFICATION_ENABLE | ntfy; |
---|
| 1220 | + ret = ucsi_send_command(ucsi, command, NULL, 0); |
---|
691 | 1221 | if (ret < 0) |
---|
692 | 1222 | goto err_reset; |
---|
693 | 1223 | |
---|
694 | 1224 | /* Get PPM capabilities */ |
---|
695 | | - UCSI_CMD_GET_CAPABILITY(ctrl); |
---|
696 | | - ret = ucsi_run_command(ucsi, &ctrl, &ucsi->cap, sizeof(ucsi->cap)); |
---|
| 1225 | + command = UCSI_GET_CAPABILITY; |
---|
| 1226 | + ret = ucsi_send_command(ucsi, command, &ucsi->cap, sizeof(ucsi->cap)); |
---|
697 | 1227 | if (ret < 0) |
---|
698 | 1228 | goto err_reset; |
---|
699 | 1229 | |
---|
.. | .. |
---|
718 | 1248 | } |
---|
719 | 1249 | |
---|
720 | 1250 | /* Enable all notifications */ |
---|
721 | | - UCSI_CMD_SET_NTFY_ENABLE(ctrl, UCSI_ENABLE_NTFY_ALL); |
---|
722 | | - ret = ucsi_run_command(ucsi, &ctrl, NULL, 0); |
---|
| 1251 | + ntfy = UCSI_ENABLE_NTFY_ALL; |
---|
| 1252 | + command = UCSI_SET_NOTIFICATION_ENABLE | ntfy; |
---|
| 1253 | + ret = ucsi_send_command(ucsi, command, NULL, 0); |
---|
723 | 1254 | if (ret < 0) |
---|
724 | 1255 | goto err_unregister; |
---|
725 | 1256 | |
---|
726 | | - mutex_unlock(&ucsi->ppm_lock); |
---|
727 | | - |
---|
728 | | - return; |
---|
| 1257 | + ucsi->ntfy = ntfy; |
---|
| 1258 | + return 0; |
---|
729 | 1259 | |
---|
730 | 1260 | err_unregister: |
---|
731 | 1261 | for (con = ucsi->connector; con->port; con++) { |
---|
732 | 1262 | ucsi_unregister_partner(con); |
---|
| 1263 | + ucsi_unregister_altmodes(con, UCSI_RECIPIENT_CON); |
---|
| 1264 | + ucsi_unregister_port_psy(con); |
---|
733 | 1265 | typec_unregister_port(con->port); |
---|
734 | 1266 | con->port = NULL; |
---|
735 | 1267 | } |
---|
.. | .. |
---|
738 | 1270 | memset(&ucsi->cap, 0, sizeof(ucsi->cap)); |
---|
739 | 1271 | ucsi_reset_ppm(ucsi); |
---|
740 | 1272 | err: |
---|
741 | | - mutex_unlock(&ucsi->ppm_lock); |
---|
742 | | - dev_err(ucsi->dev, "PPM init failed (%d)\n", ret); |
---|
| 1273 | + return ret; |
---|
| 1274 | +} |
---|
| 1275 | + |
---|
| 1276 | +static void ucsi_init_work(struct work_struct *work) |
---|
| 1277 | +{ |
---|
| 1278 | + struct ucsi_android *aucsi = container_of(work, |
---|
| 1279 | + struct ucsi_android, work.work); |
---|
| 1280 | + int ret; |
---|
| 1281 | + |
---|
| 1282 | + ret = ucsi_init(&aucsi->ucsi); |
---|
| 1283 | + if (ret) |
---|
| 1284 | + dev_err(aucsi->ucsi.dev, "PPM init failed (%d)\n", ret); |
---|
| 1285 | + |
---|
| 1286 | + if (ret == -EPROBE_DEFER) { |
---|
| 1287 | + if (aucsi->work_count++ > UCSI_ROLE_SWITCH_WAIT_COUNT) |
---|
| 1288 | + return; |
---|
| 1289 | + |
---|
| 1290 | + queue_delayed_work(system_long_wq, &aucsi->work, |
---|
| 1291 | + UCSI_ROLE_SWITCH_INTERVAL); |
---|
| 1292 | + } |
---|
743 | 1293 | } |
---|
744 | 1294 | |
---|
745 | 1295 | /** |
---|
746 | | - * ucsi_register_ppm - Register UCSI PPM Interface |
---|
747 | | - * @dev: Device interface to the PPM |
---|
748 | | - * @ppm: The PPM interface |
---|
749 | | - * |
---|
750 | | - * Allocates UCSI instance, associates it with @ppm and returns it to the |
---|
751 | | - * caller, and schedules initialization of the interface. |
---|
| 1296 | + * ucsi_get_drvdata - Return private driver data pointer |
---|
| 1297 | + * @ucsi: UCSI interface |
---|
752 | 1298 | */ |
---|
753 | | -struct ucsi *ucsi_register_ppm(struct device *dev, struct ucsi_ppm *ppm) |
---|
| 1299 | +void *ucsi_get_drvdata(struct ucsi *ucsi) |
---|
| 1300 | +{ |
---|
| 1301 | + return ucsi->driver_data; |
---|
| 1302 | +} |
---|
| 1303 | +EXPORT_SYMBOL_GPL(ucsi_get_drvdata); |
---|
| 1304 | + |
---|
| 1305 | +/** |
---|
| 1306 | + * ucsi_get_drvdata - Assign private driver data pointer |
---|
| 1307 | + * @ucsi: UCSI interface |
---|
| 1308 | + * @data: Private data pointer |
---|
| 1309 | + */ |
---|
| 1310 | +void ucsi_set_drvdata(struct ucsi *ucsi, void *data) |
---|
| 1311 | +{ |
---|
| 1312 | + ucsi->driver_data = data; |
---|
| 1313 | +} |
---|
| 1314 | +EXPORT_SYMBOL_GPL(ucsi_set_drvdata); |
---|
| 1315 | + |
---|
| 1316 | +/** |
---|
| 1317 | + * ucsi_create - Allocate UCSI instance |
---|
| 1318 | + * @dev: Device interface to the PPM (Platform Policy Manager) |
---|
| 1319 | + * @ops: I/O routines |
---|
| 1320 | + */ |
---|
| 1321 | +struct ucsi *ucsi_create(struct device *dev, const struct ucsi_operations *ops) |
---|
754 | 1322 | { |
---|
755 | 1323 | struct ucsi *ucsi; |
---|
| 1324 | + struct ucsi_android *aucsi; |
---|
756 | 1325 | |
---|
757 | | - ucsi = kzalloc(sizeof(*ucsi), GFP_KERNEL); |
---|
758 | | - if (!ucsi) |
---|
| 1326 | + if (!ops || !ops->read || !ops->sync_write || !ops->async_write) |
---|
| 1327 | + return ERR_PTR(-EINVAL); |
---|
| 1328 | + |
---|
| 1329 | + aucsi = kzalloc(sizeof(*aucsi), GFP_KERNEL); |
---|
| 1330 | + if (!aucsi) |
---|
759 | 1331 | return ERR_PTR(-ENOMEM); |
---|
760 | 1332 | |
---|
761 | | - INIT_WORK(&ucsi->work, ucsi_init); |
---|
762 | | - init_completion(&ucsi->complete); |
---|
| 1333 | + ucsi = &aucsi->ucsi; |
---|
| 1334 | + INIT_DELAYED_WORK(&aucsi->work, ucsi_init_work); |
---|
763 | 1335 | mutex_init(&ucsi->ppm_lock); |
---|
764 | | - |
---|
765 | 1336 | ucsi->dev = dev; |
---|
766 | | - ucsi->ppm = ppm; |
---|
767 | | - |
---|
768 | | - /* |
---|
769 | | - * Communication with the PPM takes a lot of time. It is not reasonable |
---|
770 | | - * to initialize the driver here. Using a work for now. |
---|
771 | | - */ |
---|
772 | | - queue_work(system_long_wq, &ucsi->work); |
---|
| 1337 | + ucsi->ops = ops; |
---|
773 | 1338 | |
---|
774 | 1339 | return ucsi; |
---|
775 | 1340 | } |
---|
776 | | -EXPORT_SYMBOL_GPL(ucsi_register_ppm); |
---|
| 1341 | +EXPORT_SYMBOL_GPL(ucsi_create); |
---|
777 | 1342 | |
---|
778 | 1343 | /** |
---|
779 | | - * ucsi_unregister_ppm - Unregister UCSI PPM Interface |
---|
780 | | - * @ucsi: struct ucsi associated with the PPM |
---|
781 | | - * |
---|
782 | | - * Unregister UCSI PPM that was created with ucsi_register(). |
---|
| 1344 | + * ucsi_destroy - Free UCSI instance |
---|
| 1345 | + * @ucsi: UCSI instance to be freed |
---|
783 | 1346 | */ |
---|
784 | | -void ucsi_unregister_ppm(struct ucsi *ucsi) |
---|
| 1347 | +void ucsi_destroy(struct ucsi *ucsi) |
---|
785 | 1348 | { |
---|
786 | | - struct ucsi_control ctrl; |
---|
| 1349 | + struct ucsi_android *aucsi = container_of(ucsi, |
---|
| 1350 | + struct ucsi_android, ucsi); |
---|
| 1351 | + kfree(aucsi); |
---|
| 1352 | +} |
---|
| 1353 | +EXPORT_SYMBOL_GPL(ucsi_destroy); |
---|
| 1354 | + |
---|
| 1355 | +/** |
---|
| 1356 | + * ucsi_register - Register UCSI interface |
---|
| 1357 | + * @ucsi: UCSI instance |
---|
| 1358 | + */ |
---|
| 1359 | +int ucsi_register(struct ucsi *ucsi) |
---|
| 1360 | +{ |
---|
| 1361 | + struct ucsi_android *aucsi = container_of(ucsi, |
---|
| 1362 | + struct ucsi_android, ucsi); |
---|
| 1363 | + int ret; |
---|
| 1364 | + |
---|
| 1365 | + ret = ucsi->ops->read(ucsi, UCSI_VERSION, &ucsi->version, |
---|
| 1366 | + sizeof(ucsi->version)); |
---|
| 1367 | + if (ret) |
---|
| 1368 | + return ret; |
---|
| 1369 | + |
---|
| 1370 | + if (!ucsi->version) |
---|
| 1371 | + return -ENODEV; |
---|
| 1372 | + |
---|
| 1373 | + queue_delayed_work(system_long_wq, &aucsi->work, 0); |
---|
| 1374 | + |
---|
| 1375 | + return 0; |
---|
| 1376 | +} |
---|
| 1377 | +EXPORT_SYMBOL_GPL(ucsi_register); |
---|
| 1378 | + |
---|
| 1379 | +/** |
---|
| 1380 | + * ucsi_unregister - Unregister UCSI interface |
---|
| 1381 | + * @ucsi: UCSI interface to be unregistered |
---|
| 1382 | + * |
---|
| 1383 | + * Unregister UCSI interface that was created with ucsi_register(). |
---|
| 1384 | + */ |
---|
| 1385 | +void ucsi_unregister(struct ucsi *ucsi) |
---|
| 1386 | +{ |
---|
| 1387 | + struct ucsi_android *aucsi = container_of(ucsi, |
---|
| 1388 | + struct ucsi_android, ucsi); |
---|
| 1389 | + u64 cmd = UCSI_SET_NOTIFICATION_ENABLE; |
---|
787 | 1390 | int i; |
---|
788 | 1391 | |
---|
789 | 1392 | /* Make sure that we are not in the middle of driver initialization */ |
---|
790 | | - cancel_work_sync(&ucsi->work); |
---|
| 1393 | + cancel_delayed_work_sync(&aucsi->work); |
---|
791 | 1394 | |
---|
792 | | - mutex_lock(&ucsi->ppm_lock); |
---|
793 | | - |
---|
794 | | - /* Disable everything except command complete notification */ |
---|
795 | | - UCSI_CMD_SET_NTFY_ENABLE(ctrl, UCSI_ENABLE_NTFY_CMD_COMPLETE) |
---|
796 | | - ucsi_run_command(ucsi, &ctrl, NULL, 0); |
---|
797 | | - |
---|
798 | | - mutex_unlock(&ucsi->ppm_lock); |
---|
| 1395 | + /* Disable notifications */ |
---|
| 1396 | + ucsi->ops->async_write(ucsi, UCSI_CONTROL, &cmd, sizeof(cmd)); |
---|
799 | 1397 | |
---|
800 | 1398 | for (i = 0; i < ucsi->cap.num_connectors; i++) { |
---|
801 | 1399 | cancel_work_sync(&ucsi->connector[i].work); |
---|
802 | 1400 | ucsi_unregister_partner(&ucsi->connector[i]); |
---|
| 1401 | + ucsi_unregister_altmodes(&ucsi->connector[i], |
---|
| 1402 | + UCSI_RECIPIENT_CON); |
---|
| 1403 | + ucsi_unregister_port_psy(&ucsi->connector[i]); |
---|
803 | 1404 | typec_unregister_port(ucsi->connector[i].port); |
---|
804 | 1405 | } |
---|
805 | 1406 | |
---|
806 | | - ucsi_reset_ppm(ucsi); |
---|
807 | | - |
---|
808 | 1407 | kfree(ucsi->connector); |
---|
809 | | - kfree(ucsi); |
---|
810 | 1408 | } |
---|
811 | | -EXPORT_SYMBOL_GPL(ucsi_unregister_ppm); |
---|
| 1409 | +EXPORT_SYMBOL_GPL(ucsi_unregister); |
---|
812 | 1410 | |
---|
813 | 1411 | MODULE_AUTHOR("Heikki Krogerus <heikki.krogerus@linux.intel.com>"); |
---|
814 | 1412 | MODULE_LICENSE("GPL v2"); |
---|