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
awusb3phy_phy_enable(struct phynode * phynode,bool enable)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
awusb3phy_get_mode(struct phynode * phynode,int * mode)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
awusb3phy_set_mode(struct phynode * phynode,int mode)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
awusb3phy_probe(device_t dev)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
awusb3phy_attach(device_t dev)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