2 * drivers/extcon/extcon-usbc-cros-ec - ChromeOS Embedded Controller extcon
4 * Copyright (C) 2017 Google, Inc
5 * Author: Benson Leung <bleung@chromium.org>
7 * This software is licensed under the terms of the GNU General Public
8 * License version 2, as published by the Free Software Foundation, and
9 * may be copied, distributed, and modified under those terms.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
17 #include <linux/extcon-provider.h>
18 #include <linux/kernel.h>
19 #include <linux/mfd/cros_ec.h>
20 #include <linux/module.h>
21 #include <linux/notifier.h>
23 #include <linux/platform_device.h>
24 #include <linux/slab.h>
25 #include <linux/sched.h>
27 struct cros_ec_extcon_info
{
29 struct extcon_dev
*edev
;
33 struct cros_ec_device
*ec
;
35 struct notifier_block notifier
;
37 unsigned int dr
; /* data role */
38 bool pr
; /* power role (true if VBUS enabled) */
39 bool dp
; /* DisplayPort enabled */
40 bool mux
; /* SuperSpeed (usb3) enabled */
41 unsigned int power_type
;
44 static const unsigned int usb_type_c_cable
[] = {
58 * cros_ec_pd_command() - Send a command to the EC.
59 * @info: pointer to struct cros_ec_extcon_info
60 * @command: EC command
61 * @version: EC command version
62 * @outdata: EC command output data
63 * @outsize: Size of outdata
64 * @indata: EC command input data
65 * @insize: Size of indata
67 * Return: 0 on success, <0 on failure.
69 static int cros_ec_pd_command(struct cros_ec_extcon_info
*info
,
77 struct cros_ec_command
*msg
;
80 msg
= kzalloc(sizeof(*msg
) + max(outsize
, insize
), GFP_KERNEL
);
84 msg
->version
= version
;
85 msg
->command
= command
;
86 msg
->outsize
= outsize
;
90 memcpy(msg
->data
, outdata
, outsize
);
92 ret
= cros_ec_cmd_xfer_status(info
->ec
, msg
);
93 if (ret
>= 0 && insize
)
94 memcpy(indata
, msg
->data
, insize
);
101 * cros_ec_usb_get_power_type() - Get power type info about PD device attached
103 * @info: pointer to struct cros_ec_extcon_info
105 * Return: power type on success, <0 on failure.
107 static int cros_ec_usb_get_power_type(struct cros_ec_extcon_info
*info
)
109 struct ec_params_usb_pd_power_info req
;
110 struct ec_response_usb_pd_power_info resp
;
113 req
.port
= info
->port_id
;
114 ret
= cros_ec_pd_command(info
, EC_CMD_USB_PD_POWER_INFO
, 0,
115 &req
, sizeof(req
), &resp
, sizeof(resp
));
123 * cros_ec_usb_get_pd_mux_state() - Get PD mux state for given port.
124 * @info: pointer to struct cros_ec_extcon_info
126 * Return: PD mux state on success, <0 on failure.
128 static int cros_ec_usb_get_pd_mux_state(struct cros_ec_extcon_info
*info
)
130 struct ec_params_usb_pd_mux_info req
;
131 struct ec_response_usb_pd_mux_info resp
;
134 req
.port
= info
->port_id
;
135 ret
= cros_ec_pd_command(info
, EC_CMD_USB_PD_MUX_INFO
, 0,
137 &resp
, sizeof(resp
));
145 * cros_ec_usb_get_role() - Get role info about possible PD device attached to a
147 * @info: pointer to struct cros_ec_extcon_info
148 * @polarity: pointer to cable polarity (return value)
150 * Return: role info on success, -ENOTCONN if no cable is connected, <0 on
153 static int cros_ec_usb_get_role(struct cros_ec_extcon_info
*info
,
156 struct ec_params_usb_pd_control pd_control
;
157 struct ec_response_usb_pd_control_v1 resp
;
160 pd_control
.port
= info
->port_id
;
161 pd_control
.role
= USB_PD_CTRL_ROLE_NO_CHANGE
;
162 pd_control
.mux
= USB_PD_CTRL_MUX_NO_CHANGE
;
163 pd_control
.swap
= USB_PD_CTRL_SWAP_NONE
;
164 ret
= cros_ec_pd_command(info
, EC_CMD_USB_PD_CONTROL
, 1,
165 &pd_control
, sizeof(pd_control
),
166 &resp
, sizeof(resp
));
170 if (!(resp
.enabled
& PD_CTRL_RESP_ENABLED_CONNECTED
))
173 *polarity
= resp
.polarity
;
179 * cros_ec_pd_get_num_ports() - Get number of EC charge ports.
180 * @info: pointer to struct cros_ec_extcon_info
182 * Return: number of ports on success, <0 on failure.
184 static int cros_ec_pd_get_num_ports(struct cros_ec_extcon_info
*info
)
186 struct ec_response_usb_pd_ports resp
;
189 ret
= cros_ec_pd_command(info
, EC_CMD_USB_PD_PORTS
,
190 0, NULL
, 0, &resp
, sizeof(resp
));
194 return resp
.num_ports
;
197 static const char *cros_ec_usb_role_string(unsigned int role
)
199 return role
== DR_NONE
? "DISCONNECTED" :
200 (role
== DR_HOST
? "DFP" : "UFP");
203 static const char *cros_ec_usb_power_type_string(unsigned int type
)
206 case USB_CHG_TYPE_NONE
:
207 return "USB_CHG_TYPE_NONE";
208 case USB_CHG_TYPE_PD
:
209 return "USB_CHG_TYPE_PD";
210 case USB_CHG_TYPE_PROPRIETARY
:
211 return "USB_CHG_TYPE_PROPRIETARY";
213 return "USB_CHG_TYPE_C";
214 case USB_CHG_TYPE_BC12_DCP
:
215 return "USB_CHG_TYPE_BC12_DCP";
216 case USB_CHG_TYPE_BC12_CDP
:
217 return "USB_CHG_TYPE_BC12_CDP";
218 case USB_CHG_TYPE_BC12_SDP
:
219 return "USB_CHG_TYPE_BC12_SDP";
220 case USB_CHG_TYPE_OTHER
:
221 return "USB_CHG_TYPE_OTHER";
222 case USB_CHG_TYPE_VBUS
:
223 return "USB_CHG_TYPE_VBUS";
224 case USB_CHG_TYPE_UNKNOWN
:
225 return "USB_CHG_TYPE_UNKNOWN";
227 return "USB_CHG_TYPE_UNKNOWN";
231 static bool cros_ec_usb_power_type_is_wall_wart(unsigned int type
,
235 /* FIXME : Guppy, Donnettes, and other chargers will be miscategorized
236 * because they identify with USB_CHG_TYPE_C, but we can't return true
237 * here from that code because that breaks Suzy-Q and other kinds of
238 * USB Type-C cables and peripherals.
240 case USB_CHG_TYPE_PROPRIETARY
:
241 case USB_CHG_TYPE_BC12_DCP
:
243 case USB_CHG_TYPE_PD
:
245 case USB_CHG_TYPE_BC12_CDP
:
246 case USB_CHG_TYPE_BC12_SDP
:
247 case USB_CHG_TYPE_OTHER
:
248 case USB_CHG_TYPE_VBUS
:
249 case USB_CHG_TYPE_UNKNOWN
:
250 case USB_CHG_TYPE_NONE
:
256 static int extcon_cros_ec_detect_cable(struct cros_ec_extcon_info
*info
,
259 struct device
*dev
= info
->dev
;
260 int role
, power_type
;
261 unsigned int dr
= DR_NONE
;
263 bool polarity
= false;
268 power_type
= cros_ec_usb_get_power_type(info
);
269 if (power_type
< 0) {
270 dev_err(dev
, "failed getting power type err = %d\n",
275 role
= cros_ec_usb_get_role(info
, &polarity
);
277 if (role
!= -ENOTCONN
) {
278 dev_err(dev
, "failed getting role err = %d\n", role
);
281 dev_dbg(dev
, "disconnected\n");
285 dr
= (role
& PD_CTRL_RESP_ROLE_DATA
) ? DR_HOST
: DR_DEVICE
;
286 pr
= (role
& PD_CTRL_RESP_ROLE_POWER
);
287 pd_mux_state
= cros_ec_usb_get_pd_mux_state(info
);
288 if (pd_mux_state
< 0)
289 pd_mux_state
= USB_PD_MUX_USB_ENABLED
;
291 dp
= pd_mux_state
& USB_PD_MUX_DP_ENABLED
;
292 mux
= pd_mux_state
& USB_PD_MUX_USB_ENABLED
;
293 hpd
= pd_mux_state
& USB_PD_MUX_HPD_IRQ
;
296 "connected role 0x%x pwr type %d dr %d pr %d pol %d mux %d dp %d hpd %d\n",
297 role
, power_type
, dr
, pr
, polarity
, mux
, dp
, hpd
);
301 * When there is no USB host (e.g. USB PD charger),
302 * we are not really a UFP for the AP.
304 if (dr
== DR_DEVICE
&&
305 cros_ec_usb_power_type_is_wall_wart(power_type
, role
))
308 if (force
|| info
->dr
!= dr
|| info
->pr
!= pr
|| info
->dp
!= dp
||
309 info
->mux
!= mux
|| info
->power_type
!= power_type
) {
310 bool host_connected
= false, device_connected
= false;
312 dev_dbg(dev
, "Type/Role switch! type = %s role = %s\n",
313 cros_ec_usb_power_type_string(power_type
),
314 cros_ec_usb_role_string(dr
));
319 info
->power_type
= power_type
;
322 device_connected
= true;
323 else if (dr
== DR_HOST
)
324 host_connected
= true;
326 extcon_set_state(info
->edev
, EXTCON_USB
, device_connected
);
327 extcon_set_state(info
->edev
, EXTCON_USB_HOST
, host_connected
);
328 extcon_set_state(info
->edev
, EXTCON_DISP_DP
, dp
);
329 extcon_set_property(info
->edev
, EXTCON_USB
,
330 EXTCON_PROP_USB_VBUS
,
331 (union extcon_property_value
)(int)pr
);
332 extcon_set_property(info
->edev
, EXTCON_USB_HOST
,
333 EXTCON_PROP_USB_VBUS
,
334 (union extcon_property_value
)(int)pr
);
335 extcon_set_property(info
->edev
, EXTCON_USB
,
336 EXTCON_PROP_USB_TYPEC_POLARITY
,
337 (union extcon_property_value
)(int)polarity
);
338 extcon_set_property(info
->edev
, EXTCON_USB_HOST
,
339 EXTCON_PROP_USB_TYPEC_POLARITY
,
340 (union extcon_property_value
)(int)polarity
);
341 extcon_set_property(info
->edev
, EXTCON_DISP_DP
,
342 EXTCON_PROP_USB_TYPEC_POLARITY
,
343 (union extcon_property_value
)(int)polarity
);
344 extcon_set_property(info
->edev
, EXTCON_USB
,
346 (union extcon_property_value
)(int)mux
);
347 extcon_set_property(info
->edev
, EXTCON_USB_HOST
,
349 (union extcon_property_value
)(int)mux
);
350 extcon_set_property(info
->edev
, EXTCON_DISP_DP
,
352 (union extcon_property_value
)(int)mux
);
353 extcon_set_property(info
->edev
, EXTCON_DISP_DP
,
354 EXTCON_PROP_DISP_HPD
,
355 (union extcon_property_value
)(int)hpd
);
357 extcon_sync(info
->edev
, EXTCON_USB
);
358 extcon_sync(info
->edev
, EXTCON_USB_HOST
);
359 extcon_sync(info
->edev
, EXTCON_DISP_DP
);
362 extcon_set_property(info
->edev
, EXTCON_DISP_DP
,
363 EXTCON_PROP_DISP_HPD
,
364 (union extcon_property_value
)(int)hpd
);
365 extcon_sync(info
->edev
, EXTCON_DISP_DP
);
371 static int extcon_cros_ec_event(struct notifier_block
*nb
,
372 unsigned long queued_during_suspend
,
375 struct cros_ec_extcon_info
*info
;
376 struct cros_ec_device
*ec
;
379 info
= container_of(nb
, struct cros_ec_extcon_info
, notifier
);
382 host_event
= cros_ec_get_host_event(ec
);
383 if (host_event
& (EC_HOST_EVENT_MASK(EC_HOST_EVENT_PD_MCU
) |
384 EC_HOST_EVENT_MASK(EC_HOST_EVENT_USB_MUX
))) {
385 extcon_cros_ec_detect_cable(info
, false);
392 static int extcon_cros_ec_probe(struct platform_device
*pdev
)
394 struct cros_ec_extcon_info
*info
;
395 struct cros_ec_device
*ec
= dev_get_drvdata(pdev
->dev
.parent
);
396 struct device
*dev
= &pdev
->dev
;
397 struct device_node
*np
= dev
->of_node
;
400 info
= devm_kzalloc(dev
, sizeof(*info
), GFP_KERNEL
);
410 ret
= of_property_read_u32(np
, "google,usb-port-id", &port
);
412 dev_err(dev
, "Missing google,usb-port-id property\n");
415 info
->port_id
= port
;
417 info
->port_id
= pdev
->id
;
420 numports
= cros_ec_pd_get_num_ports(info
);
422 dev_err(dev
, "failed getting number of ports! ret = %d\n",
427 if (info
->port_id
>= numports
) {
428 dev_err(dev
, "This system only supports %d ports\n", numports
);
432 info
->edev
= devm_extcon_dev_allocate(dev
, usb_type_c_cable
);
433 if (IS_ERR(info
->edev
)) {
434 dev_err(dev
, "failed to allocate extcon device\n");
438 ret
= devm_extcon_dev_register(dev
, info
->edev
);
440 dev_err(dev
, "failed to register extcon device\n");
444 extcon_set_property_capability(info
->edev
, EXTCON_USB
,
445 EXTCON_PROP_USB_VBUS
);
446 extcon_set_property_capability(info
->edev
, EXTCON_USB_HOST
,
447 EXTCON_PROP_USB_VBUS
);
448 extcon_set_property_capability(info
->edev
, EXTCON_USB
,
449 EXTCON_PROP_USB_TYPEC_POLARITY
);
450 extcon_set_property_capability(info
->edev
, EXTCON_USB_HOST
,
451 EXTCON_PROP_USB_TYPEC_POLARITY
);
452 extcon_set_property_capability(info
->edev
, EXTCON_DISP_DP
,
453 EXTCON_PROP_USB_TYPEC_POLARITY
);
454 extcon_set_property_capability(info
->edev
, EXTCON_USB
,
456 extcon_set_property_capability(info
->edev
, EXTCON_USB_HOST
,
458 extcon_set_property_capability(info
->edev
, EXTCON_DISP_DP
,
460 extcon_set_property_capability(info
->edev
, EXTCON_DISP_DP
,
461 EXTCON_PROP_DISP_HPD
);
466 platform_set_drvdata(pdev
, info
);
468 /* Get PD events from the EC */
469 info
->notifier
.notifier_call
= extcon_cros_ec_event
;
470 ret
= blocking_notifier_chain_register(&info
->ec
->event_notifier
,
473 dev_err(dev
, "failed to register notifier\n");
477 /* Perform initial detection */
478 ret
= extcon_cros_ec_detect_cable(info
, true);
480 dev_err(dev
, "failed to detect initial cable state\n");
481 goto unregister_notifier
;
487 blocking_notifier_chain_unregister(&info
->ec
->event_notifier
,
492 static int extcon_cros_ec_remove(struct platform_device
*pdev
)
494 struct cros_ec_extcon_info
*info
= platform_get_drvdata(pdev
);
496 blocking_notifier_chain_unregister(&info
->ec
->event_notifier
,
502 #ifdef CONFIG_PM_SLEEP
503 static int extcon_cros_ec_suspend(struct device
*dev
)
508 static int extcon_cros_ec_resume(struct device
*dev
)
511 struct cros_ec_extcon_info
*info
= dev_get_drvdata(dev
);
513 ret
= extcon_cros_ec_detect_cable(info
, true);
515 dev_err(dev
, "failed to detect cable state on resume\n");
520 static const struct dev_pm_ops extcon_cros_ec_dev_pm_ops
= {
521 SET_SYSTEM_SLEEP_PM_OPS(extcon_cros_ec_suspend
, extcon_cros_ec_resume
)
524 #define DEV_PM_OPS (&extcon_cros_ec_dev_pm_ops)
526 #define DEV_PM_OPS NULL
527 #endif /* CONFIG_PM_SLEEP */
530 static const struct of_device_id extcon_cros_ec_of_match
[] = {
531 { .compatible
= "google,extcon-usbc-cros-ec" },
534 MODULE_DEVICE_TABLE(of
, extcon_cros_ec_of_match
);
535 #endif /* CONFIG_OF */
537 static struct platform_driver extcon_cros_ec_driver
= {
539 .name
= "extcon-usbc-cros-ec",
540 .of_match_table
= of_match_ptr(extcon_cros_ec_of_match
),
543 .remove
= extcon_cros_ec_remove
,
544 .probe
= extcon_cros_ec_probe
,
547 module_platform_driver(extcon_cros_ec_driver
);
549 MODULE_DESCRIPTION("ChromeOS Embedded Controller extcon driver");
550 MODULE_AUTHOR("Benson Leung <bleung@chromium.org>");
551 MODULE_LICENSE("GPL");