Merge tag 'for-linus-4.17-rc1-tag' of git://git.kernel.org/pub/scm/linux/kernel/git...
[linux-2.6-microblaze.git] / drivers / extcon / extcon-usbc-cros-ec.c
1 /**
2  * drivers/extcon/extcon-usbc-cros-ec - ChromeOS Embedded Controller extcon
3  *
4  * Copyright (C) 2017 Google, Inc
5  * Author: Benson Leung <bleung@chromium.org>
6  *
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.
10  *
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.
15  */
16
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>
22 #include <linux/of.h>
23 #include <linux/platform_device.h>
24 #include <linux/slab.h>
25 #include <linux/sched.h>
26
27 struct cros_ec_extcon_info {
28         struct device *dev;
29         struct extcon_dev *edev;
30
31         int port_id;
32
33         struct cros_ec_device *ec;
34
35         struct notifier_block notifier;
36
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;
42 };
43
44 static const unsigned int usb_type_c_cable[] = {
45         EXTCON_USB,
46         EXTCON_USB_HOST,
47         EXTCON_DISP_DP,
48         EXTCON_NONE,
49 };
50
51 enum usb_data_roles {
52         DR_NONE,
53         DR_HOST,
54         DR_DEVICE,
55 };
56
57 /**
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
66  *
67  * Return: 0 on success, <0 on failure.
68  */
69 static int cros_ec_pd_command(struct cros_ec_extcon_info *info,
70                               unsigned int command,
71                               unsigned int version,
72                               void *outdata,
73                               unsigned int outsize,
74                               void *indata,
75                               unsigned int insize)
76 {
77         struct cros_ec_command *msg;
78         int ret;
79
80         msg = kzalloc(sizeof(*msg) + max(outsize, insize), GFP_KERNEL);
81         if (!msg)
82                 return -ENOMEM;
83
84         msg->version = version;
85         msg->command = command;
86         msg->outsize = outsize;
87         msg->insize = insize;
88
89         if (outsize)
90                 memcpy(msg->data, outdata, outsize);
91
92         ret = cros_ec_cmd_xfer_status(info->ec, msg);
93         if (ret >= 0 && insize)
94                 memcpy(indata, msg->data, insize);
95
96         kfree(msg);
97         return ret;
98 }
99
100 /**
101  * cros_ec_usb_get_power_type() - Get power type info about PD device attached
102  * to given port.
103  * @info: pointer to struct cros_ec_extcon_info
104  *
105  * Return: power type on success, <0 on failure.
106  */
107 static int cros_ec_usb_get_power_type(struct cros_ec_extcon_info *info)
108 {
109         struct ec_params_usb_pd_power_info req;
110         struct ec_response_usb_pd_power_info resp;
111         int ret;
112
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));
116         if (ret < 0)
117                 return ret;
118
119         return resp.type;
120 }
121
122 /**
123  * cros_ec_usb_get_pd_mux_state() - Get PD mux state for given port.
124  * @info: pointer to struct cros_ec_extcon_info
125  *
126  * Return: PD mux state on success, <0 on failure.
127  */
128 static int cros_ec_usb_get_pd_mux_state(struct cros_ec_extcon_info *info)
129 {
130         struct ec_params_usb_pd_mux_info req;
131         struct ec_response_usb_pd_mux_info resp;
132         int ret;
133
134         req.port = info->port_id;
135         ret = cros_ec_pd_command(info, EC_CMD_USB_PD_MUX_INFO, 0,
136                                  &req, sizeof(req),
137                                  &resp, sizeof(resp));
138         if (ret < 0)
139                 return ret;
140
141         return resp.flags;
142 }
143
144 /**
145  * cros_ec_usb_get_role() - Get role info about possible PD device attached to a
146  * given port.
147  * @info: pointer to struct cros_ec_extcon_info
148  * @polarity: pointer to cable polarity (return value)
149  *
150  * Return: role info on success, -ENOTCONN if no cable is connected, <0 on
151  * failure.
152  */
153 static int cros_ec_usb_get_role(struct cros_ec_extcon_info *info,
154                                 bool *polarity)
155 {
156         struct ec_params_usb_pd_control pd_control;
157         struct ec_response_usb_pd_control_v1 resp;
158         int ret;
159
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));
167         if (ret < 0)
168                 return ret;
169
170         if (!(resp.enabled & PD_CTRL_RESP_ENABLED_CONNECTED))
171                 return -ENOTCONN;
172
173         *polarity = resp.polarity;
174
175         return resp.role;
176 }
177
178 /**
179  * cros_ec_pd_get_num_ports() - Get number of EC charge ports.
180  * @info: pointer to struct cros_ec_extcon_info
181  *
182  * Return: number of ports on success, <0 on failure.
183  */
184 static int cros_ec_pd_get_num_ports(struct cros_ec_extcon_info *info)
185 {
186         struct ec_response_usb_pd_ports resp;
187         int ret;
188
189         ret = cros_ec_pd_command(info, EC_CMD_USB_PD_PORTS,
190                                  0, NULL, 0, &resp, sizeof(resp));
191         if (ret < 0)
192                 return ret;
193
194         return resp.num_ports;
195 }
196
197 static const char *cros_ec_usb_role_string(unsigned int role)
198 {
199         return role == DR_NONE ? "DISCONNECTED" :
200                 (role == DR_HOST ? "DFP" : "UFP");
201 }
202
203 static const char *cros_ec_usb_power_type_string(unsigned int type)
204 {
205         switch (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";
212         case USB_CHG_TYPE_C:
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";
226         default:
227                 return "USB_CHG_TYPE_UNKNOWN";
228         }
229 }
230
231 static bool cros_ec_usb_power_type_is_wall_wart(unsigned int type,
232                                                 unsigned int role)
233 {
234         switch (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.
239          */
240         case USB_CHG_TYPE_PROPRIETARY:
241         case USB_CHG_TYPE_BC12_DCP:
242                 return true;
243         case USB_CHG_TYPE_PD:
244         case USB_CHG_TYPE_C:
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:
251         default:
252                 return false;
253         }
254 }
255
256 static int extcon_cros_ec_detect_cable(struct cros_ec_extcon_info *info,
257                                        bool force)
258 {
259         struct device *dev = info->dev;
260         int role, power_type;
261         unsigned int dr = DR_NONE;
262         bool pr = false;
263         bool polarity = false;
264         bool dp = false;
265         bool mux = false;
266         bool hpd = false;
267
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",
271                         power_type);
272                 return power_type;
273         }
274
275         role = cros_ec_usb_get_role(info, &polarity);
276         if (role < 0) {
277                 if (role != -ENOTCONN) {
278                         dev_err(dev, "failed getting role err = %d\n", role);
279                         return role;
280                 }
281                 dev_dbg(dev, "disconnected\n");
282         } else {
283                 int pd_mux_state;
284
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;
290
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;
294
295                 dev_dbg(dev,
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);
298         }
299
300         /*
301          * When there is no USB host (e.g. USB PD charger),
302          * we are not really a UFP for the AP.
303          */
304         if (dr == DR_DEVICE &&
305             cros_ec_usb_power_type_is_wall_wart(power_type, role))
306                 dr = DR_NONE;
307
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;
311
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));
315                 info->dr = dr;
316                 info->pr = pr;
317                 info->dp = dp;
318                 info->mux = mux;
319                 info->power_type = power_type;
320
321                 if (dr == DR_DEVICE)
322                         device_connected = true;
323                 else if (dr == DR_HOST)
324                         host_connected = true;
325
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,
345                                     EXTCON_PROP_USB_SS,
346                                     (union extcon_property_value)(int)mux);
347                 extcon_set_property(info->edev, EXTCON_USB_HOST,
348                                     EXTCON_PROP_USB_SS,
349                                     (union extcon_property_value)(int)mux);
350                 extcon_set_property(info->edev, EXTCON_DISP_DP,
351                                     EXTCON_PROP_USB_SS,
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);
356
357                 extcon_sync(info->edev, EXTCON_USB);
358                 extcon_sync(info->edev, EXTCON_USB_HOST);
359                 extcon_sync(info->edev, EXTCON_DISP_DP);
360
361         } else if (hpd) {
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);
366         }
367
368         return 0;
369 }
370
371 static int extcon_cros_ec_event(struct notifier_block *nb,
372                                 unsigned long queued_during_suspend,
373                                 void *_notify)
374 {
375         struct cros_ec_extcon_info *info;
376         struct cros_ec_device *ec;
377         u32 host_event;
378
379         info = container_of(nb, struct cros_ec_extcon_info, notifier);
380         ec = info->ec;
381
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);
386                 return NOTIFY_OK;
387         }
388
389         return NOTIFY_DONE;
390 }
391
392 static int extcon_cros_ec_probe(struct platform_device *pdev)
393 {
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;
398         int numports, ret;
399
400         info = devm_kzalloc(dev, sizeof(*info), GFP_KERNEL);
401         if (!info)
402                 return -ENOMEM;
403
404         info->dev = dev;
405         info->ec = ec;
406
407         if (np) {
408                 u32 port;
409
410                 ret = of_property_read_u32(np, "google,usb-port-id", &port);
411                 if (ret < 0) {
412                         dev_err(dev, "Missing google,usb-port-id property\n");
413                         return ret;
414                 }
415                 info->port_id = port;
416         } else {
417                 info->port_id = pdev->id;
418         }
419
420         numports = cros_ec_pd_get_num_ports(info);
421         if (numports < 0) {
422                 dev_err(dev, "failed getting number of ports! ret = %d\n",
423                         numports);
424                 return numports;
425         }
426
427         if (info->port_id >= numports) {
428                 dev_err(dev, "This system only supports %d ports\n", numports);
429                 return -ENODEV;
430         }
431
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");
435                 return -ENOMEM;
436         }
437
438         ret = devm_extcon_dev_register(dev, info->edev);
439         if (ret < 0) {
440                 dev_err(dev, "failed to register extcon device\n");
441                 return ret;
442         }
443
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,
455                                        EXTCON_PROP_USB_SS);
456         extcon_set_property_capability(info->edev, EXTCON_USB_HOST,
457                                        EXTCON_PROP_USB_SS);
458         extcon_set_property_capability(info->edev, EXTCON_DISP_DP,
459                                        EXTCON_PROP_USB_SS);
460         extcon_set_property_capability(info->edev, EXTCON_DISP_DP,
461                                        EXTCON_PROP_DISP_HPD);
462
463         info->dr = DR_NONE;
464         info->pr = false;
465
466         platform_set_drvdata(pdev, info);
467
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,
471                                                &info->notifier);
472         if (ret < 0) {
473                 dev_err(dev, "failed to register notifier\n");
474                 return ret;
475         }
476
477         /* Perform initial detection */
478         ret = extcon_cros_ec_detect_cable(info, true);
479         if (ret < 0) {
480                 dev_err(dev, "failed to detect initial cable state\n");
481                 goto unregister_notifier;
482         }
483
484         return 0;
485
486 unregister_notifier:
487         blocking_notifier_chain_unregister(&info->ec->event_notifier,
488                                            &info->notifier);
489         return ret;
490 }
491
492 static int extcon_cros_ec_remove(struct platform_device *pdev)
493 {
494         struct cros_ec_extcon_info *info = platform_get_drvdata(pdev);
495
496         blocking_notifier_chain_unregister(&info->ec->event_notifier,
497                                            &info->notifier);
498
499         return 0;
500 }
501
502 #ifdef CONFIG_PM_SLEEP
503 static int extcon_cros_ec_suspend(struct device *dev)
504 {
505         return 0;
506 }
507
508 static int extcon_cros_ec_resume(struct device *dev)
509 {
510         int ret;
511         struct cros_ec_extcon_info *info = dev_get_drvdata(dev);
512
513         ret = extcon_cros_ec_detect_cable(info, true);
514         if (ret < 0)
515                 dev_err(dev, "failed to detect cable state on resume\n");
516
517         return 0;
518 }
519
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)
522 };
523
524 #define DEV_PM_OPS      (&extcon_cros_ec_dev_pm_ops)
525 #else
526 #define DEV_PM_OPS      NULL
527 #endif /* CONFIG_PM_SLEEP */
528
529 #ifdef CONFIG_OF
530 static const struct of_device_id extcon_cros_ec_of_match[] = {
531         { .compatible = "google,extcon-usbc-cros-ec" },
532         { /* sentinel */ }
533 };
534 MODULE_DEVICE_TABLE(of, extcon_cros_ec_of_match);
535 #endif /* CONFIG_OF */
536
537 static struct platform_driver extcon_cros_ec_driver = {
538         .driver = {
539                 .name  = "extcon-usbc-cros-ec",
540                 .of_match_table = of_match_ptr(extcon_cros_ec_of_match),
541                 .pm = DEV_PM_OPS,
542         },
543         .remove  = extcon_cros_ec_remove,
544         .probe   = extcon_cros_ec_probe,
545 };
546
547 module_platform_driver(extcon_cros_ec_driver);
548
549 MODULE_DESCRIPTION("ChromeOS Embedded Controller extcon driver");
550 MODULE_AUTHOR("Benson Leung <bleung@chromium.org>");
551 MODULE_LICENSE("GPL");