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