xref: /linux/drivers/platform/chrome/cros_ec_typec.c (revision 3503d56cc7233ced602e38a4c13caa64f00ab2aa)
1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3  * Copyright 2020 Google LLC
4  *
5  * This driver provides the ability to view and manage Type C ports through the
6  * Chrome OS EC.
7  */
8 
9 #include <linux/acpi.h>
10 #include <linux/module.h>
11 #include <linux/of.h>
12 #include <linux/platform_data/cros_ec_commands.h>
13 #include <linux/platform_data/cros_ec_proto.h>
14 #include <linux/platform_data/cros_usbpd_notify.h>
15 #include <linux/platform_device.h>
16 #include <linux/usb/typec.h>
17 
18 #define DRV_NAME "cros-ec-typec"
19 
20 /* Per port data. */
21 struct cros_typec_port {
22 	struct typec_port *port;
23 	/* Initial capabilities for the port. */
24 	struct typec_capability caps;
25 	struct typec_partner *partner;
26 	/* Port partner PD identity info. */
27 	struct usb_pd_identity p_identity;
28 };
29 
30 /* Platform-specific data for the Chrome OS EC Type C controller. */
31 struct cros_typec_data {
32 	struct device *dev;
33 	struct cros_ec_device *ec;
34 	int num_ports;
35 	unsigned int cmd_ver;
36 	/* Array of ports, indexed by port number. */
37 	struct cros_typec_port *ports[EC_USB_PD_MAX_PORTS];
38 	struct notifier_block nb;
39 };
40 
41 static int cros_typec_parse_port_props(struct typec_capability *cap,
42 				       struct fwnode_handle *fwnode,
43 				       struct device *dev)
44 {
45 	const char *buf;
46 	int ret;
47 
48 	memset(cap, 0, sizeof(*cap));
49 	ret = fwnode_property_read_string(fwnode, "power-role", &buf);
50 	if (ret) {
51 		dev_err(dev, "power-role not found: %d\n", ret);
52 		return ret;
53 	}
54 
55 	ret = typec_find_port_power_role(buf);
56 	if (ret < 0)
57 		return ret;
58 	cap->type = ret;
59 
60 	ret = fwnode_property_read_string(fwnode, "data-role", &buf);
61 	if (ret) {
62 		dev_err(dev, "data-role not found: %d\n", ret);
63 		return ret;
64 	}
65 
66 	ret = typec_find_port_data_role(buf);
67 	if (ret < 0)
68 		return ret;
69 	cap->data = ret;
70 
71 	ret = fwnode_property_read_string(fwnode, "try-power-role", &buf);
72 	if (ret) {
73 		dev_err(dev, "try-power-role not found: %d\n", ret);
74 		return ret;
75 	}
76 
77 	ret = typec_find_power_role(buf);
78 	if (ret < 0)
79 		return ret;
80 	cap->prefer_role = ret;
81 
82 	cap->fwnode = fwnode;
83 
84 	return 0;
85 }
86 
87 static void cros_unregister_ports(struct cros_typec_data *typec)
88 {
89 	int i;
90 
91 	for (i = 0; i < typec->num_ports; i++) {
92 		if (!typec->ports[i])
93 			continue;
94 		typec_unregister_port(typec->ports[i]->port);
95 	}
96 }
97 
98 static int cros_typec_init_ports(struct cros_typec_data *typec)
99 {
100 	struct device *dev = typec->dev;
101 	struct typec_capability *cap;
102 	struct fwnode_handle *fwnode;
103 	struct cros_typec_port *cros_port;
104 	const char *port_prop;
105 	int ret;
106 	int nports;
107 	u32 port_num = 0;
108 
109 	nports = device_get_child_node_count(dev);
110 	if (nports == 0) {
111 		dev_err(dev, "No port entries found.\n");
112 		return -ENODEV;
113 	}
114 
115 	if (nports > typec->num_ports) {
116 		dev_err(dev, "More ports listed than can be supported.\n");
117 		return -EINVAL;
118 	}
119 
120 	/* DT uses "reg" to specify port number. */
121 	port_prop = dev->of_node ? "reg" : "port-number";
122 	device_for_each_child_node(dev, fwnode) {
123 		if (fwnode_property_read_u32(fwnode, port_prop, &port_num)) {
124 			ret = -EINVAL;
125 			dev_err(dev, "No port-number for port, aborting.\n");
126 			goto unregister_ports;
127 		}
128 
129 		if (port_num >= typec->num_ports) {
130 			dev_err(dev, "Invalid port number.\n");
131 			ret = -EINVAL;
132 			goto unregister_ports;
133 		}
134 
135 		dev_dbg(dev, "Registering port %d\n", port_num);
136 
137 		cros_port = devm_kzalloc(dev, sizeof(*cros_port), GFP_KERNEL);
138 		if (!cros_port) {
139 			ret = -ENOMEM;
140 			goto unregister_ports;
141 		}
142 
143 		typec->ports[port_num] = cros_port;
144 		cap = &cros_port->caps;
145 
146 		ret = cros_typec_parse_port_props(cap, fwnode, dev);
147 		if (ret < 0)
148 			goto unregister_ports;
149 
150 		cros_port->port = typec_register_port(dev, cap);
151 		if (IS_ERR(cros_port->port)) {
152 			dev_err(dev, "Failed to register port %d\n", port_num);
153 			ret = PTR_ERR(cros_port->port);
154 			goto unregister_ports;
155 		}
156 	}
157 
158 	return 0;
159 
160 unregister_ports:
161 	cros_unregister_ports(typec);
162 	return ret;
163 }
164 
165 static int cros_typec_ec_command(struct cros_typec_data *typec,
166 				 unsigned int version,
167 				 unsigned int command,
168 				 void *outdata,
169 				 unsigned int outsize,
170 				 void *indata,
171 				 unsigned int insize)
172 {
173 	struct cros_ec_command *msg;
174 	int ret;
175 
176 	msg = kzalloc(sizeof(*msg) + max(outsize, insize), GFP_KERNEL);
177 	if (!msg)
178 		return -ENOMEM;
179 
180 	msg->version = version;
181 	msg->command = command;
182 	msg->outsize = outsize;
183 	msg->insize = insize;
184 
185 	if (outsize)
186 		memcpy(msg->data, outdata, outsize);
187 
188 	ret = cros_ec_cmd_xfer_status(typec->ec, msg);
189 	if (ret >= 0 && insize)
190 		memcpy(indata, msg->data, insize);
191 
192 	kfree(msg);
193 	return ret;
194 }
195 
196 static int cros_typec_add_partner(struct cros_typec_data *typec, int port_num,
197 				  bool pd_en)
198 {
199 	struct cros_typec_port *port = typec->ports[port_num];
200 	struct typec_partner_desc p_desc = {
201 		.usb_pd = pd_en,
202 	};
203 	int ret = 0;
204 
205 	/*
206 	 * Fill an initial PD identity, which will then be updated with info
207 	 * from the EC.
208 	 */
209 	p_desc.identity = &port->p_identity;
210 
211 	port->partner = typec_register_partner(port->port, &p_desc);
212 	if (IS_ERR(port->partner)) {
213 		ret = PTR_ERR(port->partner);
214 		port->partner = NULL;
215 	}
216 
217 	return ret;
218 }
219 
220 static void cros_typec_set_port_params_v0(struct cros_typec_data *typec,
221 		int port_num, struct ec_response_usb_pd_control *resp)
222 {
223 	struct typec_port *port = typec->ports[port_num]->port;
224 	enum typec_orientation polarity;
225 
226 	if (!resp->enabled)
227 		polarity = TYPEC_ORIENTATION_NONE;
228 	else if (!resp->polarity)
229 		polarity = TYPEC_ORIENTATION_NORMAL;
230 	else
231 		polarity = TYPEC_ORIENTATION_REVERSE;
232 
233 	typec_set_pwr_role(port, resp->role ? TYPEC_SOURCE : TYPEC_SINK);
234 	typec_set_orientation(port, polarity);
235 }
236 
237 static void cros_typec_set_port_params_v1(struct cros_typec_data *typec,
238 		int port_num, struct ec_response_usb_pd_control_v1 *resp)
239 {
240 	struct typec_port *port = typec->ports[port_num]->port;
241 	enum typec_orientation polarity;
242 	bool pd_en;
243 	int ret;
244 
245 	if (!(resp->enabled & PD_CTRL_RESP_ENABLED_CONNECTED))
246 		polarity = TYPEC_ORIENTATION_NONE;
247 	else if (!resp->polarity)
248 		polarity = TYPEC_ORIENTATION_NORMAL;
249 	else
250 		polarity = TYPEC_ORIENTATION_REVERSE;
251 	typec_set_orientation(port, polarity);
252 	typec_set_data_role(port, resp->role & PD_CTRL_RESP_ROLE_DATA ?
253 			TYPEC_HOST : TYPEC_DEVICE);
254 	typec_set_pwr_role(port, resp->role & PD_CTRL_RESP_ROLE_POWER ?
255 			TYPEC_SOURCE : TYPEC_SINK);
256 	typec_set_vconn_role(port, resp->role & PD_CTRL_RESP_ROLE_VCONN ?
257 			TYPEC_SOURCE : TYPEC_SINK);
258 
259 	/* Register/remove partners when a connect/disconnect occurs. */
260 	if (resp->enabled & PD_CTRL_RESP_ENABLED_CONNECTED) {
261 		if (typec->ports[port_num]->partner)
262 			return;
263 
264 		pd_en = resp->enabled & PD_CTRL_RESP_ENABLED_PD_CAPABLE;
265 		ret = cros_typec_add_partner(typec, port_num, pd_en);
266 		if (ret)
267 			dev_warn(typec->dev,
268 				 "Failed to register partner on port: %d\n",
269 				 port_num);
270 	} else {
271 		if (!typec->ports[port_num]->partner)
272 			return;
273 
274 		typec_unregister_partner(typec->ports[port_num]->partner);
275 		typec->ports[port_num]->partner = NULL;
276 	}
277 }
278 
279 static int cros_typec_port_update(struct cros_typec_data *typec, int port_num)
280 {
281 	struct ec_params_usb_pd_control req;
282 	struct ec_response_usb_pd_control_v1 resp;
283 	int ret;
284 
285 	if (port_num < 0 || port_num >= typec->num_ports) {
286 		dev_err(typec->dev, "cannot get status for invalid port %d\n",
287 			port_num);
288 		return -EINVAL;
289 	}
290 
291 	req.port = port_num;
292 	req.role = USB_PD_CTRL_ROLE_NO_CHANGE;
293 	req.mux = USB_PD_CTRL_MUX_NO_CHANGE;
294 	req.swap = USB_PD_CTRL_SWAP_NONE;
295 
296 	ret = cros_typec_ec_command(typec, typec->cmd_ver,
297 				    EC_CMD_USB_PD_CONTROL, &req, sizeof(req),
298 				    &resp, sizeof(resp));
299 	if (ret < 0)
300 		return ret;
301 
302 	dev_dbg(typec->dev, "Enabled %d: 0x%hhx\n", port_num, resp.enabled);
303 	dev_dbg(typec->dev, "Role %d: 0x%hhx\n", port_num, resp.role);
304 	dev_dbg(typec->dev, "Polarity %d: 0x%hhx\n", port_num, resp.polarity);
305 	dev_dbg(typec->dev, "State %d: %s\n", port_num, resp.state);
306 
307 	if (typec->cmd_ver == 1)
308 		cros_typec_set_port_params_v1(typec, port_num, &resp);
309 	else
310 		cros_typec_set_port_params_v0(typec, port_num,
311 			(struct ec_response_usb_pd_control *) &resp);
312 
313 	return 0;
314 }
315 
316 static int cros_typec_get_cmd_version(struct cros_typec_data *typec)
317 {
318 	struct ec_params_get_cmd_versions_v1 req_v1;
319 	struct ec_response_get_cmd_versions resp;
320 	int ret;
321 
322 	/* We're interested in the PD control command version. */
323 	req_v1.cmd = EC_CMD_USB_PD_CONTROL;
324 	ret = cros_typec_ec_command(typec, 1, EC_CMD_GET_CMD_VERSIONS,
325 				    &req_v1, sizeof(req_v1), &resp,
326 				    sizeof(resp));
327 	if (ret < 0)
328 		return ret;
329 
330 	if (resp.version_mask & EC_VER_MASK(1))
331 		typec->cmd_ver = 1;
332 	else
333 		typec->cmd_ver = 0;
334 
335 	dev_dbg(typec->dev, "PD Control has version mask 0x%hhx\n",
336 		typec->cmd_ver);
337 
338 	return 0;
339 }
340 
341 static int cros_ec_typec_event(struct notifier_block *nb,
342 			       unsigned long host_event, void *_notify)
343 {
344 	struct cros_typec_data *typec = container_of(nb, struct cros_typec_data,
345 						     nb);
346 	int ret, i;
347 
348 	for (i = 0; i < typec->num_ports; i++) {
349 		ret = cros_typec_port_update(typec, i);
350 		if (ret < 0)
351 			dev_warn(typec->dev, "Update failed for port: %d\n", i);
352 	}
353 
354 	return NOTIFY_OK;
355 }
356 
357 #ifdef CONFIG_ACPI
358 static const struct acpi_device_id cros_typec_acpi_id[] = {
359 	{ "GOOG0014", 0 },
360 	{}
361 };
362 MODULE_DEVICE_TABLE(acpi, cros_typec_acpi_id);
363 #endif
364 
365 #ifdef CONFIG_OF
366 static const struct of_device_id cros_typec_of_match[] = {
367 	{ .compatible = "google,cros-ec-typec", },
368 	{}
369 };
370 MODULE_DEVICE_TABLE(of, cros_typec_of_match);
371 #endif
372 
373 static int cros_typec_probe(struct platform_device *pdev)
374 {
375 	struct device *dev = &pdev->dev;
376 	struct cros_typec_data *typec;
377 	struct ec_response_usb_pd_ports resp;
378 	int ret, i;
379 
380 	typec = devm_kzalloc(dev, sizeof(*typec), GFP_KERNEL);
381 	if (!typec)
382 		return -ENOMEM;
383 
384 	typec->dev = dev;
385 	typec->ec = dev_get_drvdata(pdev->dev.parent);
386 	platform_set_drvdata(pdev, typec);
387 
388 	ret = cros_typec_get_cmd_version(typec);
389 	if (ret < 0) {
390 		dev_err(dev, "failed to get PD command version info\n");
391 		return ret;
392 	}
393 
394 	ret = cros_typec_ec_command(typec, 0, EC_CMD_USB_PD_PORTS, NULL, 0,
395 				    &resp, sizeof(resp));
396 	if (ret < 0)
397 		return ret;
398 
399 	typec->num_ports = resp.num_ports;
400 	if (typec->num_ports > EC_USB_PD_MAX_PORTS) {
401 		dev_warn(typec->dev,
402 			 "Too many ports reported: %d, limiting to max: %d\n",
403 			 typec->num_ports, EC_USB_PD_MAX_PORTS);
404 		typec->num_ports = EC_USB_PD_MAX_PORTS;
405 	}
406 
407 	ret = cros_typec_init_ports(typec);
408 	if (ret < 0)
409 		return ret;
410 
411 	for (i = 0; i < typec->num_ports; i++) {
412 		ret = cros_typec_port_update(typec, i);
413 		if (ret < 0)
414 			goto unregister_ports;
415 	}
416 
417 	typec->nb.notifier_call = cros_ec_typec_event;
418 	ret = cros_usbpd_register_notify(&typec->nb);
419 	if (ret < 0)
420 		goto unregister_ports;
421 
422 	return 0;
423 
424 unregister_ports:
425 	cros_unregister_ports(typec);
426 	return ret;
427 }
428 
429 static struct platform_driver cros_typec_driver = {
430 	.driver	= {
431 		.name = DRV_NAME,
432 		.acpi_match_table = ACPI_PTR(cros_typec_acpi_id),
433 		.of_match_table = of_match_ptr(cros_typec_of_match),
434 	},
435 	.probe = cros_typec_probe,
436 };
437 
438 module_platform_driver(cros_typec_driver);
439 
440 MODULE_AUTHOR("Prashant Malani <pmalani@chromium.org>");
441 MODULE_DESCRIPTION("Chrome OS EC Type C control");
442 MODULE_LICENSE("GPL");
443