1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3 * ucsi-huawei-gaokun - A UCSI driver for HUAWEI Matebook E Go
4 *
5 * Copyright (C) 2024-2025 Pengyu Luo <mitltlatltl@gmail.com>
6 */
7
8 #include <drm/bridge/aux-bridge.h>
9 #include <linux/auxiliary_bus.h>
10 #include <linux/bitops.h>
11 #include <linux/completion.h>
12 #include <linux/container_of.h>
13 #include <linux/module.h>
14 #include <linux/notifier.h>
15 #include <linux/of.h>
16 #include <linux/platform_data/huawei-gaokun-ec.h>
17 #include <linux/string.h>
18 #include <linux/usb/pd_vdo.h>
19 #include <linux/usb/typec_altmode.h>
20 #include <linux/usb/typec_dp.h>
21 #include <linux/workqueue_types.h>
22
23 #include "ucsi.h"
24
25 #define EC_EVENT_UCSI 0x21
26 #define EC_EVENT_USB 0x22
27
28 #define GAOKUN_CCX_MASK GENMASK(1, 0)
29 #define GAOKUN_MUX_MASK GENMASK(3, 2)
30
31 #define GAOKUN_DPAM_MASK GENMASK(3, 0)
32 #define GAOKUN_HPD_STATE_MASK BIT(4)
33 #define GAOKUN_HPD_IRQ_MASK BIT(5)
34
35 #define GET_IDX(updt) (ffs(updt) - 1)
36
37 #define CCX_TO_ORI(ccx) (++(ccx) % 3) /* convert ccx to enum typec_orientation */
38
39 /* Configuration Channel Extension */
40 enum gaokun_ucsi_ccx {
41 USBC_CCX_NORMAL,
42 USBC_CCX_REVERSE,
43 USBC_CCX_NONE,
44 };
45
46 enum gaokun_ucsi_mux {
47 USBC_MUX_NONE,
48 USBC_MUX_USB_2L,
49 USBC_MUX_DP_4L,
50 USBC_MUX_USB_DP,
51 };
52
53 /* based on pmic_glink_altmode_pin_assignment */
54 enum gaokun_ucsi_dpam_pan { /* DP Alt Mode Pin Assignments */
55 USBC_DPAM_PAN_NONE,
56 USBC_DPAM_PAN_A, /* Not supported after USB Type-C Standard v1.0b */
57 USBC_DPAM_PAN_B, /* Not supported after USB Type-C Standard v1.0b */
58 USBC_DPAM_PAN_C, /* USBC_DPAM_PAN_C_REVERSE - 6 */
59 USBC_DPAM_PAN_D,
60 USBC_DPAM_PAN_E,
61 USBC_DPAM_PAN_F, /* Not supported after USB Type-C Standard v1.0b */
62 USBC_DPAM_PAN_A_REVERSE,/* Not supported after USB Type-C Standard v1.0b */
63 USBC_DPAM_PAN_B_REVERSE,/* Not supported after USB Type-C Standard v1.0b */
64 USBC_DPAM_PAN_C_REVERSE,
65 USBC_DPAM_PAN_D_REVERSE,
66 USBC_DPAM_PAN_E_REVERSE,
67 USBC_DPAM_PAN_F_REVERSE,/* Not supported after USB Type-C Standard v1.0b */
68 };
69
70 struct gaokun_ucsi_reg {
71 u8 num_ports;
72 u8 port_updt;
73 u8 port_data[4];
74 u8 checksum;
75 u8 reserved;
76 } __packed;
77
78 struct gaokun_ucsi_port {
79 struct completion usb_ack;
80 spinlock_t lock; /* serializing port resource access */
81
82 struct gaokun_ucsi *ucsi;
83 struct auxiliary_device *bridge;
84
85 int idx;
86 enum gaokun_ucsi_ccx ccx;
87 enum gaokun_ucsi_mux mux;
88 u8 mode;
89 u16 svid;
90 u8 hpd_state;
91 u8 hpd_irq;
92 };
93
94 struct gaokun_ucsi {
95 struct gaokun_ec *ec;
96 struct ucsi *ucsi;
97 struct gaokun_ucsi_port *ports;
98 struct device *dev;
99 struct delayed_work work;
100 struct notifier_block nb;
101 u16 version;
102 u8 num_ports;
103 };
104
105 /* -------------------------------------------------------------------------- */
106 /* For UCSI */
107
gaokun_ucsi_read_version(struct ucsi * ucsi,u16 * version)108 static int gaokun_ucsi_read_version(struct ucsi *ucsi, u16 *version)
109 {
110 struct gaokun_ucsi *uec = ucsi_get_drvdata(ucsi);
111
112 *version = uec->version;
113
114 return 0;
115 }
116
gaokun_ucsi_read_cci(struct ucsi * ucsi,u32 * cci)117 static int gaokun_ucsi_read_cci(struct ucsi *ucsi, u32 *cci)
118 {
119 struct gaokun_ucsi *uec = ucsi_get_drvdata(ucsi);
120 u8 buf[GAOKUN_UCSI_READ_SIZE];
121 int ret;
122
123 ret = gaokun_ec_ucsi_read(uec->ec, buf);
124 if (ret)
125 return ret;
126
127 memcpy(cci, buf, sizeof(*cci));
128
129 return 0;
130 }
131
gaokun_ucsi_read_message_in(struct ucsi * ucsi,void * val,size_t val_len)132 static int gaokun_ucsi_read_message_in(struct ucsi *ucsi,
133 void *val, size_t val_len)
134 {
135 struct gaokun_ucsi *uec = ucsi_get_drvdata(ucsi);
136 u8 buf[GAOKUN_UCSI_READ_SIZE];
137 int ret;
138
139 ret = gaokun_ec_ucsi_read(uec->ec, buf);
140 if (ret)
141 return ret;
142
143 memcpy(val, buf + GAOKUN_UCSI_CCI_SIZE,
144 min(val_len, GAOKUN_UCSI_MSGI_SIZE));
145
146 return 0;
147 }
148
gaokun_ucsi_async_control(struct ucsi * ucsi,u64 command)149 static int gaokun_ucsi_async_control(struct ucsi *ucsi, u64 command)
150 {
151 struct gaokun_ucsi *uec = ucsi_get_drvdata(ucsi);
152 u8 buf[GAOKUN_UCSI_WRITE_SIZE] = {};
153
154 memcpy(buf, &command, sizeof(command));
155
156 return gaokun_ec_ucsi_write(uec->ec, buf);
157 }
158
gaokun_ucsi_update_connector(struct ucsi_connector * con)159 static void gaokun_ucsi_update_connector(struct ucsi_connector *con)
160 {
161 struct gaokun_ucsi *uec = ucsi_get_drvdata(con->ucsi);
162
163 if (con->num > uec->num_ports)
164 return;
165
166 con->typec_cap.orientation_aware = true;
167 }
168
gaokun_set_orientation(struct ucsi_connector * con,struct gaokun_ucsi_port * port)169 static void gaokun_set_orientation(struct ucsi_connector *con,
170 struct gaokun_ucsi_port *port)
171 {
172 enum gaokun_ucsi_ccx ccx;
173 unsigned long flags;
174
175 spin_lock_irqsave(&port->lock, flags);
176 ccx = port->ccx;
177 spin_unlock_irqrestore(&port->lock, flags);
178
179 typec_set_orientation(con->port, CCX_TO_ORI(ccx));
180 }
181
gaokun_ucsi_connector_status(struct ucsi_connector * con)182 static void gaokun_ucsi_connector_status(struct ucsi_connector *con)
183 {
184 struct gaokun_ucsi *uec = ucsi_get_drvdata(con->ucsi);
185 int idx;
186
187 idx = con->num - 1;
188 if (con->num > uec->num_ports) {
189 dev_warn(uec->dev, "set orientation out of range: con%d\n", idx);
190 return;
191 }
192
193 gaokun_set_orientation(con, &uec->ports[idx]);
194 }
195
196 const struct ucsi_operations gaokun_ucsi_ops = {
197 .read_version = gaokun_ucsi_read_version,
198 .read_cci = gaokun_ucsi_read_cci,
199 .poll_cci = gaokun_ucsi_read_cci,
200 .read_message_in = gaokun_ucsi_read_message_in,
201 .sync_control = ucsi_sync_control_common,
202 .async_control = gaokun_ucsi_async_control,
203 .update_connector = gaokun_ucsi_update_connector,
204 .connector_status = gaokun_ucsi_connector_status,
205 };
206
207 /* -------------------------------------------------------------------------- */
208 /* For Altmode */
209
gaokun_ucsi_port_update(struct gaokun_ucsi_port * port,const u8 * port_data)210 static void gaokun_ucsi_port_update(struct gaokun_ucsi_port *port,
211 const u8 *port_data)
212 {
213 struct gaokun_ucsi *uec = port->ucsi;
214 int offset = port->idx * 2; /* every port has 2 Bytes data */
215 unsigned long flags;
216 u8 dcc, ddi;
217
218 dcc = port_data[offset];
219 ddi = port_data[offset + 1];
220
221 spin_lock_irqsave(&port->lock, flags);
222
223 port->ccx = FIELD_GET(GAOKUN_CCX_MASK, dcc);
224 port->mux = FIELD_GET(GAOKUN_MUX_MASK, dcc);
225 port->mode = FIELD_GET(GAOKUN_DPAM_MASK, ddi);
226 port->hpd_state = FIELD_GET(GAOKUN_HPD_STATE_MASK, ddi);
227 port->hpd_irq = FIELD_GET(GAOKUN_HPD_IRQ_MASK, ddi);
228
229 /* Mode and SVID are unused; keeping them to make things clearer */
230 switch (port->mode) {
231 case USBC_DPAM_PAN_C:
232 case USBC_DPAM_PAN_C_REVERSE:
233 port->mode = DP_PIN_ASSIGN_C; /* correct it for usb later */
234 break;
235 case USBC_DPAM_PAN_D:
236 case USBC_DPAM_PAN_D_REVERSE:
237 port->mode = DP_PIN_ASSIGN_D;
238 break;
239 case USBC_DPAM_PAN_E:
240 case USBC_DPAM_PAN_E_REVERSE:
241 port->mode = DP_PIN_ASSIGN_E;
242 break;
243 case USBC_DPAM_PAN_NONE:
244 port->mode = TYPEC_STATE_SAFE;
245 break;
246 default:
247 dev_warn(uec->dev, "unknown mode %d\n", port->mode);
248 break;
249 }
250
251 switch (port->mux) {
252 case USBC_MUX_NONE:
253 port->svid = 0;
254 break;
255 case USBC_MUX_USB_2L:
256 port->svid = USB_SID_PD;
257 port->mode = TYPEC_STATE_USB; /* same as PAN_C, correct it */
258 break;
259 case USBC_MUX_DP_4L:
260 case USBC_MUX_USB_DP:
261 port->svid = USB_SID_DISPLAYPORT;
262 break;
263 default:
264 dev_warn(uec->dev, "unknown mux state %d\n", port->mux);
265 break;
266 }
267
268 spin_unlock_irqrestore(&port->lock, flags);
269 }
270
gaokun_ucsi_refresh(struct gaokun_ucsi * uec)271 static int gaokun_ucsi_refresh(struct gaokun_ucsi *uec)
272 {
273 struct gaokun_ucsi_reg ureg;
274 int ret, idx;
275
276 ret = gaokun_ec_ucsi_get_reg(uec->ec, &ureg);
277 if (ret)
278 return GAOKUN_UCSI_NO_PORT_UPDATE;
279
280 uec->num_ports = ureg.num_ports;
281 idx = GET_IDX(ureg.port_updt);
282
283 if (idx < 0 || idx >= ureg.num_ports)
284 return GAOKUN_UCSI_NO_PORT_UPDATE;
285
286 gaokun_ucsi_port_update(&uec->ports[idx], ureg.port_data);
287 return idx;
288 }
289
gaokun_ucsi_handle_altmode(struct gaokun_ucsi_port * port)290 static void gaokun_ucsi_handle_altmode(struct gaokun_ucsi_port *port)
291 {
292 struct gaokun_ucsi *uec = port->ucsi;
293 int idx = port->idx;
294
295 if (idx >= uec->ucsi->cap.num_connectors) {
296 dev_warn(uec->dev, "altmode port out of range: %d\n", idx);
297 return;
298 }
299
300 /* UCSI callback .connector_status() have set orientation */
301 if (port->bridge)
302 drm_aux_hpd_bridge_notify(&port->bridge->dev,
303 port->hpd_state ?
304 connector_status_connected :
305 connector_status_disconnected);
306
307 gaokun_ec_ucsi_pan_ack(uec->ec, port->idx);
308 }
309
gaokun_ucsi_altmode_notify_ind(struct gaokun_ucsi * uec)310 static void gaokun_ucsi_altmode_notify_ind(struct gaokun_ucsi *uec)
311 {
312 int idx;
313
314 if (!uec->ucsi->connector) { /* slow to register */
315 dev_err_ratelimited(uec->dev, "ucsi connector is not initialized yet\n");
316 return;
317 }
318
319 idx = gaokun_ucsi_refresh(uec);
320 if (idx == GAOKUN_UCSI_NO_PORT_UPDATE)
321 gaokun_ec_ucsi_pan_ack(uec->ec, idx); /* ack directly if no update */
322 else
323 gaokun_ucsi_handle_altmode(&uec->ports[idx]);
324 }
325
326 /*
327 * When inserting, 2 UCSI events(connector change) are followed by USB events.
328 * If we received one USB event, that means USB events are not blocked, so we
329 * can complelte for all ports, and we should signal all events.
330 */
gaokun_ucsi_complete_usb_ack(struct gaokun_ucsi * uec)331 static void gaokun_ucsi_complete_usb_ack(struct gaokun_ucsi *uec)
332 {
333 struct gaokun_ucsi_port *port;
334 int idx = 0;
335
336 while (idx < uec->num_ports) {
337 port = &uec->ports[idx++];
338 if (!completion_done(&port->usb_ack))
339 complete_all(&port->usb_ack);
340 }
341 }
342
343 /*
344 * USB event is necessary for enabling altmode, the event should follow
345 * UCSI event, if not after timeout(this notify may be disabled somehow),
346 * then force to enable altmode.
347 */
gaokun_ucsi_handle_no_usb_event(struct gaokun_ucsi * uec,int idx)348 static void gaokun_ucsi_handle_no_usb_event(struct gaokun_ucsi *uec, int idx)
349 {
350 struct gaokun_ucsi_port *port;
351
352 port = &uec->ports[idx];
353 if (!wait_for_completion_timeout(&port->usb_ack, 2 * HZ)) {
354 dev_warn(uec->dev, "No USB EVENT, triggered by UCSI EVENT");
355 gaokun_ucsi_altmode_notify_ind(uec);
356 }
357 }
358
gaokun_ucsi_notify(struct notifier_block * nb,unsigned long action,void * data)359 static int gaokun_ucsi_notify(struct notifier_block *nb,
360 unsigned long action, void *data)
361 {
362 u32 cci;
363 int ret;
364 struct gaokun_ucsi *uec = container_of(nb, struct gaokun_ucsi, nb);
365
366 switch (action) {
367 case EC_EVENT_USB:
368 gaokun_ucsi_complete_usb_ack(uec);
369 gaokun_ucsi_altmode_notify_ind(uec);
370 return NOTIFY_OK;
371
372 case EC_EVENT_UCSI:
373 ret = gaokun_ucsi_read_cci(uec->ucsi, &cci);
374 if (ret)
375 return NOTIFY_DONE;
376
377 ucsi_notify_common(uec->ucsi, cci);
378 if (UCSI_CCI_CONNECTOR(cci))
379 gaokun_ucsi_handle_no_usb_event(uec, UCSI_CCI_CONNECTOR(cci) - 1);
380
381 return NOTIFY_OK;
382
383 default:
384 return NOTIFY_DONE;
385 }
386 }
387
gaokun_ucsi_ports_init(struct gaokun_ucsi * uec)388 static int gaokun_ucsi_ports_init(struct gaokun_ucsi *uec)
389 {
390 struct gaokun_ucsi_port *ucsi_port;
391 struct gaokun_ucsi_reg ureg = {};
392 struct device *dev = uec->dev;
393 struct fwnode_handle *fwnode;
394 int i, ret, num_ports;
395 u32 port;
396
397 gaokun_ec_ucsi_get_reg(uec->ec, &ureg);
398 num_ports = ureg.num_ports;
399 uec->ports = devm_kcalloc(dev, num_ports, sizeof(*uec->ports),
400 GFP_KERNEL);
401 if (!uec->ports)
402 return -ENOMEM;
403
404 for (i = 0; i < num_ports; ++i) {
405 ucsi_port = &uec->ports[i];
406 ucsi_port->ccx = USBC_CCX_NONE;
407 ucsi_port->idx = i;
408 ucsi_port->ucsi = uec;
409 init_completion(&ucsi_port->usb_ack);
410 spin_lock_init(&ucsi_port->lock);
411 }
412
413 device_for_each_child_node(dev, fwnode) {
414 ret = fwnode_property_read_u32(fwnode, "reg", &port);
415 if (ret < 0) {
416 dev_err(dev, "missing reg property of %pOFn\n", fwnode);
417 fwnode_handle_put(fwnode);
418 return ret;
419 }
420
421 if (port >= num_ports) {
422 dev_warn(dev, "invalid connector number %d, ignoring\n", port);
423 continue;
424 }
425
426 ucsi_port = &uec->ports[port];
427 ucsi_port->bridge = devm_drm_dp_hpd_bridge_alloc(dev, to_of_node(fwnode));
428 if (IS_ERR(ucsi_port->bridge)) {
429 fwnode_handle_put(fwnode);
430 return PTR_ERR(ucsi_port->bridge);
431 }
432 }
433
434 for (i = 0; i < num_ports; i++) {
435 if (!uec->ports[i].bridge)
436 continue;
437
438 ret = devm_drm_dp_hpd_bridge_add(dev, uec->ports[i].bridge);
439 if (ret)
440 return ret;
441 }
442
443 return 0;
444 }
445
gaokun_ucsi_register_worker(struct work_struct * work)446 static void gaokun_ucsi_register_worker(struct work_struct *work)
447 {
448 struct gaokun_ucsi *uec;
449 struct ucsi *ucsi;
450 int ret;
451
452 uec = container_of(work, struct gaokun_ucsi, work.work);
453 ucsi = uec->ucsi;
454
455 ret = gaokun_ec_register_notify(uec->ec, &uec->nb);
456 if (ret) {
457 dev_err_probe(ucsi->dev, ret, "notifier register failed\n");
458 return;
459 }
460
461 ret = ucsi_register(ucsi);
462 if (ret)
463 dev_err_probe(ucsi->dev, ret, "ucsi register failed\n");
464 }
465
gaokun_ucsi_probe(struct auxiliary_device * adev,const struct auxiliary_device_id * id)466 static int gaokun_ucsi_probe(struct auxiliary_device *adev,
467 const struct auxiliary_device_id *id)
468 {
469 struct gaokun_ec *ec = adev->dev.platform_data;
470 struct device *dev = &adev->dev;
471 struct gaokun_ucsi *uec;
472 int ret;
473
474 uec = devm_kzalloc(dev, sizeof(*uec), GFP_KERNEL);
475 if (!uec)
476 return -ENOMEM;
477
478 uec->ec = ec;
479 uec->dev = dev;
480 uec->version = UCSI_VERSION_1_0;
481 uec->nb.notifier_call = gaokun_ucsi_notify;
482
483 INIT_DELAYED_WORK(&uec->work, gaokun_ucsi_register_worker);
484
485 ret = gaokun_ucsi_ports_init(uec);
486 if (ret)
487 return ret;
488
489 uec->ucsi = ucsi_create(dev, &gaokun_ucsi_ops);
490 if (IS_ERR(uec->ucsi))
491 return PTR_ERR(uec->ucsi);
492
493 ucsi_set_drvdata(uec->ucsi, uec);
494 auxiliary_set_drvdata(adev, uec);
495
496 /* EC can't handle UCSI properly in the early stage */
497 schedule_delayed_work(&uec->work, 3 * HZ);
498
499 return 0;
500 }
501
gaokun_ucsi_remove(struct auxiliary_device * adev)502 static void gaokun_ucsi_remove(struct auxiliary_device *adev)
503 {
504 struct gaokun_ucsi *uec = auxiliary_get_drvdata(adev);
505
506 disable_delayed_work_sync(&uec->work);
507 gaokun_ec_unregister_notify(uec->ec, &uec->nb);
508 ucsi_unregister(uec->ucsi);
509 ucsi_destroy(uec->ucsi);
510 }
511
512 static const struct auxiliary_device_id gaokun_ucsi_id_table[] = {
513 { .name = GAOKUN_MOD_NAME "." GAOKUN_DEV_UCSI, },
514 {}
515 };
516 MODULE_DEVICE_TABLE(auxiliary, gaokun_ucsi_id_table);
517
518 static struct auxiliary_driver gaokun_ucsi_driver = {
519 .name = GAOKUN_DEV_UCSI,
520 .id_table = gaokun_ucsi_id_table,
521 .probe = gaokun_ucsi_probe,
522 .remove = gaokun_ucsi_remove,
523 };
524
525 module_auxiliary_driver(gaokun_ucsi_driver);
526
527 MODULE_DESCRIPTION("HUAWEI Matebook E Go UCSI driver");
528 MODULE_LICENSE("GPL");
529