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