1*86f0c3ecSAdrian Chadd /*- 2*86f0c3ecSAdrian Chadd * SPDX-License-Identifier: BSD-2-Clause-FreeBSD 3*86f0c3ecSAdrian Chadd * 4*86f0c3ecSAdrian Chadd * Copyright (c) 2019 Michal Meloun <mmel@FreeBSD.org> 5*86f0c3ecSAdrian Chadd * 6*86f0c3ecSAdrian Chadd * Redistribution and use in source and binary forms, with or without 7*86f0c3ecSAdrian Chadd * modification, are permitted provided that the following conditions 8*86f0c3ecSAdrian Chadd * are met: 9*86f0c3ecSAdrian Chadd * 1. Redistributions of source code must retain the above copyright 10*86f0c3ecSAdrian Chadd * notice, this list of conditions and the following disclaimer. 11*86f0c3ecSAdrian Chadd * 2. Redistributions in binary form must reproduce the above copyright 12*86f0c3ecSAdrian Chadd * notice, this list of conditions and the following disclaimer in the 13*86f0c3ecSAdrian Chadd * documentation and/or other materials provided with the distribution. 14*86f0c3ecSAdrian Chadd * 15*86f0c3ecSAdrian Chadd * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 16*86f0c3ecSAdrian Chadd * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17*86f0c3ecSAdrian Chadd * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18*86f0c3ecSAdrian Chadd * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 19*86f0c3ecSAdrian Chadd * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20*86f0c3ecSAdrian Chadd * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21*86f0c3ecSAdrian Chadd * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22*86f0c3ecSAdrian Chadd * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23*86f0c3ecSAdrian Chadd * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24*86f0c3ecSAdrian Chadd * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25*86f0c3ecSAdrian Chadd * SUCH DAMAGE. 26*86f0c3ecSAdrian Chadd */ 27*86f0c3ecSAdrian Chadd 28*86f0c3ecSAdrian Chadd #include <sys/cdefs.h> 29*86f0c3ecSAdrian Chadd __FBSDID("$FreeBSD$"); 30*86f0c3ecSAdrian Chadd 31*86f0c3ecSAdrian Chadd #include <sys/param.h> 32*86f0c3ecSAdrian Chadd #include <sys/systm.h> 33*86f0c3ecSAdrian Chadd #include <sys/bus.h> 34*86f0c3ecSAdrian Chadd #include <sys/gpio.h> 35*86f0c3ecSAdrian Chadd #include <sys/kernel.h> 36*86f0c3ecSAdrian Chadd #include <sys/module.h> 37*86f0c3ecSAdrian Chadd #include <sys/malloc.h> 38*86f0c3ecSAdrian Chadd #include <sys/rman.h> 39*86f0c3ecSAdrian Chadd 40*86f0c3ecSAdrian Chadd #include <machine/bus.h> 41*86f0c3ecSAdrian Chadd 42*86f0c3ecSAdrian Chadd #include <dev/extres/hwreset/hwreset.h> 43*86f0c3ecSAdrian Chadd #include <dev/extres/phy/phy_usb.h> 44*86f0c3ecSAdrian Chadd #include <dev/extres/regulator/regulator.h> 45*86f0c3ecSAdrian Chadd #include <dev/ofw/ofw_bus.h> 46*86f0c3ecSAdrian Chadd #include <dev/ofw/ofw_bus_subr.h> 47*86f0c3ecSAdrian Chadd 48*86f0c3ecSAdrian Chadd #include <dev/fdt/simple_mfd.h> 49*86f0c3ecSAdrian Chadd #include "phynode_if.h" 50*86f0c3ecSAdrian Chadd #include "phynode_usb_if.h" 51*86f0c3ecSAdrian Chadd 52*86f0c3ecSAdrian Chadd static struct ofw_compat_data compat_data[] = { 53*86f0c3ecSAdrian Chadd {"qcom,usb-hs-ipq4019-phy", 1}, 54*86f0c3ecSAdrian Chadd {NULL, 0}, 55*86f0c3ecSAdrian Chadd }; 56*86f0c3ecSAdrian Chadd 57*86f0c3ecSAdrian Chadd struct ipq4018_usb_hs_phy_softc { 58*86f0c3ecSAdrian Chadd device_t dev; 59*86f0c3ecSAdrian Chadd }; 60*86f0c3ecSAdrian Chadd 61*86f0c3ecSAdrian Chadd struct ipq4018_usb_hs_phynode_sc { 62*86f0c3ecSAdrian Chadd struct phynode_usb_sc usb_sc; 63*86f0c3ecSAdrian Chadd int mode; 64*86f0c3ecSAdrian Chadd hwreset_t por_rst; 65*86f0c3ecSAdrian Chadd hwreset_t srif_rst; 66*86f0c3ecSAdrian Chadd }; 67*86f0c3ecSAdrian Chadd 68*86f0c3ecSAdrian Chadd static int 69*86f0c3ecSAdrian Chadd ipq4018_usb_hs_phynode_phy_enable(struct phynode *phynode, bool enable) 70*86f0c3ecSAdrian Chadd { 71*86f0c3ecSAdrian Chadd struct ipq4018_usb_hs_phynode_sc *sc; 72*86f0c3ecSAdrian Chadd device_t dev; 73*86f0c3ecSAdrian Chadd int rv; 74*86f0c3ecSAdrian Chadd 75*86f0c3ecSAdrian Chadd dev = phynode_get_device(phynode); 76*86f0c3ecSAdrian Chadd sc = phynode_get_softc(phynode); 77*86f0c3ecSAdrian Chadd 78*86f0c3ecSAdrian Chadd /* 79*86f0c3ecSAdrian Chadd * 80*86f0c3ecSAdrian Chadd * For power-off - assert por, sleep for 10ms, assert srif, 81*86f0c3ecSAdrian Chadd * sleep for 10ms 82*86f0c3ecSAdrian Chadd */ 83*86f0c3ecSAdrian Chadd rv = hwreset_assert(sc->por_rst); 84*86f0c3ecSAdrian Chadd if (rv != 0) 85*86f0c3ecSAdrian Chadd goto done; 86*86f0c3ecSAdrian Chadd DELAY(10*1000); 87*86f0c3ecSAdrian Chadd rv = hwreset_assert(sc->srif_rst); 88*86f0c3ecSAdrian Chadd if (rv != 0) 89*86f0c3ecSAdrian Chadd goto done; 90*86f0c3ecSAdrian Chadd DELAY(10*1000); 91*86f0c3ecSAdrian Chadd 92*86f0c3ecSAdrian Chadd /* 93*86f0c3ecSAdrian Chadd * For power-on - power off first, then deassert srif, then 94*86f0c3ecSAdrian Chadd * sleep for 10ms, then deassert por. 95*86f0c3ecSAdrian Chadd */ 96*86f0c3ecSAdrian Chadd if (enable) { 97*86f0c3ecSAdrian Chadd rv = hwreset_deassert(sc->srif_rst); 98*86f0c3ecSAdrian Chadd if (rv != 0) 99*86f0c3ecSAdrian Chadd goto done; 100*86f0c3ecSAdrian Chadd DELAY(10*1000); 101*86f0c3ecSAdrian Chadd rv = hwreset_deassert(sc->por_rst); 102*86f0c3ecSAdrian Chadd if (rv != 0) 103*86f0c3ecSAdrian Chadd goto done; 104*86f0c3ecSAdrian Chadd DELAY(10*1000); 105*86f0c3ecSAdrian Chadd } 106*86f0c3ecSAdrian Chadd 107*86f0c3ecSAdrian Chadd done: 108*86f0c3ecSAdrian Chadd if (rv != 0) { 109*86f0c3ecSAdrian Chadd device_printf(dev, "%s: failed, rv=%d\n", __func__, rv); 110*86f0c3ecSAdrian Chadd } 111*86f0c3ecSAdrian Chadd return (rv); 112*86f0c3ecSAdrian Chadd } 113*86f0c3ecSAdrian Chadd 114*86f0c3ecSAdrian Chadd /* Phy controller class and methods. */ 115*86f0c3ecSAdrian Chadd static phynode_method_t ipq4018_usb_hs_phynode_methods[] = { 116*86f0c3ecSAdrian Chadd PHYNODEUSBMETHOD(phynode_enable, ipq4018_usb_hs_phynode_phy_enable), 117*86f0c3ecSAdrian Chadd PHYNODEUSBMETHOD_END 118*86f0c3ecSAdrian Chadd }; 119*86f0c3ecSAdrian Chadd DEFINE_CLASS_1(ipq4018_usb_hs_phynode, ipq4018_usb_hs_phynode_class, 120*86f0c3ecSAdrian Chadd ipq4018_usb_hs_phynode_methods, 121*86f0c3ecSAdrian Chadd sizeof(struct ipq4018_usb_hs_phynode_sc), phynode_usb_class); 122*86f0c3ecSAdrian Chadd 123*86f0c3ecSAdrian Chadd static int 124*86f0c3ecSAdrian Chadd ipq4018_usb_hs_usbphy_init_phy(struct ipq4018_usb_hs_phy_softc *sc, 125*86f0c3ecSAdrian Chadd phandle_t node) 126*86f0c3ecSAdrian Chadd { 127*86f0c3ecSAdrian Chadd struct phynode *phynode; 128*86f0c3ecSAdrian Chadd struct phynode_init_def phy_init; 129*86f0c3ecSAdrian Chadd struct ipq4018_usb_hs_phynode_sc *phy_sc; 130*86f0c3ecSAdrian Chadd int rv; 131*86f0c3ecSAdrian Chadd hwreset_t por_rst = NULL, srif_rst = NULL; 132*86f0c3ecSAdrian Chadd 133*86f0c3ecSAdrian Chadd /* FDT resources */ 134*86f0c3ecSAdrian Chadd rv = hwreset_get_by_ofw_name(sc->dev, node, "por_rst", &por_rst); 135*86f0c3ecSAdrian Chadd if (rv != 0 && rv != ENOENT) { 136*86f0c3ecSAdrian Chadd device_printf(sc->dev, "Cannot get 'por_rst' reset\n"); 137*86f0c3ecSAdrian Chadd goto fail; 138*86f0c3ecSAdrian Chadd } 139*86f0c3ecSAdrian Chadd rv = hwreset_get_by_ofw_name(sc->dev, node, "srif_rst", &srif_rst); 140*86f0c3ecSAdrian Chadd if (rv != 0 && rv != ENOENT) { 141*86f0c3ecSAdrian Chadd device_printf(sc->dev, "Cannot get 'srif_rst' reset\n"); 142*86f0c3ecSAdrian Chadd goto fail; 143*86f0c3ecSAdrian Chadd } 144*86f0c3ecSAdrian Chadd 145*86f0c3ecSAdrian Chadd /* Create and register phy. */ 146*86f0c3ecSAdrian Chadd bzero(&phy_init, sizeof(phy_init)); 147*86f0c3ecSAdrian Chadd phy_init.id = 1; 148*86f0c3ecSAdrian Chadd phy_init.ofw_node = node; 149*86f0c3ecSAdrian Chadd phynode = phynode_create(sc->dev, &ipq4018_usb_hs_phynode_class, 150*86f0c3ecSAdrian Chadd &phy_init); 151*86f0c3ecSAdrian Chadd if (phynode == NULL) { 152*86f0c3ecSAdrian Chadd device_printf(sc->dev, "Cannot create phy.\n"); 153*86f0c3ecSAdrian Chadd return (ENXIO); 154*86f0c3ecSAdrian Chadd } 155*86f0c3ecSAdrian Chadd 156*86f0c3ecSAdrian Chadd phy_sc = phynode_get_softc(phynode); 157*86f0c3ecSAdrian Chadd phy_sc->por_rst = por_rst; 158*86f0c3ecSAdrian Chadd phy_sc->srif_rst = srif_rst; 159*86f0c3ecSAdrian Chadd 160*86f0c3ecSAdrian Chadd if (phynode_register(phynode) == NULL) { 161*86f0c3ecSAdrian Chadd device_printf(sc->dev, "Cannot register phy.\n"); 162*86f0c3ecSAdrian Chadd return (ENXIO); 163*86f0c3ecSAdrian Chadd } 164*86f0c3ecSAdrian Chadd 165*86f0c3ecSAdrian Chadd (void) ipq4018_usb_hs_phynode_phy_enable(phynode, true); 166*86f0c3ecSAdrian Chadd 167*86f0c3ecSAdrian Chadd return (0); 168*86f0c3ecSAdrian Chadd 169*86f0c3ecSAdrian Chadd fail: 170*86f0c3ecSAdrian Chadd if (por_rst != NULL) 171*86f0c3ecSAdrian Chadd hwreset_release(por_rst); 172*86f0c3ecSAdrian Chadd if (srif_rst != NULL) 173*86f0c3ecSAdrian Chadd hwreset_release(srif_rst); 174*86f0c3ecSAdrian Chadd 175*86f0c3ecSAdrian Chadd return (ENXIO); 176*86f0c3ecSAdrian Chadd } 177*86f0c3ecSAdrian Chadd 178*86f0c3ecSAdrian Chadd static int 179*86f0c3ecSAdrian Chadd ipq4018_usb_hs_usbphy_probe(device_t dev) 180*86f0c3ecSAdrian Chadd { 181*86f0c3ecSAdrian Chadd 182*86f0c3ecSAdrian Chadd if (!ofw_bus_status_okay(dev)) 183*86f0c3ecSAdrian Chadd return (ENXIO); 184*86f0c3ecSAdrian Chadd 185*86f0c3ecSAdrian Chadd if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0) 186*86f0c3ecSAdrian Chadd return (ENXIO); 187*86f0c3ecSAdrian Chadd 188*86f0c3ecSAdrian Chadd device_set_desc(dev, "IPQ4018/IPQ4019 USB HS PHY"); 189*86f0c3ecSAdrian Chadd return (BUS_PROBE_DEFAULT); 190*86f0c3ecSAdrian Chadd } 191*86f0c3ecSAdrian Chadd 192*86f0c3ecSAdrian Chadd static int 193*86f0c3ecSAdrian Chadd ipq4018_usb_hs_usbphy_attach(device_t dev) 194*86f0c3ecSAdrian Chadd { 195*86f0c3ecSAdrian Chadd struct ipq4018_usb_hs_phy_softc *sc; 196*86f0c3ecSAdrian Chadd phandle_t node; 197*86f0c3ecSAdrian Chadd int rv; 198*86f0c3ecSAdrian Chadd 199*86f0c3ecSAdrian Chadd sc = device_get_softc(dev); 200*86f0c3ecSAdrian Chadd sc->dev = dev; 201*86f0c3ecSAdrian Chadd node = ofw_bus_get_node(sc->dev); 202*86f0c3ecSAdrian Chadd 203*86f0c3ecSAdrian Chadd rv = ipq4018_usb_hs_usbphy_init_phy(sc, node); 204*86f0c3ecSAdrian Chadd if (rv != 0) 205*86f0c3ecSAdrian Chadd goto fail; 206*86f0c3ecSAdrian Chadd return (bus_generic_attach(dev)); 207*86f0c3ecSAdrian Chadd 208*86f0c3ecSAdrian Chadd fail: 209*86f0c3ecSAdrian Chadd return (ENXIO); 210*86f0c3ecSAdrian Chadd } 211*86f0c3ecSAdrian Chadd 212*86f0c3ecSAdrian Chadd static int 213*86f0c3ecSAdrian Chadd ipq4018_usb_hs_usbphy_detach(device_t dev) 214*86f0c3ecSAdrian Chadd { 215*86f0c3ecSAdrian Chadd struct ipq4018_usb_hs_phy_softc *sc; 216*86f0c3ecSAdrian Chadd sc = device_get_softc(dev); 217*86f0c3ecSAdrian Chadd 218*86f0c3ecSAdrian Chadd return (0); 219*86f0c3ecSAdrian Chadd } 220*86f0c3ecSAdrian Chadd 221*86f0c3ecSAdrian Chadd static device_method_t ipq4018_usb_hs_usbphy_methods[] = { 222*86f0c3ecSAdrian Chadd /* Device interface */ 223*86f0c3ecSAdrian Chadd DEVMETHOD(device_probe, ipq4018_usb_hs_usbphy_probe), 224*86f0c3ecSAdrian Chadd DEVMETHOD(device_attach, ipq4018_usb_hs_usbphy_attach), 225*86f0c3ecSAdrian Chadd DEVMETHOD(device_detach, ipq4018_usb_hs_usbphy_detach), 226*86f0c3ecSAdrian Chadd DEVMETHOD_END 227*86f0c3ecSAdrian Chadd }; 228*86f0c3ecSAdrian Chadd 229*86f0c3ecSAdrian Chadd static devclass_t ipq4018_usb_hs_usbphy_devclass; 230*86f0c3ecSAdrian Chadd static DEFINE_CLASS_0(ipq4018_usb_hs_usbphy, ipq4018_usb_hs_usbphy_driver, 231*86f0c3ecSAdrian Chadd ipq4018_usb_hs_usbphy_methods, 232*86f0c3ecSAdrian Chadd sizeof(struct ipq4018_usb_hs_phy_softc)); 233*86f0c3ecSAdrian Chadd EARLY_DRIVER_MODULE(ipq4018_usb_hs_usbphy, simplebus, 234*86f0c3ecSAdrian Chadd ipq4018_usb_hs_usbphy_driver, 235*86f0c3ecSAdrian Chadd ipq4018_usb_hs_usbphy_devclass, NULL, NULL, 236*86f0c3ecSAdrian Chadd BUS_PASS_TIMER + BUS_PASS_ORDER_LAST); 237