186f0c3ecSAdrian Chadd /*- 286f0c3ecSAdrian Chadd * SPDX-License-Identifier: BSD-2-Clause-FreeBSD 386f0c3ecSAdrian Chadd * 486f0c3ecSAdrian Chadd * Copyright (c) 2019 Michal Meloun <mmel@FreeBSD.org> 586f0c3ecSAdrian Chadd * 686f0c3ecSAdrian Chadd * Redistribution and use in source and binary forms, with or without 786f0c3ecSAdrian Chadd * modification, are permitted provided that the following conditions 886f0c3ecSAdrian Chadd * are met: 986f0c3ecSAdrian Chadd * 1. Redistributions of source code must retain the above copyright 1086f0c3ecSAdrian Chadd * notice, this list of conditions and the following disclaimer. 1186f0c3ecSAdrian Chadd * 2. Redistributions in binary form must reproduce the above copyright 1286f0c3ecSAdrian Chadd * notice, this list of conditions and the following disclaimer in the 1386f0c3ecSAdrian Chadd * documentation and/or other materials provided with the distribution. 1486f0c3ecSAdrian Chadd * 1586f0c3ecSAdrian Chadd * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 1686f0c3ecSAdrian Chadd * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 1786f0c3ecSAdrian Chadd * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 1886f0c3ecSAdrian Chadd * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 1986f0c3ecSAdrian Chadd * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2086f0c3ecSAdrian Chadd * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2186f0c3ecSAdrian Chadd * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2286f0c3ecSAdrian Chadd * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2386f0c3ecSAdrian Chadd * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2486f0c3ecSAdrian Chadd * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2586f0c3ecSAdrian Chadd * SUCH DAMAGE. 2686f0c3ecSAdrian Chadd */ 2786f0c3ecSAdrian Chadd 2886f0c3ecSAdrian Chadd #include <sys/cdefs.h> 2986f0c3ecSAdrian Chadd __FBSDID("$FreeBSD$"); 3086f0c3ecSAdrian Chadd 3186f0c3ecSAdrian Chadd #include <sys/param.h> 3286f0c3ecSAdrian Chadd #include <sys/systm.h> 3386f0c3ecSAdrian Chadd #include <sys/bus.h> 3486f0c3ecSAdrian Chadd #include <sys/gpio.h> 3586f0c3ecSAdrian Chadd #include <sys/kernel.h> 3686f0c3ecSAdrian Chadd #include <sys/module.h> 3786f0c3ecSAdrian Chadd #include <sys/malloc.h> 3886f0c3ecSAdrian Chadd #include <sys/rman.h> 3986f0c3ecSAdrian Chadd 4086f0c3ecSAdrian Chadd #include <machine/bus.h> 4186f0c3ecSAdrian Chadd 4286f0c3ecSAdrian Chadd #include <dev/extres/hwreset/hwreset.h> 4386f0c3ecSAdrian Chadd #include <dev/extres/phy/phy_usb.h> 4486f0c3ecSAdrian Chadd #include <dev/extres/regulator/regulator.h> 4586f0c3ecSAdrian Chadd #include <dev/ofw/ofw_bus.h> 4686f0c3ecSAdrian Chadd #include <dev/ofw/ofw_bus_subr.h> 4786f0c3ecSAdrian Chadd 4886f0c3ecSAdrian Chadd #include <dev/fdt/simple_mfd.h> 4986f0c3ecSAdrian Chadd #include "phynode_if.h" 5086f0c3ecSAdrian Chadd #include "phynode_usb_if.h" 5186f0c3ecSAdrian Chadd 5286f0c3ecSAdrian Chadd static struct ofw_compat_data compat_data[] = { 5386f0c3ecSAdrian Chadd {"qcom,usb-hs-ipq4019-phy", 1}, 5486f0c3ecSAdrian Chadd {NULL, 0}, 5586f0c3ecSAdrian Chadd }; 5686f0c3ecSAdrian Chadd 5786f0c3ecSAdrian Chadd struct ipq4018_usb_hs_phy_softc { 5886f0c3ecSAdrian Chadd device_t dev; 5986f0c3ecSAdrian Chadd }; 6086f0c3ecSAdrian Chadd 6186f0c3ecSAdrian Chadd struct ipq4018_usb_hs_phynode_sc { 6286f0c3ecSAdrian Chadd struct phynode_usb_sc usb_sc; 6386f0c3ecSAdrian Chadd int mode; 6486f0c3ecSAdrian Chadd hwreset_t por_rst; 6586f0c3ecSAdrian Chadd hwreset_t srif_rst; 6686f0c3ecSAdrian Chadd }; 6786f0c3ecSAdrian Chadd 6886f0c3ecSAdrian Chadd static int 6986f0c3ecSAdrian Chadd ipq4018_usb_hs_phynode_phy_enable(struct phynode *phynode, bool enable) 7086f0c3ecSAdrian Chadd { 7186f0c3ecSAdrian Chadd struct ipq4018_usb_hs_phynode_sc *sc; 7286f0c3ecSAdrian Chadd device_t dev; 7386f0c3ecSAdrian Chadd int rv; 7486f0c3ecSAdrian Chadd 7586f0c3ecSAdrian Chadd dev = phynode_get_device(phynode); 7686f0c3ecSAdrian Chadd sc = phynode_get_softc(phynode); 7786f0c3ecSAdrian Chadd 7886f0c3ecSAdrian Chadd /* 7986f0c3ecSAdrian Chadd * 8086f0c3ecSAdrian Chadd * For power-off - assert por, sleep for 10ms, assert srif, 8186f0c3ecSAdrian Chadd * sleep for 10ms 8286f0c3ecSAdrian Chadd */ 8386f0c3ecSAdrian Chadd rv = hwreset_assert(sc->por_rst); 8486f0c3ecSAdrian Chadd if (rv != 0) 8586f0c3ecSAdrian Chadd goto done; 8686f0c3ecSAdrian Chadd DELAY(10*1000); 8786f0c3ecSAdrian Chadd rv = hwreset_assert(sc->srif_rst); 8886f0c3ecSAdrian Chadd if (rv != 0) 8986f0c3ecSAdrian Chadd goto done; 9086f0c3ecSAdrian Chadd DELAY(10*1000); 9186f0c3ecSAdrian Chadd 9286f0c3ecSAdrian Chadd /* 9386f0c3ecSAdrian Chadd * For power-on - power off first, then deassert srif, then 9486f0c3ecSAdrian Chadd * sleep for 10ms, then deassert por. 9586f0c3ecSAdrian Chadd */ 9686f0c3ecSAdrian Chadd if (enable) { 9786f0c3ecSAdrian Chadd rv = hwreset_deassert(sc->srif_rst); 9886f0c3ecSAdrian Chadd if (rv != 0) 9986f0c3ecSAdrian Chadd goto done; 10086f0c3ecSAdrian Chadd DELAY(10*1000); 10186f0c3ecSAdrian Chadd rv = hwreset_deassert(sc->por_rst); 10286f0c3ecSAdrian Chadd if (rv != 0) 10386f0c3ecSAdrian Chadd goto done; 10486f0c3ecSAdrian Chadd DELAY(10*1000); 10586f0c3ecSAdrian Chadd } 10686f0c3ecSAdrian Chadd 10786f0c3ecSAdrian Chadd done: 10886f0c3ecSAdrian Chadd if (rv != 0) { 10986f0c3ecSAdrian Chadd device_printf(dev, "%s: failed, rv=%d\n", __func__, rv); 11086f0c3ecSAdrian Chadd } 11186f0c3ecSAdrian Chadd return (rv); 11286f0c3ecSAdrian Chadd } 11386f0c3ecSAdrian Chadd 11486f0c3ecSAdrian Chadd /* Phy controller class and methods. */ 11586f0c3ecSAdrian Chadd static phynode_method_t ipq4018_usb_hs_phynode_methods[] = { 11686f0c3ecSAdrian Chadd PHYNODEUSBMETHOD(phynode_enable, ipq4018_usb_hs_phynode_phy_enable), 11786f0c3ecSAdrian Chadd PHYNODEUSBMETHOD_END 11886f0c3ecSAdrian Chadd }; 11986f0c3ecSAdrian Chadd DEFINE_CLASS_1(ipq4018_usb_hs_phynode, ipq4018_usb_hs_phynode_class, 12086f0c3ecSAdrian Chadd ipq4018_usb_hs_phynode_methods, 12186f0c3ecSAdrian Chadd sizeof(struct ipq4018_usb_hs_phynode_sc), phynode_usb_class); 12286f0c3ecSAdrian Chadd 12386f0c3ecSAdrian Chadd static int 12486f0c3ecSAdrian Chadd ipq4018_usb_hs_usbphy_init_phy(struct ipq4018_usb_hs_phy_softc *sc, 12586f0c3ecSAdrian Chadd phandle_t node) 12686f0c3ecSAdrian Chadd { 12786f0c3ecSAdrian Chadd struct phynode *phynode; 12886f0c3ecSAdrian Chadd struct phynode_init_def phy_init; 12986f0c3ecSAdrian Chadd struct ipq4018_usb_hs_phynode_sc *phy_sc; 13086f0c3ecSAdrian Chadd int rv; 13186f0c3ecSAdrian Chadd hwreset_t por_rst = NULL, srif_rst = NULL; 13286f0c3ecSAdrian Chadd 13386f0c3ecSAdrian Chadd /* FDT resources */ 13486f0c3ecSAdrian Chadd rv = hwreset_get_by_ofw_name(sc->dev, node, "por_rst", &por_rst); 13586f0c3ecSAdrian Chadd if (rv != 0 && rv != ENOENT) { 13686f0c3ecSAdrian Chadd device_printf(sc->dev, "Cannot get 'por_rst' reset\n"); 13786f0c3ecSAdrian Chadd goto fail; 13886f0c3ecSAdrian Chadd } 13986f0c3ecSAdrian Chadd rv = hwreset_get_by_ofw_name(sc->dev, node, "srif_rst", &srif_rst); 14086f0c3ecSAdrian Chadd if (rv != 0 && rv != ENOENT) { 14186f0c3ecSAdrian Chadd device_printf(sc->dev, "Cannot get 'srif_rst' reset\n"); 14286f0c3ecSAdrian Chadd goto fail; 14386f0c3ecSAdrian Chadd } 14486f0c3ecSAdrian Chadd 14586f0c3ecSAdrian Chadd /* Create and register phy. */ 14686f0c3ecSAdrian Chadd bzero(&phy_init, sizeof(phy_init)); 14786f0c3ecSAdrian Chadd phy_init.id = 1; 14886f0c3ecSAdrian Chadd phy_init.ofw_node = node; 14986f0c3ecSAdrian Chadd phynode = phynode_create(sc->dev, &ipq4018_usb_hs_phynode_class, 15086f0c3ecSAdrian Chadd &phy_init); 15186f0c3ecSAdrian Chadd if (phynode == NULL) { 15286f0c3ecSAdrian Chadd device_printf(sc->dev, "Cannot create phy.\n"); 15386f0c3ecSAdrian Chadd return (ENXIO); 15486f0c3ecSAdrian Chadd } 15586f0c3ecSAdrian Chadd 15686f0c3ecSAdrian Chadd phy_sc = phynode_get_softc(phynode); 15786f0c3ecSAdrian Chadd phy_sc->por_rst = por_rst; 15886f0c3ecSAdrian Chadd phy_sc->srif_rst = srif_rst; 15986f0c3ecSAdrian Chadd 16086f0c3ecSAdrian Chadd if (phynode_register(phynode) == NULL) { 16186f0c3ecSAdrian Chadd device_printf(sc->dev, "Cannot register phy.\n"); 16286f0c3ecSAdrian Chadd return (ENXIO); 16386f0c3ecSAdrian Chadd } 16486f0c3ecSAdrian Chadd 16586f0c3ecSAdrian Chadd (void) ipq4018_usb_hs_phynode_phy_enable(phynode, true); 16686f0c3ecSAdrian Chadd 16786f0c3ecSAdrian Chadd return (0); 16886f0c3ecSAdrian Chadd 16986f0c3ecSAdrian Chadd fail: 17086f0c3ecSAdrian Chadd if (por_rst != NULL) 17186f0c3ecSAdrian Chadd hwreset_release(por_rst); 17286f0c3ecSAdrian Chadd if (srif_rst != NULL) 17386f0c3ecSAdrian Chadd hwreset_release(srif_rst); 17486f0c3ecSAdrian Chadd 17586f0c3ecSAdrian Chadd return (ENXIO); 17686f0c3ecSAdrian Chadd } 17786f0c3ecSAdrian Chadd 17886f0c3ecSAdrian Chadd static int 17986f0c3ecSAdrian Chadd ipq4018_usb_hs_usbphy_probe(device_t dev) 18086f0c3ecSAdrian Chadd { 18186f0c3ecSAdrian Chadd 18286f0c3ecSAdrian Chadd if (!ofw_bus_status_okay(dev)) 18386f0c3ecSAdrian Chadd return (ENXIO); 18486f0c3ecSAdrian Chadd 18586f0c3ecSAdrian Chadd if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0) 18686f0c3ecSAdrian Chadd return (ENXIO); 18786f0c3ecSAdrian Chadd 18886f0c3ecSAdrian Chadd device_set_desc(dev, "IPQ4018/IPQ4019 USB HS PHY"); 18986f0c3ecSAdrian Chadd return (BUS_PROBE_DEFAULT); 19086f0c3ecSAdrian Chadd } 19186f0c3ecSAdrian Chadd 19286f0c3ecSAdrian Chadd static int 19386f0c3ecSAdrian Chadd ipq4018_usb_hs_usbphy_attach(device_t dev) 19486f0c3ecSAdrian Chadd { 19586f0c3ecSAdrian Chadd struct ipq4018_usb_hs_phy_softc *sc; 19686f0c3ecSAdrian Chadd phandle_t node; 19786f0c3ecSAdrian Chadd int rv; 19886f0c3ecSAdrian Chadd 19986f0c3ecSAdrian Chadd sc = device_get_softc(dev); 20086f0c3ecSAdrian Chadd sc->dev = dev; 20186f0c3ecSAdrian Chadd node = ofw_bus_get_node(sc->dev); 20286f0c3ecSAdrian Chadd 20386f0c3ecSAdrian Chadd rv = ipq4018_usb_hs_usbphy_init_phy(sc, node); 20486f0c3ecSAdrian Chadd if (rv != 0) 20586f0c3ecSAdrian Chadd goto fail; 20686f0c3ecSAdrian Chadd return (bus_generic_attach(dev)); 20786f0c3ecSAdrian Chadd 20886f0c3ecSAdrian Chadd fail: 20986f0c3ecSAdrian Chadd return (ENXIO); 21086f0c3ecSAdrian Chadd } 21186f0c3ecSAdrian Chadd 21286f0c3ecSAdrian Chadd static int 21386f0c3ecSAdrian Chadd ipq4018_usb_hs_usbphy_detach(device_t dev) 21486f0c3ecSAdrian Chadd { 21586f0c3ecSAdrian Chadd struct ipq4018_usb_hs_phy_softc *sc; 21686f0c3ecSAdrian Chadd sc = device_get_softc(dev); 21786f0c3ecSAdrian Chadd 21886f0c3ecSAdrian Chadd return (0); 21986f0c3ecSAdrian Chadd } 22086f0c3ecSAdrian Chadd 22186f0c3ecSAdrian Chadd static device_method_t ipq4018_usb_hs_usbphy_methods[] = { 22286f0c3ecSAdrian Chadd /* Device interface */ 22386f0c3ecSAdrian Chadd DEVMETHOD(device_probe, ipq4018_usb_hs_usbphy_probe), 22486f0c3ecSAdrian Chadd DEVMETHOD(device_attach, ipq4018_usb_hs_usbphy_attach), 22586f0c3ecSAdrian Chadd DEVMETHOD(device_detach, ipq4018_usb_hs_usbphy_detach), 22686f0c3ecSAdrian Chadd DEVMETHOD_END 22786f0c3ecSAdrian Chadd }; 22886f0c3ecSAdrian Chadd 22986f0c3ecSAdrian Chadd static DEFINE_CLASS_0(ipq4018_usb_hs_usbphy, ipq4018_usb_hs_usbphy_driver, 23086f0c3ecSAdrian Chadd ipq4018_usb_hs_usbphy_methods, 23186f0c3ecSAdrian Chadd sizeof(struct ipq4018_usb_hs_phy_softc)); 23286f0c3ecSAdrian Chadd EARLY_DRIVER_MODULE(ipq4018_usb_hs_usbphy, simplebus, 233*9978ade4SJohn Baldwin ipq4018_usb_hs_usbphy_driver, NULL, NULL, 23486f0c3ecSAdrian Chadd BUS_PASS_TIMER + BUS_PASS_ORDER_LAST); 235