xref: /openbmc/linux/drivers/usb/dwc3/drd.c (revision 8cb5d748)
1 /**
2  * drd.c - DesignWare USB3 DRD Controller Dual-role support
3  *
4  * Copyright (C) 2017 Texas Instruments Incorporated - http://www.ti.com
5  *
6  * Authors: Roger Quadros <rogerq@ti.com>
7  *
8  * This program is free software: you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License version 2  of
10  * the License as published by the Free Software Foundation.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
19  */
20 
21 #include <linux/extcon.h>
22 
23 #include "debug.h"
24 #include "core.h"
25 #include "gadget.h"
26 
27 static void dwc3_drd_update(struct dwc3 *dwc)
28 {
29 	int id;
30 
31 	id = extcon_get_state(dwc->edev, EXTCON_USB_HOST);
32 	if (id < 0)
33 		id = 0;
34 
35 	dwc3_set_mode(dwc, id ?
36 		      DWC3_GCTL_PRTCAP_HOST :
37 		      DWC3_GCTL_PRTCAP_DEVICE);
38 }
39 
40 static int dwc3_drd_notifier(struct notifier_block *nb,
41 			     unsigned long event, void *ptr)
42 {
43 	struct dwc3 *dwc = container_of(nb, struct dwc3, edev_nb);
44 
45 	dwc3_set_mode(dwc, event ?
46 		      DWC3_GCTL_PRTCAP_HOST :
47 		      DWC3_GCTL_PRTCAP_DEVICE);
48 
49 	return NOTIFY_DONE;
50 }
51 
52 int dwc3_drd_init(struct dwc3 *dwc)
53 {
54 	int ret;
55 
56 	if (dwc->dev->of_node) {
57 		if (of_property_read_bool(dwc->dev->of_node, "extcon"))
58 			dwc->edev = extcon_get_edev_by_phandle(dwc->dev, 0);
59 
60 		if (IS_ERR(dwc->edev))
61 			return PTR_ERR(dwc->edev);
62 
63 		dwc->edev_nb.notifier_call = dwc3_drd_notifier;
64 		ret = extcon_register_notifier(dwc->edev, EXTCON_USB_HOST,
65 					       &dwc->edev_nb);
66 		if (ret < 0) {
67 			dev_err(dwc->dev, "couldn't register cable notifier\n");
68 			return ret;
69 		}
70 	}
71 
72 	dwc3_drd_update(dwc);
73 
74 	return 0;
75 }
76 
77 void dwc3_drd_exit(struct dwc3 *dwc)
78 {
79 	extcon_unregister_notifier(dwc->edev, EXTCON_USB_HOST,
80 				   &dwc->edev_nb);
81 
82 	dwc3_set_mode(dwc, DWC3_GCTL_PRTCAP_DEVICE);
83 	flush_work(&dwc->drd_work);
84 	dwc3_gadget_exit(dwc);
85 }
86