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 bool dp
; /* DisplayPort enabled */
38 bool mux
; /* SuperSpeed (usb3) enabled */
39 unsigned int power_type
;
42 static const unsigned int usb_type_c_cable
[] = {
48 * cros_ec_pd_command() - Send a command to the EC.
49 * @info: pointer to struct cros_ec_extcon_info
50 * @command: EC command
51 * @version: EC command version
52 * @outdata: EC command output data
53 * @outsize: Size of outdata
54 * @indata: EC command input data
55 * @insize: Size of indata
57 * Return: 0 on success, <0 on failure.
59 static int cros_ec_pd_command(struct cros_ec_extcon_info
*info
,
67 struct cros_ec_command
*msg
;
70 msg
= kzalloc(sizeof(*msg
) + max(outsize
, insize
), GFP_KERNEL
);
74 msg
->version
= version
;
75 msg
->command
= command
;
76 msg
->outsize
= outsize
;
80 memcpy(msg
->data
, outdata
, outsize
);
82 ret
= cros_ec_cmd_xfer_status(info
->ec
, msg
);
83 if (ret
>= 0 && insize
)
84 memcpy(indata
, msg
->data
, insize
);
91 * cros_ec_usb_get_power_type() - Get power type info about PD device attached
93 * @info: pointer to struct cros_ec_extcon_info
95 * Return: power type on success, <0 on failure.
97 static int cros_ec_usb_get_power_type(struct cros_ec_extcon_info
*info
)
99 struct ec_params_usb_pd_power_info req
;
100 struct ec_response_usb_pd_power_info resp
;
103 req
.port
= info
->port_id
;
104 ret
= cros_ec_pd_command(info
, EC_CMD_USB_PD_POWER_INFO
, 0,
105 &req
, sizeof(req
), &resp
, sizeof(resp
));
113 * cros_ec_usb_get_pd_mux_state() - Get PD mux state for given port.
114 * @info: pointer to struct cros_ec_extcon_info
116 * Return: PD mux state on success, <0 on failure.
118 static int cros_ec_usb_get_pd_mux_state(struct cros_ec_extcon_info
*info
)
120 struct ec_params_usb_pd_mux_info req
;
121 struct ec_response_usb_pd_mux_info resp
;
124 req
.port
= info
->port_id
;
125 ret
= cros_ec_pd_command(info
, EC_CMD_USB_PD_MUX_INFO
, 0,
127 &resp
, sizeof(resp
));
135 * cros_ec_usb_get_role() - Get role info about possible PD device attached to a
137 * @info: pointer to struct cros_ec_extcon_info
138 * @polarity: pointer to cable polarity (return value)
140 * Return: role info on success, -ENOTCONN if no cable is connected, <0 on
143 static int cros_ec_usb_get_role(struct cros_ec_extcon_info
*info
,
146 struct ec_params_usb_pd_control pd_control
;
147 struct ec_response_usb_pd_control_v1 resp
;
150 pd_control
.port
= info
->port_id
;
151 pd_control
.role
= USB_PD_CTRL_ROLE_NO_CHANGE
;
152 pd_control
.mux
= USB_PD_CTRL_MUX_NO_CHANGE
;
153 ret
= cros_ec_pd_command(info
, EC_CMD_USB_PD_CONTROL
, 1,
154 &pd_control
, sizeof(pd_control
),
155 &resp
, sizeof(resp
));
159 if (!(resp
.enabled
& PD_CTRL_RESP_ENABLED_CONNECTED
))
162 *polarity
= resp
.polarity
;
168 * cros_ec_pd_get_num_ports() - Get number of EC charge ports.
169 * @info: pointer to struct cros_ec_extcon_info
171 * Return: number of ports on success, <0 on failure.
173 static int cros_ec_pd_get_num_ports(struct cros_ec_extcon_info
*info
)
175 struct ec_response_usb_pd_ports resp
;
178 ret
= cros_ec_pd_command(info
, EC_CMD_USB_PD_PORTS
,
179 0, NULL
, 0, &resp
, sizeof(resp
));
183 return resp
.num_ports
;
186 static int extcon_cros_ec_detect_cable(struct cros_ec_extcon_info
*info
,
189 struct device
*dev
= info
->dev
;
190 int role
, power_type
;
191 bool polarity
= false;
196 power_type
= cros_ec_usb_get_power_type(info
);
197 if (power_type
< 0) {
198 dev_err(dev
, "failed getting power type err = %d\n",
203 role
= cros_ec_usb_get_role(info
, &polarity
);
205 if (role
!= -ENOTCONN
) {
206 dev_err(dev
, "failed getting role err = %d\n", role
);
212 pd_mux_state
= cros_ec_usb_get_pd_mux_state(info
);
213 if (pd_mux_state
< 0)
214 pd_mux_state
= USB_PD_MUX_USB_ENABLED
;
216 dp
= pd_mux_state
& USB_PD_MUX_DP_ENABLED
;
217 mux
= pd_mux_state
& USB_PD_MUX_USB_ENABLED
;
218 hpd
= pd_mux_state
& USB_PD_MUX_HPD_IRQ
;
221 if (force
|| info
->dp
!= dp
|| info
->mux
!= mux
||
222 info
->power_type
!= power_type
) {
226 info
->power_type
= power_type
;
228 extcon_set_state(info
->edev
, EXTCON_DISP_DP
, dp
);
230 extcon_set_property(info
->edev
, EXTCON_DISP_DP
,
231 EXTCON_PROP_USB_TYPEC_POLARITY
,
232 (union extcon_property_value
)(int)polarity
);
233 extcon_set_property(info
->edev
, EXTCON_DISP_DP
,
235 (union extcon_property_value
)(int)mux
);
236 extcon_set_property(info
->edev
, EXTCON_DISP_DP
,
237 EXTCON_PROP_DISP_HPD
,
238 (union extcon_property_value
)(int)hpd
);
240 extcon_sync(info
->edev
, EXTCON_DISP_DP
);
243 extcon_set_property(info
->edev
, EXTCON_DISP_DP
,
244 EXTCON_PROP_DISP_HPD
,
245 (union extcon_property_value
)(int)hpd
);
246 extcon_sync(info
->edev
, EXTCON_DISP_DP
);
252 static int extcon_cros_ec_event(struct notifier_block
*nb
,
253 unsigned long queued_during_suspend
,
256 struct cros_ec_extcon_info
*info
;
257 struct cros_ec_device
*ec
;
260 info
= container_of(nb
, struct cros_ec_extcon_info
, notifier
);
263 host_event
= cros_ec_get_host_event(ec
);
264 if (host_event
& (EC_HOST_EVENT_MASK(EC_HOST_EVENT_PD_MCU
) |
265 EC_HOST_EVENT_MASK(EC_HOST_EVENT_USB_MUX
))) {
266 extcon_cros_ec_detect_cable(info
, false);
273 static int extcon_cros_ec_probe(struct platform_device
*pdev
)
275 struct cros_ec_extcon_info
*info
;
276 struct cros_ec_device
*ec
= dev_get_drvdata(pdev
->dev
.parent
);
277 struct device
*dev
= &pdev
->dev
;
278 struct device_node
*np
= dev
->of_node
;
281 info
= devm_kzalloc(dev
, sizeof(*info
), GFP_KERNEL
);
291 ret
= of_property_read_u32(np
, "google,usb-port-id", &port
);
293 dev_err(dev
, "Missing google,usb-port-id property\n");
296 info
->port_id
= port
;
298 info
->port_id
= pdev
->id
;
301 numports
= cros_ec_pd_get_num_ports(info
);
303 dev_err(dev
, "failed getting number of ports! ret = %d\n",
308 if (info
->port_id
>= numports
) {
309 dev_err(dev
, "This system only supports %d ports\n", numports
);
313 info
->edev
= devm_extcon_dev_allocate(dev
, usb_type_c_cable
);
314 if (IS_ERR(info
->edev
)) {
315 dev_err(dev
, "failed to allocate extcon device\n");
319 ret
= devm_extcon_dev_register(dev
, info
->edev
);
321 dev_err(dev
, "failed to register extcon device\n");
325 extcon_set_property_capability(info
->edev
, EXTCON_DISP_DP
,
326 EXTCON_PROP_USB_TYPEC_POLARITY
);
327 extcon_set_property_capability(info
->edev
, EXTCON_DISP_DP
,
329 extcon_set_property_capability(info
->edev
, EXTCON_DISP_DP
,
330 EXTCON_PROP_DISP_HPD
);
332 platform_set_drvdata(pdev
, info
);
334 /* Get PD events from the EC */
335 info
->notifier
.notifier_call
= extcon_cros_ec_event
;
336 ret
= blocking_notifier_chain_register(&info
->ec
->event_notifier
,
339 dev_err(dev
, "failed to register notifier\n");
343 /* Perform initial detection */
344 ret
= extcon_cros_ec_detect_cable(info
, true);
346 dev_err(dev
, "failed to detect initial cable state\n");
347 goto unregister_notifier
;
353 blocking_notifier_chain_unregister(&info
->ec
->event_notifier
,
358 static int extcon_cros_ec_remove(struct platform_device
*pdev
)
360 struct cros_ec_extcon_info
*info
= platform_get_drvdata(pdev
);
362 blocking_notifier_chain_unregister(&info
->ec
->event_notifier
,
368 #ifdef CONFIG_PM_SLEEP
369 static int extcon_cros_ec_suspend(struct device
*dev
)
374 static int extcon_cros_ec_resume(struct device
*dev
)
377 struct cros_ec_extcon_info
*info
= dev_get_drvdata(dev
);
379 ret
= extcon_cros_ec_detect_cable(info
, true);
381 dev_err(dev
, "failed to detect cable state on resume\n");
386 static const struct dev_pm_ops extcon_cros_ec_dev_pm_ops
= {
387 SET_SYSTEM_SLEEP_PM_OPS(extcon_cros_ec_suspend
, extcon_cros_ec_resume
)
390 #define DEV_PM_OPS (&extcon_cros_ec_dev_pm_ops)
392 #define DEV_PM_OPS NULL
393 #endif /* CONFIG_PM_SLEEP */
396 static const struct of_device_id extcon_cros_ec_of_match
[] = {
397 { .compatible
= "google,extcon-usbc-cros-ec" },
400 MODULE_DEVICE_TABLE(of
, extcon_cros_ec_of_match
);
401 #endif /* CONFIG_OF */
403 static struct platform_driver extcon_cros_ec_driver
= {
405 .name
= "extcon-usbc-cros-ec",
406 .of_match_table
= of_match_ptr(extcon_cros_ec_of_match
),
409 .remove
= extcon_cros_ec_remove
,
410 .probe
= extcon_cros_ec_probe
,
413 module_platform_driver(extcon_cros_ec_driver
);
415 MODULE_DESCRIPTION("ChromeOS Embedded Controller extcon driver");
416 MODULE_AUTHOR("Benson Leung <bleung@chromium.org>");
417 MODULE_LICENSE("GPL");