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 * 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 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 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 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 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 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 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 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 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 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 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