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 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 * RK3568 has two USB2PHY nodes that have a GRF each. Each GRF has 66 * the same register layout. 67 */ 68 69 static 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 82 static 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 static 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 106 static 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 129 struct 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 142 static int rkusbphy_match(struct device *, void *, void *); 143 static void rkusbphy_attach(struct device *, struct device *, 144 void *); 145 146 static uint32_t rkusbphy_rd(struct rkusbphy_softc *, 147 const struct rkusbphy_reg *); 148 static int rkusbphy_isset(struct rkusbphy_softc *, 149 const struct rkusbphy_reg *); 150 static void rkusbphy_wr(struct rkusbphy_softc *, 151 const struct rkusbphy_reg *, uint32_t); 152 static void rkusbphy_set(struct rkusbphy_softc *, 153 const struct rkusbphy_reg *); 154 155 static int rkusbphy_otg_phy_enable(void *, uint32_t *); 156 static int rkusbphy_host_phy_enable(void *, uint32_t *); 157 158 struct rkusbphy_port_config { 159 const char *pc_name; 160 int (*pc_enable)(void *, uint32_t *); 161 }; 162 163 static void rkusbphy_register(struct rkusbphy_softc *, 164 struct phy_device *, const struct rkusbphy_port_config *); 165 166 static const struct rkusbphy_port_config rkusbphy_otg_config = { 167 .pc_name = "otg-port", 168 .pc_enable = rkusbphy_otg_phy_enable, 169 }; 170 171 static const struct rkusbphy_port_config rkusbphy_host_config = { 172 .pc_name = "host-port", 173 .pc_enable = rkusbphy_host_phy_enable, 174 }; 175 176 const struct cfattach rkusbphy_ca = { 177 sizeof (struct rkusbphy_softc), rkusbphy_match, rkusbphy_attach 178 }; 179 180 struct cfdriver rkusbphy_cd = { 181 NULL, "rkusbphy", DV_DULL 182 }; 183 184 struct 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 192 static const struct rkusbphy_id rkusbphy_ids[] = { 193 RKUSBPHY_ID("rockchip,rk3568-usb2phy", rkusbphy_rk3568), 194 RKUSBPHY_ID("rockchip,rk3588-usb2phy", rkusbphy_rk3588), 195 }; 196 197 static const struct rkusbphy_id * 198 rkusbphy_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 211 static int 212 rkusbphy_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 219 static void 220 rkusbphy_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 263 static uint32_t 264 rkusbphy_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 276 static int 277 rkusbphy_isset(struct rkusbphy_softc *sc, const struct rkusbphy_reg *r) 278 { 279 return (rkusbphy_rd(sc, r) == r->r_set); 280 } 281 282 static void 283 rkusbphy_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 292 static void 293 rkusbphy_set(struct rkusbphy_softc *sc, const struct rkusbphy_reg *r) 294 { 295 rkusbphy_wr(sc, r, r->r_set); 296 } 297 298 static void 299 rkusbphy_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 319 static void 320 rkusbphy_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 342 static int 343 rkusbphy_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 355 static int 356 rkusbphy_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