1*b3b04c1dSkettenis /* $OpenBSD: rkusbphy.c,v 1.6 2024/11/24 22:46:54 kettenis Exp $ */
2302cd816Sdlg
3302cd816Sdlg /*
4302cd816Sdlg * Copyright (c) 2023 David Gwynne <dlg@openbsd.org>
5302cd816Sdlg *
6302cd816Sdlg * Permission to use, copy, modify, and distribute this software for any
7302cd816Sdlg * purpose with or without fee is hereby granted, provided that the above
8302cd816Sdlg * copyright notice and this permission notice appear in all copies.
9302cd816Sdlg *
10302cd816Sdlg * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11302cd816Sdlg * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12302cd816Sdlg * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13302cd816Sdlg * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14302cd816Sdlg * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15302cd816Sdlg * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16302cd816Sdlg * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17302cd816Sdlg */
18302cd816Sdlg
19302cd816Sdlg /*
20302cd816Sdlg * Rockchip USB2PHY with Innosilicon IP
21302cd816Sdlg */
22302cd816Sdlg
23302cd816Sdlg #include <sys/param.h>
24302cd816Sdlg #include <sys/systm.h>
25302cd816Sdlg #include <sys/device.h>
26302cd816Sdlg
27302cd816Sdlg #include <machine/intr.h>
28302cd816Sdlg #include <machine/bus.h>
29302cd816Sdlg #include <machine/fdt.h>
30302cd816Sdlg
31302cd816Sdlg #include <dev/ofw/openfirm.h>
32302cd816Sdlg #include <dev/ofw/ofw_clock.h>
33302cd816Sdlg #include <dev/ofw/ofw_regulator.h>
34302cd816Sdlg #include <dev/ofw/ofw_misc.h>
35302cd816Sdlg #include <dev/ofw/fdt.h>
36302cd816Sdlg
37302cd816Sdlg /*
38302cd816Sdlg * chip stuff
39302cd816Sdlg */
40302cd816Sdlg
41302cd816Sdlg struct rkusbphy_reg {
42302cd816Sdlg bus_size_t r_offs;
435b0e3269Sdlg unsigned int r_shift;
445b0e3269Sdlg uint32_t r_mask;
455b0e3269Sdlg uint32_t r_set;
46302cd816Sdlg };
47302cd816Sdlg
48302cd816Sdlg struct rkusbphy_port_regs {
495b0e3269Sdlg struct rkusbphy_reg phy_enable;
505b0e3269Sdlg };
515b0e3269Sdlg
525b0e3269Sdlg struct rkusbphy_regs {
535b0e3269Sdlg struct rkusbphy_reg clk_enable;
545b0e3269Sdlg
555b0e3269Sdlg struct rkusbphy_port_regs otg;
565b0e3269Sdlg struct rkusbphy_port_regs host;
57302cd816Sdlg };
58302cd816Sdlg
59302cd816Sdlg struct rkusbphy_chip {
60302cd816Sdlg bus_addr_t c_base_addr;
615b0e3269Sdlg const struct rkusbphy_regs *c_regs;
625b0e3269Sdlg };
635b0e3269Sdlg
645b0e3269Sdlg /*
65*b3b04c1dSkettenis * RK3399 has two USB2PHY nodes that share a GRF.
66*b3b04c1dSkettenis */
67*b3b04c1dSkettenis
68*b3b04c1dSkettenis static const struct rkusbphy_regs rkusbphy_rk3399_usb0_regs = {
69*b3b04c1dSkettenis /* shift, mask, set */
70*b3b04c1dSkettenis .clk_enable = { 0xe450, 4, 0x1, 0x0 },
71*b3b04c1dSkettenis
72*b3b04c1dSkettenis .otg = {
73*b3b04c1dSkettenis .phy_enable = { 0xe454, 0, 0x3, 0x2 },
74*b3b04c1dSkettenis },
75*b3b04c1dSkettenis
76*b3b04c1dSkettenis .host = {
77*b3b04c1dSkettenis .phy_enable = { 0xe458, 0, 0x3, 0x2 },
78*b3b04c1dSkettenis },
79*b3b04c1dSkettenis };
80*b3b04c1dSkettenis
81*b3b04c1dSkettenis static const struct rkusbphy_regs rkusbphy_rk3399_usb1_regs = {
82*b3b04c1dSkettenis /* shift, mask, set */
83*b3b04c1dSkettenis .clk_enable = { 0xe460, 4, 0x1, 0x0 },
84*b3b04c1dSkettenis
85*b3b04c1dSkettenis .otg = {
86*b3b04c1dSkettenis .phy_enable = { 0xe464, 0, 0x3, 0x2 },
87*b3b04c1dSkettenis },
88*b3b04c1dSkettenis
89*b3b04c1dSkettenis .host = {
90*b3b04c1dSkettenis .phy_enable = { 0xe468, 0, 0x3, 0x2 },
91*b3b04c1dSkettenis },
92*b3b04c1dSkettenis };
93*b3b04c1dSkettenis
94*b3b04c1dSkettenis static const struct rkusbphy_chip rkusbphy_rk3399[] = {
95*b3b04c1dSkettenis {
96*b3b04c1dSkettenis .c_base_addr = 0xe450,
97*b3b04c1dSkettenis .c_regs = &rkusbphy_rk3399_usb0_regs,
98*b3b04c1dSkettenis },
99*b3b04c1dSkettenis {
100*b3b04c1dSkettenis .c_base_addr = 0xe460,
101*b3b04c1dSkettenis .c_regs = &rkusbphy_rk3399_usb1_regs,
102*b3b04c1dSkettenis },
103*b3b04c1dSkettenis };
104*b3b04c1dSkettenis
105*b3b04c1dSkettenis /*
1065b0e3269Sdlg * RK3568 has two USB2PHY nodes that have a GRF each. Each GRF has
1075b0e3269Sdlg * the same register layout.
1085b0e3269Sdlg */
1095b0e3269Sdlg
1105b0e3269Sdlg static const struct rkusbphy_regs rkusbphy_rk3568_regs = {
1115b0e3269Sdlg /* shift, mask, set */
1125b0e3269Sdlg .clk_enable = { 0x0008, 4, 0x1, 0x0 },
1135b0e3269Sdlg
1145b0e3269Sdlg .otg = {
1155b0e3269Sdlg .phy_enable = { 0x0000, 0, 0x1ff, 0x1d2 },
1165b0e3269Sdlg },
1175b0e3269Sdlg
1185b0e3269Sdlg .host = {
1195b0e3269Sdlg .phy_enable = { 0x0004, 0, 0x1ff, 0x1d2 },
1205b0e3269Sdlg },
121302cd816Sdlg };
122302cd816Sdlg
123302cd816Sdlg static const struct rkusbphy_chip rkusbphy_rk3568[] = {
124302cd816Sdlg {
125302cd816Sdlg .c_base_addr = 0xfe8a0000,
1265b0e3269Sdlg .c_regs = &rkusbphy_rk3568_regs,
127302cd816Sdlg },
128302cd816Sdlg {
129302cd816Sdlg .c_base_addr = 0xfe8b0000,
1305b0e3269Sdlg .c_regs = &rkusbphy_rk3568_regs,
131302cd816Sdlg },
132302cd816Sdlg };
133302cd816Sdlg
134411d8e97Skettenis static const struct rkusbphy_regs rkusbphy_rk3588_regs = {
135411d8e97Skettenis /* shift, mask, set */
136411d8e97Skettenis .clk_enable = { 0x0000, 0, 0x1, 0x0 },
137411d8e97Skettenis
138411d8e97Skettenis .otg = {
139411d8e97Skettenis .phy_enable = { 0x000c, 11, 0x1, 0x0 },
140411d8e97Skettenis },
141411d8e97Skettenis
142411d8e97Skettenis .host = {
143411d8e97Skettenis .phy_enable = { 0x0008, 2, 0x1, 0x0 },
144411d8e97Skettenis },
145411d8e97Skettenis };
146411d8e97Skettenis
147411d8e97Skettenis static const struct rkusbphy_chip rkusbphy_rk3588[] = {
148411d8e97Skettenis {
149411d8e97Skettenis .c_base_addr = 0x0000,
150411d8e97Skettenis .c_regs = &rkusbphy_rk3588_regs,
151411d8e97Skettenis },
152411d8e97Skettenis {
153411d8e97Skettenis .c_base_addr = 0x4000,
154411d8e97Skettenis .c_regs = &rkusbphy_rk3588_regs,
155411d8e97Skettenis },
156411d8e97Skettenis {
157411d8e97Skettenis .c_base_addr = 0x8000,
158411d8e97Skettenis .c_regs = &rkusbphy_rk3588_regs,
159411d8e97Skettenis },
160411d8e97Skettenis {
161411d8e97Skettenis .c_base_addr = 0xc000,
162411d8e97Skettenis .c_regs = &rkusbphy_rk3588_regs,
163411d8e97Skettenis },
164411d8e97Skettenis };
165411d8e97Skettenis
166302cd816Sdlg /*
167302cd816Sdlg * driver stuff
168302cd816Sdlg */
169302cd816Sdlg
170302cd816Sdlg struct rkusbphy_softc {
171302cd816Sdlg struct device sc_dev;
1725b0e3269Sdlg const struct rkusbphy_regs *sc_regs;
173302cd816Sdlg struct regmap *sc_grf;
174302cd816Sdlg int sc_node;
175302cd816Sdlg
1765b0e3269Sdlg int sc_running;
1775b0e3269Sdlg
178302cd816Sdlg struct phy_device sc_otg_phy;
179302cd816Sdlg struct phy_device sc_host_phy;
180302cd816Sdlg };
181302cd816Sdlg #define DEVNAME(_sc) ((_sc)->sc_dev.dv_xname)
182302cd816Sdlg
183302cd816Sdlg static int rkusbphy_match(struct device *, void *, void *);
1845b0e3269Sdlg static void rkusbphy_attach(struct device *, struct device *,
1855b0e3269Sdlg void *);
1865b0e3269Sdlg
1875b0e3269Sdlg static uint32_t rkusbphy_rd(struct rkusbphy_softc *,
1885b0e3269Sdlg const struct rkusbphy_reg *);
1895b0e3269Sdlg static int rkusbphy_isset(struct rkusbphy_softc *,
1905b0e3269Sdlg const struct rkusbphy_reg *);
1915b0e3269Sdlg static void rkusbphy_wr(struct rkusbphy_softc *,
1925b0e3269Sdlg const struct rkusbphy_reg *, uint32_t);
1935b0e3269Sdlg static void rkusbphy_set(struct rkusbphy_softc *,
1945b0e3269Sdlg const struct rkusbphy_reg *);
195302cd816Sdlg
196302cd816Sdlg static int rkusbphy_otg_phy_enable(void *, uint32_t *);
197302cd816Sdlg static int rkusbphy_host_phy_enable(void *, uint32_t *);
198302cd816Sdlg
199302cd816Sdlg struct rkusbphy_port_config {
200302cd816Sdlg const char *pc_name;
201302cd816Sdlg int (*pc_enable)(void *, uint32_t *);
202302cd816Sdlg };
203302cd816Sdlg
204302cd816Sdlg static void rkusbphy_register(struct rkusbphy_softc *,
205302cd816Sdlg struct phy_device *, const struct rkusbphy_port_config *);
206302cd816Sdlg
207302cd816Sdlg static const struct rkusbphy_port_config rkusbphy_otg_config = {
208302cd816Sdlg .pc_name = "otg-port",
209302cd816Sdlg .pc_enable = rkusbphy_otg_phy_enable,
210302cd816Sdlg };
211302cd816Sdlg
212302cd816Sdlg static const struct rkusbphy_port_config rkusbphy_host_config = {
213302cd816Sdlg .pc_name = "host-port",
214302cd816Sdlg .pc_enable = rkusbphy_host_phy_enable,
215302cd816Sdlg };
216302cd816Sdlg
217302cd816Sdlg const struct cfattach rkusbphy_ca = {
218302cd816Sdlg sizeof (struct rkusbphy_softc), rkusbphy_match, rkusbphy_attach
219302cd816Sdlg };
220302cd816Sdlg
221302cd816Sdlg struct cfdriver rkusbphy_cd = {
222302cd816Sdlg NULL, "rkusbphy", DV_DULL
223302cd816Sdlg };
224302cd816Sdlg
225302cd816Sdlg struct rkusbphy_id {
226302cd816Sdlg const char *id_name;
227302cd816Sdlg const struct rkusbphy_chip *id_chips;
228302cd816Sdlg size_t id_nchips;
229302cd816Sdlg };
230302cd816Sdlg
231302cd816Sdlg #define RKUSBPHY_ID(_n, _c) { _n, _c, nitems(_c) }
232302cd816Sdlg
233302cd816Sdlg static const struct rkusbphy_id rkusbphy_ids[] = {
234*b3b04c1dSkettenis RKUSBPHY_ID("rockchip,rk3399-usb2phy", rkusbphy_rk3399),
235302cd816Sdlg RKUSBPHY_ID("rockchip,rk3568-usb2phy", rkusbphy_rk3568),
236411d8e97Skettenis RKUSBPHY_ID("rockchip,rk3588-usb2phy", rkusbphy_rk3588),
237302cd816Sdlg };
238302cd816Sdlg
239302cd816Sdlg static const struct rkusbphy_id *
rkusbphy_lookup(struct fdt_attach_args * faa)240302cd816Sdlg rkusbphy_lookup(struct fdt_attach_args *faa)
241302cd816Sdlg {
242302cd816Sdlg size_t i;
243302cd816Sdlg
244302cd816Sdlg for (i = 0; i < nitems(rkusbphy_ids); i++) {
245302cd816Sdlg const struct rkusbphy_id *id = &rkusbphy_ids[i];
246302cd816Sdlg if (OF_is_compatible(faa->fa_node, id->id_name))
247302cd816Sdlg return (id);
248302cd816Sdlg }
249302cd816Sdlg
250302cd816Sdlg return (NULL);
251302cd816Sdlg }
252302cd816Sdlg
253302cd816Sdlg static int
rkusbphy_match(struct device * parent,void * match,void * aux)254302cd816Sdlg rkusbphy_match(struct device *parent, void *match, void *aux)
255302cd816Sdlg {
256302cd816Sdlg struct fdt_attach_args *faa = aux;
257302cd816Sdlg
258302cd816Sdlg return (rkusbphy_lookup(faa) != NULL ? 1 : 0);
259302cd816Sdlg }
260302cd816Sdlg
261302cd816Sdlg static void
rkusbphy_attach(struct device * parent,struct device * self,void * aux)262302cd816Sdlg rkusbphy_attach(struct device *parent, struct device *self, void *aux)
263302cd816Sdlg {
264302cd816Sdlg struct rkusbphy_softc *sc = (struct rkusbphy_softc *)self;
265302cd816Sdlg struct fdt_attach_args *faa = aux;
266302cd816Sdlg const struct rkusbphy_id *id = rkusbphy_lookup(faa);
267302cd816Sdlg size_t i;
268302cd816Sdlg uint32_t grfph;
269302cd816Sdlg
270302cd816Sdlg if (faa->fa_nreg < 1) {
271302cd816Sdlg printf(": no registers\n");
272302cd816Sdlg return;
273302cd816Sdlg }
274302cd816Sdlg
275302cd816Sdlg for (i = 0; i < id->id_nchips; i++) {
276302cd816Sdlg const struct rkusbphy_chip *c = &id->id_chips[i];
277302cd816Sdlg if (faa->fa_reg[0].addr == c->c_base_addr) {
278302cd816Sdlg printf(": phy %zu\n", i);
2795b0e3269Sdlg sc->sc_regs = c->c_regs;
280302cd816Sdlg break;
281302cd816Sdlg }
282302cd816Sdlg }
2835b0e3269Sdlg if (sc->sc_regs == NULL) {
284302cd816Sdlg printf(": unknown base address 0x%llu\n", faa->fa_reg[0].addr);
285302cd816Sdlg return;
286302cd816Sdlg }
287302cd816Sdlg
288302cd816Sdlg sc->sc_node = faa->fa_node;
289302cd816Sdlg
290302cd816Sdlg grfph = OF_getpropint(sc->sc_node, "rockchip,usbgrf", 0);
291411d8e97Skettenis if (grfph)
292302cd816Sdlg sc->sc_grf = regmap_byphandle(grfph);
293411d8e97Skettenis else
294411d8e97Skettenis sc->sc_grf = regmap_bynode(OF_parent(faa->fa_node));
295302cd816Sdlg if (sc->sc_grf == NULL) {
296302cd816Sdlg printf("%s: rockchip,usbgrf 0x%x not found\n", DEVNAME(sc),
297302cd816Sdlg grfph);
298302cd816Sdlg return;
299302cd816Sdlg }
300302cd816Sdlg
301302cd816Sdlg rkusbphy_register(sc, &sc->sc_otg_phy, &rkusbphy_otg_config);
302302cd816Sdlg rkusbphy_register(sc, &sc->sc_host_phy, &rkusbphy_host_config);
303302cd816Sdlg }
304302cd816Sdlg
3055b0e3269Sdlg static uint32_t
rkusbphy_rd(struct rkusbphy_softc * sc,const struct rkusbphy_reg * r)3065b0e3269Sdlg rkusbphy_rd(struct rkusbphy_softc *sc, const struct rkusbphy_reg *r)
3075b0e3269Sdlg {
3085b0e3269Sdlg uint32_t v;
3095b0e3269Sdlg
3105b0e3269Sdlg if (r->r_mask == 0)
3115b0e3269Sdlg return (0);
3125b0e3269Sdlg
3135b0e3269Sdlg v = regmap_read_4(sc->sc_grf, r->r_offs);
3145b0e3269Sdlg
3155b0e3269Sdlg return ((v >> r->r_shift) & r->r_mask);
3165b0e3269Sdlg }
3175b0e3269Sdlg
3185b0e3269Sdlg static int
rkusbphy_isset(struct rkusbphy_softc * sc,const struct rkusbphy_reg * r)3195b0e3269Sdlg rkusbphy_isset(struct rkusbphy_softc *sc, const struct rkusbphy_reg *r)
3205b0e3269Sdlg {
3215b0e3269Sdlg return (rkusbphy_rd(sc, r) == r->r_set);
3225b0e3269Sdlg }
3235b0e3269Sdlg
3245b0e3269Sdlg static void
rkusbphy_wr(struct rkusbphy_softc * sc,const struct rkusbphy_reg * r,uint32_t v)3255b0e3269Sdlg rkusbphy_wr(struct rkusbphy_softc *sc, const struct rkusbphy_reg *r, uint32_t v)
3265b0e3269Sdlg {
3275b0e3269Sdlg if (r->r_mask == 0)
3285b0e3269Sdlg return;
3295b0e3269Sdlg
3305b0e3269Sdlg regmap_write_4(sc->sc_grf, r->r_offs,
3315b0e3269Sdlg (r->r_mask << (r->r_shift + 16)) | (v << r->r_shift));
3325b0e3269Sdlg }
3335b0e3269Sdlg
3345b0e3269Sdlg static void
rkusbphy_set(struct rkusbphy_softc * sc,const struct rkusbphy_reg * r)3355b0e3269Sdlg rkusbphy_set(struct rkusbphy_softc *sc, const struct rkusbphy_reg *r)
3365b0e3269Sdlg {
3375b0e3269Sdlg rkusbphy_wr(sc, r, r->r_set);
3385b0e3269Sdlg }
3395b0e3269Sdlg
340302cd816Sdlg static void
rkusbphy_register(struct rkusbphy_softc * sc,struct phy_device * pd,const struct rkusbphy_port_config * pc)341302cd816Sdlg rkusbphy_register(struct rkusbphy_softc *sc, struct phy_device *pd,
342302cd816Sdlg const struct rkusbphy_port_config *pc)
343302cd816Sdlg {
344302cd816Sdlg char status[32];
345302cd816Sdlg int node;
346302cd816Sdlg
347302cd816Sdlg node = OF_getnodebyname(sc->sc_node, pc->pc_name);
348411d8e97Skettenis if (node == 0)
349302cd816Sdlg return;
350302cd816Sdlg
351302cd816Sdlg if (OF_getprop(node, "status", status, sizeof(status)) > 0 &&
352302cd816Sdlg strcmp(status, "disabled") == 0)
353302cd816Sdlg return;
354302cd816Sdlg
355302cd816Sdlg pd->pd_node = node;
356302cd816Sdlg pd->pd_cookie = sc;
357302cd816Sdlg pd->pd_enable = pc->pc_enable;
358302cd816Sdlg phy_register(pd);
359302cd816Sdlg }
360302cd816Sdlg
361302cd816Sdlg static void
rkusbphy_phy_supply(struct rkusbphy_softc * sc,int node)362302cd816Sdlg rkusbphy_phy_supply(struct rkusbphy_softc *sc, int node)
363302cd816Sdlg {
364302cd816Sdlg int phandle;
365302cd816Sdlg
3665b0e3269Sdlg if (!sc->sc_running) {
3675b0e3269Sdlg clock_enable(sc->sc_node, "phyclk");
3685b0e3269Sdlg if (!rkusbphy_isset(sc, &sc->sc_regs->clk_enable)) {
3695b0e3269Sdlg rkusbphy_set(sc, &sc->sc_regs->clk_enable);
3705b0e3269Sdlg
3715b0e3269Sdlg delay(1200);
3725b0e3269Sdlg }
3735b0e3269Sdlg
3745b0e3269Sdlg sc->sc_running = 1;
3755b0e3269Sdlg }
3765b0e3269Sdlg
377302cd816Sdlg phandle = OF_getpropint(node, "phy-supply", 0);
378302cd816Sdlg if (phandle == 0)
379302cd816Sdlg return;
380302cd816Sdlg
381302cd816Sdlg regulator_enable(phandle);
382302cd816Sdlg }
383302cd816Sdlg
384302cd816Sdlg static int
rkusbphy_otg_phy_enable(void * cookie,uint32_t * cells)385302cd816Sdlg rkusbphy_otg_phy_enable(void *cookie, uint32_t *cells)
386302cd816Sdlg {
387302cd816Sdlg struct rkusbphy_softc *sc = cookie;
388302cd816Sdlg
389302cd816Sdlg rkusbphy_phy_supply(sc, sc->sc_otg_phy.pd_node);
390302cd816Sdlg
3915b0e3269Sdlg rkusbphy_set(sc, &sc->sc_regs->otg.phy_enable);
3925b0e3269Sdlg delay(1500);
3935b0e3269Sdlg
394302cd816Sdlg return (EINVAL);
395302cd816Sdlg }
396302cd816Sdlg
397302cd816Sdlg static int
rkusbphy_host_phy_enable(void * cookie,uint32_t * cells)398302cd816Sdlg rkusbphy_host_phy_enable(void *cookie, uint32_t *cells)
399302cd816Sdlg {
400302cd816Sdlg struct rkusbphy_softc *sc = cookie;
401302cd816Sdlg
402302cd816Sdlg rkusbphy_phy_supply(sc, sc->sc_host_phy.pd_node);
403302cd816Sdlg
4045b0e3269Sdlg rkusbphy_set(sc, &sc->sc_regs->host.phy_enable);
4055b0e3269Sdlg delay(1500);
4065b0e3269Sdlg
407302cd816Sdlg return (EINVAL);
408302cd816Sdlg }
409