186f0c3ecSAdrian Chadd /*- 24d846d26SWarner Losh * SPDX-License-Identifier: BSD-2-Clause 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/param.h> 2986f0c3ecSAdrian Chadd #include <sys/systm.h> 3086f0c3ecSAdrian Chadd #include <sys/bus.h> 3186f0c3ecSAdrian Chadd #include <sys/gpio.h> 3286f0c3ecSAdrian Chadd #include <sys/kernel.h> 3386f0c3ecSAdrian Chadd #include <sys/module.h> 3486f0c3ecSAdrian Chadd #include <sys/malloc.h> 3586f0c3ecSAdrian Chadd #include <sys/rman.h> 3686f0c3ecSAdrian Chadd 3786f0c3ecSAdrian Chadd #include <machine/bus.h> 3886f0c3ecSAdrian Chadd 39*1f469a9fSEmmanuel Vadot #include <dev/hwreset/hwreset.h> 4086f0c3ecSAdrian Chadd #include <dev/extres/phy/phy_usb.h> 4186f0c3ecSAdrian Chadd #include <dev/extres/regulator/regulator.h> 4286f0c3ecSAdrian Chadd #include <dev/ofw/ofw_bus.h> 4386f0c3ecSAdrian Chadd #include <dev/ofw/ofw_bus_subr.h> 4486f0c3ecSAdrian Chadd 4586f0c3ecSAdrian Chadd #include <dev/fdt/simple_mfd.h> 4686f0c3ecSAdrian Chadd #include "phynode_if.h" 4786f0c3ecSAdrian Chadd #include "phynode_usb_if.h" 4886f0c3ecSAdrian Chadd 4986f0c3ecSAdrian Chadd static struct ofw_compat_data compat_data[] = { 5086f0c3ecSAdrian Chadd {"qcom,usb-ss-ipq4019-phy", 1}, 5186f0c3ecSAdrian Chadd {NULL, 0}, 5286f0c3ecSAdrian Chadd }; 5386f0c3ecSAdrian Chadd 5486f0c3ecSAdrian Chadd struct ipq4018_usb_ss_phy_softc { 5586f0c3ecSAdrian Chadd device_t dev; 5686f0c3ecSAdrian Chadd }; 5786f0c3ecSAdrian Chadd 5886f0c3ecSAdrian Chadd struct ipq4018_usb_ss_phynode_sc { 5986f0c3ecSAdrian Chadd struct phynode_usb_sc usb_sc; 6086f0c3ecSAdrian Chadd int mode; 6186f0c3ecSAdrian Chadd hwreset_t por_rst; 6286f0c3ecSAdrian Chadd }; 6386f0c3ecSAdrian Chadd 6486f0c3ecSAdrian Chadd static int 6586f0c3ecSAdrian Chadd ipq4018_usb_ss_phynode_phy_enable(struct phynode *phynode, bool enable) 6686f0c3ecSAdrian Chadd { 6786f0c3ecSAdrian Chadd struct ipq4018_usb_ss_phynode_sc *sc; 6886f0c3ecSAdrian Chadd device_t dev; 6986f0c3ecSAdrian Chadd int rv; 7086f0c3ecSAdrian Chadd 7186f0c3ecSAdrian Chadd dev = phynode_get_device(phynode); 7286f0c3ecSAdrian Chadd sc = phynode_get_softc(phynode); 7386f0c3ecSAdrian Chadd 7486f0c3ecSAdrian Chadd /* 7586f0c3ecSAdrian Chadd * For power-off - assert por, sleep for 10ms 7686f0c3ecSAdrian Chadd */ 7786f0c3ecSAdrian Chadd rv = hwreset_assert(sc->por_rst); 7886f0c3ecSAdrian Chadd if (rv != 0) 7986f0c3ecSAdrian Chadd goto done; 8086f0c3ecSAdrian Chadd DELAY(10*1000); 8186f0c3ecSAdrian Chadd 8286f0c3ecSAdrian Chadd /* 8386f0c3ecSAdrian Chadd * For power-on - power off first, then deassert por. 8486f0c3ecSAdrian Chadd */ 8586f0c3ecSAdrian Chadd if (enable) { 8686f0c3ecSAdrian Chadd rv = hwreset_deassert(sc->por_rst); 8786f0c3ecSAdrian Chadd if (rv != 0) 8886f0c3ecSAdrian Chadd goto done; 8986f0c3ecSAdrian Chadd DELAY(10*1000); 9086f0c3ecSAdrian Chadd } 9186f0c3ecSAdrian Chadd 9286f0c3ecSAdrian Chadd done: 9386f0c3ecSAdrian Chadd if (rv != 0) { 9486f0c3ecSAdrian Chadd device_printf(dev, "%s: failed, rv=%d\n", __func__, rv); 9586f0c3ecSAdrian Chadd } 9686f0c3ecSAdrian Chadd return (rv); 9786f0c3ecSAdrian Chadd } 9886f0c3ecSAdrian Chadd 9986f0c3ecSAdrian Chadd /* Phy controller class and methods. */ 10086f0c3ecSAdrian Chadd static phynode_method_t ipq4018_usb_ss_phynode_methods[] = { 10186f0c3ecSAdrian Chadd PHYNODEUSBMETHOD(phynode_enable, ipq4018_usb_ss_phynode_phy_enable), 10286f0c3ecSAdrian Chadd PHYNODEUSBMETHOD_END 10386f0c3ecSAdrian Chadd }; 10486f0c3ecSAdrian Chadd DEFINE_CLASS_1(ipq4018_usb_ss_phynode, ipq4018_usb_ss_phynode_class, 10586f0c3ecSAdrian Chadd ipq4018_usb_ss_phynode_methods, 10686f0c3ecSAdrian Chadd sizeof(struct ipq4018_usb_ss_phynode_sc), phynode_usb_class); 10786f0c3ecSAdrian Chadd 10886f0c3ecSAdrian Chadd static int 10986f0c3ecSAdrian Chadd ipq4018_usb_ss_usbphy_init_phy(struct ipq4018_usb_ss_phy_softc *sc, 11086f0c3ecSAdrian Chadd phandle_t node) 11186f0c3ecSAdrian Chadd { 11286f0c3ecSAdrian Chadd struct phynode *phynode; 11386f0c3ecSAdrian Chadd struct phynode_init_def phy_init; 11486f0c3ecSAdrian Chadd struct ipq4018_usb_ss_phynode_sc *phy_sc; 11586f0c3ecSAdrian Chadd int rv; 11686f0c3ecSAdrian Chadd hwreset_t por_rst = NULL; 11786f0c3ecSAdrian Chadd 11886f0c3ecSAdrian Chadd /* FDT resources */ 11986f0c3ecSAdrian Chadd rv = hwreset_get_by_ofw_name(sc->dev, node, "por_rst", &por_rst); 12086f0c3ecSAdrian Chadd if (rv != 0 && rv != ENOENT) { 12186f0c3ecSAdrian Chadd device_printf(sc->dev, "Cannot get 'por_rst' reset\n"); 12286f0c3ecSAdrian Chadd goto fail; 12386f0c3ecSAdrian Chadd } 12486f0c3ecSAdrian Chadd 12586f0c3ecSAdrian Chadd /* Create and register phy. */ 12686f0c3ecSAdrian Chadd bzero(&phy_init, sizeof(phy_init)); 12786f0c3ecSAdrian Chadd phy_init.id = 1; 12886f0c3ecSAdrian Chadd phy_init.ofw_node = node; 12986f0c3ecSAdrian Chadd phynode = phynode_create(sc->dev, &ipq4018_usb_ss_phynode_class, 13086f0c3ecSAdrian Chadd &phy_init); 13186f0c3ecSAdrian Chadd if (phynode == NULL) { 13286f0c3ecSAdrian Chadd device_printf(sc->dev, "Cannot create phy.\n"); 13386f0c3ecSAdrian Chadd return (ENXIO); 13486f0c3ecSAdrian Chadd } 13586f0c3ecSAdrian Chadd 13686f0c3ecSAdrian Chadd phy_sc = phynode_get_softc(phynode); 13786f0c3ecSAdrian Chadd phy_sc->por_rst = por_rst; 13886f0c3ecSAdrian Chadd 13986f0c3ecSAdrian Chadd if (phynode_register(phynode) == NULL) { 14086f0c3ecSAdrian Chadd device_printf(sc->dev, "Cannot register phy.\n"); 14186f0c3ecSAdrian Chadd return (ENXIO); 14286f0c3ecSAdrian Chadd } 14386f0c3ecSAdrian Chadd 14486f0c3ecSAdrian Chadd (void) ipq4018_usb_ss_phynode_phy_enable(phynode, true); 14586f0c3ecSAdrian Chadd 14686f0c3ecSAdrian Chadd return (0); 14786f0c3ecSAdrian Chadd 14886f0c3ecSAdrian Chadd fail: 14986f0c3ecSAdrian Chadd if (por_rst != NULL) 15086f0c3ecSAdrian Chadd hwreset_release(por_rst); 15186f0c3ecSAdrian Chadd 15286f0c3ecSAdrian Chadd return (ENXIO); 15386f0c3ecSAdrian Chadd } 15486f0c3ecSAdrian Chadd 15586f0c3ecSAdrian Chadd static int 15686f0c3ecSAdrian Chadd ipq4018_usb_ss_usbphy_probe(device_t dev) 15786f0c3ecSAdrian Chadd { 15886f0c3ecSAdrian Chadd 15986f0c3ecSAdrian Chadd if (!ofw_bus_status_okay(dev)) 16086f0c3ecSAdrian Chadd return (ENXIO); 16186f0c3ecSAdrian Chadd 16286f0c3ecSAdrian Chadd if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0) 16386f0c3ecSAdrian Chadd return (ENXIO); 16486f0c3ecSAdrian Chadd 16586f0c3ecSAdrian Chadd device_set_desc(dev, "IPQ4018/IPQ4019 USB SS PHY"); 16686f0c3ecSAdrian Chadd return (BUS_PROBE_DEFAULT); 16786f0c3ecSAdrian Chadd } 16886f0c3ecSAdrian Chadd 16986f0c3ecSAdrian Chadd static int 17086f0c3ecSAdrian Chadd ipq4018_usb_ss_usbphy_attach(device_t dev) 17186f0c3ecSAdrian Chadd { 17286f0c3ecSAdrian Chadd struct ipq4018_usb_ss_phy_softc *sc; 17386f0c3ecSAdrian Chadd phandle_t node; 17486f0c3ecSAdrian Chadd int rv; 17586f0c3ecSAdrian Chadd 17686f0c3ecSAdrian Chadd sc = device_get_softc(dev); 17786f0c3ecSAdrian Chadd sc->dev = dev; 17886f0c3ecSAdrian Chadd node = ofw_bus_get_node(sc->dev); 17986f0c3ecSAdrian Chadd 18086f0c3ecSAdrian Chadd rv = ipq4018_usb_ss_usbphy_init_phy(sc, node); 18186f0c3ecSAdrian Chadd if (rv != 0) 18286f0c3ecSAdrian Chadd goto fail; 18386f0c3ecSAdrian Chadd return (bus_generic_attach(dev)); 18486f0c3ecSAdrian Chadd 18586f0c3ecSAdrian Chadd fail: 18686f0c3ecSAdrian Chadd return (ENXIO); 18786f0c3ecSAdrian Chadd } 18886f0c3ecSAdrian Chadd 18986f0c3ecSAdrian Chadd static int 19086f0c3ecSAdrian Chadd ipq4018_usb_ss_usbphy_detach(device_t dev) 19186f0c3ecSAdrian Chadd { 19286f0c3ecSAdrian Chadd 19386f0c3ecSAdrian Chadd return (0); 19486f0c3ecSAdrian Chadd } 19586f0c3ecSAdrian Chadd 19686f0c3ecSAdrian Chadd static device_method_t ipq4018_usb_ss_usbphy_methods[] = { 19786f0c3ecSAdrian Chadd /* Device interface */ 19886f0c3ecSAdrian Chadd DEVMETHOD(device_probe, ipq4018_usb_ss_usbphy_probe), 19986f0c3ecSAdrian Chadd DEVMETHOD(device_attach, ipq4018_usb_ss_usbphy_attach), 20086f0c3ecSAdrian Chadd DEVMETHOD(device_detach, ipq4018_usb_ss_usbphy_detach), 20186f0c3ecSAdrian Chadd DEVMETHOD_END 20286f0c3ecSAdrian Chadd }; 20386f0c3ecSAdrian Chadd 20486f0c3ecSAdrian Chadd static DEFINE_CLASS_0(ipq4018_usb_ss_usbphy, ipq4018_usb_ss_usbphy_driver, 20586f0c3ecSAdrian Chadd ipq4018_usb_ss_usbphy_methods, 20686f0c3ecSAdrian Chadd sizeof(struct ipq4018_usb_ss_phy_softc)); 20786f0c3ecSAdrian Chadd EARLY_DRIVER_MODULE(ipq4018_usb_ss_usbphy, simplebus, 2089978ade4SJohn Baldwin ipq4018_usb_ss_usbphy_driver, NULL, NULL, 20986f0c3ecSAdrian Chadd BUS_PASS_TIMER + BUS_PASS_ORDER_LAST); 210