extcon-usbc-cros-ec.c 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542
  1. // SPDX-License-Identifier: GPL-2.0
  2. // ChromeOS Embedded Controller extcon
  3. //
  4. // Copyright (C) 2017 Google, Inc.
  5. // Author: Benson Leung <bleung@chromium.org>
  6. #include <linux/extcon-provider.h>
  7. #include <linux/kernel.h>
  8. #include <linux/mfd/cros_ec.h>
  9. #include <linux/module.h>
  10. #include <linux/notifier.h>
  11. #include <linux/of.h>
  12. #include <linux/platform_device.h>
  13. #include <linux/slab.h>
  14. #include <linux/sched.h>
  15. struct cros_ec_extcon_info {
  16. struct device *dev;
  17. struct extcon_dev *edev;
  18. int port_id;
  19. struct cros_ec_device *ec;
  20. struct notifier_block notifier;
  21. unsigned int dr; /* data role */
  22. bool pr; /* power role (true if VBUS enabled) */
  23. bool dp; /* DisplayPort enabled */
  24. bool mux; /* SuperSpeed (usb3) enabled */
  25. unsigned int power_type;
  26. };
  27. static const unsigned int usb_type_c_cable[] = {
  28. EXTCON_USB,
  29. EXTCON_USB_HOST,
  30. EXTCON_DISP_DP,
  31. EXTCON_NONE,
  32. };
  33. enum usb_data_roles {
  34. DR_NONE,
  35. DR_HOST,
  36. DR_DEVICE,
  37. };
  38. /**
  39. * cros_ec_pd_command() - Send a command to the EC.
  40. * @info: pointer to struct cros_ec_extcon_info
  41. * @command: EC command
  42. * @version: EC command version
  43. * @outdata: EC command output data
  44. * @outsize: Size of outdata
  45. * @indata: EC command input data
  46. * @insize: Size of indata
  47. *
  48. * Return: 0 on success, <0 on failure.
  49. */
  50. static int cros_ec_pd_command(struct cros_ec_extcon_info *info,
  51. unsigned int command,
  52. unsigned int version,
  53. void *outdata,
  54. unsigned int outsize,
  55. void *indata,
  56. unsigned int insize)
  57. {
  58. struct cros_ec_command *msg;
  59. int ret;
  60. msg = kzalloc(sizeof(*msg) + max(outsize, insize), GFP_KERNEL);
  61. if (!msg)
  62. return -ENOMEM;
  63. msg->version = version;
  64. msg->command = command;
  65. msg->outsize = outsize;
  66. msg->insize = insize;
  67. if (outsize)
  68. memcpy(msg->data, outdata, outsize);
  69. ret = cros_ec_cmd_xfer_status(info->ec, msg);
  70. if (ret >= 0 && insize)
  71. memcpy(indata, msg->data, insize);
  72. kfree(msg);
  73. return ret;
  74. }
  75. /**
  76. * cros_ec_usb_get_power_type() - Get power type info about PD device attached
  77. * to given port.
  78. * @info: pointer to struct cros_ec_extcon_info
  79. *
  80. * Return: power type on success, <0 on failure.
  81. */
  82. static int cros_ec_usb_get_power_type(struct cros_ec_extcon_info *info)
  83. {
  84. struct ec_params_usb_pd_power_info req;
  85. struct ec_response_usb_pd_power_info resp;
  86. int ret;
  87. req.port = info->port_id;
  88. ret = cros_ec_pd_command(info, EC_CMD_USB_PD_POWER_INFO, 0,
  89. &req, sizeof(req), &resp, sizeof(resp));
  90. if (ret < 0)
  91. return ret;
  92. return resp.type;
  93. }
  94. /**
  95. * cros_ec_usb_get_pd_mux_state() - Get PD mux state for given port.
  96. * @info: pointer to struct cros_ec_extcon_info
  97. *
  98. * Return: PD mux state on success, <0 on failure.
  99. */
  100. static int cros_ec_usb_get_pd_mux_state(struct cros_ec_extcon_info *info)
  101. {
  102. struct ec_params_usb_pd_mux_info req;
  103. struct ec_response_usb_pd_mux_info resp;
  104. int ret;
  105. req.port = info->port_id;
  106. ret = cros_ec_pd_command(info, EC_CMD_USB_PD_MUX_INFO, 0,
  107. &req, sizeof(req),
  108. &resp, sizeof(resp));
  109. if (ret < 0)
  110. return ret;
  111. return resp.flags;
  112. }
  113. /**
  114. * cros_ec_usb_get_role() - Get role info about possible PD device attached to a
  115. * given port.
  116. * @info: pointer to struct cros_ec_extcon_info
  117. * @polarity: pointer to cable polarity (return value)
  118. *
  119. * Return: role info on success, -ENOTCONN if no cable is connected, <0 on
  120. * failure.
  121. */
  122. static int cros_ec_usb_get_role(struct cros_ec_extcon_info *info,
  123. bool *polarity)
  124. {
  125. struct ec_params_usb_pd_control pd_control;
  126. struct ec_response_usb_pd_control_v1 resp;
  127. int ret;
  128. pd_control.port = info->port_id;
  129. pd_control.role = USB_PD_CTRL_ROLE_NO_CHANGE;
  130. pd_control.mux = USB_PD_CTRL_MUX_NO_CHANGE;
  131. pd_control.swap = USB_PD_CTRL_SWAP_NONE;
  132. ret = cros_ec_pd_command(info, EC_CMD_USB_PD_CONTROL, 1,
  133. &pd_control, sizeof(pd_control),
  134. &resp, sizeof(resp));
  135. if (ret < 0)
  136. return ret;
  137. if (!(resp.enabled & PD_CTRL_RESP_ENABLED_CONNECTED))
  138. return -ENOTCONN;
  139. *polarity = resp.polarity;
  140. return resp.role;
  141. }
  142. /**
  143. * cros_ec_pd_get_num_ports() - Get number of EC charge ports.
  144. * @info: pointer to struct cros_ec_extcon_info
  145. *
  146. * Return: number of ports on success, <0 on failure.
  147. */
  148. static int cros_ec_pd_get_num_ports(struct cros_ec_extcon_info *info)
  149. {
  150. struct ec_response_usb_pd_ports resp;
  151. int ret;
  152. ret = cros_ec_pd_command(info, EC_CMD_USB_PD_PORTS,
  153. 0, NULL, 0, &resp, sizeof(resp));
  154. if (ret < 0)
  155. return ret;
  156. return resp.num_ports;
  157. }
  158. static const char *cros_ec_usb_role_string(unsigned int role)
  159. {
  160. return role == DR_NONE ? "DISCONNECTED" :
  161. (role == DR_HOST ? "DFP" : "UFP");
  162. }
  163. static const char *cros_ec_usb_power_type_string(unsigned int type)
  164. {
  165. switch (type) {
  166. case USB_CHG_TYPE_NONE:
  167. return "USB_CHG_TYPE_NONE";
  168. case USB_CHG_TYPE_PD:
  169. return "USB_CHG_TYPE_PD";
  170. case USB_CHG_TYPE_PROPRIETARY:
  171. return "USB_CHG_TYPE_PROPRIETARY";
  172. case USB_CHG_TYPE_C:
  173. return "USB_CHG_TYPE_C";
  174. case USB_CHG_TYPE_BC12_DCP:
  175. return "USB_CHG_TYPE_BC12_DCP";
  176. case USB_CHG_TYPE_BC12_CDP:
  177. return "USB_CHG_TYPE_BC12_CDP";
  178. case USB_CHG_TYPE_BC12_SDP:
  179. return "USB_CHG_TYPE_BC12_SDP";
  180. case USB_CHG_TYPE_OTHER:
  181. return "USB_CHG_TYPE_OTHER";
  182. case USB_CHG_TYPE_VBUS:
  183. return "USB_CHG_TYPE_VBUS";
  184. case USB_CHG_TYPE_UNKNOWN:
  185. return "USB_CHG_TYPE_UNKNOWN";
  186. default:
  187. return "USB_CHG_TYPE_UNKNOWN";
  188. }
  189. }
  190. static bool cros_ec_usb_power_type_is_wall_wart(unsigned int type,
  191. unsigned int role)
  192. {
  193. switch (type) {
  194. /* FIXME : Guppy, Donnettes, and other chargers will be miscategorized
  195. * because they identify with USB_CHG_TYPE_C, but we can't return true
  196. * here from that code because that breaks Suzy-Q and other kinds of
  197. * USB Type-C cables and peripherals.
  198. */
  199. case USB_CHG_TYPE_PROPRIETARY:
  200. case USB_CHG_TYPE_BC12_DCP:
  201. return true;
  202. case USB_CHG_TYPE_PD:
  203. case USB_CHG_TYPE_C:
  204. case USB_CHG_TYPE_BC12_CDP:
  205. case USB_CHG_TYPE_BC12_SDP:
  206. case USB_CHG_TYPE_OTHER:
  207. case USB_CHG_TYPE_VBUS:
  208. case USB_CHG_TYPE_UNKNOWN:
  209. case USB_CHG_TYPE_NONE:
  210. default:
  211. return false;
  212. }
  213. }
  214. static int extcon_cros_ec_detect_cable(struct cros_ec_extcon_info *info,
  215. bool force)
  216. {
  217. struct device *dev = info->dev;
  218. int role, power_type;
  219. unsigned int dr = DR_NONE;
  220. bool pr = false;
  221. bool polarity = false;
  222. bool dp = false;
  223. bool mux = false;
  224. bool hpd = false;
  225. power_type = cros_ec_usb_get_power_type(info);
  226. if (power_type < 0) {
  227. dev_err(dev, "failed getting power type err = %d\n",
  228. power_type);
  229. return power_type;
  230. }
  231. role = cros_ec_usb_get_role(info, &polarity);
  232. if (role < 0) {
  233. if (role != -ENOTCONN) {
  234. dev_err(dev, "failed getting role err = %d\n", role);
  235. return role;
  236. }
  237. dev_dbg(dev, "disconnected\n");
  238. } else {
  239. int pd_mux_state;
  240. dr = (role & PD_CTRL_RESP_ROLE_DATA) ? DR_HOST : DR_DEVICE;
  241. pr = (role & PD_CTRL_RESP_ROLE_POWER);
  242. pd_mux_state = cros_ec_usb_get_pd_mux_state(info);
  243. if (pd_mux_state < 0)
  244. pd_mux_state = USB_PD_MUX_USB_ENABLED;
  245. dp = pd_mux_state & USB_PD_MUX_DP_ENABLED;
  246. mux = pd_mux_state & USB_PD_MUX_USB_ENABLED;
  247. hpd = pd_mux_state & USB_PD_MUX_HPD_IRQ;
  248. dev_dbg(dev,
  249. "connected role 0x%x pwr type %d dr %d pr %d pol %d mux %d dp %d hpd %d\n",
  250. role, power_type, dr, pr, polarity, mux, dp, hpd);
  251. }
  252. /*
  253. * When there is no USB host (e.g. USB PD charger),
  254. * we are not really a UFP for the AP.
  255. */
  256. if (dr == DR_DEVICE &&
  257. cros_ec_usb_power_type_is_wall_wart(power_type, role))
  258. dr = DR_NONE;
  259. if (force || info->dr != dr || info->pr != pr || info->dp != dp ||
  260. info->mux != mux || info->power_type != power_type) {
  261. bool host_connected = false, device_connected = false;
  262. dev_dbg(dev, "Type/Role switch! type = %s role = %s\n",
  263. cros_ec_usb_power_type_string(power_type),
  264. cros_ec_usb_role_string(dr));
  265. info->dr = dr;
  266. info->pr = pr;
  267. info->dp = dp;
  268. info->mux = mux;
  269. info->power_type = power_type;
  270. if (dr == DR_DEVICE)
  271. device_connected = true;
  272. else if (dr == DR_HOST)
  273. host_connected = true;
  274. extcon_set_state(info->edev, EXTCON_USB, device_connected);
  275. extcon_set_state(info->edev, EXTCON_USB_HOST, host_connected);
  276. extcon_set_state(info->edev, EXTCON_DISP_DP, dp);
  277. extcon_set_property(info->edev, EXTCON_USB,
  278. EXTCON_PROP_USB_VBUS,
  279. (union extcon_property_value)(int)pr);
  280. extcon_set_property(info->edev, EXTCON_USB_HOST,
  281. EXTCON_PROP_USB_VBUS,
  282. (union extcon_property_value)(int)pr);
  283. extcon_set_property(info->edev, EXTCON_USB,
  284. EXTCON_PROP_USB_TYPEC_POLARITY,
  285. (union extcon_property_value)(int)polarity);
  286. extcon_set_property(info->edev, EXTCON_USB_HOST,
  287. EXTCON_PROP_USB_TYPEC_POLARITY,
  288. (union extcon_property_value)(int)polarity);
  289. extcon_set_property(info->edev, EXTCON_DISP_DP,
  290. EXTCON_PROP_USB_TYPEC_POLARITY,
  291. (union extcon_property_value)(int)polarity);
  292. extcon_set_property(info->edev, EXTCON_USB,
  293. EXTCON_PROP_USB_SS,
  294. (union extcon_property_value)(int)mux);
  295. extcon_set_property(info->edev, EXTCON_USB_HOST,
  296. EXTCON_PROP_USB_SS,
  297. (union extcon_property_value)(int)mux);
  298. extcon_set_property(info->edev, EXTCON_DISP_DP,
  299. EXTCON_PROP_USB_SS,
  300. (union extcon_property_value)(int)mux);
  301. extcon_set_property(info->edev, EXTCON_DISP_DP,
  302. EXTCON_PROP_DISP_HPD,
  303. (union extcon_property_value)(int)hpd);
  304. extcon_sync(info->edev, EXTCON_USB);
  305. extcon_sync(info->edev, EXTCON_USB_HOST);
  306. extcon_sync(info->edev, EXTCON_DISP_DP);
  307. } else if (hpd) {
  308. extcon_set_property(info->edev, EXTCON_DISP_DP,
  309. EXTCON_PROP_DISP_HPD,
  310. (union extcon_property_value)(int)hpd);
  311. extcon_sync(info->edev, EXTCON_DISP_DP);
  312. }
  313. return 0;
  314. }
  315. static int extcon_cros_ec_event(struct notifier_block *nb,
  316. unsigned long queued_during_suspend,
  317. void *_notify)
  318. {
  319. struct cros_ec_extcon_info *info;
  320. struct cros_ec_device *ec;
  321. u32 host_event;
  322. info = container_of(nb, struct cros_ec_extcon_info, notifier);
  323. ec = info->ec;
  324. host_event = cros_ec_get_host_event(ec);
  325. if (host_event & (EC_HOST_EVENT_MASK(EC_HOST_EVENT_PD_MCU) |
  326. EC_HOST_EVENT_MASK(EC_HOST_EVENT_USB_MUX))) {
  327. extcon_cros_ec_detect_cable(info, false);
  328. return NOTIFY_OK;
  329. }
  330. return NOTIFY_DONE;
  331. }
  332. static int extcon_cros_ec_probe(struct platform_device *pdev)
  333. {
  334. struct cros_ec_extcon_info *info;
  335. struct cros_ec_device *ec = dev_get_drvdata(pdev->dev.parent);
  336. struct device *dev = &pdev->dev;
  337. struct device_node *np = dev->of_node;
  338. int numports, ret;
  339. info = devm_kzalloc(dev, sizeof(*info), GFP_KERNEL);
  340. if (!info)
  341. return -ENOMEM;
  342. info->dev = dev;
  343. info->ec = ec;
  344. if (np) {
  345. u32 port;
  346. ret = of_property_read_u32(np, "google,usb-port-id", &port);
  347. if (ret < 0) {
  348. dev_err(dev, "Missing google,usb-port-id property\n");
  349. return ret;
  350. }
  351. info->port_id = port;
  352. } else {
  353. info->port_id = pdev->id;
  354. }
  355. numports = cros_ec_pd_get_num_ports(info);
  356. if (numports < 0) {
  357. dev_err(dev, "failed getting number of ports! ret = %d\n",
  358. numports);
  359. return numports;
  360. }
  361. if (info->port_id >= numports) {
  362. dev_err(dev, "This system only supports %d ports\n", numports);
  363. return -ENODEV;
  364. }
  365. info->edev = devm_extcon_dev_allocate(dev, usb_type_c_cable);
  366. if (IS_ERR(info->edev)) {
  367. dev_err(dev, "failed to allocate extcon device\n");
  368. return -ENOMEM;
  369. }
  370. ret = devm_extcon_dev_register(dev, info->edev);
  371. if (ret < 0) {
  372. dev_err(dev, "failed to register extcon device\n");
  373. return ret;
  374. }
  375. extcon_set_property_capability(info->edev, EXTCON_USB,
  376. EXTCON_PROP_USB_VBUS);
  377. extcon_set_property_capability(info->edev, EXTCON_USB_HOST,
  378. EXTCON_PROP_USB_VBUS);
  379. extcon_set_property_capability(info->edev, EXTCON_USB,
  380. EXTCON_PROP_USB_TYPEC_POLARITY);
  381. extcon_set_property_capability(info->edev, EXTCON_USB_HOST,
  382. EXTCON_PROP_USB_TYPEC_POLARITY);
  383. extcon_set_property_capability(info->edev, EXTCON_DISP_DP,
  384. EXTCON_PROP_USB_TYPEC_POLARITY);
  385. extcon_set_property_capability(info->edev, EXTCON_USB,
  386. EXTCON_PROP_USB_SS);
  387. extcon_set_property_capability(info->edev, EXTCON_USB_HOST,
  388. EXTCON_PROP_USB_SS);
  389. extcon_set_property_capability(info->edev, EXTCON_DISP_DP,
  390. EXTCON_PROP_USB_SS);
  391. extcon_set_property_capability(info->edev, EXTCON_DISP_DP,
  392. EXTCON_PROP_DISP_HPD);
  393. info->dr = DR_NONE;
  394. info->pr = false;
  395. platform_set_drvdata(pdev, info);
  396. /* Get PD events from the EC */
  397. info->notifier.notifier_call = extcon_cros_ec_event;
  398. ret = blocking_notifier_chain_register(&info->ec->event_notifier,
  399. &info->notifier);
  400. if (ret < 0) {
  401. dev_err(dev, "failed to register notifier\n");
  402. return ret;
  403. }
  404. /* Perform initial detection */
  405. ret = extcon_cros_ec_detect_cable(info, true);
  406. if (ret < 0) {
  407. dev_err(dev, "failed to detect initial cable state\n");
  408. goto unregister_notifier;
  409. }
  410. return 0;
  411. unregister_notifier:
  412. blocking_notifier_chain_unregister(&info->ec->event_notifier,
  413. &info->notifier);
  414. return ret;
  415. }
  416. static int extcon_cros_ec_remove(struct platform_device *pdev)
  417. {
  418. struct cros_ec_extcon_info *info = platform_get_drvdata(pdev);
  419. blocking_notifier_chain_unregister(&info->ec->event_notifier,
  420. &info->notifier);
  421. return 0;
  422. }
  423. #ifdef CONFIG_PM_SLEEP
  424. static int extcon_cros_ec_suspend(struct device *dev)
  425. {
  426. return 0;
  427. }
  428. static int extcon_cros_ec_resume(struct device *dev)
  429. {
  430. int ret;
  431. struct cros_ec_extcon_info *info = dev_get_drvdata(dev);
  432. ret = extcon_cros_ec_detect_cable(info, true);
  433. if (ret < 0)
  434. dev_err(dev, "failed to detect cable state on resume\n");
  435. return 0;
  436. }
  437. static const struct dev_pm_ops extcon_cros_ec_dev_pm_ops = {
  438. SET_SYSTEM_SLEEP_PM_OPS(extcon_cros_ec_suspend, extcon_cros_ec_resume)
  439. };
  440. #define DEV_PM_OPS (&extcon_cros_ec_dev_pm_ops)
  441. #else
  442. #define DEV_PM_OPS NULL
  443. #endif /* CONFIG_PM_SLEEP */
  444. #ifdef CONFIG_OF
  445. static const struct of_device_id extcon_cros_ec_of_match[] = {
  446. { .compatible = "google,extcon-usbc-cros-ec" },
  447. { /* sentinel */ }
  448. };
  449. MODULE_DEVICE_TABLE(of, extcon_cros_ec_of_match);
  450. #endif /* CONFIG_OF */
  451. static struct platform_driver extcon_cros_ec_driver = {
  452. .driver = {
  453. .name = "extcon-usbc-cros-ec",
  454. .of_match_table = of_match_ptr(extcon_cros_ec_of_match),
  455. .pm = DEV_PM_OPS,
  456. },
  457. .remove = extcon_cros_ec_remove,
  458. .probe = extcon_cros_ec_probe,
  459. };
  460. module_platform_driver(extcon_cros_ec_driver);
  461. MODULE_DESCRIPTION("ChromeOS Embedded Controller extcon driver");
  462. MODULE_AUTHOR("Benson Leung <bleung@chromium.org>");
  463. MODULE_LICENSE("GPL v2");