1/* $NetBSD: ti_omapmusb.c,v 1.1 2025/12/16 12:20:22 skrll Exp $ */ 2 3/*- 4 * Copyright (c) 2025 Rui-Xiang Guo 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_omapmusb.c,v 1.1 2025/12/16 12:20:22 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#include <sys/pool.h> 38 39#include <dev/usb/usb.h> 40#include <dev/usb/usbdi.h> 41#include <dev/usb/usbdivar.h> 42#include <dev/usb/motgvar.h> 43#include <dev/usb/motgreg.h> 44 45#include <dev/fdt/fdtvar.h> 46 47#include <arm/ti/ti_prcm.h> 48 49#define OTG_SYSCONFIG 0x404 50#define OTG_SYSCONFIG_MIDLEMODE __BITS(13,12) 51#define OTG_SYSCONFIG_SIDLEMODE __BITS(4,3) 52#define OTG_SYSCONFIG_ENAWAKEUP __BIT(2) 53#define OTG_SYSCONFIG_SOFTRESET __BIT(1) 54#define OTG_SYSCONFIG_AUTOIDLE __BIT(0) 55 56#define OTG_SYSSTATUS 0x408 57#define OTG_SYSSTATUS_RESETDONE __BIT(0) 58 59#define IDLEMODE_FORCE 0x0 60#define IDLEMODE_NO 0x1 61#define IDLEMODE_SMART 0x2 62#define IDLEMODE_SMART_WKUP 0x3 63 64static const struct device_compatible_entry compat_data[] = { 65 { .compat = "ti,omap4-musb" }, 66 DEVICE_COMPAT_EOL 67}; 68 69static int omapmusb_match(device_t, cfdata_t, void *); 70static void omapmusb_attach(device_t, device_t, void *); 71 72CFATTACH_DECL_NEW(omapmusb, sizeof(struct motg_softc), 73 omapmusb_match, omapmusb_attach, NULL, NULL); 74 75#define RD1(sc, reg) \ 76 bus_space_read_1((sc)->sc_iot, (sc)->sc_ioh, (reg)) 77#define WR1(sc, reg, val) \ 78 bus_space_write_1((sc)->sc_iot, (sc)->sc_ioh, (reg), (val)) 79#define RD2(sc, reg) \ 80 bus_space_read_2((sc)->sc_iot, (sc)->sc_ioh, (reg)) 81#define WR2(sc, reg, val) \ 82 bus_space_write_2((sc)->sc_iot, (sc)->sc_ioh, (reg), (val)) 83#define RD4(sc, reg) \ 84 bus_space_read_4((sc)->sc_iot, (sc)->sc_ioh, (reg)) 85#define WR4(sc, reg, val) \ 86 bus_space_write_4((sc)->sc_iot, (sc)->sc_ioh, (reg), (val)) 87 88static int 89omapmusb_intr(void *priv) 90{ 91 struct motg_softc * const sc = priv; 92 uint16_t inttx, intrx; 93 uint8_t intusb; 94 int ret; 95 96 mutex_enter(&sc->sc_intr_lock); 97 98 intusb = RD1(sc, MUSB2_REG_INTUSB); 99 inttx = RD2(sc, MUSB2_REG_INTTX); 100 intrx = RD2(sc, MUSB2_REG_INTRX); 101 if (!intusb && !inttx && !intrx) { 102 mutex_exit(&sc->sc_intr_lock); 103 return 0; 104 } 105 106 if (intusb) 107 WR1(sc, MUSB2_REG_INTUSB, intusb); 108 if (inttx) 109 WR2(sc, MUSB2_REG_INTTX, inttx); 110 if (intrx) 111 WR2(sc, MUSB2_REG_INTRX, intrx); 112 113 ret = motg_intr(sc, intrx, inttx, intusb); 114 115 mutex_exit(&sc->sc_intr_lock); 116 117 return ret; 118} 119 120static void 121omapmusb_poll(void *priv) 122{ 123 omapmusb_intr(priv); 124} 125 126static void 127omapmusb_reset(struct motg_softc *sc) 128{ 129 uint32_t val; 130 int retry = 5000; 131 132 WR4(sc, OTG_SYSCONFIG, OTG_SYSCONFIG_SOFTRESET); 133 do { 134 val = RD4(sc, OTG_SYSSTATUS); 135 if (val & OTG_SYSSTATUS_RESETDONE) 136 break; 137 delay(10); 138 } while (--retry > 0); 139 if (retry == 0) 140 aprint_error_dev(sc->sc_dev, "reset timeout\n"); 141} 142 143static void 144omapmusb_init(struct motg_softc *sc) 145{ 146 uint32_t val; 147 148 omapmusb_reset(sc); 149 150 val = __SHIFTIN(IDLEMODE_SMART, OTG_SYSCONFIG_MIDLEMODE) | 151 __SHIFTIN(IDLEMODE_SMART, OTG_SYSCONFIG_SIDLEMODE) | 152 OTG_SYSCONFIG_AUTOIDLE; 153 WR4(sc, OTG_SYSCONFIG, val); 154 155 motg_init(sc); 156} 157 158static int 159omapmusb_match(device_t parent, cfdata_t match, void *aux) 160{ 161 struct fdt_attach_args * const faa = aux; 162 163 return of_compatible_match(faa->faa_phandle, compat_data); 164} 165 166static void 167omapmusb_attach(device_t parent, device_t self, void *aux) 168{ 169 struct motg_softc * const sc = device_private(self); 170 struct fdt_attach_args * const faa = aux; 171 const int phandle = faa->faa_phandle; 172 struct fdtbus_phy *phy; 173 char intrstr[128]; 174 bus_addr_t addr; 175 bus_size_t size; 176 uint32_t ep_max; 177 void *ih; 178 179 if (fdtbus_get_reg(phandle, 0, &addr, &size) != 0) { 180 aprint_error(": couldn't get registers\n"); 181 return; 182 } 183 184 if (ti_prcm_enable_hwmod(phandle, 0) != 0) { 185 aprint_error(": couldn't enable module\n"); 186 return; 187 } 188 189 if (of_getprop_uint32(phandle, "num-eps", &ep_max)) 190 ep_max = MOTG_MAX_HW_EP; 191 192 phy = fdtbus_phy_get(phandle, "usb2-phy"); 193 if (phy && fdtbus_phy_enable(phy, true) != 0) { 194 aprint_error(": couldn't enable phy\n"); 195 return; 196 } 197 198 sc->sc_dev = self; 199 sc->sc_bus.ub_hcpriv = sc; 200 sc->sc_bus.ub_dmatag = faa->faa_dmat; 201 sc->sc_size = size; 202 sc->sc_iot = faa->faa_bst; 203 if (bus_space_map(sc->sc_iot, addr, size, 0, &sc->sc_ioh) != 0) { 204 aprint_error(": couldn't map registers\n"); 205 return; 206 } 207 sc->sc_intr_poll = omapmusb_poll; 208 sc->sc_intr_poll_arg = sc; 209 sc->sc_mode = MOTG_MODE_HOST; 210 sc->sc_ep_max = ep_max; 211 sc->sc_ep_fifosize = 512; 212 213 aprint_naive("\n"); 214 aprint_normal(": USB OTG\n"); 215 216 if (!fdtbus_intr_str(phandle, 0, intrstr, sizeof(intrstr))) { 217 aprint_error_dev(self, "failed to decode interrupt\n"); 218 return; 219 } 220 221 ih = fdtbus_intr_establish_xname(phandle, 0, IPL_USB, FDT_INTR_MPSAFE, 222 omapmusb_intr, sc, device_xname(self)); 223 if (ih == NULL) { 224 aprint_error_dev(self, "couldn't establish interrupt on %s\n", 225 intrstr); 226 return; 227 } 228 aprint_normal_dev(self, "interrupting on %s\n", intrstr); 229 230 omapmusb_init(sc); 231} 232