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_device.h>
15 #include <linux/usb/typec.h>
16 
17 #define DRV_NAME "cros-ec-typec"
18 
19 /* Platform-specific data for the Chrome OS EC Type C controller. */
20 struct cros_typec_data {
21 	struct device *dev;
22 	struct cros_ec_device *ec;
23 	int num_ports;
24 	unsigned int cmd_ver;
25 	/* Array of ports, indexed by port number. */
26 	struct typec_port *ports[EC_USB_PD_MAX_PORTS];
27 	/* Initial capabilities for each port. */
28 	struct typec_capability *caps[EC_USB_PD_MAX_PORTS];
29 };
30 
31 static int cros_typec_parse_port_props(struct typec_capability *cap,
32 				       struct fwnode_handle *fwnode,
33 				       struct device *dev)
34 {
35 	const char *buf;
36 	int ret;
37 
38 	memset(cap, 0, sizeof(*cap));
39 	ret = fwnode_property_read_string(fwnode, "power-role", &buf);
40 	if (ret) {
41 		dev_err(dev, "power-role not found: %d\n", ret);
42 		return ret;
43 	}
44 
45 	ret = typec_find_port_power_role(buf);
46 	if (ret < 0)
47 		return ret;
48 	cap->type = ret;
49 
50 	ret = fwnode_property_read_string(fwnode, "data-role", &buf);
51 	if (ret) {
52 		dev_err(dev, "data-role not found: %d\n", ret);
53 		return ret;
54 	}
55 
56 	ret = typec_find_port_data_role(buf);
57 	if (ret < 0)
58 		return ret;
59 	cap->data = ret;
60 
61 	ret = fwnode_property_read_string(fwnode, "try-power-role", &buf);
62 	if (ret) {
63 		dev_err(dev, "try-power-role not found: %d\n", ret);
64 		return ret;
65 	}
66 
67 	ret = typec_find_power_role(buf);
68 	if (ret < 0)
69 		return ret;
70 	cap->prefer_role = ret;
71 
72 	cap->fwnode = fwnode;
73 
74 	return 0;
75 }
76 
77 static int cros_typec_init_ports(struct cros_typec_data *typec)
78 {
79 	struct device *dev = typec->dev;
80 	struct typec_capability *cap;
81 	struct fwnode_handle *fwnode;
82 	const char *port_prop;
83 	int ret;
84 	int i;
85 	int nports;
86 	u32 port_num = 0;
87 
88 	nports = device_get_child_node_count(dev);
89 	if (nports == 0) {
90 		dev_err(dev, "No port entries found.\n");
91 		return -ENODEV;
92 	}
93 
94 	if (nports > typec->num_ports) {
95 		dev_err(dev, "More ports listed than can be supported.\n");
96 		return -EINVAL;
97 	}
98 
99 	/* DT uses "reg" to specify port number. */
100 	port_prop = dev->of_node ? "reg" : "port-number";
101 	device_for_each_child_node(dev, fwnode) {
102 		if (fwnode_property_read_u32(fwnode, port_prop, &port_num)) {
103 			ret = -EINVAL;
104 			dev_err(dev, "No port-number for port, aborting.\n");
105 			goto unregister_ports;
106 		}
107 
108 		if (port_num >= typec->num_ports) {
109 			dev_err(dev, "Invalid port number.\n");
110 			ret = -EINVAL;
111 			goto unregister_ports;
112 		}
113 
114 		dev_dbg(dev, "Registering port %d\n", port_num);
115 
116 		cap = devm_kzalloc(dev, sizeof(*cap), GFP_KERNEL);
117 		if (!cap) {
118 			ret = -ENOMEM;
119 			goto unregister_ports;
120 		}
121 
122 		typec->caps[port_num] = cap;
123 
124 		ret = cros_typec_parse_port_props(cap, fwnode, dev);
125 		if (ret < 0)
126 			goto unregister_ports;
127 
128 		typec->ports[port_num] = typec_register_port(dev, cap);
129 		if (IS_ERR(typec->ports[port_num])) {
130 			dev_err(dev, "Failed to register port %d\n", port_num);
131 			ret = PTR_ERR(typec->ports[port_num]);
132 			goto unregister_ports;
133 		}
134 	}
135 
136 	return 0;
137 
138 unregister_ports:
139 	for (i = 0; i < typec->num_ports; i++)
140 		typec_unregister_port(typec->ports[i]);
141 	return ret;
142 }
143 
144 static int cros_typec_ec_command(struct cros_typec_data *typec,
145 				 unsigned int version,
146 				 unsigned int command,
147 				 void *outdata,
148 				 unsigned int outsize,
149 				 void *indata,
150 				 unsigned int insize)
151 {
152 	struct cros_ec_command *msg;
153 	int ret;
154 
155 	msg = kzalloc(sizeof(*msg) + max(outsize, insize), GFP_KERNEL);
156 	if (!msg)
157 		return -ENOMEM;
158 
159 	msg->version = version;
160 	msg->command = command;
161 	msg->outsize = outsize;
162 	msg->insize = insize;
163 
164 	if (outsize)
165 		memcpy(msg->data, outdata, outsize);
166 
167 	ret = cros_ec_cmd_xfer_status(typec->ec, msg);
168 	if (ret >= 0 && insize)
169 		memcpy(indata, msg->data, insize);
170 
171 	kfree(msg);
172 	return ret;
173 }
174 
175 static void cros_typec_set_port_params_v0(struct cros_typec_data *typec,
176 		int port_num, struct ec_response_usb_pd_control *resp)
177 {
178 	struct typec_port *port = typec->ports[port_num];
179 	enum typec_orientation polarity;
180 
181 	if (!resp->enabled)
182 		polarity = TYPEC_ORIENTATION_NONE;
183 	else if (!resp->polarity)
184 		polarity = TYPEC_ORIENTATION_NORMAL;
185 	else
186 		polarity = TYPEC_ORIENTATION_REVERSE;
187 
188 	typec_set_pwr_role(port, resp->role ? TYPEC_SOURCE : TYPEC_SINK);
189 	typec_set_orientation(port, polarity);
190 }
191 
192 static void cros_typec_set_port_params_v1(struct cros_typec_data *typec,
193 		int port_num, struct ec_response_usb_pd_control_v1 *resp)
194 {
195 	struct typec_port *port = typec->ports[port_num];
196 	enum typec_orientation polarity;
197 
198 	if (!(resp->enabled & PD_CTRL_RESP_ENABLED_CONNECTED))
199 		polarity = TYPEC_ORIENTATION_NONE;
200 	else if (!resp->polarity)
201 		polarity = TYPEC_ORIENTATION_NORMAL;
202 	else
203 		polarity = TYPEC_ORIENTATION_REVERSE;
204 	typec_set_orientation(port, polarity);
205 	typec_set_data_role(port, resp->role & PD_CTRL_RESP_ROLE_DATA ?
206 			TYPEC_HOST : TYPEC_DEVICE);
207 	typec_set_pwr_role(port, resp->role & PD_CTRL_RESP_ROLE_POWER ?
208 			TYPEC_SOURCE : TYPEC_SINK);
209 	typec_set_vconn_role(port, resp->role & PD_CTRL_RESP_ROLE_VCONN ?
210 			TYPEC_SOURCE : TYPEC_SINK);
211 }
212 
213 static int cros_typec_port_update(struct cros_typec_data *typec, int port_num)
214 {
215 	struct ec_params_usb_pd_control req;
216 	struct ec_response_usb_pd_control_v1 resp;
217 	int ret;
218 
219 	if (port_num < 0 || port_num >= typec->num_ports) {
220 		dev_err(typec->dev, "cannot get status for invalid port %d\n",
221 			port_num);
222 		return -EINVAL;
223 	}
224 
225 	req.port = port_num;
226 	req.role = USB_PD_CTRL_ROLE_NO_CHANGE;
227 	req.mux = USB_PD_CTRL_MUX_NO_CHANGE;
228 	req.swap = USB_PD_CTRL_SWAP_NONE;
229 
230 	ret = cros_typec_ec_command(typec, typec->cmd_ver,
231 				    EC_CMD_USB_PD_CONTROL, &req, sizeof(req),
232 				    &resp, sizeof(resp));
233 	if (ret < 0)
234 		return ret;
235 
236 	dev_dbg(typec->dev, "Enabled %d: 0x%hhx\n", port_num, resp.enabled);
237 	dev_dbg(typec->dev, "Role %d: 0x%hhx\n", port_num, resp.role);
238 	dev_dbg(typec->dev, "Polarity %d: 0x%hhx\n", port_num, resp.polarity);
239 	dev_dbg(typec->dev, "State %d: %s\n", port_num, resp.state);
240 
241 	if (typec->cmd_ver == 1)
242 		cros_typec_set_port_params_v1(typec, port_num, &resp);
243 	else
244 		cros_typec_set_port_params_v0(typec, port_num,
245 			(struct ec_response_usb_pd_control *) &resp);
246 
247 	return 0;
248 }
249 
250 static int cros_typec_get_cmd_version(struct cros_typec_data *typec)
251 {
252 	struct ec_params_get_cmd_versions_v1 req_v1;
253 	struct ec_response_get_cmd_versions resp;
254 	int ret;
255 
256 	/* We're interested in the PD control command version. */
257 	req_v1.cmd = EC_CMD_USB_PD_CONTROL;
258 	ret = cros_typec_ec_command(typec, 1, EC_CMD_GET_CMD_VERSIONS,
259 				    &req_v1, sizeof(req_v1), &resp,
260 				    sizeof(resp));
261 	if (ret < 0)
262 		return ret;
263 
264 	if (resp.version_mask & EC_VER_MASK(1))
265 		typec->cmd_ver = 1;
266 	else
267 		typec->cmd_ver = 0;
268 
269 	dev_dbg(typec->dev, "PD Control has version mask 0x%hhx\n",
270 		typec->cmd_ver);
271 
272 	return 0;
273 }
274 
275 #ifdef CONFIG_ACPI
276 static const struct acpi_device_id cros_typec_acpi_id[] = {
277 	{ "GOOG0014", 0 },
278 	{}
279 };
280 MODULE_DEVICE_TABLE(acpi, cros_typec_acpi_id);
281 #endif
282 
283 #ifdef CONFIG_OF
284 static const struct of_device_id cros_typec_of_match[] = {
285 	{ .compatible = "google,cros-ec-typec", },
286 	{}
287 };
288 MODULE_DEVICE_TABLE(of, cros_typec_of_match);
289 #endif
290 
291 static int cros_typec_probe(struct platform_device *pdev)
292 {
293 	struct device *dev = &pdev->dev;
294 	struct cros_typec_data *typec;
295 	struct ec_response_usb_pd_ports resp;
296 	int ret, i;
297 
298 	typec = devm_kzalloc(dev, sizeof(*typec), GFP_KERNEL);
299 	if (!typec)
300 		return -ENOMEM;
301 
302 	typec->dev = dev;
303 	typec->ec = dev_get_drvdata(pdev->dev.parent);
304 	platform_set_drvdata(pdev, typec);
305 
306 	ret = cros_typec_get_cmd_version(typec);
307 	if (ret < 0) {
308 		dev_err(dev, "failed to get PD command version info\n");
309 		return ret;
310 	}
311 
312 	ret = cros_typec_ec_command(typec, 0, EC_CMD_USB_PD_PORTS, NULL, 0,
313 				    &resp, sizeof(resp));
314 	if (ret < 0)
315 		return ret;
316 
317 	typec->num_ports = resp.num_ports;
318 	if (typec->num_ports > EC_USB_PD_MAX_PORTS) {
319 		dev_warn(typec->dev,
320 			 "Too many ports reported: %d, limiting to max: %d\n",
321 			 typec->num_ports, EC_USB_PD_MAX_PORTS);
322 		typec->num_ports = EC_USB_PD_MAX_PORTS;
323 	}
324 
325 	ret = cros_typec_init_ports(typec);
326 	if (ret < 0)
327 		return ret;
328 
329 	for (i = 0; i < typec->num_ports; i++) {
330 		ret = cros_typec_port_update(typec, i);
331 		if (ret < 0)
332 			goto unregister_ports;
333 	}
334 
335 	return 0;
336 
337 unregister_ports:
338 	for (i = 0; i < typec->num_ports; i++)
339 		if (typec->ports[i])
340 			typec_unregister_port(typec->ports[i]);
341 	return ret;
342 }
343 
344 static struct platform_driver cros_typec_driver = {
345 	.driver	= {
346 		.name = DRV_NAME,
347 		.acpi_match_table = ACPI_PTR(cros_typec_acpi_id),
348 		.of_match_table = of_match_ptr(cros_typec_of_match),
349 	},
350 	.probe = cros_typec_probe,
351 };
352 
353 module_platform_driver(cros_typec_driver);
354 
355 MODULE_AUTHOR("Prashant Malani <pmalani@chromium.org>");
356 MODULE_DESCRIPTION("Chrome OS EC Type C control");
357 MODULE_LICENSE("GPL");
358