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