xref: /openbmc/linux/drivers/extcon/extcon-usbc-cros-ec.c (revision c698316661096e036b54448039b35e1c2c5809f0)
1*c6983166SBenson Leung /**
2*c6983166SBenson Leung  * drivers/extcon/extcon-usbc-cros-ec - ChromeOS Embedded Controller extcon
3*c6983166SBenson Leung  *
4*c6983166SBenson Leung  * Copyright (C) 2017 Google, Inc
5*c6983166SBenson Leung  * Author: Benson Leung <bleung@chromium.org>
6*c6983166SBenson Leung  *
7*c6983166SBenson Leung  * This software is licensed under the terms of the GNU General Public
8*c6983166SBenson Leung  * License version 2, as published by the Free Software Foundation, and
9*c6983166SBenson Leung  * may be copied, distributed, and modified under those terms.
10*c6983166SBenson Leung  *
11*c6983166SBenson Leung  * This program is distributed in the hope that it will be useful,
12*c6983166SBenson Leung  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13*c6983166SBenson Leung  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14*c6983166SBenson Leung  * GNU General Public License for more details.
15*c6983166SBenson Leung  */
16*c6983166SBenson Leung 
17*c6983166SBenson Leung #include <linux/extcon.h>
18*c6983166SBenson Leung #include <linux/kernel.h>
19*c6983166SBenson Leung #include <linux/mfd/cros_ec.h>
20*c6983166SBenson Leung #include <linux/module.h>
21*c6983166SBenson Leung #include <linux/notifier.h>
22*c6983166SBenson Leung #include <linux/of.h>
23*c6983166SBenson Leung #include <linux/platform_device.h>
24*c6983166SBenson Leung #include <linux/slab.h>
25*c6983166SBenson Leung #include <linux/sched.h>
26*c6983166SBenson Leung 
27*c6983166SBenson Leung struct cros_ec_extcon_info {
28*c6983166SBenson Leung 	struct device *dev;
29*c6983166SBenson Leung 	struct extcon_dev *edev;
30*c6983166SBenson Leung 
31*c6983166SBenson Leung 	int port_id;
32*c6983166SBenson Leung 
33*c6983166SBenson Leung 	struct cros_ec_device *ec;
34*c6983166SBenson Leung 
35*c6983166SBenson Leung 	struct notifier_block notifier;
36*c6983166SBenson Leung 
37*c6983166SBenson Leung 	bool dp; /* DisplayPort enabled */
38*c6983166SBenson Leung 	bool mux; /* SuperSpeed (usb3) enabled */
39*c6983166SBenson Leung 	unsigned int power_type;
40*c6983166SBenson Leung };
41*c6983166SBenson Leung 
42*c6983166SBenson Leung static const unsigned int usb_type_c_cable[] = {
43*c6983166SBenson Leung 	EXTCON_DISP_DP,
44*c6983166SBenson Leung 	EXTCON_NONE,
45*c6983166SBenson Leung };
46*c6983166SBenson Leung 
47*c6983166SBenson Leung /**
48*c6983166SBenson Leung  * cros_ec_pd_command() - Send a command to the EC.
49*c6983166SBenson Leung  * @info: pointer to struct cros_ec_extcon_info
50*c6983166SBenson Leung  * @command: EC command
51*c6983166SBenson Leung  * @version: EC command version
52*c6983166SBenson Leung  * @outdata: EC command output data
53*c6983166SBenson Leung  * @outsize: Size of outdata
54*c6983166SBenson Leung  * @indata: EC command input data
55*c6983166SBenson Leung  * @insize: Size of indata
56*c6983166SBenson Leung  *
57*c6983166SBenson Leung  * Return: 0 on success, <0 on failure.
58*c6983166SBenson Leung  */
59*c6983166SBenson Leung static int cros_ec_pd_command(struct cros_ec_extcon_info *info,
60*c6983166SBenson Leung 			      unsigned int command,
61*c6983166SBenson Leung 			      unsigned int version,
62*c6983166SBenson Leung 			      void *outdata,
63*c6983166SBenson Leung 			      unsigned int outsize,
64*c6983166SBenson Leung 			      void *indata,
65*c6983166SBenson Leung 			      unsigned int insize)
66*c6983166SBenson Leung {
67*c6983166SBenson Leung 	struct cros_ec_command *msg;
68*c6983166SBenson Leung 	int ret;
69*c6983166SBenson Leung 
70*c6983166SBenson Leung 	msg = kzalloc(sizeof(*msg) + max(outsize, insize), GFP_KERNEL);
71*c6983166SBenson Leung 
72*c6983166SBenson Leung 	msg->version = version;
73*c6983166SBenson Leung 	msg->command = command;
74*c6983166SBenson Leung 	msg->outsize = outsize;
75*c6983166SBenson Leung 	msg->insize = insize;
76*c6983166SBenson Leung 
77*c6983166SBenson Leung 	if (outsize)
78*c6983166SBenson Leung 		memcpy(msg->data, outdata, outsize);
79*c6983166SBenson Leung 
80*c6983166SBenson Leung 	ret = cros_ec_cmd_xfer_status(info->ec, msg);
81*c6983166SBenson Leung 	if (ret >= 0 && insize)
82*c6983166SBenson Leung 		memcpy(indata, msg->data, insize);
83*c6983166SBenson Leung 
84*c6983166SBenson Leung 	kfree(msg);
85*c6983166SBenson Leung 	return ret;
86*c6983166SBenson Leung }
87*c6983166SBenson Leung 
88*c6983166SBenson Leung /**
89*c6983166SBenson Leung  * cros_ec_usb_get_power_type() - Get power type info about PD device attached
90*c6983166SBenson Leung  * to given port.
91*c6983166SBenson Leung  * @info: pointer to struct cros_ec_extcon_info
92*c6983166SBenson Leung  *
93*c6983166SBenson Leung  * Return: power type on success, <0 on failure.
94*c6983166SBenson Leung  */
95*c6983166SBenson Leung static int cros_ec_usb_get_power_type(struct cros_ec_extcon_info *info)
96*c6983166SBenson Leung {
97*c6983166SBenson Leung 	struct ec_params_usb_pd_power_info req;
98*c6983166SBenson Leung 	struct ec_response_usb_pd_power_info resp;
99*c6983166SBenson Leung 	int ret;
100*c6983166SBenson Leung 
101*c6983166SBenson Leung 	req.port = info->port_id;
102*c6983166SBenson Leung 	ret = cros_ec_pd_command(info, EC_CMD_USB_PD_POWER_INFO, 0,
103*c6983166SBenson Leung 				 &req, sizeof(req), &resp, sizeof(resp));
104*c6983166SBenson Leung 	if (ret < 0)
105*c6983166SBenson Leung 		return ret;
106*c6983166SBenson Leung 
107*c6983166SBenson Leung 	return resp.type;
108*c6983166SBenson Leung }
109*c6983166SBenson Leung 
110*c6983166SBenson Leung /**
111*c6983166SBenson Leung  * cros_ec_usb_get_pd_mux_state() - Get PD mux state for given port.
112*c6983166SBenson Leung  * @info: pointer to struct cros_ec_extcon_info
113*c6983166SBenson Leung  *
114*c6983166SBenson Leung  * Return: PD mux state on success, <0 on failure.
115*c6983166SBenson Leung  */
116*c6983166SBenson Leung static int cros_ec_usb_get_pd_mux_state(struct cros_ec_extcon_info *info)
117*c6983166SBenson Leung {
118*c6983166SBenson Leung 	struct ec_params_usb_pd_mux_info req;
119*c6983166SBenson Leung 	struct ec_response_usb_pd_mux_info resp;
120*c6983166SBenson Leung 	int ret;
121*c6983166SBenson Leung 
122*c6983166SBenson Leung 	req.port = info->port_id;
123*c6983166SBenson Leung 	ret = cros_ec_pd_command(info, EC_CMD_USB_PD_MUX_INFO, 0,
124*c6983166SBenson Leung 				 &req, sizeof(req),
125*c6983166SBenson Leung 				 &resp, sizeof(resp));
126*c6983166SBenson Leung 	if (ret < 0)
127*c6983166SBenson Leung 		return ret;
128*c6983166SBenson Leung 
129*c6983166SBenson Leung 	return resp.flags;
130*c6983166SBenson Leung }
131*c6983166SBenson Leung 
132*c6983166SBenson Leung /**
133*c6983166SBenson Leung  * cros_ec_usb_get_role() - Get role info about possible PD device attached to a
134*c6983166SBenson Leung  * given port.
135*c6983166SBenson Leung  * @info: pointer to struct cros_ec_extcon_info
136*c6983166SBenson Leung  * @polarity: pointer to cable polarity (return value)
137*c6983166SBenson Leung  *
138*c6983166SBenson Leung  * Return: role info on success, -ENOTCONN if no cable is connected, <0 on
139*c6983166SBenson Leung  * failure.
140*c6983166SBenson Leung  */
141*c6983166SBenson Leung static int cros_ec_usb_get_role(struct cros_ec_extcon_info *info,
142*c6983166SBenson Leung 				bool *polarity)
143*c6983166SBenson Leung {
144*c6983166SBenson Leung 	struct ec_params_usb_pd_control pd_control;
145*c6983166SBenson Leung 	struct ec_response_usb_pd_control_v1 resp;
146*c6983166SBenson Leung 	int ret;
147*c6983166SBenson Leung 
148*c6983166SBenson Leung 	pd_control.port = info->port_id;
149*c6983166SBenson Leung 	pd_control.role = USB_PD_CTRL_ROLE_NO_CHANGE;
150*c6983166SBenson Leung 	pd_control.mux = USB_PD_CTRL_MUX_NO_CHANGE;
151*c6983166SBenson Leung 	ret = cros_ec_pd_command(info, EC_CMD_USB_PD_CONTROL, 1,
152*c6983166SBenson Leung 				 &pd_control, sizeof(pd_control),
153*c6983166SBenson Leung 				 &resp, sizeof(resp));
154*c6983166SBenson Leung 	if (ret < 0)
155*c6983166SBenson Leung 		return ret;
156*c6983166SBenson Leung 
157*c6983166SBenson Leung 	if (!(resp.enabled & PD_CTRL_RESP_ENABLED_CONNECTED))
158*c6983166SBenson Leung 		return -ENOTCONN;
159*c6983166SBenson Leung 
160*c6983166SBenson Leung 	*polarity = resp.polarity;
161*c6983166SBenson Leung 
162*c6983166SBenson Leung 	return resp.role;
163*c6983166SBenson Leung }
164*c6983166SBenson Leung 
165*c6983166SBenson Leung /**
166*c6983166SBenson Leung  * cros_ec_pd_get_num_ports() - Get number of EC charge ports.
167*c6983166SBenson Leung  * @info: pointer to struct cros_ec_extcon_info
168*c6983166SBenson Leung  *
169*c6983166SBenson Leung  * Return: number of ports on success, <0 on failure.
170*c6983166SBenson Leung  */
171*c6983166SBenson Leung static int cros_ec_pd_get_num_ports(struct cros_ec_extcon_info *info)
172*c6983166SBenson Leung {
173*c6983166SBenson Leung 	struct ec_response_usb_pd_ports resp;
174*c6983166SBenson Leung 	int ret;
175*c6983166SBenson Leung 
176*c6983166SBenson Leung 	ret = cros_ec_pd_command(info, EC_CMD_USB_PD_PORTS,
177*c6983166SBenson Leung 				 0, NULL, 0, &resp, sizeof(resp));
178*c6983166SBenson Leung 	if (ret < 0)
179*c6983166SBenson Leung 		return ret;
180*c6983166SBenson Leung 
181*c6983166SBenson Leung 	return resp.num_ports;
182*c6983166SBenson Leung }
183*c6983166SBenson Leung 
184*c6983166SBenson Leung static int extcon_cros_ec_detect_cable(struct cros_ec_extcon_info *info,
185*c6983166SBenson Leung 				       bool force)
186*c6983166SBenson Leung {
187*c6983166SBenson Leung 	struct device *dev = info->dev;
188*c6983166SBenson Leung 	int role, power_type;
189*c6983166SBenson Leung 	bool polarity = false;
190*c6983166SBenson Leung 	bool dp = false;
191*c6983166SBenson Leung 	bool mux = false;
192*c6983166SBenson Leung 	bool hpd = false;
193*c6983166SBenson Leung 
194*c6983166SBenson Leung 	power_type = cros_ec_usb_get_power_type(info);
195*c6983166SBenson Leung 	if (power_type < 0) {
196*c6983166SBenson Leung 		dev_err(dev, "failed getting power type err = %d\n",
197*c6983166SBenson Leung 			power_type);
198*c6983166SBenson Leung 		return power_type;
199*c6983166SBenson Leung 	}
200*c6983166SBenson Leung 
201*c6983166SBenson Leung 	role = cros_ec_usb_get_role(info, &polarity);
202*c6983166SBenson Leung 	if (role < 0) {
203*c6983166SBenson Leung 		if (role != -ENOTCONN) {
204*c6983166SBenson Leung 			dev_err(dev, "failed getting role err = %d\n", role);
205*c6983166SBenson Leung 			return role;
206*c6983166SBenson Leung 		}
207*c6983166SBenson Leung 	} else {
208*c6983166SBenson Leung 		int pd_mux_state;
209*c6983166SBenson Leung 
210*c6983166SBenson Leung 		pd_mux_state = cros_ec_usb_get_pd_mux_state(info);
211*c6983166SBenson Leung 		if (pd_mux_state < 0)
212*c6983166SBenson Leung 			pd_mux_state = USB_PD_MUX_USB_ENABLED;
213*c6983166SBenson Leung 
214*c6983166SBenson Leung 		dp = pd_mux_state & USB_PD_MUX_DP_ENABLED;
215*c6983166SBenson Leung 		mux = pd_mux_state & USB_PD_MUX_USB_ENABLED;
216*c6983166SBenson Leung 		hpd = pd_mux_state & USB_PD_MUX_HPD_IRQ;
217*c6983166SBenson Leung 	}
218*c6983166SBenson Leung 
219*c6983166SBenson Leung 	if (force || info->dp != dp || info->mux != mux ||
220*c6983166SBenson Leung 		info->power_type != power_type) {
221*c6983166SBenson Leung 
222*c6983166SBenson Leung 		info->dp = dp;
223*c6983166SBenson Leung 		info->mux = mux;
224*c6983166SBenson Leung 		info->power_type = power_type;
225*c6983166SBenson Leung 
226*c6983166SBenson Leung 		extcon_set_state(info->edev, EXTCON_DISP_DP, dp);
227*c6983166SBenson Leung 
228*c6983166SBenson Leung 		extcon_set_property(info->edev, EXTCON_DISP_DP,
229*c6983166SBenson Leung 				    EXTCON_PROP_USB_TYPEC_POLARITY,
230*c6983166SBenson Leung 				    (union extcon_property_value)(int)polarity);
231*c6983166SBenson Leung 		extcon_set_property(info->edev, EXTCON_DISP_DP,
232*c6983166SBenson Leung 				    EXTCON_PROP_USB_SS,
233*c6983166SBenson Leung 				    (union extcon_property_value)(int)mux);
234*c6983166SBenson Leung 		extcon_set_property(info->edev, EXTCON_DISP_DP,
235*c6983166SBenson Leung 				    EXTCON_PROP_DISP_HPD,
236*c6983166SBenson Leung 				    (union extcon_property_value)(int)hpd);
237*c6983166SBenson Leung 
238*c6983166SBenson Leung 		extcon_sync(info->edev, EXTCON_DISP_DP);
239*c6983166SBenson Leung 
240*c6983166SBenson Leung 	} else if (hpd) {
241*c6983166SBenson Leung 		extcon_set_property(info->edev, EXTCON_DISP_DP,
242*c6983166SBenson Leung 				    EXTCON_PROP_DISP_HPD,
243*c6983166SBenson Leung 				    (union extcon_property_value)(int)hpd);
244*c6983166SBenson Leung 		extcon_sync(info->edev, EXTCON_DISP_DP);
245*c6983166SBenson Leung 	}
246*c6983166SBenson Leung 
247*c6983166SBenson Leung 	return 0;
248*c6983166SBenson Leung }
249*c6983166SBenson Leung 
250*c6983166SBenson Leung static int extcon_cros_ec_event(struct notifier_block *nb,
251*c6983166SBenson Leung 				unsigned long queued_during_suspend,
252*c6983166SBenson Leung 				void *_notify)
253*c6983166SBenson Leung {
254*c6983166SBenson Leung 	struct cros_ec_extcon_info *info;
255*c6983166SBenson Leung 	struct cros_ec_device *ec;
256*c6983166SBenson Leung 	u32 host_event;
257*c6983166SBenson Leung 
258*c6983166SBenson Leung 	info = container_of(nb, struct cros_ec_extcon_info, notifier);
259*c6983166SBenson Leung 	ec = info->ec;
260*c6983166SBenson Leung 
261*c6983166SBenson Leung 	host_event = cros_ec_get_host_event(ec);
262*c6983166SBenson Leung 	if (host_event & (EC_HOST_EVENT_MASK(EC_HOST_EVENT_PD_MCU) |
263*c6983166SBenson Leung 			  EC_HOST_EVENT_MASK(EC_HOST_EVENT_USB_MUX))) {
264*c6983166SBenson Leung 		extcon_cros_ec_detect_cable(info, false);
265*c6983166SBenson Leung 		return NOTIFY_OK;
266*c6983166SBenson Leung 	}
267*c6983166SBenson Leung 
268*c6983166SBenson Leung 	return NOTIFY_DONE;
269*c6983166SBenson Leung }
270*c6983166SBenson Leung 
271*c6983166SBenson Leung static int extcon_cros_ec_probe(struct platform_device *pdev)
272*c6983166SBenson Leung {
273*c6983166SBenson Leung 	struct cros_ec_extcon_info *info;
274*c6983166SBenson Leung 	struct cros_ec_device *ec = dev_get_drvdata(pdev->dev.parent);
275*c6983166SBenson Leung 	struct device *dev = &pdev->dev;
276*c6983166SBenson Leung 	struct device_node *np = dev->of_node;
277*c6983166SBenson Leung 	int numports, ret;
278*c6983166SBenson Leung 
279*c6983166SBenson Leung 	info = devm_kzalloc(dev, sizeof(*info), GFP_KERNEL);
280*c6983166SBenson Leung 	if (!info)
281*c6983166SBenson Leung 		return -ENOMEM;
282*c6983166SBenson Leung 
283*c6983166SBenson Leung 	info->dev = dev;
284*c6983166SBenson Leung 	info->ec = ec;
285*c6983166SBenson Leung 
286*c6983166SBenson Leung 	if (np) {
287*c6983166SBenson Leung 		u32 port;
288*c6983166SBenson Leung 
289*c6983166SBenson Leung 		ret = of_property_read_u32(np, "google,usb-port-id", &port);
290*c6983166SBenson Leung 		if (ret < 0) {
291*c6983166SBenson Leung 			dev_err(dev, "Missing google,usb-port-id property\n");
292*c6983166SBenson Leung 			return ret;
293*c6983166SBenson Leung 		}
294*c6983166SBenson Leung 		info->port_id = port;
295*c6983166SBenson Leung 	} else {
296*c6983166SBenson Leung 		info->port_id = pdev->id;
297*c6983166SBenson Leung 	}
298*c6983166SBenson Leung 
299*c6983166SBenson Leung 	numports = cros_ec_pd_get_num_ports(info);
300*c6983166SBenson Leung 	if (numports < 0) {
301*c6983166SBenson Leung 		dev_err(dev, "failed getting number of ports! ret = %d\n",
302*c6983166SBenson Leung 			numports);
303*c6983166SBenson Leung 		return numports;
304*c6983166SBenson Leung 	}
305*c6983166SBenson Leung 
306*c6983166SBenson Leung 	if (info->port_id >= numports) {
307*c6983166SBenson Leung 		dev_err(dev, "This system only supports %d ports\n", numports);
308*c6983166SBenson Leung 		return -ENODEV;
309*c6983166SBenson Leung 	}
310*c6983166SBenson Leung 
311*c6983166SBenson Leung 	info->edev = devm_extcon_dev_allocate(dev, usb_type_c_cable);
312*c6983166SBenson Leung 	if (IS_ERR(info->edev)) {
313*c6983166SBenson Leung 		dev_err(dev, "failed to allocate extcon device\n");
314*c6983166SBenson Leung 		return -ENOMEM;
315*c6983166SBenson Leung 	}
316*c6983166SBenson Leung 
317*c6983166SBenson Leung 	ret = devm_extcon_dev_register(dev, info->edev);
318*c6983166SBenson Leung 	if (ret < 0) {
319*c6983166SBenson Leung 		dev_err(dev, "failed to register extcon device\n");
320*c6983166SBenson Leung 		return ret;
321*c6983166SBenson Leung 	}
322*c6983166SBenson Leung 
323*c6983166SBenson Leung 	extcon_set_property_capability(info->edev, EXTCON_DISP_DP,
324*c6983166SBenson Leung 				       EXTCON_PROP_USB_TYPEC_POLARITY);
325*c6983166SBenson Leung 	extcon_set_property_capability(info->edev, EXTCON_DISP_DP,
326*c6983166SBenson Leung 				       EXTCON_PROP_USB_SS);
327*c6983166SBenson Leung 	extcon_set_property_capability(info->edev, EXTCON_DISP_DP,
328*c6983166SBenson Leung 				       EXTCON_PROP_DISP_HPD);
329*c6983166SBenson Leung 
330*c6983166SBenson Leung 	platform_set_drvdata(pdev, info);
331*c6983166SBenson Leung 
332*c6983166SBenson Leung 	/* Get PD events from the EC */
333*c6983166SBenson Leung 	info->notifier.notifier_call = extcon_cros_ec_event;
334*c6983166SBenson Leung 	ret = blocking_notifier_chain_register(&info->ec->event_notifier,
335*c6983166SBenson Leung 					       &info->notifier);
336*c6983166SBenson Leung 	if (ret < 0) {
337*c6983166SBenson Leung 		dev_err(dev, "failed to register notifier\n");
338*c6983166SBenson Leung 		return ret;
339*c6983166SBenson Leung 	}
340*c6983166SBenson Leung 
341*c6983166SBenson Leung 	/* Perform initial detection */
342*c6983166SBenson Leung 	ret = extcon_cros_ec_detect_cable(info, true);
343*c6983166SBenson Leung 	if (ret < 0) {
344*c6983166SBenson Leung 		dev_err(dev, "failed to detect initial cable state\n");
345*c6983166SBenson Leung 		goto unregister_notifier;
346*c6983166SBenson Leung 	}
347*c6983166SBenson Leung 
348*c6983166SBenson Leung 	return 0;
349*c6983166SBenson Leung 
350*c6983166SBenson Leung unregister_notifier:
351*c6983166SBenson Leung 	blocking_notifier_chain_unregister(&info->ec->event_notifier,
352*c6983166SBenson Leung 					   &info->notifier);
353*c6983166SBenson Leung 	return ret;
354*c6983166SBenson Leung }
355*c6983166SBenson Leung 
356*c6983166SBenson Leung static int extcon_cros_ec_remove(struct platform_device *pdev)
357*c6983166SBenson Leung {
358*c6983166SBenson Leung 	struct cros_ec_extcon_info *info = platform_get_drvdata(pdev);
359*c6983166SBenson Leung 
360*c6983166SBenson Leung 	blocking_notifier_chain_unregister(&info->ec->event_notifier,
361*c6983166SBenson Leung 					   &info->notifier);
362*c6983166SBenson Leung 
363*c6983166SBenson Leung 	return 0;
364*c6983166SBenson Leung }
365*c6983166SBenson Leung 
366*c6983166SBenson Leung #ifdef CONFIG_PM_SLEEP
367*c6983166SBenson Leung static int extcon_cros_ec_suspend(struct device *dev)
368*c6983166SBenson Leung {
369*c6983166SBenson Leung 	return 0;
370*c6983166SBenson Leung }
371*c6983166SBenson Leung 
372*c6983166SBenson Leung static int extcon_cros_ec_resume(struct device *dev)
373*c6983166SBenson Leung {
374*c6983166SBenson Leung 	int ret;
375*c6983166SBenson Leung 	struct cros_ec_extcon_info *info = dev_get_drvdata(dev);
376*c6983166SBenson Leung 
377*c6983166SBenson Leung 	ret = extcon_cros_ec_detect_cable(info, true);
378*c6983166SBenson Leung 	if (ret < 0)
379*c6983166SBenson Leung 		dev_err(dev, "failed to detect cable state on resume\n");
380*c6983166SBenson Leung 
381*c6983166SBenson Leung 	return 0;
382*c6983166SBenson Leung }
383*c6983166SBenson Leung 
384*c6983166SBenson Leung static const struct dev_pm_ops extcon_cros_ec_dev_pm_ops = {
385*c6983166SBenson Leung 	SET_SYSTEM_SLEEP_PM_OPS(extcon_cros_ec_suspend, extcon_cros_ec_resume)
386*c6983166SBenson Leung };
387*c6983166SBenson Leung 
388*c6983166SBenson Leung #define DEV_PM_OPS	(&extcon_cros_ec_dev_pm_ops)
389*c6983166SBenson Leung #else
390*c6983166SBenson Leung #define DEV_PM_OPS	NULL
391*c6983166SBenson Leung #endif /* CONFIG_PM_SLEEP */
392*c6983166SBenson Leung 
393*c6983166SBenson Leung #ifdef CONFIG_OF
394*c6983166SBenson Leung static const struct of_device_id extcon_cros_ec_of_match[] = {
395*c6983166SBenson Leung 	{ .compatible = "google,extcon-usbc-cros-ec" },
396*c6983166SBenson Leung 	{ /* sentinel */ }
397*c6983166SBenson Leung };
398*c6983166SBenson Leung MODULE_DEVICE_TABLE(of, extcon_cros_ec_of_match);
399*c6983166SBenson Leung #endif /* CONFIG_OF */
400*c6983166SBenson Leung 
401*c6983166SBenson Leung static struct platform_driver extcon_cros_ec_driver = {
402*c6983166SBenson Leung 	.driver = {
403*c6983166SBenson Leung 		.name  = "extcon-usbc-cros-ec",
404*c6983166SBenson Leung 		.of_match_table = of_match_ptr(extcon_cros_ec_of_match),
405*c6983166SBenson Leung 		.pm = DEV_PM_OPS,
406*c6983166SBenson Leung 	},
407*c6983166SBenson Leung 	.remove  = extcon_cros_ec_remove,
408*c6983166SBenson Leung 	.probe   = extcon_cros_ec_probe,
409*c6983166SBenson Leung };
410*c6983166SBenson Leung 
411*c6983166SBenson Leung module_platform_driver(extcon_cros_ec_driver);
412*c6983166SBenson Leung 
413*c6983166SBenson Leung MODULE_DESCRIPTION("ChromeOS Embedded Controller extcon driver");
414*c6983166SBenson Leung MODULE_AUTHOR("Benson Leung <bleung@chromium.org>");
415*c6983166SBenson Leung MODULE_LICENSE("GPL");
416