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 .read_message_in = gaokun_ucsi_read_message_in,
200 .sync_control = ucsi_sync_control_common,
201 .async_control = gaokun_ucsi_async_control,
202 .update_connector = gaokun_ucsi_update_connector,
203 .connector_status = gaokun_ucsi_connector_status,
204 };
205
206 /* -------------------------------------------------------------------------- */
207 /* For Altmode */
208
gaokun_ucsi_port_update(struct gaokun_ucsi_port * port,const u8 * port_data)209 static void gaokun_ucsi_port_update(struct gaokun_ucsi_port *port,
210 const u8 *port_data)
211 {
212 struct gaokun_ucsi *uec = port->ucsi;
213 int offset = port->idx * 2; /* every port has 2 Bytes data */
214 unsigned long flags;
215 u8 dcc, ddi;
216
217 dcc = port_data[offset];
218 ddi = port_data[offset + 1];
219
220 spin_lock_irqsave(&port->lock, flags);
221
222 port->ccx = FIELD_GET(GAOKUN_CCX_MASK, dcc);
223 port->mux = FIELD_GET(GAOKUN_MUX_MASK, dcc);
224 port->mode = FIELD_GET(GAOKUN_DPAM_MASK, ddi);
225 port->hpd_state = FIELD_GET(GAOKUN_HPD_STATE_MASK, ddi);
226 port->hpd_irq = FIELD_GET(GAOKUN_HPD_IRQ_MASK, ddi);
227
228 /* Mode and SVID are unused; keeping them to make things clearer */
229 switch (port->mode) {
230 case USBC_DPAM_PAN_C:
231 case USBC_DPAM_PAN_C_REVERSE:
232 port->mode = DP_PIN_ASSIGN_C; /* correct it for usb later */
233 break;
234 case USBC_DPAM_PAN_D:
235 case USBC_DPAM_PAN_D_REVERSE:
236 port->mode = DP_PIN_ASSIGN_D;
237 break;
238 case USBC_DPAM_PAN_E:
239 case USBC_DPAM_PAN_E_REVERSE:
240 port->mode = DP_PIN_ASSIGN_E;
241 break;
242 case USBC_DPAM_PAN_NONE:
243 port->mode = TYPEC_STATE_SAFE;
244 break;
245 default:
246 dev_warn(uec->dev, "unknown mode %d\n", port->mode);
247 break;
248 }
249
250 switch (port->mux) {
251 case USBC_MUX_NONE:
252 port->svid = 0;
253 break;
254 case USBC_MUX_USB_2L:
255 port->svid = USB_SID_PD;
256 port->mode = TYPEC_STATE_USB; /* same as PAN_C, correct it */
257 break;
258 case USBC_MUX_DP_4L:
259 case USBC_MUX_USB_DP:
260 port->svid = USB_SID_DISPLAYPORT;
261 break;
262 default:
263 dev_warn(uec->dev, "unknown mux state %d\n", port->mux);
264 break;
265 }
266
267 spin_unlock_irqrestore(&port->lock, flags);
268 }
269
gaokun_ucsi_refresh(struct gaokun_ucsi * uec)270 static int gaokun_ucsi_refresh(struct gaokun_ucsi *uec)
271 {
272 struct gaokun_ucsi_reg ureg;
273 int ret, idx;
274
275 ret = gaokun_ec_ucsi_get_reg(uec->ec, &ureg);
276 if (ret)
277 return GAOKUN_UCSI_NO_PORT_UPDATE;
278
279 uec->num_ports = ureg.num_ports;
280 idx = GET_IDX(ureg.port_updt);
281
282 if (idx < 0 || idx >= ureg.num_ports)
283 return GAOKUN_UCSI_NO_PORT_UPDATE;
284
285 gaokun_ucsi_port_update(&uec->ports[idx], ureg.port_data);
286 return idx;
287 }
288
gaokun_ucsi_handle_altmode(struct gaokun_ucsi_port * port)289 static void gaokun_ucsi_handle_altmode(struct gaokun_ucsi_port *port)
290 {
291 struct gaokun_ucsi *uec = port->ucsi;
292 int idx = port->idx;
293
294 if (idx >= uec->ucsi->cap.num_connectors) {
295 dev_warn(uec->dev, "altmode port out of range: %d\n", idx);
296 return;
297 }
298
299 /* UCSI callback .connector_status() have set orientation */
300 if (port->bridge)
301 drm_aux_hpd_bridge_notify(&port->bridge->dev,
302 port->hpd_state ?
303 connector_status_connected :
304 connector_status_disconnected);
305
306 gaokun_ec_ucsi_pan_ack(uec->ec, port->idx);
307 }
308
gaokun_ucsi_altmode_notify_ind(struct gaokun_ucsi * uec)309 static void gaokun_ucsi_altmode_notify_ind(struct gaokun_ucsi *uec)
310 {
311 int idx;
312
313 if (!uec->ucsi->connector) { /* slow to register */
314 dev_err_ratelimited(uec->dev, "ucsi connector is not initialized yet\n");
315 return;
316 }
317
318 idx = gaokun_ucsi_refresh(uec);
319 if (idx == GAOKUN_UCSI_NO_PORT_UPDATE)
320 gaokun_ec_ucsi_pan_ack(uec->ec, idx); /* ack directly if no update */
321 else
322 gaokun_ucsi_handle_altmode(&uec->ports[idx]);
323 }
324
325 /*
326 * When inserting, 2 UCSI events(connector change) are followed by USB events.
327 * If we received one USB event, that means USB events are not blocked, so we
328 * can complelte for all ports, and we should signal all events.
329 */
gaokun_ucsi_complete_usb_ack(struct gaokun_ucsi * uec)330 static void gaokun_ucsi_complete_usb_ack(struct gaokun_ucsi *uec)
331 {
332 struct gaokun_ucsi_port *port;
333 int idx = 0;
334
335 while (idx < uec->num_ports) {
336 port = &uec->ports[idx++];
337 if (!completion_done(&port->usb_ack))
338 complete_all(&port->usb_ack);
339 }
340 }
341
342 /*
343 * USB event is necessary for enabling altmode, the event should follow
344 * UCSI event, if not after timeout(this notify may be disabled somehow),
345 * then force to enable altmode.
346 */
gaokun_ucsi_handle_no_usb_event(struct gaokun_ucsi * uec,int idx)347 static void gaokun_ucsi_handle_no_usb_event(struct gaokun_ucsi *uec, int idx)
348 {
349 struct gaokun_ucsi_port *port;
350
351 port = &uec->ports[idx];
352 if (!wait_for_completion_timeout(&port->usb_ack, 2 * HZ)) {
353 dev_warn(uec->dev, "No USB EVENT, triggered by UCSI EVENT");
354 gaokun_ucsi_altmode_notify_ind(uec);
355 }
356 }
357
gaokun_ucsi_notify(struct notifier_block * nb,unsigned long action,void * data)358 static int gaokun_ucsi_notify(struct notifier_block *nb,
359 unsigned long action, void *data)
360 {
361 u32 cci;
362 int ret;
363 struct gaokun_ucsi *uec = container_of(nb, struct gaokun_ucsi, nb);
364
365 switch (action) {
366 case EC_EVENT_USB:
367 gaokun_ucsi_complete_usb_ack(uec);
368 gaokun_ucsi_altmode_notify_ind(uec);
369 return NOTIFY_OK;
370
371 case EC_EVENT_UCSI:
372 ret = gaokun_ucsi_read_cci(uec->ucsi, &cci);
373 if (ret)
374 return NOTIFY_DONE;
375
376 ucsi_notify_common(uec->ucsi, cci);
377 if (UCSI_CCI_CONNECTOR(cci))
378 gaokun_ucsi_handle_no_usb_event(uec, UCSI_CCI_CONNECTOR(cci) - 1);
379
380 return NOTIFY_OK;
381
382 default:
383 return NOTIFY_DONE;
384 }
385 }
386
gaokun_ucsi_ports_init(struct gaokun_ucsi * uec)387 static int gaokun_ucsi_ports_init(struct gaokun_ucsi *uec)
388 {
389 struct gaokun_ucsi_port *ucsi_port;
390 struct gaokun_ucsi_reg ureg = {};
391 struct device *dev = uec->dev;
392 struct fwnode_handle *fwnode;
393 int i, ret, num_ports;
394 u32 port;
395
396 gaokun_ec_ucsi_get_reg(uec->ec, &ureg);
397 num_ports = ureg.num_ports;
398 uec->ports = devm_kcalloc(dev, num_ports, sizeof(*uec->ports),
399 GFP_KERNEL);
400 if (!uec->ports)
401 return -ENOMEM;
402
403 for (i = 0; i < num_ports; ++i) {
404 ucsi_port = &uec->ports[i];
405 ucsi_port->ccx = USBC_CCX_NONE;
406 ucsi_port->idx = i;
407 ucsi_port->ucsi = uec;
408 init_completion(&ucsi_port->usb_ack);
409 spin_lock_init(&ucsi_port->lock);
410 }
411
412 device_for_each_child_node(dev, fwnode) {
413 ret = fwnode_property_read_u32(fwnode, "reg", &port);
414 if (ret < 0) {
415 dev_err(dev, "missing reg property of %pOFn\n", fwnode);
416 fwnode_handle_put(fwnode);
417 return ret;
418 }
419
420 if (port >= num_ports) {
421 dev_warn(dev, "invalid connector number %d, ignoring\n", port);
422 continue;
423 }
424
425 ucsi_port = &uec->ports[port];
426 ucsi_port->bridge = devm_drm_dp_hpd_bridge_alloc(dev, to_of_node(fwnode));
427 if (IS_ERR(ucsi_port->bridge)) {
428 fwnode_handle_put(fwnode);
429 return PTR_ERR(ucsi_port->bridge);
430 }
431 }
432
433 for (i = 0; i < num_ports; i++) {
434 if (!uec->ports[i].bridge)
435 continue;
436
437 ret = devm_drm_dp_hpd_bridge_add(dev, uec->ports[i].bridge);
438 if (ret)
439 return ret;
440 }
441
442 return 0;
443 }
444
gaokun_ucsi_register_worker(struct work_struct * work)445 static void gaokun_ucsi_register_worker(struct work_struct *work)
446 {
447 struct gaokun_ucsi *uec;
448 struct ucsi *ucsi;
449 int ret;
450
451 uec = container_of(work, struct gaokun_ucsi, work.work);
452 ucsi = uec->ucsi;
453
454 ret = gaokun_ec_register_notify(uec->ec, &uec->nb);
455 if (ret) {
456 dev_err_probe(ucsi->dev, ret, "notifier register failed\n");
457 return;
458 }
459
460 ret = ucsi_register(ucsi);
461 if (ret)
462 dev_err_probe(ucsi->dev, ret, "ucsi register failed\n");
463 }
464
gaokun_ucsi_probe(struct auxiliary_device * adev,const struct auxiliary_device_id * id)465 static int gaokun_ucsi_probe(struct auxiliary_device *adev,
466 const struct auxiliary_device_id *id)
467 {
468 struct gaokun_ec *ec = adev->dev.platform_data;
469 struct device *dev = &adev->dev;
470 struct gaokun_ucsi *uec;
471 int ret;
472
473 uec = devm_kzalloc(dev, sizeof(*uec), GFP_KERNEL);
474 if (!uec)
475 return -ENOMEM;
476
477 uec->ec = ec;
478 uec->dev = dev;
479 uec->version = UCSI_VERSION_1_0;
480 uec->nb.notifier_call = gaokun_ucsi_notify;
481
482 INIT_DELAYED_WORK(&uec->work, gaokun_ucsi_register_worker);
483
484 ret = gaokun_ucsi_ports_init(uec);
485 if (ret)
486 return ret;
487
488 uec->ucsi = ucsi_create(dev, &gaokun_ucsi_ops);
489 if (IS_ERR(uec->ucsi))
490 return PTR_ERR(uec->ucsi);
491
492 ucsi_set_drvdata(uec->ucsi, uec);
493 auxiliary_set_drvdata(adev, uec);
494
495 /* EC can't handle UCSI properly in the early stage */
496 schedule_delayed_work(&uec->work, 3 * HZ);
497
498 return 0;
499 }
500
gaokun_ucsi_remove(struct auxiliary_device * adev)501 static void gaokun_ucsi_remove(struct auxiliary_device *adev)
502 {
503 struct gaokun_ucsi *uec = auxiliary_get_drvdata(adev);
504
505 gaokun_ec_unregister_notify(uec->ec, &uec->nb);
506 ucsi_unregister(uec->ucsi);
507 ucsi_destroy(uec->ucsi);
508 }
509
510 static const struct auxiliary_device_id gaokun_ucsi_id_table[] = {
511 { .name = GAOKUN_MOD_NAME "." GAOKUN_DEV_UCSI, },
512 {}
513 };
514 MODULE_DEVICE_TABLE(auxiliary, gaokun_ucsi_id_table);
515
516 static struct auxiliary_driver gaokun_ucsi_driver = {
517 .name = GAOKUN_DEV_UCSI,
518 .id_table = gaokun_ucsi_id_table,
519 .probe = gaokun_ucsi_probe,
520 .remove = gaokun_ucsi_remove,
521 };
522
523 module_auxiliary_driver(gaokun_ucsi_driver);
524
525 MODULE_DESCRIPTION("HUAWEI Matebook E Go UCSI driver");
526 MODULE_LICENSE("GPL");
527