1 /* $NetBSD: ti_usb.c,v 1.3 2025/12/16 12:20:23 skrll Exp $ */ 2 3 /*- 4 * Copyright (c) 2019 Jared McNeill <jmcneill (at) invisible.ca> 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. The name of the author may not be used to endorse or promote products 13 * derived from this software without specific prior written permission. 14 * 15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 16 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 17 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 18 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 19 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 20 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 21 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 22 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 23 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25 * SUCH DAMAGE. 26 */ 27 28 #include <sys/cdefs.h> 29 __KERNEL_RCSID(0, "$NetBSD: ti_usb.c,v 1.3 2025/12/16 12:20:23 skrll Exp $"); 30 31 #include <sys/param.h> 32 #include <sys/systm.h> 33 #include <sys/device.h> 34 #include <sys/conf.h> 35 #include <sys/mutex.h> 36 #include <sys/bus.h> 37 38 #include <dev/fdt/fdtvar.h> 39 40 #include <arm/ti/ti_prcm.h> 41 42 #define UHH_REVISION 0x00 43 #define UHH_REVISION1 0x00000010 /* OMAP3 */ 44 #define UHH_REVISION2 0x50700100 /* OMAP4 */ 45 46 47 #define UHH_SYSCONFIG 0x10 48 /* OMAP4 definitions */ 49 #define UHH_SYSCONFIG_IDLEMODE_MASK __BITS(3, 2) 50 #define UHH_SYSCONFIG_IDLEMODE_NOIDLE 1 51 #define UHH_SYSCONFIG_STANDBYMODE_MASK __BITS(5, 4) 52 #define UHH_SYSCONFIG_STANDBYMODE_NOSTDBY 1 53 /* OMAP definitions */ 54 #define UHH_SYSCONFIG_MIDLEMODE_MASK __BITS(13,12) 55 #define UHH_SYSCONFIG_MIDLEMODE_SMARTSTANDBY __BIT(12) 56 #define UHH_SYSCONFIG_CLOCKACTIVITY __BIT(8) 57 #define UHH_SYSCONFIG_SIDLEMODE_MASK __BITS(4,3) 58 #define UHH_SYSCONFIG_SIDLEMODE_SMARTIDLE __BIT(3) 59 #define UHH_SYSCONFIG_ENAWAKEUP __BIT(2) 60 #define UHH_SYSCONFIG_SOFTRESET __BIT(1) 61 #define UHH_SYSCONFIG_AUTOIDLE __BIT(0) 62 63 #define UHH_HOSTCONFIG 0x40 64 #define UHH_HOSTCONFIG_APP_START_CLK __BIT(31) 65 /* OMAP4 definitions */ 66 #define UHH_HOSTCONFIG_PN_MODE_MASK(p) __BITS(1 + 16 + 2 * (p), 16 + 2 * (p)) 67 #define UHH_HOSTCONFIG_PMODE_ULPI_PHY 0 68 #define UHH_HOSTCONFIG_PMODE_TLL 1 69 #define UHH_HOSTCONFIG_PMODE_HSIC 3 70 /* OMAP definitions */ 71 #define UHH_HOSTCONFIG_P3_ULPI_BYPASS __BIT(12) 72 #define UHH_HOSTCONFIG_P2_ULPI_BYPASS __BIT(11) 73 #define UHH_HOSTCONFIG_P3_CONNECT_STATUS __BIT(10) 74 #define UHH_HOSTCONFIG_P2_CONNECT_STATUS __BIT(9) 75 #define UHH_HOSTCONFIG_P1_CONNECT_STATUS __BIT(8) 76 #define UHH_HOSTCONFIG_ENA_INCR_ALIGN __BIT(5) 77 #define UHH_HOSTCONFIG_ENA_INCR16 __BIT(4) 78 #define UHH_HOSTCONFIG_ENA_INCR8 __BIT(3) 79 #define UHH_HOSTCONFIG_ENA_INCR4 __BIT(2) 80 #define UHH_HOSTCONFIG_AUTOPPD_ON_OVERCUR_EN __BIT(1) 81 #define UHH_HOSTCONFIG_P1_ULPI_BYPASS __BIT(0) 82 83 extern void tl_usbtll_enable_port(u_int); 84 85 static const struct device_compatible_entry compat_data[] = { 86 { .compat = "ti,usbhs-host" }, 87 DEVICE_COMPAT_EOL 88 }; 89 90 /* Maximum number of ports */ 91 #define TI_USB_NPORTS 3 92 93 enum { 94 CONNECT_STATUS, 95 ULPI_BYPASS, 96 TI_USB_NBITS 97 }; 98 99 static const uint32_t ti_usb_portbits[TI_USB_NPORTS][TI_USB_NBITS] = { 100 [0] = { 101 [CONNECT_STATUS] = UHH_HOSTCONFIG_P1_CONNECT_STATUS, 102 [ULPI_BYPASS] = UHH_HOSTCONFIG_P1_ULPI_BYPASS, 103 }, 104 [1] = { 105 [CONNECT_STATUS] = UHH_HOSTCONFIG_P2_CONNECT_STATUS, 106 [ULPI_BYPASS] = UHH_HOSTCONFIG_P2_ULPI_BYPASS, 107 }, 108 [2] = { 109 [CONNECT_STATUS] = UHH_HOSTCONFIG_P3_CONNECT_STATUS, 110 [ULPI_BYPASS] = UHH_HOSTCONFIG_P3_ULPI_BYPASS, 111 }, 112 }; 113 114 enum ti_usb_portmode_type { 115 PORT_UNUSED, 116 PORT_EHCI_PHY, 117 PORT_EHCI_TLL, 118 PORT_EHCI_HSIC, 119 PORT_OHCI, 120 }; 121 122 struct ti_usb_softc { 123 device_t sc_dev; 124 bus_space_tag_t sc_bst; 125 bus_space_handle_t sc_bsh; 126 127 uint32_t sc_usbhsrev; 128 u_int sc_nports; 129 130 u_int sc_portmode[TI_USB_NPORTS]; 131 }; 132 133 static int ti_usb_match(device_t, cfdata_t, void *); 134 static void ti_usb_attach(device_t, device_t, void *); 135 136 CFATTACH_DECL_NEW(ti_usb, sizeof(struct ti_usb_softc), 137 ti_usb_match, ti_usb_attach, NULL, NULL); 138 139 #define RD4(sc, reg) \ 140 bus_space_read_4((sc)->sc_bst, (sc)->sc_bsh, (reg)) 141 #define WR4(sc, reg, val) \ 142 bus_space_write_4((sc)->sc_bst, (sc)->sc_bsh, (reg), (val)) 143 144 145 static uint32_t 146 ti_usb_hostconfig1(struct ti_usb_softc *sc, uint32_t val) 147 { 148 for (u_int port = 0; port < sc->sc_nports; port++) { 149 if (sc->sc_portmode[port] == PORT_UNUSED) 150 val &= ~ti_usb_portbits[port][CONNECT_STATUS]; 151 if (sc->sc_portmode[port] == PORT_EHCI_PHY) 152 val &= ~ti_usb_portbits[port][ULPI_BYPASS]; 153 else 154 val |= ti_usb_portbits[port][ULPI_BYPASS]; 155 } 156 return val; 157 } 158 159 static uint32_t 160 ti_usb_hostconfig2(struct ti_usb_softc *sc, uint32_t val) 161 { 162 for (u_int port = 0; port < sc->sc_nports; port++) { 163 uint32_t mode = 0; 164 165 if (sc->sc_portmode[port] == PORT_UNUSED) 166 val &= ~UHH_HOSTCONFIG_PN_MODE_MASK(port); 167 if (sc->sc_portmode[port] == PORT_EHCI_TLL || 168 sc->sc_portmode[port] == PORT_OHCI) 169 mode = UHH_HOSTCONFIG_PMODE_TLL; 170 else if (sc->sc_portmode[port] == PORT_EHCI_HSIC) 171 mode = UHH_HOSTCONFIG_PMODE_HSIC; 172 val |= __SHIFTIN(mode, UHH_HOSTCONFIG_PN_MODE_MASK(port)); 173 } 174 return val; 175 } 176 177 178 static void 179 ti_usb_init(struct ti_usb_softc *sc) 180 { 181 uint32_t val; 182 183 val = RD4(sc, UHH_SYSCONFIG); 184 switch (sc->sc_usbhsrev) { 185 case UHH_REVISION1: 186 val &= ~UHH_SYSCONFIG_SIDLEMODE_MASK; 187 val &= ~UHH_SYSCONFIG_MIDLEMODE_MASK; 188 val |= UHH_SYSCONFIG_MIDLEMODE_SMARTSTANDBY; 189 val |= UHH_SYSCONFIG_CLOCKACTIVITY; 190 val |= UHH_SYSCONFIG_SIDLEMODE_SMARTIDLE; 191 val |= UHH_SYSCONFIG_ENAWAKEUP; 192 val &= ~UHH_SYSCONFIG_AUTOIDLE; 193 break; 194 case UHH_REVISION2: 195 val &= ~UHH_SYSCONFIG_IDLEMODE_MASK; 196 val |= __SHIFTIN(UHH_SYSCONFIG_IDLEMODE_NOIDLE, UHH_SYSCONFIG_IDLEMODE_MASK); 197 val &= ~UHH_SYSCONFIG_STANDBYMODE_MASK; 198 val |= __SHIFTIN(UHH_SYSCONFIG_STANDBYMODE_NOSTDBY, UHH_SYSCONFIG_STANDBYMODE_MASK); 199 break; 200 } 201 WR4(sc, UHH_SYSCONFIG, val); 202 203 val = RD4(sc, UHH_HOSTCONFIG); 204 val |= UHH_HOSTCONFIG_ENA_INCR16; 205 val |= UHH_HOSTCONFIG_ENA_INCR8; 206 val |= UHH_HOSTCONFIG_ENA_INCR4; 207 val |= UHH_HOSTCONFIG_APP_START_CLK; 208 val &= ~UHH_HOSTCONFIG_ENA_INCR_ALIGN; 209 210 switch (sc->sc_usbhsrev) { 211 case UHH_REVISION1: 212 val = ti_usb_hostconfig1(sc, val); 213 break; 214 case UHH_REVISION2: 215 default: 216 val = ti_usb_hostconfig2(sc, val); 217 } 218 219 WR4(sc, UHH_HOSTCONFIG, val); 220 } 221 222 static int 223 ti_usb_match(device_t parent, cfdata_t match, void *aux) 224 { 225 struct fdt_attach_args * const faa = aux; 226 227 return of_compatible_match(faa->faa_phandle, compat_data); 228 } 229 230 struct ti_usb_portmode { 231 const char * tup_name; 232 enum ti_usb_portmode_type tup_type; 233 } ti_usb_portmodes[] = { 234 { "ehci-phy", PORT_EHCI_PHY }, 235 { "ehci-tll", PORT_EHCI_TLL }, 236 { "ehci-hsic", PORT_EHCI_HSIC }, 237 { "ohci-phy-6pin-datse0", PORT_OHCI, }, 238 { "ohci-phy-6pin-dpdm", PORT_OHCI, }, 239 { "ohci-phy-3pin-datse0", PORT_OHCI, }, 240 { "ohci-phy-4pin-dpdm", PORT_OHCI, }, 241 { "ohci-tll-6pin-datse0", PORT_OHCI, }, 242 { "ohci-tll-6pin-dpdm", PORT_OHCI, }, 243 { "ohci-tll-3pin-datse0", PORT_OHCI, }, 244 { "ohci-tll-4pin-dpdm", PORT_OHCI, }, 245 { "ohci-tll-2pin-datse0", PORT_OHCI, }, 246 { "ohci-tll-2pin-dpdm", PORT_OHCI, }, 247 }; 248 249 static void 250 ti_usb_attach(device_t parent, device_t self, void *aux) 251 { 252 struct ti_usb_softc *sc = device_private(self); 253 struct fdt_attach_args * const faa = aux; 254 const int phandle = faa->faa_phandle; 255 bus_addr_t addr; 256 bus_size_t size; 257 char propname[16]; 258 const char *portmode; 259 int port; 260 261 if (fdtbus_get_reg(phandle, 0, &addr, &size) != 0) { 262 aprint_error(": couldn't get registers\n"); 263 return; 264 } 265 266 if (ti_prcm_enable_hwmod(phandle, 0) != 0) { 267 aprint_error(": couldn't enable module\n"); 268 return; 269 } 270 271 sc->sc_dev = self; 272 sc->sc_bst = faa->faa_bst; 273 if (bus_space_map(sc->sc_bst, addr, size, 0, &sc->sc_bsh) != 0) { 274 aprint_error(": couldn't map registers\n"); 275 return; 276 } 277 278 sc->sc_usbhsrev = RD4(sc, UHH_REVISION); 279 sc->sc_nports = TI_USB_NPORTS; 280 switch (sc->sc_usbhsrev) { 281 case UHH_REVISION1: 282 sc->sc_nports = 3; 283 break; 284 case UHH_REVISION2: 285 sc->sc_nports = 2; 286 break; 287 } 288 289 for (port = 0; port < sc->sc_nports; port++) { 290 snprintf(propname, sizeof(propname), "port%d-mode", port + 1); 291 portmode = fdtbus_get_string(phandle, propname); 292 if (portmode == NULL) 293 continue; 294 for (u_int i = 0; i < __arraycount(ti_usb_portmodes); i++) { 295 if (strcmp(portmode, ti_usb_portmodes[i].tup_name) == 0) 296 sc->sc_portmode[port] = 297 ti_usb_portmodes[i].tup_type; 298 } 299 300 if (sc->sc_portmode[port] != PORT_UNUSED) 301 tl_usbtll_enable_port(port); 302 } 303 304 aprint_naive("\n"); 305 aprint_normal(": OMAP HS USB Host (ports %u)\n", sc->sc_nports); 306 aprint_verbose_dev(sc->sc_dev, "revision %x\n", sc->sc_usbhsrev); 307 308 ti_usb_init(sc); 309 310 fdt_add_bus(self, phandle, faa); 311 } 312