sunxi_usbphy.c revision 1.14
1/* $NetBSD: sunxi_usbphy.c,v 1.14 2021/01/25 14:20:39 thorpej Exp $ */ 2 3/*- 4 * Copyright (c) 2017 Jared McNeill <jmcneill@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. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 17 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 18 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 19 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 20 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 26 * POSSIBILITY OF SUCH DAMAGE. 27 */ 28 29#include <sys/cdefs.h> 30 31__KERNEL_RCSID(0, "$NetBSD: sunxi_usbphy.c,v 1.14 2021/01/25 14:20:39 thorpej Exp $"); 32 33#include <sys/param.h> 34#include <sys/bus.h> 35#include <sys/device.h> 36#include <sys/intr.h> 37#include <sys/systm.h> 38#include <sys/time.h> 39 40#include <dev/fdt/fdtvar.h> 41 42/* PHY control registers */ 43#define PHYCTL_ICR 0x00 44#define PHYCTL_ICR_ID_PULLUP __BIT(17) 45#define PHYCTL_ICR_DPDM_PULLUP __BIT(16) 46#define PHYCTL_ICR_FORCE_ID __BITS(15,14) 47#define PHYCTL_ICR_FORCE_ID_LOW 2 48#define PHYCTL_ICR_FORCE_ID_HIGH 3 49#define PHYCTL_ICR_FORCE_VBUS __BITS(13,12) 50#define PHYCTL_ICR_FORCE_VBUS_LOW 2 51#define PHYCTL_ICR_FORCE_VBUS_HIGH 3 52#define PHYCTL_A10 0x04 53#define PHYCTL_A33 0x10 54#define PHYCTL_ADDR __BITS(15,8) 55#define PHYCTL_DATA __BIT(7) 56#define PHYCTL_OTG_CFG 0x20 57#define PHYCTL_OTG_ROUTE_OTG __BIT(0) 58 59/* PHY registers */ 60#define PHY_RES45_CAL_EN 0x0c 61#define PHY_TX_AMPLITUDE_TUNE 0x20 62#define PHY_DISCON_TH_SEL 0x2a 63 64/* PMU registers */ 65#define PMU_CFG 0x00 66#define AHB_INCR8 __BIT(10) 67#define AHB_INCR4 __BIT(9) 68#define AHB_INCRX_ALIGN __BIT(8) 69#define ULPI_BYPASS __BIT(0) 70#define PMU_UNK_H3 0x10 71#define PMU_UNK_H3_CLR __BIT(1) 72 73static int sunxi_usbphy_match(device_t, cfdata_t, void *); 74static void sunxi_usbphy_attach(device_t, device_t, void *); 75 76enum sunxi_usbphy_type { 77 USBPHY_A10 = 1, 78 USBPHY_A13, 79 USBPHY_A20, 80 USBPHY_A31, 81 USBPHY_A64, 82 USBPHY_A83T, 83 USBPHY_H3, 84 USBPHY_H6, 85}; 86 87static const struct device_compatible_entry compat_data[] = { 88 { .compat = "allwinner,sun4i-a10-usb-phy", .value = USBPHY_A10 }, 89 { .compat = "allwinner,sun5i-a13-usb-phy", .value = USBPHY_A13 }, 90 { .compat = "allwinner,sun6i-a31-usb-phy", .value = USBPHY_A31 }, 91 { .compat = "allwinner,sun7i-a20-usb-phy", .value = USBPHY_A20 }, 92 { .compat = "allwinner,sun8i-a83t-usb-phy", .value = USBPHY_A83T }, 93 { .compat = "allwinner,sun8i-h3-usb-phy", .value = USBPHY_H3 }, 94 { .compat = "allwinner,sun50i-a64-usb-phy", .value = USBPHY_A64 }, 95 { .compat = "allwinner,sun50i-h6-usb-phy", .value = USBPHY_H6 }, 96 { } 97}; 98 99#define SUNXI_MAXUSBPHY 4 100 101struct sunxi_usbphy { 102 u_int phy_index; 103 bus_space_handle_t phy_bsh; 104 struct fdtbus_regulator *phy_reg; 105}; 106 107struct sunxi_usbphy_softc { 108 device_t sc_dev; 109 bus_space_tag_t sc_bst; 110 bus_space_handle_t sc_bsh_phy_ctrl; 111 enum sunxi_usbphy_type sc_type; 112 113 struct sunxi_usbphy sc_phys[SUNXI_MAXUSBPHY]; 114 u_int sc_nphys; 115 116 struct fdtbus_gpio_pin *sc_gpio_id_det; 117 struct fdtbus_gpio_pin *sc_gpio_vbus_det; 118}; 119 120#define PHYCTL_READ(sc, reg) \ 121 bus_space_read_4((sc)->sc_bst, \ 122 (sc)->sc_bsh_phy_ctrl, (reg)) 123#define PHYCTL_WRITE(sc, reg, val) \ 124 bus_space_write_4((sc)->sc_bst, \ 125 (sc)->sc_bsh_phy_ctrl, (reg), (val)) 126#define PMU_READ(sc, id, reg) \ 127 bus_space_read_4((sc)->sc_bst, \ 128 (sc)->sc_phys[(id)].phy_bsh, (reg)) 129#define PMU_WRITE(sc, id, reg, val) \ 130 bus_space_write_4((sc)->sc_bst, \ 131 (sc)->sc_phys[(id)].phy_bsh, (reg), (val)) 132 133CFATTACH_DECL_NEW(sunxi_usbphy, sizeof(struct sunxi_usbphy_softc), 134 sunxi_usbphy_match, sunxi_usbphy_attach, NULL, NULL); 135 136static void 137sunxi_usbphy_write(struct sunxi_usbphy_softc *sc, 138 struct sunxi_usbphy *phy, u_int bit_addr, u_int bits, 139 u_int len) 140{ 141 const uint32_t usbc_mask = __BIT(phy->phy_index * 2); 142 bus_size_t reg; 143 uint32_t val; 144 145 switch (sc->sc_type) { 146 case USBPHY_A10: 147 case USBPHY_A13: 148 case USBPHY_A20: 149 case USBPHY_A31: 150 reg = PHYCTL_A10; 151 break; 152 case USBPHY_H3: 153 case USBPHY_H6: 154 case USBPHY_A64: 155 case USBPHY_A83T: 156 reg = PHYCTL_A33; 157 break; 158 default: 159 panic("unsupported phy type"); 160 } 161 162 if (reg == PHYCTL_A33) 163 PHYCTL_WRITE(sc, reg, 0); 164 165 for (; len > 0; bit_addr++, bits >>= 1, len--) { 166 val = PHYCTL_READ(sc, reg); 167 val &= ~PHYCTL_ADDR; 168 val |= __SHIFTIN(bit_addr, PHYCTL_ADDR); 169 PHYCTL_WRITE(sc, reg, val); 170 171 val = PHYCTL_READ(sc, reg); 172 val &= ~PHYCTL_DATA; 173 val |= __SHIFTIN(bits & 1, PHYCTL_DATA); 174 PHYCTL_WRITE(sc, reg, val); 175 176 PHYCTL_READ(sc, reg); 177 val |= usbc_mask; 178 PHYCTL_WRITE(sc, reg, val); 179 180 PHYCTL_READ(sc, reg); 181 val &= ~usbc_mask; 182 PHYCTL_WRITE(sc, reg, val); 183 } 184} 185 186static bool 187sunxi_usbphy_vbus_detect(struct sunxi_usbphy_softc *sc) 188{ 189 if (sc->sc_gpio_vbus_det) 190 return fdtbus_gpio_read(sc->sc_gpio_vbus_det); 191 return 1; 192} 193 194static void * 195sunxi_usbphy_acquire(device_t dev, const void *data, size_t len) 196{ 197 struct sunxi_usbphy_softc * const sc = device_private(dev); 198 199 if (len != 4) 200 return NULL; 201 202 const int phy_id = be32dec(data); 203 if (phy_id >= sc->sc_nphys || !sc->sc_phys[phy_id].phy_bsh) 204 return NULL; 205 206 return &sc->sc_phys[phy_id]; 207} 208 209static void 210sunxi_usbphy_release(device_t dev, void *priv) 211{ 212} 213 214static int 215sunxi_usbphy_enable(device_t dev, void *priv, bool enable) 216{ 217 struct sunxi_usbphy_softc * const sc = device_private(dev); 218 struct sunxi_usbphy * const phy = priv; 219 u_int disc_thresh; 220 bool phy0_reroute; 221 uint32_t val; 222 223 switch (sc->sc_type) { 224 case USBPHY_A13: 225 disc_thresh = 0x2; 226 phy0_reroute = false; 227 break; 228 case USBPHY_A10: 229 case USBPHY_A20: 230 case USBPHY_A31: 231 disc_thresh = 0x3; 232 phy0_reroute = false; 233 break; 234 case USBPHY_A64: 235 case USBPHY_H3: 236 case USBPHY_H6: 237 disc_thresh = 0x3; 238 phy0_reroute = true; 239 break; 240 case USBPHY_A83T: 241 disc_thresh = 0x0; 242 phy0_reroute = false; 243 break; 244 default: 245 aprint_error_dev(dev, "unsupported board\n"); 246 return ENXIO; 247 } 248 249 if (phy->phy_bsh) { 250 /* Enable/disable passby */ 251 const uint32_t mask = 252 ULPI_BYPASS|AHB_INCR8|AHB_INCR4|AHB_INCRX_ALIGN; 253 val = PMU_READ(sc, phy->phy_index, PMU_CFG); 254 if (enable) 255 val |= mask; 256 else 257 val &= ~mask; 258 PMU_WRITE(sc, phy->phy_index, PMU_CFG, val); 259 } 260 261 switch (sc->sc_type) { 262 case USBPHY_H3: 263 case USBPHY_A64: 264 if (enable && phy->phy_bsh) { 265 val = PMU_READ(sc, phy->phy_index, PMU_UNK_H3); 266 val &= ~PMU_UNK_H3_CLR; 267 PMU_WRITE(sc, phy->phy_index, PMU_UNK_H3, val); 268 } 269 break; 270 default: 271 break; 272 } 273 274 if (enable) { 275 switch (sc->sc_type) { 276 case USBPHY_A83T: 277 case USBPHY_H6: 278 break; 279 default: 280 if (phy->phy_index == 0) 281 sunxi_usbphy_write(sc, phy, PHY_RES45_CAL_EN, 0x1, 1); 282 sunxi_usbphy_write(sc, phy, PHY_TX_AMPLITUDE_TUNE, 0x14, 5); 283 sunxi_usbphy_write(sc, phy, PHY_DISCON_TH_SEL, disc_thresh, 2); 284 break; 285 } 286 } 287 288 if (phy->phy_index == 0) { 289 const uint32_t mask = 290 PHYCTL_ICR_ID_PULLUP|PHYCTL_ICR_DPDM_PULLUP; 291 val = PHYCTL_READ(sc, PHYCTL_ICR); 292 293 if (enable) 294 val |= mask; 295 else 296 val &= ~mask; 297 298 /* XXX only host mode is supported */ 299 val &= ~PHYCTL_ICR_FORCE_ID; 300 val |= __SHIFTIN(PHYCTL_ICR_FORCE_ID_LOW, PHYCTL_ICR_FORCE_ID); 301 val &= ~PHYCTL_ICR_FORCE_VBUS; 302 val |= __SHIFTIN(PHYCTL_ICR_FORCE_VBUS_HIGH, PHYCTL_ICR_FORCE_VBUS); 303 304 PHYCTL_WRITE(sc, PHYCTL_ICR, val); 305 306 if (phy0_reroute) { 307 val = PHYCTL_READ(sc, PHYCTL_OTG_CFG); 308 val &= ~PHYCTL_OTG_ROUTE_OTG; 309 PHYCTL_WRITE(sc, PHYCTL_OTG_CFG, val); 310 } 311 } 312 313 if (phy->phy_reg == NULL) 314 return 0; 315 316 if (enable) { 317 /* If an external vbus is detected, do not enable phy 0 */ 318 if (phy->phy_index == 0 && sunxi_usbphy_vbus_detect(sc)) 319 return 0; 320 return fdtbus_regulator_enable(phy->phy_reg); 321 } else { 322 return fdtbus_regulator_disable(phy->phy_reg); 323 } 324} 325 326const struct fdtbus_phy_controller_func sunxi_usbphy_funcs = { 327 .acquire = sunxi_usbphy_acquire, 328 .release = sunxi_usbphy_release, 329 .enable = sunxi_usbphy_enable, 330}; 331 332static int 333sunxi_usbphy_match(device_t parent, cfdata_t cf, void *aux) 334{ 335 struct fdt_attach_args * const faa = aux; 336 337 return of_match_compat_data(faa->faa_phandle, compat_data); 338} 339 340static void 341sunxi_usbphy_attach(device_t parent, device_t self, void *aux) 342{ 343 struct sunxi_usbphy_softc * const sc = device_private(self); 344 struct fdt_attach_args * const faa = aux; 345 const int phandle = faa->faa_phandle; 346 struct fdtbus_reset *rst; 347 struct sunxi_usbphy *phy; 348 struct clk *clk; 349 bus_addr_t addr; 350 bus_size_t size; 351 char pname[20]; 352 u_int n; 353 354 sc->sc_dev = self; 355 sc->sc_bst = faa->faa_bst; 356 sc->sc_type = of_search_compatible(phandle, compat_data)->value; 357 358 if (fdtbus_get_reg_byname(phandle, "phy_ctrl", &addr, &size) != 0) { 359 aprint_error(": couldn't get phy ctrl registers\n"); 360 return; 361 } 362 if (bus_space_map(sc->sc_bst, addr, size, 0, &sc->sc_bsh_phy_ctrl) != 0) { 363 aprint_error(": couldn't map phy ctrl registers\n"); 364 return; 365 } 366 367 for (sc->sc_nphys = 0; sc->sc_nphys < SUNXI_MAXUSBPHY; sc->sc_nphys++) { 368 phy = &sc->sc_phys[sc->sc_nphys]; 369 phy->phy_index = sc->sc_nphys; 370 snprintf(pname, sizeof(pname), "pmu%d", sc->sc_nphys); 371 if (fdtbus_get_reg_byname(phandle, pname, &addr, &size) != 0) { 372 continue; 373 } else if (bus_space_map(sc->sc_bst, addr, size, 0, &phy->phy_bsh) != 0) { 374 aprint_error(": failed to map reg #%d\n", sc->sc_nphys); 375 return; 376 } 377 /* Get optional regulator */ 378 snprintf(pname, sizeof(pname), "usb%d_vbus-supply", sc->sc_nphys); 379 phy->phy_reg = fdtbus_regulator_acquire(phandle, pname); 380 } 381 382 /* Enable clocks */ 383 for (n = 0; (clk = fdtbus_clock_get_index(phandle, n)) != NULL; n++) 384 if (clk_enable(clk) != 0) { 385 aprint_error(": couldn't enable clock #%d\n", n); 386 return; 387 } 388 /* De-assert resets */ 389 for (n = 0; (rst = fdtbus_reset_get_index(phandle, n)) != NULL; n++) 390 if (fdtbus_reset_deassert(rst) != 0) { 391 aprint_error(": couldn't de-assert reset #%d\n", n); 392 return; 393 } 394 395 aprint_naive("\n"); 396 aprint_normal(": USB PHY\n"); 397 398 fdtbus_register_phy_controller(self, phandle, &sunxi_usbphy_funcs); 399} 400