Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/sys/arm64/rockchip/rk_usb2phy.c
39478 views
1
/*-
2
* SPDX-License-Identifier: BSD-2-Clause
3
*
4
* Copyright (c) 2019 Emmanuel Vadot <[email protected]>
5
*
6
* Redistribution and use in source and binary forms, with or without
7
* modification, are permitted provided that the following conditions
8
* are met:
9
* 1. Redistributions of source code must retain the above copyright
10
* notice, this list of conditions and the following disclaimer.
11
* 2. Redistributions in binary form must reproduce the above copyright
12
* notice, this list of conditions and the following disclaimer in the
13
* documentation and/or other materials provided with the distribution.
14
*
15
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25
* SUCH DAMAGE.
26
*/
27
28
/*
29
* Rockchip USB2PHY
30
*/
31
32
#include <sys/param.h>
33
#include <sys/systm.h>
34
#include <sys/bus.h>
35
#include <sys/rman.h>
36
#include <sys/kernel.h>
37
#include <sys/module.h>
38
#include <sys/gpio.h>
39
#include <machine/bus.h>
40
41
#include <dev/fdt/fdt_common.h>
42
#include <dev/ofw/ofw_bus.h>
43
#include <dev/ofw/ofw_bus_subr.h>
44
#include <dev/ofw/ofw_subr.h>
45
46
#include <dev/clk/clk.h>
47
#include <dev/phy/phy_usb.h>
48
#include <dev/regulator/regulator.h>
49
#include <dev/syscon/syscon.h>
50
51
#include "clkdev_if.h"
52
#include "syscon_if.h"
53
54
struct rk_usb2phy_reg {
55
uint32_t offset;
56
uint32_t enable_mask;
57
uint32_t disable_mask;
58
};
59
60
struct rk_usb2phy_regs {
61
struct rk_usb2phy_reg clk_ctl;
62
};
63
64
struct rk_usb2phy_regs rk3399_regs = {
65
.clk_ctl = {
66
.offset = 0x0000,
67
/* bit 4 put pll in suspend */
68
.enable_mask = 0x100000,
69
.disable_mask = 0x100010,
70
}
71
};
72
73
struct rk_usb2phy_regs rk3568_regs = {
74
.clk_ctl = {
75
.offset = 0x0008,
76
.enable_mask = 0x100000,
77
/* bit 4 put pll in suspend */
78
.disable_mask = 0x100010,
79
}
80
};
81
82
static struct ofw_compat_data compat_data[] = {
83
{ "rockchip,rk3399-usb2phy", (uintptr_t)&rk3399_regs },
84
{ "rockchip,rk3568-usb2phy", (uintptr_t)&rk3568_regs },
85
{ NULL, 0 }
86
};
87
88
struct rk_usb2phy_softc {
89
device_t dev;
90
struct syscon *grf;
91
regulator_t phy_supply;
92
clk_t clk;
93
int mode;
94
};
95
96
/* Phy class and methods. */
97
static int rk_usb2phy_enable(struct phynode *phynode, bool enable);
98
static int rk_usb2phy_get_mode(struct phynode *phy, int *mode);
99
static int rk_usb2phy_set_mode(struct phynode *phy, int mode);
100
static phynode_method_t rk_usb2phy_phynode_methods[] = {
101
PHYNODEMETHOD(phynode_enable, rk_usb2phy_enable),
102
PHYNODEMETHOD(phynode_usb_get_mode, rk_usb2phy_get_mode),
103
PHYNODEMETHOD(phynode_usb_set_mode, rk_usb2phy_set_mode),
104
105
PHYNODEMETHOD_END
106
};
107
108
DEFINE_CLASS_1(rk_usb2phy_phynode, rk_usb2phy_phynode_class,
109
rk_usb2phy_phynode_methods,
110
sizeof(struct phynode_usb_sc), phynode_usb_class);
111
112
enum RK_USBPHY {
113
RK_USBPHY_HOST = 0,
114
RK_USBPHY_OTG,
115
};
116
117
static int
118
rk_usb2phy_enable(struct phynode *phynode, bool enable)
119
{
120
struct rk_usb2phy_softc *sc;
121
device_t dev;
122
intptr_t phy;
123
int error;
124
125
dev = phynode_get_device(phynode);
126
phy = phynode_get_id(phynode);
127
sc = device_get_softc(dev);
128
129
if (phy != RK_USBPHY_HOST)
130
return (ERANGE);
131
132
if (sc->phy_supply) {
133
if (enable)
134
error = regulator_enable(sc->phy_supply);
135
else
136
error = regulator_disable(sc->phy_supply);
137
if (error != 0) {
138
device_printf(dev, "Cannot %sable the regulator\n",
139
enable ? "En" : "Dis");
140
goto fail;
141
}
142
}
143
144
return (0);
145
fail:
146
return (ENXIO);
147
}
148
149
static int
150
rk_usb2phy_get_mode(struct phynode *phynode, int *mode)
151
{
152
struct rk_usb2phy_softc *sc;
153
intptr_t phy;
154
device_t dev;
155
156
dev = phynode_get_device(phynode);
157
phy = phynode_get_id(phynode);
158
sc = device_get_softc(dev);
159
160
if (phy != RK_USBPHY_HOST)
161
return (ERANGE);
162
163
*mode = sc->mode;
164
165
return (0);
166
}
167
168
static int
169
rk_usb2phy_set_mode(struct phynode *phynode, int mode)
170
{
171
struct rk_usb2phy_softc *sc;
172
intptr_t phy;
173
device_t dev;
174
175
dev = phynode_get_device(phynode);
176
phy = phynode_get_id(phynode);
177
sc = device_get_softc(dev);
178
179
if (phy != RK_USBPHY_HOST)
180
return (ERANGE);
181
182
sc->mode = mode;
183
184
return (0);
185
}
186
187
/* Clock class and method */
188
struct rk_usb2phy_clk_sc {
189
device_t clkdev;
190
struct syscon *grf;
191
struct rk_usb2phy_regs *regs;
192
};
193
194
static int
195
rk_usb2phy_clk_init(struct clknode *clk, device_t dev)
196
{
197
198
clknode_init_parent_idx(clk, 0);
199
return (0);
200
}
201
202
static int
203
rk_usb2phy_clk_set_gate(struct clknode *clk, bool enable)
204
{
205
struct rk_usb2phy_clk_sc *sc;
206
207
sc = clknode_get_softc(clk);
208
209
if (enable)
210
SYSCON_WRITE_4(sc->grf, sc->regs->clk_ctl.offset,
211
sc->regs->clk_ctl.enable_mask);
212
else
213
SYSCON_WRITE_4(sc->grf, sc->regs->clk_ctl.offset,
214
sc->regs->clk_ctl.disable_mask);
215
return (0);
216
}
217
218
static int
219
rk_usb2phy_clk_recalc(struct clknode *clk, uint64_t *freq)
220
{
221
222
*freq = 480000000;
223
224
return (0);
225
}
226
227
static clknode_method_t rk_usb2phy_clk_clknode_methods[] = {
228
/* Device interface */
229
230
CLKNODEMETHOD(clknode_init, rk_usb2phy_clk_init),
231
CLKNODEMETHOD(clknode_set_gate, rk_usb2phy_clk_set_gate),
232
CLKNODEMETHOD(clknode_recalc_freq, rk_usb2phy_clk_recalc),
233
CLKNODEMETHOD_END
234
};
235
236
DEFINE_CLASS_1(rk_usb2phy_clk_clknode, rk_usb2phy_clk_clknode_class,
237
rk_usb2phy_clk_clknode_methods, sizeof(struct rk_usb2phy_clk_sc),
238
clknode_class);
239
240
static int
241
rk_usb2phy_clk_ofw_map(struct clkdom *clkdom, uint32_t ncells,
242
phandle_t *cells, struct clknode **clk)
243
{
244
245
if (ncells != 0)
246
return (ERANGE);
247
248
*clk = clknode_find_by_id(clkdom, 0);
249
250
if (*clk == NULL)
251
return (ENXIO);
252
return (0);
253
}
254
255
static int
256
rk_usb2phy_export_clock(struct rk_usb2phy_softc *devsc)
257
{
258
struct clknode_init_def def;
259
struct rk_usb2phy_clk_sc *sc;
260
const char **clknames;
261
struct clkdom *clkdom;
262
struct clknode *clk;
263
clk_t clk_parent;
264
phandle_t node;
265
phandle_t regs[2];
266
int i, nclocks, ncells, error;
267
268
node = ofw_bus_get_node(devsc->dev);
269
270
error = ofw_bus_parse_xref_list_get_length(node, "clocks",
271
"#clock-cells", &ncells);
272
if (error != 0 || ncells != 1) {
273
device_printf(devsc->dev, "couldn't find parent clock\n");
274
return (ENXIO);
275
}
276
277
nclocks = ofw_bus_string_list_to_array(node, "clock-output-names",
278
&clknames);
279
if (nclocks != 1)
280
return (ENXIO);
281
282
clkdom = clkdom_create(devsc->dev);
283
clkdom_set_ofw_mapper(clkdom, rk_usb2phy_clk_ofw_map);
284
285
memset(&def, 0, sizeof(def));
286
def.id = 0;
287
def.name = clknames[0];
288
def.parent_names = malloc(sizeof(char *) * ncells, M_OFWPROP, M_WAITOK);
289
for (i = 0; i < ncells; i++) {
290
error = clk_get_by_ofw_index(devsc->dev, 0, i, &clk_parent);
291
if (error != 0) {
292
device_printf(devsc->dev, "cannot get clock %d\n", error);
293
return (ENXIO);
294
}
295
def.parent_names[i] = clk_get_name(clk_parent);
296
clk_release(clk_parent);
297
}
298
def.parent_cnt = ncells;
299
300
clk = clknode_create(clkdom, &rk_usb2phy_clk_clknode_class, &def);
301
if (clk == NULL) {
302
device_printf(devsc->dev, "cannot create clknode\n");
303
return (ENXIO);
304
}
305
306
sc = clknode_get_softc(clk);
307
sc->clkdev = device_get_parent(devsc->dev);
308
sc->grf = devsc->grf;
309
sc->regs = (struct rk_usb2phy_regs *)ofw_bus_search_compatible(devsc->dev, compat_data)->ocd_data;
310
if (sc->regs->clk_ctl.offset == 0) {
311
OF_getencprop(node, "reg", regs, sizeof(regs));
312
sc->regs->clk_ctl.offset = regs[0];
313
}
314
clknode_register(clkdom, clk);
315
316
if (clkdom_finit(clkdom) != 0) {
317
device_printf(devsc->dev, "cannot finalize clkdom initialization\n");
318
return (ENXIO);
319
}
320
321
if (bootverbose)
322
clkdom_dump(clkdom);
323
324
return (0);
325
}
326
327
static int
328
rk_usb2phy_probe(device_t dev)
329
{
330
331
if (!ofw_bus_status_okay(dev))
332
return (ENXIO);
333
334
if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0)
335
return (ENXIO);
336
337
device_set_desc(dev, "Rockchip USB2PHY");
338
return (BUS_PROBE_DEFAULT);
339
}
340
341
static int
342
rk_usb2phy_attach(device_t dev)
343
{
344
struct rk_usb2phy_softc *sc;
345
struct phynode_init_def phy_init;
346
struct phynode *phynode;
347
phandle_t node, host;
348
int err;
349
350
sc = device_get_softc(dev);
351
sc->dev = dev;
352
node = ofw_bus_get_node(dev);
353
354
if (OF_hasprop(node, "rockchip,usbgrf")) {
355
if (syscon_get_by_ofw_property(dev, node, "rockchip,usbgrf",
356
&sc->grf)) {
357
device_printf(dev, "Cannot get syscon handle\n");
358
return (ENXIO);
359
}
360
}
361
else {
362
if (syscon_get_handle_default(dev, &sc->grf)) {
363
device_printf(dev, "Cannot get syscon handle\n");
364
return (ENXIO);
365
}
366
}
367
368
if (clk_get_by_ofw_name(dev, 0, "phyclk", &sc->clk) != 0) {
369
device_printf(dev, "Cannot get clock\n");
370
return (ENXIO);
371
}
372
err = clk_enable(sc->clk);
373
if (err != 0) {
374
device_printf(dev, "Could not enable clock %s\n",
375
clk_get_name(sc->clk));
376
return (ENXIO);
377
}
378
379
err = rk_usb2phy_export_clock(sc);
380
if (err != 0)
381
return (err);
382
383
/* Only host is supported right now */
384
385
host = ofw_bus_find_child(node, "host-port");
386
if (host == 0) {
387
device_printf(dev, "Cannot find host-port child node\n");
388
return (ENXIO);
389
}
390
391
if (!ofw_bus_node_status_okay(host)) {
392
device_printf(dev, "host-port isn't okay\n");
393
return (0);
394
}
395
396
regulator_get_by_ofw_property(dev, host, "phy-supply", &sc->phy_supply);
397
phy_init.id = RK_USBPHY_HOST;
398
phy_init.ofw_node = host;
399
phynode = phynode_create(dev, &rk_usb2phy_phynode_class, &phy_init);
400
if (phynode == NULL) {
401
device_printf(dev, "failed to create host USB2PHY\n");
402
return (ENXIO);
403
}
404
if (phynode_register(phynode) == NULL) {
405
device_printf(dev, "failed to register host USB2PHY\n");
406
return (ENXIO);
407
}
408
409
OF_device_register_xref(OF_xref_from_node(host), dev);
410
411
return (0);
412
}
413
414
static device_method_t rk_usb2phy_methods[] = {
415
/* Device interface */
416
DEVMETHOD(device_probe, rk_usb2phy_probe),
417
DEVMETHOD(device_attach, rk_usb2phy_attach),
418
419
DEVMETHOD_END
420
};
421
422
static driver_t rk_usb2phy_driver = {
423
"rk_usb2phy",
424
rk_usb2phy_methods,
425
sizeof(struct rk_usb2phy_softc)
426
};
427
428
EARLY_DRIVER_MODULE(rk_usb2phy, simplebus, rk_usb2phy_driver, 0, 0,
429
BUS_PASS_SUPPORTDEV + BUS_PASS_ORDER_MIDDLE);
430
MODULE_VERSION(rk_usb2phy, 1);
431
432