xref: /freebsd/sys/arm/allwinner/aw_usb3phy.c (revision 7fdf597e96a02165cfe22ff357b857d5fa15ed8a)
1 /* $NetBSD: sunxi_usb3phy.c,v 1.1 2018/05/01 23:59:42 jmcneill Exp $ */
2 
3 /*-
4  * Copyright (c) 2018 Jared McNeill <jmcneill@invisible.ca>
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
17  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
18  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
20  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26  * POSSIBILITY OF SUCH DAMAGE.
27  */
28 
29 /*
30  * Allwinner USB3PHY
31  */
32 
33 #include <sys/param.h>
34 #include <sys/systm.h>
35 #include <sys/bus.h>
36 #include <sys/rman.h>
37 #include <sys/kernel.h>
38 #include <sys/module.h>
39 #include <sys/gpio.h>
40 #include <machine/bus.h>
41 
42 #include <dev/ofw/ofw_bus.h>
43 #include <dev/ofw/ofw_bus_subr.h>
44 
45 #include <dev/clk/clk.h>
46 #include <dev/hwreset/hwreset.h>
47 #include <dev/regulator/regulator.h>
48 #include <dev/phy/phy_usb.h>
49 
50 #include "phynode_if.h"
51 
52 #define	USB3PHY_APP			0x00
53 #define	 APP_FORCE_VBUS			(0x3 << 12)
54 
55 #define	USB3PHY_PIPE_CLOCK_CONTROL	0x14
56 #define	 PCC_PIPE_CLK_OPEN		(1 << 6)
57 
58 #define	USB3PHY_PHY_TUNE_LOW		0x18
59 #define	 PTL_MAGIC			0x0047fc87
60 
61 #define	USB3PHY_PHY_TUNE_HIGH		0x1c
62 #define	 PTH_TX_DEEMPH_3P5DB		(0x1F << 19)
63 #define	 PTH_TX_DEEMPH_6DB		(0x3F << 13)
64 #define	 PTH_TX_SWING_FULL		(0x7F << 6)
65 #define	 PTH_LOS_BIAS			(0x7 << 3)
66 #define	 PTH_TX_BOOST_LVL		(0x7 << 0)
67 
68 #define	USB3PHY_PHY_EXTERNAL_CONTROL	0x20
69 #define	 PEC_REF_SSP_EN			(1 << 26)
70 #define	 PEC_SSC_EN			(1 << 24)
71 #define	 PEC_EXTERN_VBUS		(0x3 << 1)
72 
73 #define __LOWEST_SET_BIT(__mask) ((((__mask) - 1) & (__mask)) ^ (__mask))
74 #define __SHIFTIN(__x, __mask) ((__x) * __LOWEST_SET_BIT(__mask))
75 
76 static struct ofw_compat_data compat_data[] = {
77 	{ "allwinner,sun50i-h6-usb3-phy",	1 },
78 	{ NULL,					0 }
79 };
80 
81 static struct resource_spec aw_usb3phy_spec[] = {
82 	{ SYS_RES_MEMORY,	0,	RF_ACTIVE },
83 	{ -1, 0 }
84 };
85 
86 struct awusb3phy_softc {
87 	struct resource *	res;
88 	regulator_t		reg;
89 	int			mode;
90 };
91 
92  /* Phy class and methods. */
93 static int awusb3phy_phy_enable(struct phynode *phy, bool enable);
94 static int awusb3phy_get_mode(struct phynode *phy, int *mode);
95 static int awusb3phy_set_mode(struct phynode *phy, int mode);
96 static phynode_usb_method_t awusb3phy_phynode_methods[] = {
97 	PHYNODEMETHOD(phynode_enable, awusb3phy_phy_enable),
98 	PHYNODEMETHOD(phynode_usb_get_mode, awusb3phy_get_mode),
99 	PHYNODEMETHOD(phynode_usb_set_mode, awusb3phy_set_mode),
100 
101 	PHYNODEMETHOD_END
102 };
103 DEFINE_CLASS_1(awusb3phy_phynode, awusb3phy_phynode_class, awusb3phy_phynode_methods,
104   sizeof(struct phynode_usb_sc), phynode_usb_class);
105 
106 #define	RD4(res, o)	bus_read_4(res, (o))
107 #define	WR4(res, o, v)	bus_write_4(res, (o), (v))
108 
109 static int
110 awusb3phy_phy_enable(struct phynode *phynode, bool enable)
111 {
112 	struct awusb3phy_softc *sc;
113 	device_t dev;
114 	uint32_t val;
115 	int error = 0;
116 
117 	dev = phynode_get_device(phynode);
118 	sc = device_get_softc(dev);
119 
120 	device_printf(dev, "%s: called\n", __func__);
121 
122 	if (enable) {
123 		val = RD4(sc->res, USB3PHY_PHY_EXTERNAL_CONTROL);
124 		device_printf(dev, "EXTERNAL_CONTROL: %x\n", val);
125 		val |= PEC_EXTERN_VBUS;
126 		val |= PEC_SSC_EN;
127 		val |= PEC_REF_SSP_EN;
128 		device_printf(dev, "EXTERNAL_CONTROL: %x\n", val);
129 		WR4(sc->res, USB3PHY_PHY_EXTERNAL_CONTROL, val);
130 
131 		val = RD4(sc->res, USB3PHY_PIPE_CLOCK_CONTROL);
132 		device_printf(dev, "PIPE_CONTROL: %x\n", val);
133 		val |= PCC_PIPE_CLK_OPEN;
134 		device_printf(dev, "PIPE_CONTROL: %x\n", val);
135 		WR4(sc->res, USB3PHY_PIPE_CLOCK_CONTROL, val);
136 
137 		val = RD4(sc->res, USB3PHY_APP);
138 		device_printf(dev, "APP: %x\n", val);
139 		val |= APP_FORCE_VBUS;
140 		device_printf(dev, "APP: %x\n", val);
141 		WR4(sc->res, USB3PHY_APP, val);
142 
143 		WR4(sc->res, USB3PHY_PHY_TUNE_LOW, PTL_MAGIC);
144 
145 		val = RD4(sc->res, USB3PHY_PHY_TUNE_HIGH);
146 		device_printf(dev, "PHY_TUNE_HIGH: %x\n", val);
147 		val |= PTH_TX_BOOST_LVL;
148 		val |= PTH_LOS_BIAS;
149 		val &= ~PTH_TX_SWING_FULL;
150 		val |= __SHIFTIN(0x55, PTH_TX_SWING_FULL);
151 		val &= ~PTH_TX_DEEMPH_6DB;
152 		val |= __SHIFTIN(0x20, PTH_TX_DEEMPH_6DB);
153 		val &= ~PTH_TX_DEEMPH_3P5DB;
154 		val |= __SHIFTIN(0x15, PTH_TX_DEEMPH_3P5DB);
155 		device_printf(dev, "PHY_TUNE_HIGH: %x\n", val);
156 		WR4(sc->res, USB3PHY_PHY_TUNE_HIGH, val);
157 
158 		if (sc->reg)
159 			error = regulator_enable(sc->reg);
160 	} else {
161 		if (sc->reg)
162 			error = regulator_disable(sc->reg);
163 	}
164 
165 	if (error != 0) {
166 		device_printf(dev,
167 		    "couldn't %s regulator for phy\n",
168 		    enable ? "enable" : "disable");
169 		return (error);
170 	}
171 
172 	return (0);
173 }
174 
175 static int
176 awusb3phy_get_mode(struct phynode *phynode, int *mode)
177 {
178 	struct awusb3phy_softc *sc;
179 	device_t dev;
180 
181 	dev = phynode_get_device(phynode);
182 	sc = device_get_softc(dev);
183 
184 	*mode = sc->mode;
185 
186 	return (0);
187 }
188 
189 static int
190 awusb3phy_set_mode(struct phynode *phynode, int mode)
191 {
192 	device_t dev;
193 	struct awusb3phy_softc *sc;
194 
195 	dev = phynode_get_device(phynode);
196 	sc = device_get_softc(dev);
197 
198 	if (mode != PHY_USB_MODE_HOST)
199 		return (EINVAL);
200 
201 	sc->mode = mode;
202 
203 	return (0);
204 }
205 
206 static int
207 awusb3phy_probe(device_t dev)
208 {
209 	if (!ofw_bus_status_okay(dev))
210 		return (ENXIO);
211 
212 	if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0)
213 		return (ENXIO);
214 
215 	device_set_desc(dev, "Allwinner USB3PHY");
216 	return (BUS_PROBE_DEFAULT);
217 }
218 
219 static int
220 awusb3phy_attach(device_t dev)
221 {
222 	struct phynode *phynode;
223 	struct phynode_init_def phy_init;
224 	struct awusb3phy_softc *sc;
225 	clk_t clk;
226 	hwreset_t rst;
227 	phandle_t node;
228 	int error, i;
229 
230 	sc = device_get_softc(dev);
231 	node = ofw_bus_get_node(dev);
232 
233 	if (bus_alloc_resources(dev, aw_usb3phy_spec, &sc->res) != 0) {
234 		device_printf(dev, "cannot allocate resources for device\n");
235 		return (ENXIO);
236 	}
237 
238 	/* Enable clocks */
239 	for (i = 0; clk_get_by_ofw_index(dev, 0, i, &clk) == 0; i++) {
240 		error = clk_enable(clk);
241 		if (error != 0) {
242 			device_printf(dev, "couldn't enable clock %s\n",
243 			    clk_get_name(clk));
244 			return (error);
245 		}
246 	}
247 
248 	/* De-assert resets */
249 	for (i = 0; hwreset_get_by_ofw_idx(dev, 0, i, &rst) == 0; i++) {
250 		error = hwreset_deassert(rst);
251 		if (error != 0) {
252 			device_printf(dev, "couldn't de-assert reset %d\n",
253 			    i);
254 			return (error);
255 		}
256 	}
257 
258 	/* Get regulators */
259 	regulator_get_by_ofw_property(dev, node, "phy-supply", &sc->reg);
260 
261 	/* Create the phy */
262 	phy_init.ofw_node = ofw_bus_get_node(dev);
263 	phynode = phynode_create(dev, &awusb3phy_phynode_class,
264 	    &phy_init);
265 	if (phynode == NULL) {
266 		device_printf(dev, "failed to create USB PHY\n");
267 		return (ENXIO);
268 	}
269 	if (phynode_register(phynode) == NULL) {
270 		device_printf(dev, "failed to create USB PHY\n");
271 		return (ENXIO);
272 	}
273 
274 	return (error);
275 }
276 
277 static device_method_t awusb3phy_methods[] = {
278 	/* Device interface */
279 	DEVMETHOD(device_probe,		awusb3phy_probe),
280 	DEVMETHOD(device_attach,	awusb3phy_attach),
281 
282 	DEVMETHOD_END
283 };
284 
285 static driver_t awusb3phy_driver = {
286 	"awusb3phy",
287 	awusb3phy_methods,
288 	sizeof(struct awusb3phy_softc)
289 };
290 
291 /* aw_usb3phy needs to come up after regulators/gpio/etc, but before ehci/ohci */
292 EARLY_DRIVER_MODULE(awusb3phy, simplebus, awusb3phy_driver, 0, 0,
293     BUS_PASS_SUPPORTDEV + BUS_PASS_ORDER_MIDDLE);
294 MODULE_VERSION(awusb3phy, 1);
295