sunxi_usbphy.c revision 1.1
1/* $NetBSD: sunxi_usbphy.c,v 1.1 2017/06/29 17:08:52 jmcneill 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.1 2017/06/29 17:08:52 jmcneill 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#define OTG_PHY_CFG 0x20 43#define OTG_PHY_ROUTE_OTG __BIT(0) 44 45#define HCI_ICR 0x00 46#define HCI_AHB_INCR8 __BIT(10) 47#define HCI_AHB_INCR4 __BIT(9) 48#define HCI_AHB_INCRX_ALIGN __BIT(8) 49#define HCI_ULPI_BYPASS __BIT(0) 50#define PMU_UNK_H3 0x10 51#define PMU_UNK_H3_CLR __BIT(1) 52 53static int sunxi_usbphy_match(device_t, cfdata_t, void *); 54static void sunxi_usbphy_attach(device_t, device_t, void *); 55 56static const char * const compatible[] = { 57 "allwinner,sun8i-h3-usb-phy", 58 NULL 59}; 60 61#define SUNXI_MAXUSBPHY 5 62 63struct sunxi_usbphy { 64 u_int phy_index; 65 bus_space_handle_t phy_bsh; 66 struct fdtbus_regulator *phy_reg; 67}; 68 69struct sunxi_usbphy_softc { 70 device_t sc_dev; 71 bus_space_tag_t sc_bst; 72 73 struct sunxi_usbphy sc_phys[SUNXI_MAXUSBPHY]; 74 u_int sc_nphys; 75 76 struct fdtbus_gpio_pin *sc_gpio_id_det; 77 struct fdtbus_gpio_pin *sc_gpio_vbus_det; 78}; 79 80#define USBPHY_READ(sc, id, reg) \ 81 bus_space_read_4((sc)->sc_bst, \ 82 (sc)->sc_phys[(id)].phy_bsh, (reg)) 83#define USBPHY_WRITE(sc, id, reg, val) \ 84 bus_space_write_4((sc)->sc_bst, \ 85 (sc)->sc_phys[(id)].phy_bsh, (reg), (val)) 86 87CFATTACH_DECL_NEW(sunxi_usbphy, sizeof(struct sunxi_usbphy_softc), 88 sunxi_usbphy_match, sunxi_usbphy_attach, NULL, NULL); 89 90static bool 91sunxi_usbphy_vbus_detect(struct sunxi_usbphy_softc *sc) 92{ 93 if (sc->sc_gpio_vbus_det) 94 return fdtbus_gpio_read(sc->sc_gpio_vbus_det); 95 return 1; 96} 97 98static void * 99sunxi_usbphy_acquire(device_t dev, const void *data, size_t len) 100{ 101 struct sunxi_usbphy_softc * const sc = device_private(dev); 102 103 if (len != 4) 104 return NULL; 105 106 const int phy_id = be32dec(data); 107 if (phy_id >= sc->sc_nphys) 108 return NULL; 109 110 return &sc->sc_phys[phy_id]; 111} 112 113static void 114sunxi_usbphy_release(device_t dev, void *priv) 115{ 116} 117 118static int 119sunxi_usbphy_enable(device_t dev, void *priv, bool enable) 120{ 121 struct sunxi_usbphy_softc * const sc = device_private(dev); 122 struct sunxi_usbphy * const phy = priv; 123 uint32_t val; 124 125 if (phy->phy_index > 0) { 126 /* Enable passby */ 127 val = USBPHY_READ(sc, phy->phy_index, HCI_ICR); 128 val |= HCI_ULPI_BYPASS; 129 val |= HCI_AHB_INCR8; 130 val |= HCI_AHB_INCR4; 131 val |= HCI_AHB_INCRX_ALIGN; 132 USBPHY_WRITE(sc, phy->phy_index, HCI_ICR, val); 133 } 134 135 /* H3-specific */ 136 val = USBPHY_READ(sc, phy->phy_index, PMU_UNK_H3); 137 val &= ~PMU_UNK_H3_CLR; 138 USBPHY_WRITE(sc, phy->phy_index, PMU_UNK_H3, val); 139 140 if (phy->phy_reg == NULL) 141 return 0; 142 143 if (enable) { 144 /* If an external vbus is detected, do not enable phy 0 */ 145 if (phy->phy_index == 0 && sunxi_usbphy_vbus_detect(sc)) 146 return 0; 147 return fdtbus_regulator_enable(phy->phy_reg); 148 } else { 149 return fdtbus_regulator_disable(phy->phy_reg); 150 } 151} 152 153const struct fdtbus_phy_controller_func sunxi_usbphy_funcs = { 154 .acquire = sunxi_usbphy_acquire, 155 .release = sunxi_usbphy_release, 156 .enable = sunxi_usbphy_enable, 157}; 158 159static int 160sunxi_usbphy_match(device_t parent, cfdata_t cf, void *aux) 161{ 162 struct fdt_attach_args * const faa = aux; 163 164 return of_match_compatible(faa->faa_phandle, compatible); 165} 166 167static void 168sunxi_usbphy_attach(device_t parent, device_t self, void *aux) 169{ 170 struct sunxi_usbphy_softc * const sc = device_private(self); 171 struct fdt_attach_args * const faa = aux; 172 const int phandle = faa->faa_phandle; 173 struct fdtbus_reset *rst; 174 struct sunxi_usbphy *phy; 175 struct clk *clk; 176 bus_addr_t addr; 177 bus_size_t size; 178 char pname[20]; 179 u_int n; 180 181 sc->sc_dev = self; 182 sc->sc_bst = faa->faa_bst; 183 184 for (sc->sc_nphys = 0; sc->sc_nphys < SUNXI_MAXUSBPHY; sc->sc_nphys++) { 185 if (fdtbus_get_reg(phandle, sc->sc_nphys, &addr, &size) != 0) 186 break; 187 phy = &sc->sc_phys[sc->sc_nphys]; 188 phy->phy_index = sc->sc_nphys; 189 if (bus_space_map(sc->sc_bst, addr, size, 0, &phy->phy_bsh) != 0) { 190 aprint_error(": failed to map reg #%d\n", sc->sc_nphys); 191 return; 192 } 193 /* Get optional regulator */ 194 snprintf(pname, sizeof(pname), "usb%d_vbus-supply", sc->sc_nphys); 195 phy->phy_reg = fdtbus_regulator_acquire(phandle, pname); 196 } 197 198 /* Enable clocks */ 199 for (n = 0; (clk = fdtbus_clock_get_index(phandle, n)) != NULL; n++) 200 if (clk_enable(clk) != 0) { 201 aprint_error(": couldn't enable clock #%d\n", n); 202 return; 203 } 204 /* De-assert resets */ 205 for (n = 0; (rst = fdtbus_reset_get_index(phandle, n)) != NULL; n++) 206 if (fdtbus_reset_deassert(rst) != 0) { 207 aprint_error(": couldn't de-assert reset #%d\n", n); 208 return; 209 } 210 211 aprint_naive("\n"); 212 aprint_normal(": USB PHY\n"); 213 214 fdtbus_register_phy_controller(self, phandle, &sunxi_usbphy_funcs); 215} 216