1/* $OpenBSD: rkusbphy.c,v 1.4 2023/09/29 17:30:35 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 93/* 94 * driver stuff 95 */ 96 97struct rkusbphy_softc { 98 struct device sc_dev; 99 const struct rkusbphy_regs *sc_regs; 100 struct regmap *sc_grf; 101 int sc_node; 102 103 int sc_running; 104 105 struct phy_device sc_otg_phy; 106 struct phy_device sc_host_phy; 107}; 108#define DEVNAME(_sc) ((_sc)->sc_dev.dv_xname) 109 110static int rkusbphy_match(struct device *, void *, void *); 111static void rkusbphy_attach(struct device *, struct device *, 112 void *); 113 114static uint32_t rkusbphy_rd(struct rkusbphy_softc *, 115 const struct rkusbphy_reg *); 116static int rkusbphy_isset(struct rkusbphy_softc *, 117 const struct rkusbphy_reg *); 118static void rkusbphy_wr(struct rkusbphy_softc *, 119 const struct rkusbphy_reg *, uint32_t); 120static void rkusbphy_set(struct rkusbphy_softc *, 121 const struct rkusbphy_reg *); 122 123static int rkusbphy_otg_phy_enable(void *, uint32_t *); 124static int rkusbphy_host_phy_enable(void *, uint32_t *); 125 126struct rkusbphy_port_config { 127 const char *pc_name; 128 int (*pc_enable)(void *, uint32_t *); 129}; 130 131static void rkusbphy_register(struct rkusbphy_softc *, 132 struct phy_device *, const struct rkusbphy_port_config *); 133 134static const struct rkusbphy_port_config rkusbphy_otg_config = { 135 .pc_name = "otg-port", 136 .pc_enable = rkusbphy_otg_phy_enable, 137}; 138 139static const struct rkusbphy_port_config rkusbphy_host_config = { 140 .pc_name = "host-port", 141 .pc_enable = rkusbphy_host_phy_enable, 142}; 143 144const struct cfattach rkusbphy_ca = { 145 sizeof (struct rkusbphy_softc), rkusbphy_match, rkusbphy_attach 146}; 147 148struct cfdriver rkusbphy_cd = { 149 NULL, "rkusbphy", DV_DULL 150}; 151 152struct rkusbphy_id { 153 const char *id_name; 154 const struct rkusbphy_chip *id_chips; 155 size_t id_nchips; 156}; 157 158#define RKUSBPHY_ID(_n, _c) { _n, _c, nitems(_c) } 159 160static const struct rkusbphy_id rkusbphy_ids[] = { 161 RKUSBPHY_ID("rockchip,rk3568-usb2phy", rkusbphy_rk3568), 162}; 163 164static const struct rkusbphy_id * 165rkusbphy_lookup(struct fdt_attach_args *faa) 166{ 167 size_t i; 168 169 for (i = 0; i < nitems(rkusbphy_ids); i++) { 170 const struct rkusbphy_id *id = &rkusbphy_ids[i]; 171 if (OF_is_compatible(faa->fa_node, id->id_name)) 172 return (id); 173 } 174 175 return (NULL); 176} 177 178static int 179rkusbphy_match(struct device *parent, void *match, void *aux) 180{ 181 struct fdt_attach_args *faa = aux; 182 183 return (rkusbphy_lookup(faa) != NULL ? 1 : 0); 184} 185 186static void 187rkusbphy_attach(struct device *parent, struct device *self, void *aux) 188{ 189 struct rkusbphy_softc *sc = (struct rkusbphy_softc *)self; 190 struct fdt_attach_args *faa = aux; 191 const struct rkusbphy_id *id = rkusbphy_lookup(faa); 192 size_t i; 193 uint32_t grfph; 194 195 if (faa->fa_nreg < 1) { 196 printf(": no registers\n"); 197 return; 198 } 199 200 for (i = 0; i < id->id_nchips; i++) { 201 const struct rkusbphy_chip *c = &id->id_chips[i]; 202 if (faa->fa_reg[0].addr == c->c_base_addr) { 203 printf(": phy %zu\n", i); 204 sc->sc_regs = c->c_regs; 205 break; 206 } 207 } 208 if (sc->sc_regs == NULL) { 209 printf(": unknown base address 0x%llu\n", faa->fa_reg[0].addr); 210 return; 211 } 212 213 sc->sc_node = faa->fa_node; 214 215 grfph = OF_getpropint(sc->sc_node, "rockchip,usbgrf", 0); 216 sc->sc_grf = regmap_byphandle(grfph); 217 if (sc->sc_grf == NULL) { 218 printf("%s: rockchip,usbgrf 0x%x not found\n", DEVNAME(sc), 219 grfph); 220 return; 221 } 222 223 rkusbphy_register(sc, &sc->sc_otg_phy, &rkusbphy_otg_config); 224 rkusbphy_register(sc, &sc->sc_host_phy, &rkusbphy_host_config); 225} 226 227static uint32_t 228rkusbphy_rd(struct rkusbphy_softc *sc, const struct rkusbphy_reg *r) 229{ 230 uint32_t v; 231 232 if (r->r_mask == 0) 233 return (0); 234 235 v = regmap_read_4(sc->sc_grf, r->r_offs); 236 237 return ((v >> r->r_shift) & r->r_mask); 238} 239 240static int 241rkusbphy_isset(struct rkusbphy_softc *sc, const struct rkusbphy_reg *r) 242{ 243 return (rkusbphy_rd(sc, r) == r->r_set); 244} 245 246static void 247rkusbphy_wr(struct rkusbphy_softc *sc, const struct rkusbphy_reg *r, uint32_t v) 248{ 249 if (r->r_mask == 0) 250 return; 251 252 regmap_write_4(sc->sc_grf, r->r_offs, 253 (r->r_mask << (r->r_shift + 16)) | (v << r->r_shift)); 254} 255 256static void 257rkusbphy_set(struct rkusbphy_softc *sc, const struct rkusbphy_reg *r) 258{ 259 rkusbphy_wr(sc, r, r->r_set); 260} 261 262static void 263rkusbphy_register(struct rkusbphy_softc *sc, struct phy_device *pd, 264 const struct rkusbphy_port_config *pc) 265{ 266 char status[32]; 267 int node; 268 269 node = OF_getnodebyname(sc->sc_node, pc->pc_name); 270 if (node == 0) { 271 printf("%s: cannot find %s\n", DEVNAME(sc), pc->pc_name); 272 return; 273 } 274 275 if (OF_getprop(node, "status", status, sizeof(status)) > 0 && 276 strcmp(status, "disabled") == 0) 277 return; 278 279 pd->pd_node = node; 280 pd->pd_cookie = sc; 281 pd->pd_enable = pc->pc_enable; 282 phy_register(pd); 283} 284 285static void 286rkusbphy_phy_supply(struct rkusbphy_softc *sc, int node) 287{ 288 int phandle; 289 290 if (!sc->sc_running) { 291 clock_enable(sc->sc_node, "phyclk"); 292 if (!rkusbphy_isset(sc, &sc->sc_regs->clk_enable)) { 293 rkusbphy_set(sc, &sc->sc_regs->clk_enable); 294 295 delay(1200); 296 } 297 298 sc->sc_running = 1; 299 } 300 301 phandle = OF_getpropint(node, "phy-supply", 0); 302 if (phandle == 0) 303 return; 304 305 regulator_enable(phandle); 306} 307 308static int 309rkusbphy_otg_phy_enable(void *cookie, uint32_t *cells) 310{ 311 struct rkusbphy_softc *sc = cookie; 312 313 rkusbphy_phy_supply(sc, sc->sc_otg_phy.pd_node); 314 315 rkusbphy_set(sc, &sc->sc_regs->otg.phy_enable); 316 delay(1500); 317 318 return (EINVAL); 319} 320 321static int 322rkusbphy_host_phy_enable(void *cookie, uint32_t *cells) 323{ 324 struct rkusbphy_softc *sc = cookie; 325 326 rkusbphy_phy_supply(sc, sc->sc_host_phy.pd_node); 327 328 rkusbphy_set(sc, &sc->sc_regs->host.phy_enable); 329 delay(1500); 330 331 return (EINVAL); 332} 333