Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/sys/arm/allwinner/aw_usb3phy.c
39507 views
1
/* $NetBSD: sunxi_usb3phy.c,v 1.1 2018/05/01 23:59:42 jmcneill Exp $ */
2
3
/*-
4
* Copyright (c) 2018 Jared McNeill <[email protected]>
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
296