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
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
93/*
94 * driver stuff
95 */
96
97struct 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
110static int		rkusbphy_match(struct device *, void *, void *);
111static void		rkusbphy_attach(struct device *, struct device *,
112			    void *);
113
114static uint32_t		rkusbphy_rd(struct rkusbphy_softc *,
115			    const struct rkusbphy_reg *);
116static int		rkusbphy_isset(struct rkusbphy_softc *,
117			    const struct rkusbphy_reg *);
118static void		rkusbphy_wr(struct rkusbphy_softc *,
119			    const struct rkusbphy_reg *, uint32_t);
120static void		rkusbphy_set(struct rkusbphy_softc *,
121			    const struct rkusbphy_reg *);
122
123static int		rkusbphy_otg_phy_enable(void *, uint32_t *);
124static int		rkusbphy_host_phy_enable(void *, uint32_t *);
125
126struct rkusbphy_port_config {
127	const char			*pc_name;
128	int (*pc_enable)(void *, uint32_t *);
129};
130
131static void	rkusbphy_register(struct rkusbphy_softc *,
132		    struct phy_device *, const struct rkusbphy_port_config *);
133
134static const struct rkusbphy_port_config rkusbphy_otg_config = {
135	.pc_name = "otg-port",
136	.pc_enable = rkusbphy_otg_phy_enable,
137};
138
139static const struct rkusbphy_port_config rkusbphy_host_config = {
140	.pc_name = "host-port",
141	.pc_enable = rkusbphy_host_phy_enable,
142};
143
144const struct cfattach rkusbphy_ca = {
145	sizeof (struct rkusbphy_softc), rkusbphy_match, rkusbphy_attach
146};
147
148struct cfdriver rkusbphy_cd = {
149	NULL, "rkusbphy", DV_DULL
150};
151
152struct 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
160static const struct rkusbphy_id rkusbphy_ids[] = {
161	RKUSBPHY_ID("rockchip,rk3568-usb2phy", rkusbphy_rk3568),
162};
163
164static const struct rkusbphy_id *
165rkusbphy_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
178static int
179rkusbphy_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
186static void
187rkusbphy_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
227static uint32_t
228rkusbphy_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
240static int
241rkusbphy_isset(struct rkusbphy_softc *sc, const struct rkusbphy_reg *r)
242{
243	return (rkusbphy_rd(sc, r) == r->r_set);
244}
245
246static void
247rkusbphy_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
256static void
257rkusbphy_set(struct rkusbphy_softc *sc, const struct rkusbphy_reg *r)
258{
259	rkusbphy_wr(sc, r, r->r_set);
260}
261
262static void
263rkusbphy_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
285static void
286rkusbphy_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
308static int
309rkusbphy_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
321static int
322rkusbphy_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