xref: /openbsd-src/sys/dev/fdt/rkusbphy.c (revision dcc91c2622318df8f66a9bca2d2864253df1bfc3)
1 /*	$OpenBSD: rkusbphy.c,v 1.5 2024/06/23 10:18:11 kettenis Exp $ */
2 
3 /*
4  * Copyright (c) 2023 David Gwynne <dlg@openbsd.org>
5  *
6  * Permission to use, copy, modify, and distribute this software for any
7  * purpose with or without fee is hereby granted, provided that the above
8  * copyright notice and this permission notice appear in all copies.
9  *
10  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17  */
18 
19 /*
20  * Rockchip USB2PHY with Innosilicon IP
21  */
22 
23 #include <sys/param.h>
24 #include <sys/systm.h>
25 #include <sys/device.h>
26 
27 #include <machine/intr.h>
28 #include <machine/bus.h>
29 #include <machine/fdt.h>
30 
31 #include <dev/ofw/openfirm.h>
32 #include <dev/ofw/ofw_clock.h>
33 #include <dev/ofw/ofw_regulator.h>
34 #include <dev/ofw/ofw_misc.h>
35 #include <dev/ofw/fdt.h>
36 
37 /*
38  * chip stuff
39  */
40 
41 struct rkusbphy_reg {
42 	bus_size_t			r_offs;
43 	unsigned int			r_shift;
44 	uint32_t			r_mask;
45 	uint32_t			r_set;
46 };
47 
48 struct rkusbphy_port_regs {
49 	struct rkusbphy_reg		phy_enable;
50 };
51 
52 struct rkusbphy_regs {
53 	struct rkusbphy_reg		clk_enable;
54 
55 	struct rkusbphy_port_regs	otg;
56 	struct rkusbphy_port_regs	host;
57 };
58 
59 struct rkusbphy_chip {
60 	bus_addr_t			 c_base_addr;
61 	const struct rkusbphy_regs	*c_regs;
62 };
63 
64 /*
65  * RK3568 has two USB2PHY nodes that have a GRF each. Each GRF has
66  * the same register layout.
67  */
68 
69 static const struct rkusbphy_regs rkusbphy_rk3568_regs = {
70 	/*				shift,	mask,	set */
71 	.clk_enable =	{ 0x0008,	4,	0x1,	0x0 },
72 
73 	.otg = {
74 		.phy_enable =	{ 0x0000,	0,	0x1ff,	0x1d2 },
75 	},
76 
77 	.host = {
78 		.phy_enable =	{ 0x0004,	0,	0x1ff,	0x1d2 },
79 	},
80 };
81 
82 static const struct rkusbphy_chip rkusbphy_rk3568[] = {
83 	{
84 		.c_base_addr = 0xfe8a0000,
85 		.c_regs = &rkusbphy_rk3568_regs,
86 	},
87 	{
88 		.c_base_addr = 0xfe8b0000,
89 		.c_regs = &rkusbphy_rk3568_regs,
90 	},
91 };
92 
93 static const struct rkusbphy_regs rkusbphy_rk3588_regs = {
94 	/*				shift,	mask,	set */
95 	.clk_enable =	{ 0x0000,	0,	0x1,	0x0 },
96 
97 	.otg = {
98 		.phy_enable =	{ 0x000c,	11,	0x1,	0x0 },
99 	},
100 
101 	.host = {
102 		.phy_enable =	{ 0x0008,	2,	0x1,	0x0 },
103 	},
104 };
105 
106 static const struct rkusbphy_chip rkusbphy_rk3588[] = {
107 	{
108 		.c_base_addr = 0x0000,
109 		.c_regs = &rkusbphy_rk3588_regs,
110 	},
111 	{
112 		.c_base_addr = 0x4000,
113 		.c_regs = &rkusbphy_rk3588_regs,
114 	},
115 	{
116 		.c_base_addr = 0x8000,
117 		.c_regs = &rkusbphy_rk3588_regs,
118 	},
119 	{
120 		.c_base_addr = 0xc000,
121 		.c_regs = &rkusbphy_rk3588_regs,
122 	},
123 };
124 
125 /*
126  * driver stuff
127  */
128 
129 struct rkusbphy_softc {
130 	struct device			 sc_dev;
131 	const struct rkusbphy_regs	*sc_regs;
132 	struct regmap			*sc_grf;
133 	int				 sc_node;
134 
135 	int				 sc_running;
136 
137 	struct phy_device		 sc_otg_phy;
138 	struct phy_device		 sc_host_phy;
139 };
140 #define DEVNAME(_sc) ((_sc)->sc_dev.dv_xname)
141 
142 static int		rkusbphy_match(struct device *, void *, void *);
143 static void		rkusbphy_attach(struct device *, struct device *,
144 			    void *);
145 
146 static uint32_t		rkusbphy_rd(struct rkusbphy_softc *,
147 			    const struct rkusbphy_reg *);
148 static int		rkusbphy_isset(struct rkusbphy_softc *,
149 			    const struct rkusbphy_reg *);
150 static void		rkusbphy_wr(struct rkusbphy_softc *,
151 			    const struct rkusbphy_reg *, uint32_t);
152 static void		rkusbphy_set(struct rkusbphy_softc *,
153 			    const struct rkusbphy_reg *);
154 
155 static int		rkusbphy_otg_phy_enable(void *, uint32_t *);
156 static int		rkusbphy_host_phy_enable(void *, uint32_t *);
157 
158 struct rkusbphy_port_config {
159 	const char			*pc_name;
160 	int (*pc_enable)(void *, uint32_t *);
161 };
162 
163 static void	rkusbphy_register(struct rkusbphy_softc *,
164 		    struct phy_device *, const struct rkusbphy_port_config *);
165 
166 static const struct rkusbphy_port_config rkusbphy_otg_config = {
167 	.pc_name = "otg-port",
168 	.pc_enable = rkusbphy_otg_phy_enable,
169 };
170 
171 static const struct rkusbphy_port_config rkusbphy_host_config = {
172 	.pc_name = "host-port",
173 	.pc_enable = rkusbphy_host_phy_enable,
174 };
175 
176 const struct cfattach rkusbphy_ca = {
177 	sizeof (struct rkusbphy_softc), rkusbphy_match, rkusbphy_attach
178 };
179 
180 struct cfdriver rkusbphy_cd = {
181 	NULL, "rkusbphy", DV_DULL
182 };
183 
184 struct rkusbphy_id {
185 	const char			*id_name;
186 	const struct rkusbphy_chip	*id_chips;
187 	size_t				 id_nchips;
188 };
189 
190 #define RKUSBPHY_ID(_n, _c) { _n, _c, nitems(_c) }
191 
192 static const struct rkusbphy_id rkusbphy_ids[] = {
193 	RKUSBPHY_ID("rockchip,rk3568-usb2phy", rkusbphy_rk3568),
194 	RKUSBPHY_ID("rockchip,rk3588-usb2phy", rkusbphy_rk3588),
195 };
196 
197 static const struct rkusbphy_id *
198 rkusbphy_lookup(struct fdt_attach_args *faa)
199 {
200 	size_t i;
201 
202 	for (i = 0; i < nitems(rkusbphy_ids); i++) {
203 		const struct rkusbphy_id *id = &rkusbphy_ids[i];
204 		if (OF_is_compatible(faa->fa_node, id->id_name))
205 			return (id);
206 	}
207 
208 	return (NULL);
209 }
210 
211 static int
212 rkusbphy_match(struct device *parent, void *match, void *aux)
213 {
214 	struct fdt_attach_args *faa = aux;
215 
216 	return (rkusbphy_lookup(faa) != NULL ? 1 : 0);
217 }
218 
219 static void
220 rkusbphy_attach(struct device *parent, struct device *self, void *aux)
221 {
222 	struct rkusbphy_softc *sc = (struct rkusbphy_softc *)self;
223 	struct fdt_attach_args *faa = aux;
224 	const struct rkusbphy_id *id = rkusbphy_lookup(faa);
225 	size_t i;
226 	uint32_t grfph;
227 
228 	if (faa->fa_nreg < 1) {
229 		printf(": no registers\n");
230 		return;
231 	}
232 
233 	for (i = 0; i < id->id_nchips; i++) {
234 		const struct rkusbphy_chip *c = &id->id_chips[i];
235 		if (faa->fa_reg[0].addr == c->c_base_addr) {
236 			printf(": phy %zu\n", i);
237 			sc->sc_regs = c->c_regs;
238 			break;
239 		}
240 	}
241 	if (sc->sc_regs == NULL) {
242 		printf(": unknown base address 0x%llu\n", faa->fa_reg[0].addr);
243 		return;
244 	}
245 
246 	sc->sc_node = faa->fa_node;
247 
248 	grfph = OF_getpropint(sc->sc_node, "rockchip,usbgrf", 0);
249 	if (grfph)
250 		sc->sc_grf = regmap_byphandle(grfph);
251 	else
252 		sc->sc_grf = regmap_bynode(OF_parent(faa->fa_node));
253 	if (sc->sc_grf == NULL) {
254 		printf("%s: rockchip,usbgrf 0x%x not found\n", DEVNAME(sc),
255 		    grfph);
256 		return;
257 	}
258 
259 	rkusbphy_register(sc, &sc->sc_otg_phy, &rkusbphy_otg_config);
260 	rkusbphy_register(sc, &sc->sc_host_phy, &rkusbphy_host_config);
261 }
262 
263 static uint32_t
264 rkusbphy_rd(struct rkusbphy_softc *sc, const struct rkusbphy_reg *r)
265 {
266 	uint32_t v;
267 
268 	if (r->r_mask == 0)
269 		return (0);
270 
271 	v = regmap_read_4(sc->sc_grf, r->r_offs);
272 
273 	return ((v >> r->r_shift) & r->r_mask);
274 }
275 
276 static int
277 rkusbphy_isset(struct rkusbphy_softc *sc, const struct rkusbphy_reg *r)
278 {
279 	return (rkusbphy_rd(sc, r) == r->r_set);
280 }
281 
282 static void
283 rkusbphy_wr(struct rkusbphy_softc *sc, const struct rkusbphy_reg *r, uint32_t v)
284 {
285 	if (r->r_mask == 0)
286 		return;
287 
288 	regmap_write_4(sc->sc_grf, r->r_offs,
289 	    (r->r_mask << (r->r_shift + 16)) | (v << r->r_shift));
290 }
291 
292 static void
293 rkusbphy_set(struct rkusbphy_softc *sc, const struct rkusbphy_reg *r)
294 {
295 	rkusbphy_wr(sc, r, r->r_set);
296 }
297 
298 static void
299 rkusbphy_register(struct rkusbphy_softc *sc, struct phy_device *pd,
300     const struct rkusbphy_port_config *pc)
301 {
302 	char status[32];
303 	int node;
304 
305 	node = OF_getnodebyname(sc->sc_node, pc->pc_name);
306 	if (node == 0)
307 		return;
308 
309 	if (OF_getprop(node, "status", status, sizeof(status)) > 0 &&
310 	    strcmp(status, "disabled") == 0)
311 		return;
312 
313 	pd->pd_node = node;
314 	pd->pd_cookie = sc;
315 	pd->pd_enable = pc->pc_enable;
316 	phy_register(pd);
317 }
318 
319 static void
320 rkusbphy_phy_supply(struct rkusbphy_softc *sc, int node)
321 {
322 	int phandle;
323 
324 	if (!sc->sc_running) {
325 		clock_enable(sc->sc_node, "phyclk");
326 		if (!rkusbphy_isset(sc, &sc->sc_regs->clk_enable)) {
327 			rkusbphy_set(sc, &sc->sc_regs->clk_enable);
328 
329 			delay(1200);
330 		}
331 
332 		sc->sc_running = 1;
333 	}
334 
335 	phandle = OF_getpropint(node, "phy-supply", 0);
336 	if (phandle == 0)
337 		return;
338 
339 	regulator_enable(phandle);
340 }
341 
342 static int
343 rkusbphy_otg_phy_enable(void *cookie, uint32_t *cells)
344 {
345 	struct rkusbphy_softc *sc = cookie;
346 
347 	rkusbphy_phy_supply(sc, sc->sc_otg_phy.pd_node);
348 
349 	rkusbphy_set(sc, &sc->sc_regs->otg.phy_enable);
350 	delay(1500);
351 
352 	return (EINVAL);
353 }
354 
355 static int
356 rkusbphy_host_phy_enable(void *cookie, uint32_t *cells)
357 {
358 	struct rkusbphy_softc *sc = cookie;
359 
360 	rkusbphy_phy_supply(sc, sc->sc_host_phy.pd_node);
361 
362 	rkusbphy_set(sc, &sc->sc_regs->host.phy_enable);
363 	delay(1500);
364 
365 	return (EINVAL);
366 }
367