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
41struct 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
48struct rkusbphy_port_regs {
49	struct rkusbphy_reg		phy_enable;
50};
51
52struct rkusbphy_regs {
53	struct rkusbphy_reg		clk_enable;
54
55	struct rkusbphy_port_regs	otg;
56	struct rkusbphy_port_regs	host;
57};
58
59struct 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
69static 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
82static 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
93static 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
106static 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
129struct 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
142static int		rkusbphy_match(struct device *, void *, void *);
143static void		rkusbphy_attach(struct device *, struct device *,
144			    void *);
145
146static uint32_t		rkusbphy_rd(struct rkusbphy_softc *,
147			    const struct rkusbphy_reg *);
148static int		rkusbphy_isset(struct rkusbphy_softc *,
149			    const struct rkusbphy_reg *);
150static void		rkusbphy_wr(struct rkusbphy_softc *,
151			    const struct rkusbphy_reg *, uint32_t);
152static void		rkusbphy_set(struct rkusbphy_softc *,
153			    const struct rkusbphy_reg *);
154
155static int		rkusbphy_otg_phy_enable(void *, uint32_t *);
156static int		rkusbphy_host_phy_enable(void *, uint32_t *);
157
158struct rkusbphy_port_config {
159	const char			*pc_name;
160	int (*pc_enable)(void *, uint32_t *);
161};
162
163static void	rkusbphy_register(struct rkusbphy_softc *,
164		    struct phy_device *, const struct rkusbphy_port_config *);
165
166static const struct rkusbphy_port_config rkusbphy_otg_config = {
167	.pc_name = "otg-port",
168	.pc_enable = rkusbphy_otg_phy_enable,
169};
170
171static const struct rkusbphy_port_config rkusbphy_host_config = {
172	.pc_name = "host-port",
173	.pc_enable = rkusbphy_host_phy_enable,
174};
175
176const struct cfattach rkusbphy_ca = {
177	sizeof (struct rkusbphy_softc), rkusbphy_match, rkusbphy_attach
178};
179
180struct cfdriver rkusbphy_cd = {
181	NULL, "rkusbphy", DV_DULL
182};
183
184struct 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
192static const struct rkusbphy_id rkusbphy_ids[] = {
193	RKUSBPHY_ID("rockchip,rk3568-usb2phy", rkusbphy_rk3568),
194	RKUSBPHY_ID("rockchip,rk3588-usb2phy", rkusbphy_rk3588),
195};
196
197static const struct rkusbphy_id *
198rkusbphy_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
211static int
212rkusbphy_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
219static void
220rkusbphy_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
263static uint32_t
264rkusbphy_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
276static int
277rkusbphy_isset(struct rkusbphy_softc *sc, const struct rkusbphy_reg *r)
278{
279	return (rkusbphy_rd(sc, r) == r->r_set);
280}
281
282static void
283rkusbphy_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
292static void
293rkusbphy_set(struct rkusbphy_softc *sc, const struct rkusbphy_reg *r)
294{
295	rkusbphy_wr(sc, r, r->r_set);
296}
297
298static void
299rkusbphy_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
319static void
320rkusbphy_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
342static int
343rkusbphy_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
355static int
356rkusbphy_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