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 struct awusb3phy_softc *sc; 196 197 dev = phynode_get_device(phynode); 198 sc = device_get_softc(dev); 199 200 if (mode != PHY_USB_MODE_HOST) 201 return (EINVAL); 202 203 sc->mode = mode; 204 205 return (0); 206 } 207 208 static int 209 awusb3phy_probe(device_t dev) 210 { 211 if (!ofw_bus_status_okay(dev)) 212 return (ENXIO); 213 214 if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0) 215 return (ENXIO); 216 217 device_set_desc(dev, "Allwinner USB3PHY"); 218 return (BUS_PROBE_DEFAULT); 219 } 220 221 static int 222 awusb3phy_attach(device_t dev) 223 { 224 struct phynode *phynode; 225 struct phynode_init_def phy_init; 226 struct awusb3phy_softc *sc; 227 clk_t clk; 228 hwreset_t rst; 229 phandle_t node; 230 int error, i; 231 232 sc = device_get_softc(dev); 233 node = ofw_bus_get_node(dev); 234 235 if (bus_alloc_resources(dev, aw_usb3phy_spec, &sc->res) != 0) { 236 device_printf(dev, "cannot allocate resources for device\n"); 237 return (ENXIO); 238 } 239 240 /* Enable clocks */ 241 for (i = 0; clk_get_by_ofw_index(dev, 0, i, &clk) == 0; i++) { 242 error = clk_enable(clk); 243 if (error != 0) { 244 device_printf(dev, "couldn't enable clock %s\n", 245 clk_get_name(clk)); 246 return (error); 247 } 248 } 249 250 /* De-assert resets */ 251 for (i = 0; hwreset_get_by_ofw_idx(dev, 0, i, &rst) == 0; i++) { 252 error = hwreset_deassert(rst); 253 if (error != 0) { 254 device_printf(dev, "couldn't de-assert reset %d\n", 255 i); 256 return (error); 257 } 258 } 259 260 /* Get regulators */ 261 regulator_get_by_ofw_property(dev, node, "phy-supply", &sc->reg); 262 263 /* Create the phy */ 264 phy_init.ofw_node = ofw_bus_get_node(dev); 265 phynode = phynode_create(dev, &awusb3phy_phynode_class, 266 &phy_init); 267 if (phynode == NULL) { 268 device_printf(dev, "failed to create USB PHY\n"); 269 return (ENXIO); 270 } 271 if (phynode_register(phynode) == NULL) { 272 device_printf(dev, "failed to create USB PHY\n"); 273 return (ENXIO); 274 } 275 276 return (error); 277 } 278 279 static device_method_t awusb3phy_methods[] = { 280 /* Device interface */ 281 DEVMETHOD(device_probe, awusb3phy_probe), 282 DEVMETHOD(device_attach, awusb3phy_attach), 283 284 DEVMETHOD_END 285 }; 286 287 static driver_t awusb3phy_driver = { 288 "awusb3phy", 289 awusb3phy_methods, 290 sizeof(struct awusb3phy_softc) 291 }; 292 293 static devclass_t awusb3phy_devclass; 294 /* aw_usb3phy needs to come up after regulators/gpio/etc, but before ehci/ohci */ 295 EARLY_DRIVER_MODULE(awusb3phy, simplebus, awusb3phy_driver, awusb3phy_devclass, 296 0, 0, BUS_PASS_SUPPORTDEV + BUS_PASS_ORDER_MIDDLE); 297 MODULE_VERSION(awusb3phy, 1); 298