xref: /openbsd-src/sys/dev/fdt/rkusbphy.c (revision 68dd5bb1859285b71cb62a10bf107b8ad54064d9)
1 /*	$OpenBSD: rkusbphy.c,v 1.4 2023/09/29 17:30:35 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 /*
94  * driver stuff
95  */
96 
97 struct rkusbphy_softc {
98 	struct device			 sc_dev;
99 	const struct rkusbphy_regs	*sc_regs;
100 	struct regmap			*sc_grf;
101 	int				 sc_node;
102 
103 	int				 sc_running;
104 
105 	struct phy_device		 sc_otg_phy;
106 	struct phy_device		 sc_host_phy;
107 };
108 #define DEVNAME(_sc) ((_sc)->sc_dev.dv_xname)
109 
110 static int		rkusbphy_match(struct device *, void *, void *);
111 static void		rkusbphy_attach(struct device *, struct device *,
112 			    void *);
113 
114 static uint32_t		rkusbphy_rd(struct rkusbphy_softc *,
115 			    const struct rkusbphy_reg *);
116 static int		rkusbphy_isset(struct rkusbphy_softc *,
117 			    const struct rkusbphy_reg *);
118 static void		rkusbphy_wr(struct rkusbphy_softc *,
119 			    const struct rkusbphy_reg *, uint32_t);
120 static void		rkusbphy_set(struct rkusbphy_softc *,
121 			    const struct rkusbphy_reg *);
122 
123 static int		rkusbphy_otg_phy_enable(void *, uint32_t *);
124 static int		rkusbphy_host_phy_enable(void *, uint32_t *);
125 
126 struct rkusbphy_port_config {
127 	const char			*pc_name;
128 	int (*pc_enable)(void *, uint32_t *);
129 };
130 
131 static void	rkusbphy_register(struct rkusbphy_softc *,
132 		    struct phy_device *, const struct rkusbphy_port_config *);
133 
134 static const struct rkusbphy_port_config rkusbphy_otg_config = {
135 	.pc_name = "otg-port",
136 	.pc_enable = rkusbphy_otg_phy_enable,
137 };
138 
139 static const struct rkusbphy_port_config rkusbphy_host_config = {
140 	.pc_name = "host-port",
141 	.pc_enable = rkusbphy_host_phy_enable,
142 };
143 
144 const struct cfattach rkusbphy_ca = {
145 	sizeof (struct rkusbphy_softc), rkusbphy_match, rkusbphy_attach
146 };
147 
148 struct cfdriver rkusbphy_cd = {
149 	NULL, "rkusbphy", DV_DULL
150 };
151 
152 struct rkusbphy_id {
153 	const char			*id_name;
154 	const struct rkusbphy_chip	*id_chips;
155 	size_t				 id_nchips;
156 };
157 
158 #define RKUSBPHY_ID(_n, _c) { _n, _c, nitems(_c) }
159 
160 static const struct rkusbphy_id rkusbphy_ids[] = {
161 	RKUSBPHY_ID("rockchip,rk3568-usb2phy", rkusbphy_rk3568),
162 };
163 
164 static const struct rkusbphy_id *
165 rkusbphy_lookup(struct fdt_attach_args *faa)
166 {
167 	size_t i;
168 
169 	for (i = 0; i < nitems(rkusbphy_ids); i++) {
170 		const struct rkusbphy_id *id = &rkusbphy_ids[i];
171 		if (OF_is_compatible(faa->fa_node, id->id_name))
172 			return (id);
173 	}
174 
175 	return (NULL);
176 }
177 
178 static int
179 rkusbphy_match(struct device *parent, void *match, void *aux)
180 {
181 	struct fdt_attach_args *faa = aux;
182 
183 	return (rkusbphy_lookup(faa) != NULL ? 1 : 0);
184 }
185 
186 static void
187 rkusbphy_attach(struct device *parent, struct device *self, void *aux)
188 {
189 	struct rkusbphy_softc *sc = (struct rkusbphy_softc *)self;
190 	struct fdt_attach_args *faa = aux;
191 	const struct rkusbphy_id *id = rkusbphy_lookup(faa);
192 	size_t i;
193 	uint32_t grfph;
194 
195 	if (faa->fa_nreg < 1) {
196 		printf(": no registers\n");
197 		return;
198 	}
199 
200 	for (i = 0; i < id->id_nchips; i++) {
201 		const struct rkusbphy_chip *c = &id->id_chips[i];
202 		if (faa->fa_reg[0].addr == c->c_base_addr) {
203 			printf(": phy %zu\n", i);
204 			sc->sc_regs = c->c_regs;
205 			break;
206 		}
207 	}
208 	if (sc->sc_regs == NULL) {
209 		printf(": unknown base address 0x%llu\n", faa->fa_reg[0].addr);
210 		return;
211 	}
212 
213 	sc->sc_node = faa->fa_node;
214 
215 	grfph = OF_getpropint(sc->sc_node, "rockchip,usbgrf", 0);
216 	sc->sc_grf = regmap_byphandle(grfph);
217 	if (sc->sc_grf == NULL) {
218 		printf("%s: rockchip,usbgrf 0x%x not found\n", DEVNAME(sc),
219 		    grfph);
220 		return;
221 	}
222 
223 	rkusbphy_register(sc, &sc->sc_otg_phy, &rkusbphy_otg_config);
224 	rkusbphy_register(sc, &sc->sc_host_phy, &rkusbphy_host_config);
225 }
226 
227 static uint32_t
228 rkusbphy_rd(struct rkusbphy_softc *sc, const struct rkusbphy_reg *r)
229 {
230 	uint32_t v;
231 
232 	if (r->r_mask == 0)
233 		return (0);
234 
235 	v = regmap_read_4(sc->sc_grf, r->r_offs);
236 
237 	return ((v >> r->r_shift) & r->r_mask);
238 }
239 
240 static int
241 rkusbphy_isset(struct rkusbphy_softc *sc, const struct rkusbphy_reg *r)
242 {
243 	return (rkusbphy_rd(sc, r) == r->r_set);
244 }
245 
246 static void
247 rkusbphy_wr(struct rkusbphy_softc *sc, const struct rkusbphy_reg *r, uint32_t v)
248 {
249 	if (r->r_mask == 0)
250 		return;
251 
252 	regmap_write_4(sc->sc_grf, r->r_offs,
253 	    (r->r_mask << (r->r_shift + 16)) | (v << r->r_shift));
254 }
255 
256 static void
257 rkusbphy_set(struct rkusbphy_softc *sc, const struct rkusbphy_reg *r)
258 {
259 	rkusbphy_wr(sc, r, r->r_set);
260 }
261 
262 static void
263 rkusbphy_register(struct rkusbphy_softc *sc, struct phy_device *pd,
264     const struct rkusbphy_port_config *pc)
265 {
266 	char status[32];
267 	int node;
268 
269 	node = OF_getnodebyname(sc->sc_node, pc->pc_name);
270 	if (node == 0) {
271 		printf("%s: cannot find %s\n", DEVNAME(sc), pc->pc_name);
272 		return;
273 	}
274 
275 	if (OF_getprop(node, "status", status, sizeof(status)) > 0 &&
276 	    strcmp(status, "disabled") == 0)
277 		return;
278 
279 	pd->pd_node = node;
280 	pd->pd_cookie = sc;
281 	pd->pd_enable = pc->pc_enable;
282 	phy_register(pd);
283 }
284 
285 static void
286 rkusbphy_phy_supply(struct rkusbphy_softc *sc, int node)
287 {
288 	int phandle;
289 
290 	if (!sc->sc_running) {
291 		clock_enable(sc->sc_node, "phyclk");
292 		if (!rkusbphy_isset(sc, &sc->sc_regs->clk_enable)) {
293 			rkusbphy_set(sc, &sc->sc_regs->clk_enable);
294 
295 			delay(1200);
296 		}
297 
298 		sc->sc_running = 1;
299 	}
300 
301 	phandle = OF_getpropint(node, "phy-supply", 0);
302 	if (phandle == 0)
303 		return;
304 
305 	regulator_enable(phandle);
306 }
307 
308 static int
309 rkusbphy_otg_phy_enable(void *cookie, uint32_t *cells)
310 {
311 	struct rkusbphy_softc *sc = cookie;
312 
313 	rkusbphy_phy_supply(sc, sc->sc_otg_phy.pd_node);
314 
315 	rkusbphy_set(sc, &sc->sc_regs->otg.phy_enable);
316 	delay(1500);
317 
318 	return (EINVAL);
319 }
320 
321 static int
322 rkusbphy_host_phy_enable(void *cookie, uint32_t *cells)
323 {
324 	struct rkusbphy_softc *sc = cookie;
325 
326 	rkusbphy_phy_supply(sc, sc->sc_host_phy.pd_node);
327 
328 	rkusbphy_set(sc, &sc->sc_regs->host.phy_enable);
329 	delay(1500);
330 
331 	return (EINVAL);
332 }
333