1 /* $NetBSD: cdns3_fdt.c,v 1.1 2024/01/18 07:48:57 skrll Exp $ */ 2 3 /*- 4 * Copyright (c) 2023 The NetBSD Foundation, Inc. 5 * All rights reserved. 6 * 7 * This code is derived from software contributed to The NetBSD Foundation 8 * by Nick Hudson 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 29 * POSSIBILITY OF SUCH DAMAGE. 30 */ 31 32 #include <sys/cdefs.h> 33 __KERNEL_RCSID(0, "$NetBSD: cdns3_fdt.c,v 1.1 2024/01/18 07:48:57 skrll Exp $"); 34 35 #include <sys/param.h> 36 37 #include <sys/bus.h> 38 #include <sys/device.h> 39 #include <sys/intr.h> 40 41 #include <dev/usb/xhcireg.h> 42 #include <dev/usb/xhcivar.h> 43 44 #include <dev/fdt/fdtvar.h> 45 46 struct cdns3_fdt_softc { 47 struct xhci_softc sc_xhci; 48 bus_space_tag_t sc_otg_bst; 49 bus_space_handle_t sc_otg_bsh; 50 void *sc_ih; 51 }; 52 53 #define OTGRD4(sc, reg) \ 54 bus_space_read_4((sc)->sc_otg_bst, (sc)->sc_otg_bsh, (reg)) 55 #define OTGWR4(sc, reg, val) \ 56 bus_space_write_4((sc)->sc_otg_bst, (sc)->sc_otg_bsh, (reg), (val)) 57 58 59 /* 60 * Cadence USB3 controller. 61 */ 62 63 #define OTG_DID 0x0000 64 #define OTG_DID_V1 0x4024e 65 #define OTG_CMD 0x10 66 #define OTG_CMD_HOST_BUS_REQ __BIT(1) 67 #define OTG_CMD_OTG_DIS __BIT(3) 68 #define OTG_STS 0x14 69 #define OTG_STS_XHCI_READY __BIT(26) 70 71 72 static const struct device_compatible_entry compat_data[] = { 73 { .compat = "cdns,usb3" }, 74 DEVICE_COMPAT_EOL 75 }; 76 77 static int 78 cdns3_fdt_match(device_t parent, cfdata_t cf, void *aux) 79 { 80 struct fdt_attach_args * const faa = aux; 81 82 return of_compatible_match(faa->faa_phandle, compat_data); 83 } 84 85 static void 86 cdns3_fdt_attach(device_t parent, device_t self, void *aux) 87 { 88 struct cdns3_fdt_softc * const cfsc = device_private(self); 89 struct xhci_softc * const sc = &cfsc->sc_xhci; 90 struct fdt_attach_args * const faa = aux; 91 const int phandle = faa->faa_phandle; 92 93 struct fdtbus_phy *phy; 94 char intrstr[128]; 95 bus_addr_t addr; 96 bus_size_t size; 97 int error; 98 99 /* 100 * Only host mode is supported, but this includes otg devices 101 * that have 'usb-role-switch' and 'role-switch-default-mode' of 102 * 'host' 103 */ 104 const char *dr_mode = fdtbus_get_string(phandle, "dr_mode"); 105 if (dr_mode == NULL || strcmp(dr_mode, "otg") == 0) { 106 bool ok = false; 107 if (of_hasprop(phandle, "usb-role-switch")) { 108 const char *rsdm = fdtbus_get_string(phandle, 109 "role-switch-default-mode"); 110 if (rsdm != NULL && strcmp(rsdm, "host") == 0) 111 ok = true; 112 113 if (!ok) { 114 aprint_error(": host is not default mode\n"); 115 return; 116 } 117 } 118 if (!ok) { 119 aprint_error(": cannot switch 'otg' mode to host\n"); 120 return; 121 } 122 } else if (strcmp(dr_mode, "host") != 0) { 123 aprint_error(": '%s' not supported\n", dr_mode); 124 return; 125 } 126 127 sc->sc_dev = self; 128 sc->sc_bus.ub_hcpriv = sc; 129 sc->sc_bus.ub_dmatag = faa->faa_dmat; 130 sc->sc_iot = faa->faa_bst; 131 132 bus_space_handle_t bsh; 133 if (fdtbus_get_reg_byname(phandle, "otg", &addr, &size) != 0 || 134 bus_space_map(faa->faa_bst, addr, size, 0, &bsh) != 0) { 135 aprint_error(": couldn't map otg registers\n"); 136 return; 137 } 138 cfsc->sc_otg_bst = faa->faa_bst; 139 cfsc->sc_otg_bsh = bsh; 140 141 if (fdtbus_get_reg_byname(phandle, "xhci", &addr, &size) != 0 || 142 bus_space_map(faa->faa_bst, addr, size, 0, &bsh) != 0) { 143 aprint_error(": couldn't map xhci registers\n"); 144 return; 145 } 146 147 sc->sc_ios = size; 148 sc->sc_ioh = bsh; 149 150 uint32_t did = OTGRD4(cfsc, OTG_DID); 151 if (did != OTG_DID_V1) { 152 aprint_error(": unsupported IP (%#x)\n", did); 153 return; 154 } 155 OTGWR4(cfsc, OTG_CMD, OTG_CMD_HOST_BUS_REQ | OTG_CMD_OTG_DIS); 156 int tries; 157 uint32_t sts; 158 for (tries = 100; tries > 0; tries--) { 159 sts = OTGRD4(cfsc, OTG_STS); 160 if (sts & OTG_STS_XHCI_READY) 161 break; 162 delay(1000); 163 } 164 if (tries == 0) { 165 aprint_error(": not ready (%#x)\n", sts); 166 return; 167 } 168 169 aprint_naive("\n"); 170 aprint_normal(": Cadence USB3 XHCI\n"); 171 172 /* Enable PHY devices */ 173 for (u_int i = 0; ; i++) { 174 phy = fdtbus_phy_get_index(phandle, i); 175 if (phy == NULL) 176 break; 177 if (fdtbus_phy_enable(phy, true) != 0) 178 aprint_error_dev(self, "couldn't enable phy #%d\n", i); 179 } 180 181 if (!fdtbus_intr_str(phandle, 0, intrstr, sizeof(intrstr))) { 182 aprint_error_dev(self, "failed to decode interrupt\n"); 183 return; 184 } 185 186 void *ih = fdtbus_intr_establish_xname(phandle, 0, IPL_USB, 187 FDT_INTR_MPSAFE, xhci_intr, sc, device_xname(self)); 188 if (ih == NULL) { 189 aprint_error_dev(self, "couldn't establish interrupt on %s\n", 190 intrstr); 191 return; 192 } 193 aprint_normal_dev(self, "interrupting on %s\n", intrstr); 194 195 sc->sc_bus.ub_revision = USBREV_3_0; 196 error = xhci_init(sc); 197 if (error) { 198 aprint_error_dev(self, "init failed, error = %d\n", error); 199 return; 200 } 201 202 sc->sc_child = config_found(self, &sc->sc_bus, usbctlprint, CFARGS_NONE); 203 sc->sc_child2 = config_found(self, &sc->sc_bus2, usbctlprint, 204 CFARGS_NONE); 205 } 206 207 208 CFATTACH_DECL2_NEW(cdns3_fdt, sizeof(struct cdns3_fdt_softc), 209 cdns3_fdt_match, cdns3_fdt_attach, NULL, 210 xhci_activate, NULL, xhci_childdet); 211