xref: /freebsd/sys/arm/allwinner/aw_usbphy.c (revision 342af4d5efec74bb4bc11261fdd9991c53616f54)
1 /*-
2  * Copyright (c) 2016 Jared McNeill <jmcneill@invisible.ca>
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
15  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
16  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
17  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
18  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
19  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
20  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
21  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
22  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24  * SUCH DAMAGE.
25  *
26  * $FreeBSD$
27  */
28 
29 /*
30  * Allwinner USB PHY
31  */
32 
33 #include <sys/cdefs.h>
34 __FBSDID("$FreeBSD$");
35 
36 #include <sys/param.h>
37 #include <sys/systm.h>
38 #include <sys/bus.h>
39 #include <sys/rman.h>
40 #include <sys/kernel.h>
41 #include <sys/module.h>
42 #include <sys/gpio.h>
43 
44 #include <dev/ofw/ofw_bus.h>
45 #include <dev/ofw/ofw_bus_subr.h>
46 
47 #include "gpio_if.h"
48 
49 #define	USBPHY_NUMOFF		3
50 #define	GPIO_POLARITY(flags)	(((flags) & 1) ? GPIO_PIN_LOW : GPIO_PIN_HIGH)
51 
52 static struct ofw_compat_data compat_data[] = {
53 	{ "allwinner,sun4i-a10-usb-phy",	1 },
54 	{ "allwinner,sun5i-a13-usb-phy",	1 },
55 	{ "allwinner,sun6i-a31-usb-phy",	1 },
56 	{ "allwinner,sun7i-a20-usb-phy",	1 },
57 	{ NULL,					0 }
58 };
59 
60 static int
61 awusbphy_gpio_set(device_t dev, phandle_t node, const char *pname)
62 {
63 	pcell_t gpio_prop[4];
64 	phandle_t gpio_node;
65 	device_t gpio_dev;
66 	uint32_t pin, flags;
67 	ssize_t len;
68 	int val;
69 
70 	len = OF_getencprop(node, pname, gpio_prop, sizeof(gpio_prop));
71 	if (len == -1)
72 		return (0);
73 
74 	if (len != sizeof(gpio_prop)) {
75 		device_printf(dev, "property %s length was %d, expected %d\n",
76 		    pname, len, sizeof(gpio_prop));
77 		return (ENXIO);
78 	}
79 
80 	gpio_node = OF_node_from_xref(gpio_prop[0]);
81 	gpio_dev = OF_device_from_xref(gpio_prop[0]);
82 	if (gpio_dev == NULL) {
83 		device_printf(dev, "failed to get the GPIO device for %s\n",
84 		    pname);
85 		return (ENOENT);
86 	}
87 
88 	if (GPIO_MAP_GPIOS(gpio_dev, node, gpio_node,
89 	    sizeof(gpio_prop) / sizeof(gpio_prop[0]) - 1, gpio_prop + 1,
90 	    &pin, &flags) != 0) {
91 		device_printf(dev, "failed to map the GPIO pin for %s\n",
92 		    pname);
93 		return (ENXIO);
94 	}
95 
96 	val = GPIO_POLARITY(flags);
97 
98 	GPIO_PIN_SETFLAGS(gpio_dev, pin, GPIO_PIN_OUTPUT);
99 	GPIO_PIN_SET(gpio_dev, pin, val);
100 
101 	return (0);
102 }
103 
104 static int
105 awusbphy_supply_set(device_t dev, const char *pname)
106 {
107 	phandle_t node, reg_node;
108 	pcell_t reg_xref;
109 
110 	node = ofw_bus_get_node(dev);
111 
112 	if (OF_getencprop(node, pname, &reg_xref, sizeof(reg_xref)) == -1)
113 		return (0);
114 
115 	reg_node = OF_node_from_xref(reg_xref);
116 
117 	return (awusbphy_gpio_set(dev, reg_node, "gpio"));
118 }
119 
120 static int
121 awusbphy_init(device_t dev)
122 {
123 	char pname[20];
124 	phandle_t node;
125 	int error, off;
126 
127 	node = ofw_bus_get_node(dev);
128 
129 	for (off = 0; off < USBPHY_NUMOFF; off++) {
130 		snprintf(pname, sizeof(pname), "usb%d_id_det-gpio", off);
131 		error = awusbphy_gpio_set(dev, node, pname);
132 		if (error)
133 			return (error);
134 
135 		snprintf(pname, sizeof(pname), "usb%d_vbus_det-gpio", off);
136 		error = awusbphy_gpio_set(dev, node, pname);
137 		if (error)
138 			return (error);
139 
140 		snprintf(pname, sizeof(pname), "usb%d_vbus-supply", off);
141 		error = awusbphy_supply_set(dev, pname);
142 		if (error)
143 			return (error);
144 	}
145 
146 	return (0);
147 }
148 
149 static int
150 awusbphy_probe(device_t dev)
151 {
152 	if (!ofw_bus_status_okay(dev))
153 		return (ENXIO);
154 
155 	if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0)
156 		return (ENXIO);
157 
158 	device_set_desc(dev, "Allwinner USB PHY");
159 	return (BUS_PROBE_DEFAULT);
160 }
161 
162 static int
163 awusbphy_attach(device_t dev)
164 {
165 	int error;
166 
167 	error = awusbphy_init(dev);
168 	if (error)
169 		device_printf(dev, "failed to initialize USB PHY, error %d\n",
170 		    error);
171 
172 	return (error);
173 }
174 
175 static device_method_t awusbphy_methods[] = {
176 	/* Device interface */
177 	DEVMETHOD(device_probe,		awusbphy_probe),
178 	DEVMETHOD(device_attach,	awusbphy_attach),
179 
180 	DEVMETHOD_END
181 };
182 
183 static driver_t awusbphy_driver = {
184 	"awusbphy",
185 	awusbphy_methods,
186 	0,
187 };
188 
189 static devclass_t awusbphy_devclass;
190 
191 DRIVER_MODULE(awusbphy, simplebus, awusbphy_driver, awusbphy_devclass, 0, 0);
192 MODULE_VERSION(awusbphy, 1);
193