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