1 /* $NetBSD: sunxi_usb3phy.c,v 1.1 2018/05/01 23:59:42 jmcneill Exp $ */ 2 3 /*- 4 * Copyright (c) 2018 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 /* 30 * Allwinner USB3PHY 31 */ 32 33 #include <sys/cdefs.h> 34 __FBSDID("$FreeBSD$"); 35 #include <sys/param.h> 36 #include <sys/systm.h> 37 #include <sys/bus.h> 38 #include <sys/rman.h> 39 #include <sys/kernel.h> 40 #include <sys/module.h> 41 #include <sys/gpio.h> 42 #include <machine/bus.h> 43 44 #include <dev/ofw/ofw_bus.h> 45 #include <dev/ofw/ofw_bus_subr.h> 46 47 #include <dev/extres/clk/clk.h> 48 #include <dev/extres/hwreset/hwreset.h> 49 #include <dev/extres/regulator/regulator.h> 50 #include <dev/extres/phy/phy_usb.h> 51 52 #include "phynode_if.h" 53 54 #define USB3PHY_APP 0x00 55 #define APP_FORCE_VBUS (0x3 << 12) 56 57 #define USB3PHY_PIPE_CLOCK_CONTROL 0x14 58 #define PCC_PIPE_CLK_OPEN (1 << 6) 59 60 #define USB3PHY_PHY_TUNE_LOW 0x18 61 #define PTL_MAGIC 0x0047fc87 62 63 #define USB3PHY_PHY_TUNE_HIGH 0x1c 64 #define PTH_TX_DEEMPH_3P5DB (0x1F << 19) 65 #define PTH_TX_DEEMPH_6DB (0x3F << 13) 66 #define PTH_TX_SWING_FULL (0x7F << 6) 67 #define PTH_LOS_BIAS (0x7 << 3) 68 #define PTH_TX_BOOST_LVL (0x7 << 0) 69 70 #define USB3PHY_PHY_EXTERNAL_CONTROL 0x20 71 #define PEC_REF_SSP_EN (1 << 26) 72 #define PEC_SSC_EN (1 << 24) 73 #define PEC_EXTERN_VBUS (0x3 << 1) 74 75 #define __LOWEST_SET_BIT(__mask) ((((__mask) - 1) & (__mask)) ^ (__mask)) 76 #define __SHIFTIN(__x, __mask) ((__x) * __LOWEST_SET_BIT(__mask)) 77 78 static struct ofw_compat_data compat_data[] = { 79 { "allwinner,sun50i-h6-usb3-phy", 1 }, 80 { NULL, 0 } 81 }; 82 83 static struct resource_spec aw_usb3phy_spec[] = { 84 { SYS_RES_MEMORY, 0, RF_ACTIVE }, 85 { -1, 0 } 86 }; 87 88 struct awusb3phy_softc { 89 struct resource * res; 90 regulator_t reg; 91 int mode; 92 }; 93 94 /* Phy class and methods. */ 95 static int awusb3phy_phy_enable(struct phynode *phy, bool enable); 96 static int awusb3phy_get_mode(struct phynode *phy, int *mode); 97 static int awusb3phy_set_mode(struct phynode *phy, int mode); 98 static phynode_usb_method_t awusb3phy_phynode_methods[] = { 99 PHYNODEMETHOD(phynode_enable, awusb3phy_phy_enable), 100 PHYNODEMETHOD(phynode_usb_get_mode, awusb3phy_get_mode), 101 PHYNODEMETHOD(phynode_usb_set_mode, awusb3phy_set_mode), 102 103 PHYNODEMETHOD_END 104 }; 105 DEFINE_CLASS_1(awusb3phy_phynode, awusb3phy_phynode_class, awusb3phy_phynode_methods, 106 sizeof(struct phynode_usb_sc), phynode_usb_class); 107 108 #define RD4(res, o) bus_read_4(res, (o)) 109 #define WR4(res, o, v) bus_write_4(res, (o), (v)) 110 111 static int 112 awusb3phy_phy_enable(struct phynode *phynode, bool enable) 113 { 114 struct awusb3phy_softc *sc; 115 device_t dev; 116 uint32_t val; 117 int error = 0; 118 119 dev = phynode_get_device(phynode); 120 sc = device_get_softc(dev); 121 122 device_printf(dev, "%s: called\n", __func__); 123 124 if (enable) { 125 val = RD4(sc->res, USB3PHY_PHY_EXTERNAL_CONTROL); 126 device_printf(dev, "EXTERNAL_CONTROL: %x\n", val); 127 val |= PEC_EXTERN_VBUS; 128 val |= PEC_SSC_EN; 129 val |= PEC_REF_SSP_EN; 130 device_printf(dev, "EXTERNAL_CONTROL: %x\n", val); 131 WR4(sc->res, USB3PHY_PHY_EXTERNAL_CONTROL, val); 132 133 val = RD4(sc->res, USB3PHY_PIPE_CLOCK_CONTROL); 134 device_printf(dev, "PIPE_CONTROL: %x\n", val); 135 val |= PCC_PIPE_CLK_OPEN; 136 device_printf(dev, "PIPE_CONTROL: %x\n", val); 137 WR4(sc->res, USB3PHY_PIPE_CLOCK_CONTROL, val); 138 139 val = RD4(sc->res, USB3PHY_APP); 140 device_printf(dev, "APP: %x\n", val); 141 val |= APP_FORCE_VBUS; 142 device_printf(dev, "APP: %x\n", val); 143 WR4(sc->res, USB3PHY_APP, val); 144 145 WR4(sc->res, USB3PHY_PHY_TUNE_LOW, PTL_MAGIC); 146 147 val = RD4(sc->res, USB3PHY_PHY_TUNE_HIGH); 148 device_printf(dev, "PHY_TUNE_HIGH: %x\n", val); 149 val |= PTH_TX_BOOST_LVL; 150 val |= PTH_LOS_BIAS; 151 val &= ~PTH_TX_SWING_FULL; 152 val |= __SHIFTIN(0x55, PTH_TX_SWING_FULL); 153 val &= ~PTH_TX_DEEMPH_6DB; 154 val |= __SHIFTIN(0x20, PTH_TX_DEEMPH_6DB); 155 val &= ~PTH_TX_DEEMPH_3P5DB; 156 val |= __SHIFTIN(0x15, PTH_TX_DEEMPH_3P5DB); 157 device_printf(dev, "PHY_TUNE_HIGH: %x\n", val); 158 WR4(sc->res, USB3PHY_PHY_TUNE_HIGH, val); 159 160 if (sc->reg) 161 error = regulator_enable(sc->reg); 162 } else { 163 if (sc->reg) 164 error = regulator_disable(sc->reg); 165 } 166 167 if (error != 0) { 168 device_printf(dev, 169 "couldn't %s regulator for phy\n", 170 enable ? "enable" : "disable"); 171 return (error); 172 } 173 174 return (0); 175 } 176 177 static int 178 awusb3phy_get_mode(struct phynode *phynode, int *mode) 179 { 180 struct awusb3phy_softc *sc; 181 device_t dev; 182 183 dev = phynode_get_device(phynode); 184 sc = device_get_softc(dev); 185 186 *mode = sc->mode; 187 188 return (0); 189 } 190 191 static int 192 awusb3phy_set_mode(struct phynode *phynode, int mode) 193 { 194 device_t dev; 195 intptr_t phy; 196 struct awusb3phy_softc *sc; 197 198 dev = phynode_get_device(phynode); 199 phy = phynode_get_id(phynode); 200 sc = device_get_softc(dev); 201 202 if (mode != PHY_USB_MODE_HOST) 203 return (EINVAL); 204 205 sc->mode = mode; 206 207 return (0); 208 } 209 210 static int 211 awusb3phy_probe(device_t dev) 212 { 213 if (!ofw_bus_status_okay(dev)) 214 return (ENXIO); 215 216 if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0) 217 return (ENXIO); 218 219 device_set_desc(dev, "Allwinner USB3PHY"); 220 return (BUS_PROBE_DEFAULT); 221 } 222 223 static int 224 awusb3phy_attach(device_t dev) 225 { 226 struct phynode *phynode; 227 struct phynode_init_def phy_init; 228 struct awusb3phy_softc *sc; 229 clk_t clk; 230 hwreset_t rst; 231 phandle_t node; 232 int error, i; 233 234 sc = device_get_softc(dev); 235 node = ofw_bus_get_node(dev); 236 237 if (bus_alloc_resources(dev, aw_usb3phy_spec, &sc->res) != 0) { 238 device_printf(dev, "cannot allocate resources for device\n"); 239 return (ENXIO); 240 } 241 242 /* Enable clocks */ 243 for (i = 0; clk_get_by_ofw_index(dev, 0, i, &clk) == 0; i++) { 244 error = clk_enable(clk); 245 if (error != 0) { 246 device_printf(dev, "couldn't enable clock %s\n", 247 clk_get_name(clk)); 248 return (error); 249 } 250 } 251 252 /* De-assert resets */ 253 for (i = 0; hwreset_get_by_ofw_idx(dev, 0, i, &rst) == 0; i++) { 254 error = hwreset_deassert(rst); 255 if (error != 0) { 256 device_printf(dev, "couldn't de-assert reset %d\n", 257 i); 258 return (error); 259 } 260 } 261 262 /* Get regulators */ 263 regulator_get_by_ofw_property(dev, node, "phy-supply", &sc->reg); 264 265 /* Create the phy */ 266 phy_init.ofw_node = ofw_bus_get_node(dev); 267 phynode = phynode_create(dev, &awusb3phy_phynode_class, 268 &phy_init); 269 if (phynode == NULL) { 270 device_printf(dev, "failed to create USB PHY\n"); 271 return (ENXIO); 272 } 273 if (phynode_register(phynode) == NULL) { 274 device_printf(dev, "failed to create USB PHY\n"); 275 return (ENXIO); 276 } 277 278 return (error); 279 } 280 281 static device_method_t awusb3phy_methods[] = { 282 /* Device interface */ 283 DEVMETHOD(device_probe, awusb3phy_probe), 284 DEVMETHOD(device_attach, awusb3phy_attach), 285 286 DEVMETHOD_END 287 }; 288 289 static driver_t awusb3phy_driver = { 290 "awusb3phy", 291 awusb3phy_methods, 292 sizeof(struct awusb3phy_softc) 293 }; 294 295 static devclass_t awusb3phy_devclass; 296 /* aw_usb3phy needs to come up after regulators/gpio/etc, but before ehci/ohci */ 297 EARLY_DRIVER_MODULE(awusb3phy, simplebus, awusb3phy_driver, awusb3phy_devclass, 298 0, 0, BUS_PASS_SUPPORTDEV + BUS_PASS_ORDER_MIDDLE); 299 MODULE_VERSION(awusb3phy, 1); 300