xref: /linux/drivers/usb/typec/ucsi/ucsi_huawei_gaokun.c (revision f5e9d31e79c1ce8ba948ecac74d75e9c8d2f0c87)
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