0001
0002
0003
0004
0005
0006
0007 #include <linux/extcon-provider.h>
0008 #include <linux/kernel.h>
0009 #include <linux/module.h>
0010 #include <linux/notifier.h>
0011 #include <linux/of.h>
0012 #include <linux/platform_data/cros_ec_commands.h>
0013 #include <linux/platform_data/cros_ec_proto.h>
0014 #include <linux/platform_device.h>
0015 #include <linux/slab.h>
0016 #include <linux/sched.h>
0017
0018 struct cros_ec_extcon_info {
0019 struct device *dev;
0020 struct extcon_dev *edev;
0021
0022 int port_id;
0023
0024 struct cros_ec_device *ec;
0025
0026 struct notifier_block notifier;
0027
0028 unsigned int dr;
0029 bool pr;
0030 bool dp;
0031 bool mux;
0032 unsigned int power_type;
0033 };
0034
0035 static const unsigned int usb_type_c_cable[] = {
0036 EXTCON_USB,
0037 EXTCON_USB_HOST,
0038 EXTCON_DISP_DP,
0039 EXTCON_NONE,
0040 };
0041
0042 enum usb_data_roles {
0043 DR_NONE,
0044 DR_HOST,
0045 DR_DEVICE,
0046 };
0047
0048
0049
0050
0051
0052
0053
0054
0055
0056
0057
0058
0059
0060 static int cros_ec_pd_command(struct cros_ec_extcon_info *info,
0061 unsigned int command,
0062 unsigned int version,
0063 void *outdata,
0064 unsigned int outsize,
0065 void *indata,
0066 unsigned int insize)
0067 {
0068 struct cros_ec_command *msg;
0069 int ret;
0070
0071 msg = kzalloc(struct_size(msg, data, max(outsize, insize)), GFP_KERNEL);
0072 if (!msg)
0073 return -ENOMEM;
0074
0075 msg->version = version;
0076 msg->command = command;
0077 msg->outsize = outsize;
0078 msg->insize = insize;
0079
0080 if (outsize)
0081 memcpy(msg->data, outdata, outsize);
0082
0083 ret = cros_ec_cmd_xfer_status(info->ec, msg);
0084 if (ret >= 0 && insize)
0085 memcpy(indata, msg->data, insize);
0086
0087 kfree(msg);
0088 return ret;
0089 }
0090
0091
0092
0093
0094
0095
0096
0097
0098 static int cros_ec_usb_get_power_type(struct cros_ec_extcon_info *info)
0099 {
0100 struct ec_params_usb_pd_power_info req;
0101 struct ec_response_usb_pd_power_info resp;
0102 int ret;
0103
0104 req.port = info->port_id;
0105 ret = cros_ec_pd_command(info, EC_CMD_USB_PD_POWER_INFO, 0,
0106 &req, sizeof(req), &resp, sizeof(resp));
0107 if (ret < 0)
0108 return ret;
0109
0110 return resp.type;
0111 }
0112
0113
0114
0115
0116
0117
0118
0119 static int cros_ec_usb_get_pd_mux_state(struct cros_ec_extcon_info *info)
0120 {
0121 struct ec_params_usb_pd_mux_info req;
0122 struct ec_response_usb_pd_mux_info resp;
0123 int ret;
0124
0125 req.port = info->port_id;
0126 ret = cros_ec_pd_command(info, EC_CMD_USB_PD_MUX_INFO, 0,
0127 &req, sizeof(req),
0128 &resp, sizeof(resp));
0129 if (ret < 0)
0130 return ret;
0131
0132 return resp.flags;
0133 }
0134
0135
0136
0137
0138
0139
0140
0141
0142
0143
0144 static int cros_ec_usb_get_role(struct cros_ec_extcon_info *info,
0145 bool *polarity)
0146 {
0147 struct ec_params_usb_pd_control pd_control;
0148 struct ec_response_usb_pd_control_v1 resp;
0149 int ret;
0150
0151 pd_control.port = info->port_id;
0152 pd_control.role = USB_PD_CTRL_ROLE_NO_CHANGE;
0153 pd_control.mux = USB_PD_CTRL_MUX_NO_CHANGE;
0154 pd_control.swap = USB_PD_CTRL_SWAP_NONE;
0155 ret = cros_ec_pd_command(info, EC_CMD_USB_PD_CONTROL, 1,
0156 &pd_control, sizeof(pd_control),
0157 &resp, sizeof(resp));
0158 if (ret < 0)
0159 return ret;
0160
0161 if (!(resp.enabled & PD_CTRL_RESP_ENABLED_CONNECTED))
0162 return -ENOTCONN;
0163
0164 *polarity = resp.polarity;
0165
0166 return resp.role;
0167 }
0168
0169
0170
0171
0172
0173
0174
0175 static int cros_ec_pd_get_num_ports(struct cros_ec_extcon_info *info)
0176 {
0177 struct ec_response_usb_pd_ports resp;
0178 int ret;
0179
0180 ret = cros_ec_pd_command(info, EC_CMD_USB_PD_PORTS,
0181 0, NULL, 0, &resp, sizeof(resp));
0182 if (ret < 0)
0183 return ret;
0184
0185 return resp.num_ports;
0186 }
0187
0188 static const char *cros_ec_usb_role_string(unsigned int role)
0189 {
0190 return role == DR_NONE ? "DISCONNECTED" :
0191 (role == DR_HOST ? "DFP" : "UFP");
0192 }
0193
0194 static const char *cros_ec_usb_power_type_string(unsigned int type)
0195 {
0196 switch (type) {
0197 case USB_CHG_TYPE_NONE:
0198 return "USB_CHG_TYPE_NONE";
0199 case USB_CHG_TYPE_PD:
0200 return "USB_CHG_TYPE_PD";
0201 case USB_CHG_TYPE_PROPRIETARY:
0202 return "USB_CHG_TYPE_PROPRIETARY";
0203 case USB_CHG_TYPE_C:
0204 return "USB_CHG_TYPE_C";
0205 case USB_CHG_TYPE_BC12_DCP:
0206 return "USB_CHG_TYPE_BC12_DCP";
0207 case USB_CHG_TYPE_BC12_CDP:
0208 return "USB_CHG_TYPE_BC12_CDP";
0209 case USB_CHG_TYPE_BC12_SDP:
0210 return "USB_CHG_TYPE_BC12_SDP";
0211 case USB_CHG_TYPE_OTHER:
0212 return "USB_CHG_TYPE_OTHER";
0213 case USB_CHG_TYPE_VBUS:
0214 return "USB_CHG_TYPE_VBUS";
0215 case USB_CHG_TYPE_UNKNOWN:
0216 return "USB_CHG_TYPE_UNKNOWN";
0217 default:
0218 return "USB_CHG_TYPE_UNKNOWN";
0219 }
0220 }
0221
0222 static bool cros_ec_usb_power_type_is_wall_wart(unsigned int type,
0223 unsigned int role)
0224 {
0225 switch (type) {
0226
0227
0228
0229
0230
0231 case USB_CHG_TYPE_PROPRIETARY:
0232 case USB_CHG_TYPE_BC12_DCP:
0233 return true;
0234 case USB_CHG_TYPE_PD:
0235 case USB_CHG_TYPE_C:
0236 case USB_CHG_TYPE_BC12_CDP:
0237 case USB_CHG_TYPE_BC12_SDP:
0238 case USB_CHG_TYPE_OTHER:
0239 case USB_CHG_TYPE_VBUS:
0240 case USB_CHG_TYPE_UNKNOWN:
0241 case USB_CHG_TYPE_NONE:
0242 default:
0243 return false;
0244 }
0245 }
0246
0247 static int extcon_cros_ec_detect_cable(struct cros_ec_extcon_info *info,
0248 bool force)
0249 {
0250 struct device *dev = info->dev;
0251 int role, power_type;
0252 unsigned int dr = DR_NONE;
0253 bool pr = false;
0254 bool polarity = false;
0255 bool dp = false;
0256 bool mux = false;
0257 bool hpd = false;
0258
0259 power_type = cros_ec_usb_get_power_type(info);
0260 if (power_type < 0) {
0261 dev_err(dev, "failed getting power type err = %d\n",
0262 power_type);
0263 return power_type;
0264 }
0265
0266 role = cros_ec_usb_get_role(info, &polarity);
0267 if (role < 0) {
0268 if (role != -ENOTCONN) {
0269 dev_err(dev, "failed getting role err = %d\n", role);
0270 return role;
0271 }
0272 dev_dbg(dev, "disconnected\n");
0273 } else {
0274 int pd_mux_state;
0275
0276 dr = (role & PD_CTRL_RESP_ROLE_DATA) ? DR_HOST : DR_DEVICE;
0277 pr = (role & PD_CTRL_RESP_ROLE_POWER);
0278 pd_mux_state = cros_ec_usb_get_pd_mux_state(info);
0279 if (pd_mux_state < 0)
0280 pd_mux_state = USB_PD_MUX_USB_ENABLED;
0281
0282 dp = pd_mux_state & USB_PD_MUX_DP_ENABLED;
0283 mux = pd_mux_state & USB_PD_MUX_USB_ENABLED;
0284 hpd = pd_mux_state & USB_PD_MUX_HPD_IRQ;
0285
0286 dev_dbg(dev,
0287 "connected role 0x%x pwr type %d dr %d pr %d pol %d mux %d dp %d hpd %d\n",
0288 role, power_type, dr, pr, polarity, mux, dp, hpd);
0289 }
0290
0291
0292
0293
0294
0295 if (dr == DR_DEVICE &&
0296 cros_ec_usb_power_type_is_wall_wart(power_type, role))
0297 dr = DR_NONE;
0298
0299 if (force || info->dr != dr || info->pr != pr || info->dp != dp ||
0300 info->mux != mux || info->power_type != power_type) {
0301 bool host_connected = false, device_connected = false;
0302
0303 dev_dbg(dev, "Type/Role switch! type = %s role = %s\n",
0304 cros_ec_usb_power_type_string(power_type),
0305 cros_ec_usb_role_string(dr));
0306 info->dr = dr;
0307 info->pr = pr;
0308 info->dp = dp;
0309 info->mux = mux;
0310 info->power_type = power_type;
0311
0312 if (dr == DR_DEVICE)
0313 device_connected = true;
0314 else if (dr == DR_HOST)
0315 host_connected = true;
0316
0317 extcon_set_state(info->edev, EXTCON_USB, device_connected);
0318 extcon_set_state(info->edev, EXTCON_USB_HOST, host_connected);
0319 extcon_set_state(info->edev, EXTCON_DISP_DP, dp);
0320 extcon_set_property(info->edev, EXTCON_USB,
0321 EXTCON_PROP_USB_VBUS,
0322 (union extcon_property_value)(int)pr);
0323 extcon_set_property(info->edev, EXTCON_USB_HOST,
0324 EXTCON_PROP_USB_VBUS,
0325 (union extcon_property_value)(int)pr);
0326 extcon_set_property(info->edev, EXTCON_USB,
0327 EXTCON_PROP_USB_TYPEC_POLARITY,
0328 (union extcon_property_value)(int)polarity);
0329 extcon_set_property(info->edev, EXTCON_USB_HOST,
0330 EXTCON_PROP_USB_TYPEC_POLARITY,
0331 (union extcon_property_value)(int)polarity);
0332 extcon_set_property(info->edev, EXTCON_DISP_DP,
0333 EXTCON_PROP_USB_TYPEC_POLARITY,
0334 (union extcon_property_value)(int)polarity);
0335 extcon_set_property(info->edev, EXTCON_USB,
0336 EXTCON_PROP_USB_SS,
0337 (union extcon_property_value)(int)mux);
0338 extcon_set_property(info->edev, EXTCON_USB_HOST,
0339 EXTCON_PROP_USB_SS,
0340 (union extcon_property_value)(int)mux);
0341 extcon_set_property(info->edev, EXTCON_DISP_DP,
0342 EXTCON_PROP_USB_SS,
0343 (union extcon_property_value)(int)mux);
0344 extcon_set_property(info->edev, EXTCON_DISP_DP,
0345 EXTCON_PROP_DISP_HPD,
0346 (union extcon_property_value)(int)hpd);
0347
0348 extcon_sync(info->edev, EXTCON_USB);
0349 extcon_sync(info->edev, EXTCON_USB_HOST);
0350 extcon_sync(info->edev, EXTCON_DISP_DP);
0351
0352 } else if (hpd) {
0353 extcon_set_property(info->edev, EXTCON_DISP_DP,
0354 EXTCON_PROP_DISP_HPD,
0355 (union extcon_property_value)(int)hpd);
0356 extcon_sync(info->edev, EXTCON_DISP_DP);
0357 }
0358
0359 return 0;
0360 }
0361
0362 static int extcon_cros_ec_event(struct notifier_block *nb,
0363 unsigned long queued_during_suspend,
0364 void *_notify)
0365 {
0366 struct cros_ec_extcon_info *info;
0367 struct cros_ec_device *ec;
0368 u32 host_event;
0369
0370 info = container_of(nb, struct cros_ec_extcon_info, notifier);
0371 ec = info->ec;
0372
0373 host_event = cros_ec_get_host_event(ec);
0374 if (host_event & (EC_HOST_EVENT_MASK(EC_HOST_EVENT_PD_MCU) |
0375 EC_HOST_EVENT_MASK(EC_HOST_EVENT_USB_MUX))) {
0376 extcon_cros_ec_detect_cable(info, false);
0377 return NOTIFY_OK;
0378 }
0379
0380 return NOTIFY_DONE;
0381 }
0382
0383 static int extcon_cros_ec_probe(struct platform_device *pdev)
0384 {
0385 struct cros_ec_extcon_info *info;
0386 struct cros_ec_device *ec = dev_get_drvdata(pdev->dev.parent);
0387 struct device *dev = &pdev->dev;
0388 struct device_node *np = dev->of_node;
0389 int numports, ret;
0390
0391 info = devm_kzalloc(dev, sizeof(*info), GFP_KERNEL);
0392 if (!info)
0393 return -ENOMEM;
0394
0395 info->dev = dev;
0396 info->ec = ec;
0397
0398 if (np) {
0399 u32 port;
0400
0401 ret = of_property_read_u32(np, "google,usb-port-id", &port);
0402 if (ret < 0) {
0403 dev_err(dev, "Missing google,usb-port-id property\n");
0404 return ret;
0405 }
0406 info->port_id = port;
0407 } else {
0408 info->port_id = pdev->id;
0409 }
0410
0411 numports = cros_ec_pd_get_num_ports(info);
0412 if (numports < 0) {
0413 dev_err(dev, "failed getting number of ports! ret = %d\n",
0414 numports);
0415 return numports;
0416 }
0417
0418 if (info->port_id >= numports) {
0419 dev_err(dev, "This system only supports %d ports\n", numports);
0420 return -ENODEV;
0421 }
0422
0423 info->edev = devm_extcon_dev_allocate(dev, usb_type_c_cable);
0424 if (IS_ERR(info->edev)) {
0425 dev_err(dev, "failed to allocate extcon device\n");
0426 return -ENOMEM;
0427 }
0428
0429 ret = devm_extcon_dev_register(dev, info->edev);
0430 if (ret < 0) {
0431 dev_err(dev, "failed to register extcon device\n");
0432 return ret;
0433 }
0434
0435 extcon_set_property_capability(info->edev, EXTCON_USB,
0436 EXTCON_PROP_USB_VBUS);
0437 extcon_set_property_capability(info->edev, EXTCON_USB_HOST,
0438 EXTCON_PROP_USB_VBUS);
0439 extcon_set_property_capability(info->edev, EXTCON_USB,
0440 EXTCON_PROP_USB_TYPEC_POLARITY);
0441 extcon_set_property_capability(info->edev, EXTCON_USB_HOST,
0442 EXTCON_PROP_USB_TYPEC_POLARITY);
0443 extcon_set_property_capability(info->edev, EXTCON_DISP_DP,
0444 EXTCON_PROP_USB_TYPEC_POLARITY);
0445 extcon_set_property_capability(info->edev, EXTCON_USB,
0446 EXTCON_PROP_USB_SS);
0447 extcon_set_property_capability(info->edev, EXTCON_USB_HOST,
0448 EXTCON_PROP_USB_SS);
0449 extcon_set_property_capability(info->edev, EXTCON_DISP_DP,
0450 EXTCON_PROP_USB_SS);
0451 extcon_set_property_capability(info->edev, EXTCON_DISP_DP,
0452 EXTCON_PROP_DISP_HPD);
0453
0454 info->dr = DR_NONE;
0455 info->pr = false;
0456
0457 platform_set_drvdata(pdev, info);
0458
0459
0460 info->notifier.notifier_call = extcon_cros_ec_event;
0461 ret = blocking_notifier_chain_register(&info->ec->event_notifier,
0462 &info->notifier);
0463 if (ret < 0) {
0464 dev_err(dev, "failed to register notifier\n");
0465 return ret;
0466 }
0467
0468
0469 ret = extcon_cros_ec_detect_cable(info, true);
0470 if (ret < 0) {
0471 dev_err(dev, "failed to detect initial cable state\n");
0472 goto unregister_notifier;
0473 }
0474
0475 return 0;
0476
0477 unregister_notifier:
0478 blocking_notifier_chain_unregister(&info->ec->event_notifier,
0479 &info->notifier);
0480 return ret;
0481 }
0482
0483 static int extcon_cros_ec_remove(struct platform_device *pdev)
0484 {
0485 struct cros_ec_extcon_info *info = platform_get_drvdata(pdev);
0486
0487 blocking_notifier_chain_unregister(&info->ec->event_notifier,
0488 &info->notifier);
0489
0490 return 0;
0491 }
0492
0493 #ifdef CONFIG_PM_SLEEP
0494 static int extcon_cros_ec_suspend(struct device *dev)
0495 {
0496 return 0;
0497 }
0498
0499 static int extcon_cros_ec_resume(struct device *dev)
0500 {
0501 int ret;
0502 struct cros_ec_extcon_info *info = dev_get_drvdata(dev);
0503
0504 ret = extcon_cros_ec_detect_cable(info, true);
0505 if (ret < 0)
0506 dev_err(dev, "failed to detect cable state on resume\n");
0507
0508 return 0;
0509 }
0510
0511 static const struct dev_pm_ops extcon_cros_ec_dev_pm_ops = {
0512 SET_SYSTEM_SLEEP_PM_OPS(extcon_cros_ec_suspend, extcon_cros_ec_resume)
0513 };
0514
0515 #define DEV_PM_OPS (&extcon_cros_ec_dev_pm_ops)
0516 #else
0517 #define DEV_PM_OPS NULL
0518 #endif
0519
0520 #ifdef CONFIG_OF
0521 static const struct of_device_id extcon_cros_ec_of_match[] = {
0522 { .compatible = "google,extcon-usbc-cros-ec" },
0523 { }
0524 };
0525 MODULE_DEVICE_TABLE(of, extcon_cros_ec_of_match);
0526 #endif
0527
0528 static struct platform_driver extcon_cros_ec_driver = {
0529 .driver = {
0530 .name = "extcon-usbc-cros-ec",
0531 .of_match_table = of_match_ptr(extcon_cros_ec_of_match),
0532 .pm = DEV_PM_OPS,
0533 },
0534 .remove = extcon_cros_ec_remove,
0535 .probe = extcon_cros_ec_probe,
0536 };
0537
0538 module_platform_driver(extcon_cros_ec_driver);
0539
0540 MODULE_DESCRIPTION("ChromeOS Embedded Controller extcon driver");
0541 MODULE_AUTHOR("Benson Leung <bleung@chromium.org>");
0542 MODULE_LICENSE("GPL v2");