1 /* $OpenBSD: rkusbphy.c,v 1.6 2024/11/24 22:46:54 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 41 struct 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 48 struct rkusbphy_port_regs { 49 struct rkusbphy_reg phy_enable; 50 }; 51 52 struct rkusbphy_regs { 53 struct rkusbphy_reg clk_enable; 54 55 struct rkusbphy_port_regs otg; 56 struct rkusbphy_port_regs host; 57 }; 58 59 struct rkusbphy_chip { 60 bus_addr_t c_base_addr; 61 const struct rkusbphy_regs *c_regs; 62 }; 63 64 /* 65 * RK3399 has two USB2PHY nodes that share a GRF. 66 */ 67 68 static const struct rkusbphy_regs rkusbphy_rk3399_usb0_regs = { 69 /* shift, mask, set */ 70 .clk_enable = { 0xe450, 4, 0x1, 0x0 }, 71 72 .otg = { 73 .phy_enable = { 0xe454, 0, 0x3, 0x2 }, 74 }, 75 76 .host = { 77 .phy_enable = { 0xe458, 0, 0x3, 0x2 }, 78 }, 79 }; 80 81 static const struct rkusbphy_regs rkusbphy_rk3399_usb1_regs = { 82 /* shift, mask, set */ 83 .clk_enable = { 0xe460, 4, 0x1, 0x0 }, 84 85 .otg = { 86 .phy_enable = { 0xe464, 0, 0x3, 0x2 }, 87 }, 88 89 .host = { 90 .phy_enable = { 0xe468, 0, 0x3, 0x2 }, 91 }, 92 }; 93 94 static const struct rkusbphy_chip rkusbphy_rk3399[] = { 95 { 96 .c_base_addr = 0xe450, 97 .c_regs = &rkusbphy_rk3399_usb0_regs, 98 }, 99 { 100 .c_base_addr = 0xe460, 101 .c_regs = &rkusbphy_rk3399_usb1_regs, 102 }, 103 }; 104 105 /* 106 * RK3568 has two USB2PHY nodes that have a GRF each. Each GRF has 107 * the same register layout. 108 */ 109 110 static const struct rkusbphy_regs rkusbphy_rk3568_regs = { 111 /* shift, mask, set */ 112 .clk_enable = { 0x0008, 4, 0x1, 0x0 }, 113 114 .otg = { 115 .phy_enable = { 0x0000, 0, 0x1ff, 0x1d2 }, 116 }, 117 118 .host = { 119 .phy_enable = { 0x0004, 0, 0x1ff, 0x1d2 }, 120 }, 121 }; 122 123 static const struct rkusbphy_chip rkusbphy_rk3568[] = { 124 { 125 .c_base_addr = 0xfe8a0000, 126 .c_regs = &rkusbphy_rk3568_regs, 127 }, 128 { 129 .c_base_addr = 0xfe8b0000, 130 .c_regs = &rkusbphy_rk3568_regs, 131 }, 132 }; 133 134 static const struct rkusbphy_regs rkusbphy_rk3588_regs = { 135 /* shift, mask, set */ 136 .clk_enable = { 0x0000, 0, 0x1, 0x0 }, 137 138 .otg = { 139 .phy_enable = { 0x000c, 11, 0x1, 0x0 }, 140 }, 141 142 .host = { 143 .phy_enable = { 0x0008, 2, 0x1, 0x0 }, 144 }, 145 }; 146 147 static const struct rkusbphy_chip rkusbphy_rk3588[] = { 148 { 149 .c_base_addr = 0x0000, 150 .c_regs = &rkusbphy_rk3588_regs, 151 }, 152 { 153 .c_base_addr = 0x4000, 154 .c_regs = &rkusbphy_rk3588_regs, 155 }, 156 { 157 .c_base_addr = 0x8000, 158 .c_regs = &rkusbphy_rk3588_regs, 159 }, 160 { 161 .c_base_addr = 0xc000, 162 .c_regs = &rkusbphy_rk3588_regs, 163 }, 164 }; 165 166 /* 167 * driver stuff 168 */ 169 170 struct rkusbphy_softc { 171 struct device sc_dev; 172 const struct rkusbphy_regs *sc_regs; 173 struct regmap *sc_grf; 174 int sc_node; 175 176 int sc_running; 177 178 struct phy_device sc_otg_phy; 179 struct phy_device sc_host_phy; 180 }; 181 #define DEVNAME(_sc) ((_sc)->sc_dev.dv_xname) 182 183 static int rkusbphy_match(struct device *, void *, void *); 184 static void rkusbphy_attach(struct device *, struct device *, 185 void *); 186 187 static uint32_t rkusbphy_rd(struct rkusbphy_softc *, 188 const struct rkusbphy_reg *); 189 static int rkusbphy_isset(struct rkusbphy_softc *, 190 const struct rkusbphy_reg *); 191 static void rkusbphy_wr(struct rkusbphy_softc *, 192 const struct rkusbphy_reg *, uint32_t); 193 static void rkusbphy_set(struct rkusbphy_softc *, 194 const struct rkusbphy_reg *); 195 196 static int rkusbphy_otg_phy_enable(void *, uint32_t *); 197 static int rkusbphy_host_phy_enable(void *, uint32_t *); 198 199 struct rkusbphy_port_config { 200 const char *pc_name; 201 int (*pc_enable)(void *, uint32_t *); 202 }; 203 204 static void rkusbphy_register(struct rkusbphy_softc *, 205 struct phy_device *, const struct rkusbphy_port_config *); 206 207 static const struct rkusbphy_port_config rkusbphy_otg_config = { 208 .pc_name = "otg-port", 209 .pc_enable = rkusbphy_otg_phy_enable, 210 }; 211 212 static const struct rkusbphy_port_config rkusbphy_host_config = { 213 .pc_name = "host-port", 214 .pc_enable = rkusbphy_host_phy_enable, 215 }; 216 217 const struct cfattach rkusbphy_ca = { 218 sizeof (struct rkusbphy_softc), rkusbphy_match, rkusbphy_attach 219 }; 220 221 struct cfdriver rkusbphy_cd = { 222 NULL, "rkusbphy", DV_DULL 223 }; 224 225 struct rkusbphy_id { 226 const char *id_name; 227 const struct rkusbphy_chip *id_chips; 228 size_t id_nchips; 229 }; 230 231 #define RKUSBPHY_ID(_n, _c) { _n, _c, nitems(_c) } 232 233 static const struct rkusbphy_id rkusbphy_ids[] = { 234 RKUSBPHY_ID("rockchip,rk3399-usb2phy", rkusbphy_rk3399), 235 RKUSBPHY_ID("rockchip,rk3568-usb2phy", rkusbphy_rk3568), 236 RKUSBPHY_ID("rockchip,rk3588-usb2phy", rkusbphy_rk3588), 237 }; 238 239 static const struct rkusbphy_id * 240 rkusbphy_lookup(struct fdt_attach_args *faa) 241 { 242 size_t i; 243 244 for (i = 0; i < nitems(rkusbphy_ids); i++) { 245 const struct rkusbphy_id *id = &rkusbphy_ids[i]; 246 if (OF_is_compatible(faa->fa_node, id->id_name)) 247 return (id); 248 } 249 250 return (NULL); 251 } 252 253 static int 254 rkusbphy_match(struct device *parent, void *match, void *aux) 255 { 256 struct fdt_attach_args *faa = aux; 257 258 return (rkusbphy_lookup(faa) != NULL ? 1 : 0); 259 } 260 261 static void 262 rkusbphy_attach(struct device *parent, struct device *self, void *aux) 263 { 264 struct rkusbphy_softc *sc = (struct rkusbphy_softc *)self; 265 struct fdt_attach_args *faa = aux; 266 const struct rkusbphy_id *id = rkusbphy_lookup(faa); 267 size_t i; 268 uint32_t grfph; 269 270 if (faa->fa_nreg < 1) { 271 printf(": no registers\n"); 272 return; 273 } 274 275 for (i = 0; i < id->id_nchips; i++) { 276 const struct rkusbphy_chip *c = &id->id_chips[i]; 277 if (faa->fa_reg[0].addr == c->c_base_addr) { 278 printf(": phy %zu\n", i); 279 sc->sc_regs = c->c_regs; 280 break; 281 } 282 } 283 if (sc->sc_regs == NULL) { 284 printf(": unknown base address 0x%llu\n", faa->fa_reg[0].addr); 285 return; 286 } 287 288 sc->sc_node = faa->fa_node; 289 290 grfph = OF_getpropint(sc->sc_node, "rockchip,usbgrf", 0); 291 if (grfph) 292 sc->sc_grf = regmap_byphandle(grfph); 293 else 294 sc->sc_grf = regmap_bynode(OF_parent(faa->fa_node)); 295 if (sc->sc_grf == NULL) { 296 printf("%s: rockchip,usbgrf 0x%x not found\n", DEVNAME(sc), 297 grfph); 298 return; 299 } 300 301 rkusbphy_register(sc, &sc->sc_otg_phy, &rkusbphy_otg_config); 302 rkusbphy_register(sc, &sc->sc_host_phy, &rkusbphy_host_config); 303 } 304 305 static uint32_t 306 rkusbphy_rd(struct rkusbphy_softc *sc, const struct rkusbphy_reg *r) 307 { 308 uint32_t v; 309 310 if (r->r_mask == 0) 311 return (0); 312 313 v = regmap_read_4(sc->sc_grf, r->r_offs); 314 315 return ((v >> r->r_shift) & r->r_mask); 316 } 317 318 static int 319 rkusbphy_isset(struct rkusbphy_softc *sc, const struct rkusbphy_reg *r) 320 { 321 return (rkusbphy_rd(sc, r) == r->r_set); 322 } 323 324 static void 325 rkusbphy_wr(struct rkusbphy_softc *sc, const struct rkusbphy_reg *r, uint32_t v) 326 { 327 if (r->r_mask == 0) 328 return; 329 330 regmap_write_4(sc->sc_grf, r->r_offs, 331 (r->r_mask << (r->r_shift + 16)) | (v << r->r_shift)); 332 } 333 334 static void 335 rkusbphy_set(struct rkusbphy_softc *sc, const struct rkusbphy_reg *r) 336 { 337 rkusbphy_wr(sc, r, r->r_set); 338 } 339 340 static void 341 rkusbphy_register(struct rkusbphy_softc *sc, struct phy_device *pd, 342 const struct rkusbphy_port_config *pc) 343 { 344 char status[32]; 345 int node; 346 347 node = OF_getnodebyname(sc->sc_node, pc->pc_name); 348 if (node == 0) 349 return; 350 351 if (OF_getprop(node, "status", status, sizeof(status)) > 0 && 352 strcmp(status, "disabled") == 0) 353 return; 354 355 pd->pd_node = node; 356 pd->pd_cookie = sc; 357 pd->pd_enable = pc->pc_enable; 358 phy_register(pd); 359 } 360 361 static void 362 rkusbphy_phy_supply(struct rkusbphy_softc *sc, int node) 363 { 364 int phandle; 365 366 if (!sc->sc_running) { 367 clock_enable(sc->sc_node, "phyclk"); 368 if (!rkusbphy_isset(sc, &sc->sc_regs->clk_enable)) { 369 rkusbphy_set(sc, &sc->sc_regs->clk_enable); 370 371 delay(1200); 372 } 373 374 sc->sc_running = 1; 375 } 376 377 phandle = OF_getpropint(node, "phy-supply", 0); 378 if (phandle == 0) 379 return; 380 381 regulator_enable(phandle); 382 } 383 384 static int 385 rkusbphy_otg_phy_enable(void *cookie, uint32_t *cells) 386 { 387 struct rkusbphy_softc *sc = cookie; 388 389 rkusbphy_phy_supply(sc, sc->sc_otg_phy.pd_node); 390 391 rkusbphy_set(sc, &sc->sc_regs->otg.phy_enable); 392 delay(1500); 393 394 return (EINVAL); 395 } 396 397 static int 398 rkusbphy_host_phy_enable(void *cookie, uint32_t *cells) 399 { 400 struct rkusbphy_softc *sc = cookie; 401 402 rkusbphy_phy_supply(sc, sc->sc_host_phy.pd_node); 403 404 rkusbphy_set(sc, &sc->sc_regs->host.phy_enable); 405 delay(1500); 406 407 return (EINVAL); 408 } 409