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