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