1aa7c3aa6SEmmanuel Vadot /*- 2aa7c3aa6SEmmanuel Vadot * SPDX-License-Identifier: BSD-2-Clause-FreeBSD 3aa7c3aa6SEmmanuel Vadot * 4aa7c3aa6SEmmanuel Vadot * Copyright (c) 2019 Rubicon Communications, LLC (Netgate) 5aa7c3aa6SEmmanuel Vadot * 6aa7c3aa6SEmmanuel Vadot * Redistribution and use in source and binary forms, with or without 7aa7c3aa6SEmmanuel Vadot * modification, are permitted provided that the following conditions 8aa7c3aa6SEmmanuel Vadot * are met: 9aa7c3aa6SEmmanuel Vadot * 1. Redistributions of source code must retain the above copyright 10aa7c3aa6SEmmanuel Vadot * notice, this list of conditions and the following disclaimer. 11aa7c3aa6SEmmanuel Vadot * 2. Redistributions in binary form must reproduce the above copyright 12aa7c3aa6SEmmanuel Vadot * notice, this list of conditions and the following disclaimer in the 13aa7c3aa6SEmmanuel Vadot * documentation and/or other materials provided with the distribution. 14aa7c3aa6SEmmanuel Vadot * 15aa7c3aa6SEmmanuel Vadot * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 16aa7c3aa6SEmmanuel Vadot * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17aa7c3aa6SEmmanuel Vadot * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18aa7c3aa6SEmmanuel Vadot * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 19aa7c3aa6SEmmanuel Vadot * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20aa7c3aa6SEmmanuel Vadot * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21aa7c3aa6SEmmanuel Vadot * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22aa7c3aa6SEmmanuel Vadot * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23aa7c3aa6SEmmanuel Vadot * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24aa7c3aa6SEmmanuel Vadot * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25aa7c3aa6SEmmanuel Vadot * SUCH DAMAGE. 26aa7c3aa6SEmmanuel Vadot * 27aa7c3aa6SEmmanuel Vadot */ 28aa7c3aa6SEmmanuel Vadot 29aa7c3aa6SEmmanuel Vadot #include <sys/cdefs.h> 30aa7c3aa6SEmmanuel Vadot __FBSDID("$FreeBSD$"); 31aa7c3aa6SEmmanuel Vadot 32aa7c3aa6SEmmanuel Vadot #include <sys/param.h> 33aa7c3aa6SEmmanuel Vadot #include <sys/systm.h> 34aa7c3aa6SEmmanuel Vadot #include <sys/bus.h> 35aa7c3aa6SEmmanuel Vadot 36aa7c3aa6SEmmanuel Vadot #include <sys/kernel.h> 37aa7c3aa6SEmmanuel Vadot #include <sys/module.h> 38aa7c3aa6SEmmanuel Vadot #include <sys/rman.h> 39aa7c3aa6SEmmanuel Vadot #include <sys/lock.h> 40aa7c3aa6SEmmanuel Vadot #include <sys/mutex.h> 41aa7c3aa6SEmmanuel Vadot 42aa7c3aa6SEmmanuel Vadot #include <machine/bus.h> 43aa7c3aa6SEmmanuel Vadot 44aa7c3aa6SEmmanuel Vadot #include <dev/ofw/ofw_bus.h> 45aa7c3aa6SEmmanuel Vadot #include <dev/ofw/ofw_bus_subr.h> 46aa7c3aa6SEmmanuel Vadot 47aa7c3aa6SEmmanuel Vadot #include <dev/extres/clk/clk.h> 48aa7c3aa6SEmmanuel Vadot #include <dev/extres/regulator/regulator.h> 49aa7c3aa6SEmmanuel Vadot #include <dev/extres/phy/phy_usb.h> 50aa7c3aa6SEmmanuel Vadot 51aa7c3aa6SEmmanuel Vadot #include "phynode_if.h" 52aa7c3aa6SEmmanuel Vadot 53aa7c3aa6SEmmanuel Vadot struct usb_nop_xceiv_softc { 54aa7c3aa6SEmmanuel Vadot device_t dev; 55aa7c3aa6SEmmanuel Vadot regulator_t vcc_supply; 56aa7c3aa6SEmmanuel Vadot clk_t clk; 57aa7c3aa6SEmmanuel Vadot uint32_t clk_freq; 58aa7c3aa6SEmmanuel Vadot }; 59aa7c3aa6SEmmanuel Vadot 60aa7c3aa6SEmmanuel Vadot static struct ofw_compat_data compat_data[] = { 61aa7c3aa6SEmmanuel Vadot {"usb-nop-xceiv", 1}, 62aa7c3aa6SEmmanuel Vadot {NULL, 0} 63aa7c3aa6SEmmanuel Vadot }; 64aa7c3aa6SEmmanuel Vadot 65aa7c3aa6SEmmanuel Vadot /* Phy class and methods. */ 66aa7c3aa6SEmmanuel Vadot static int usb_nop_xceiv_phy_enable(struct phynode *phy, bool enable); 67aa7c3aa6SEmmanuel Vadot static phynode_usb_method_t usb_nop_xceiv_phynode_methods[] = { 68aa7c3aa6SEmmanuel Vadot PHYNODEMETHOD(phynode_enable, usb_nop_xceiv_phy_enable), 69aa7c3aa6SEmmanuel Vadot 70aa7c3aa6SEmmanuel Vadot PHYNODEMETHOD_END 71aa7c3aa6SEmmanuel Vadot }; 72aa7c3aa6SEmmanuel Vadot DEFINE_CLASS_1(usb_nop_xceiv_phynode, usb_nop_xceiv_phynode_class, 73aa7c3aa6SEmmanuel Vadot usb_nop_xceiv_phynode_methods, 74aa7c3aa6SEmmanuel Vadot sizeof(struct phynode_usb_sc), phynode_usb_class); 75aa7c3aa6SEmmanuel Vadot 76aa7c3aa6SEmmanuel Vadot static int 77aa7c3aa6SEmmanuel Vadot usb_nop_xceiv_phy_enable(struct phynode *phynode, bool enable) 78aa7c3aa6SEmmanuel Vadot { 79aa7c3aa6SEmmanuel Vadot struct usb_nop_xceiv_softc *sc; 80aa7c3aa6SEmmanuel Vadot device_t dev; 81aa7c3aa6SEmmanuel Vadot intptr_t phy; 82aa7c3aa6SEmmanuel Vadot int error; 83aa7c3aa6SEmmanuel Vadot 84aa7c3aa6SEmmanuel Vadot dev = phynode_get_device(phynode); 85aa7c3aa6SEmmanuel Vadot phy = phynode_get_id(phynode); 86aa7c3aa6SEmmanuel Vadot sc = device_get_softc(dev); 87aa7c3aa6SEmmanuel Vadot 88aa7c3aa6SEmmanuel Vadot if (phy != 0) 89aa7c3aa6SEmmanuel Vadot return (ERANGE); 90aa7c3aa6SEmmanuel Vadot 91aa7c3aa6SEmmanuel Vadot /* Enable the phy clock */ 92aa7c3aa6SEmmanuel Vadot if (sc->clk_freq != 0) { 93aa7c3aa6SEmmanuel Vadot if (enable) { 94aa7c3aa6SEmmanuel Vadot error = clk_set_freq(sc->clk, sc->clk_freq, 95aa7c3aa6SEmmanuel Vadot CLK_SET_ROUND_ANY); 96aa7c3aa6SEmmanuel Vadot if (error != 0) { 97aa7c3aa6SEmmanuel Vadot device_printf(dev, "Cannot set clock to %dMhz\n", 98aa7c3aa6SEmmanuel Vadot sc->clk_freq); 99aa7c3aa6SEmmanuel Vadot goto fail; 100aa7c3aa6SEmmanuel Vadot } 101aa7c3aa6SEmmanuel Vadot 102aa7c3aa6SEmmanuel Vadot error = clk_enable(sc->clk); 103aa7c3aa6SEmmanuel Vadot } else 104aa7c3aa6SEmmanuel Vadot error = clk_disable(sc->clk); 105aa7c3aa6SEmmanuel Vadot 106aa7c3aa6SEmmanuel Vadot if (error != 0) { 107aa7c3aa6SEmmanuel Vadot device_printf(dev, "Cannot %sable the clock\n", 108aa7c3aa6SEmmanuel Vadot enable ? "En" : "Dis"); 109aa7c3aa6SEmmanuel Vadot goto fail; 110aa7c3aa6SEmmanuel Vadot } 111aa7c3aa6SEmmanuel Vadot } 112aa7c3aa6SEmmanuel Vadot if (sc->vcc_supply) { 113aa7c3aa6SEmmanuel Vadot if (enable) 114aa7c3aa6SEmmanuel Vadot error = regulator_enable(sc->vcc_supply); 115aa7c3aa6SEmmanuel Vadot else 116aa7c3aa6SEmmanuel Vadot error = regulator_disable(sc->vcc_supply); 117aa7c3aa6SEmmanuel Vadot if (error != 0) { 118aa7c3aa6SEmmanuel Vadot device_printf(dev, "Cannot %sable the regulator\n", 119aa7c3aa6SEmmanuel Vadot enable ? "En" : "Dis"); 120aa7c3aa6SEmmanuel Vadot goto fail; 121aa7c3aa6SEmmanuel Vadot } 122aa7c3aa6SEmmanuel Vadot } 123aa7c3aa6SEmmanuel Vadot 124aa7c3aa6SEmmanuel Vadot return (0); 125aa7c3aa6SEmmanuel Vadot 126aa7c3aa6SEmmanuel Vadot fail: 127aa7c3aa6SEmmanuel Vadot return (ENXIO); 128aa7c3aa6SEmmanuel Vadot } 129aa7c3aa6SEmmanuel Vadot 130aa7c3aa6SEmmanuel Vadot static int 131aa7c3aa6SEmmanuel Vadot usb_nop_xceiv_probe(device_t dev) 132aa7c3aa6SEmmanuel Vadot { 133aa7c3aa6SEmmanuel Vadot 134aa7c3aa6SEmmanuel Vadot if (!ofw_bus_status_okay(dev)) 135aa7c3aa6SEmmanuel Vadot return (ENXIO); 136aa7c3aa6SEmmanuel Vadot 137aa7c3aa6SEmmanuel Vadot if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0) 138aa7c3aa6SEmmanuel Vadot return (ENXIO); 139aa7c3aa6SEmmanuel Vadot 140aa7c3aa6SEmmanuel Vadot device_set_desc(dev, "USB NOP PHY"); 141aa7c3aa6SEmmanuel Vadot return (BUS_PROBE_DEFAULT); 142aa7c3aa6SEmmanuel Vadot } 143aa7c3aa6SEmmanuel Vadot 144aa7c3aa6SEmmanuel Vadot static int 145aa7c3aa6SEmmanuel Vadot usb_nop_xceiv_attach(device_t dev) 146aa7c3aa6SEmmanuel Vadot { 147aa7c3aa6SEmmanuel Vadot struct usb_nop_xceiv_softc *sc; 148aa7c3aa6SEmmanuel Vadot struct phynode *phynode; 149aa7c3aa6SEmmanuel Vadot struct phynode_init_def phy_init; 150aa7c3aa6SEmmanuel Vadot phandle_t node; 151aa7c3aa6SEmmanuel Vadot int error; 152aa7c3aa6SEmmanuel Vadot 153aa7c3aa6SEmmanuel Vadot sc = device_get_softc(dev); 154aa7c3aa6SEmmanuel Vadot sc->dev = dev; 155aa7c3aa6SEmmanuel Vadot node = ofw_bus_get_node(dev); 156aa7c3aa6SEmmanuel Vadot 157aa7c3aa6SEmmanuel Vadot /* Parse the optional properties */ 158aa7c3aa6SEmmanuel Vadot OF_getencprop(node, "clock-frequency", &sc->clk_freq, sizeof(uint32_t)); 159aa7c3aa6SEmmanuel Vadot 160aa7c3aa6SEmmanuel Vadot error = clk_get_by_ofw_name(dev, node, "main_clk", &sc->clk); 161aa7c3aa6SEmmanuel Vadot if (error != 0 && sc->clk_freq != 0) { 162aa7c3aa6SEmmanuel Vadot device_printf(dev, "clock property is mandatory if clock-frequency is present\n"); 163aa7c3aa6SEmmanuel Vadot return (ENXIO); 164aa7c3aa6SEmmanuel Vadot } 165aa7c3aa6SEmmanuel Vadot 166aa7c3aa6SEmmanuel Vadot regulator_get_by_ofw_property(dev, node, "vcc-supply", &sc->vcc_supply); 167aa7c3aa6SEmmanuel Vadot 168aa7c3aa6SEmmanuel Vadot phy_init.id = 0; 169aa7c3aa6SEmmanuel Vadot phy_init.ofw_node = node; 170aa7c3aa6SEmmanuel Vadot phynode = phynode_create(dev, &usb_nop_xceiv_phynode_class, 171aa7c3aa6SEmmanuel Vadot &phy_init); 172aa7c3aa6SEmmanuel Vadot if (phynode == NULL) { 173aa7c3aa6SEmmanuel Vadot device_printf(dev, "failed to create USB NOP PHY\n"); 174aa7c3aa6SEmmanuel Vadot return (ENXIO); 175aa7c3aa6SEmmanuel Vadot } 176aa7c3aa6SEmmanuel Vadot if (phynode_register(phynode) == NULL) { 177aa7c3aa6SEmmanuel Vadot device_printf(dev, "failed to create USB NOP PHY\n"); 178aa7c3aa6SEmmanuel Vadot return (ENXIO); 179aa7c3aa6SEmmanuel Vadot } 180aa7c3aa6SEmmanuel Vadot 181aa7c3aa6SEmmanuel Vadot OF_device_register_xref(OF_xref_from_node(node), dev); 182aa7c3aa6SEmmanuel Vadot 183aa7c3aa6SEmmanuel Vadot return (0); 184aa7c3aa6SEmmanuel Vadot } 185aa7c3aa6SEmmanuel Vadot 186aa7c3aa6SEmmanuel Vadot static int 187aa7c3aa6SEmmanuel Vadot usb_nop_xceiv_detach(device_t dev) 188aa7c3aa6SEmmanuel Vadot { 189aa7c3aa6SEmmanuel Vadot 190aa7c3aa6SEmmanuel Vadot return (EBUSY); 191aa7c3aa6SEmmanuel Vadot } 192aa7c3aa6SEmmanuel Vadot 193aa7c3aa6SEmmanuel Vadot static device_method_t usb_nop_xceiv_methods[] = { 194aa7c3aa6SEmmanuel Vadot /* Device interface */ 195aa7c3aa6SEmmanuel Vadot DEVMETHOD(device_probe, usb_nop_xceiv_probe), 196aa7c3aa6SEmmanuel Vadot DEVMETHOD(device_attach, usb_nop_xceiv_attach), 197aa7c3aa6SEmmanuel Vadot DEVMETHOD(device_detach, usb_nop_xceiv_detach), 198aa7c3aa6SEmmanuel Vadot 199aa7c3aa6SEmmanuel Vadot DEVMETHOD_END 200aa7c3aa6SEmmanuel Vadot }; 201aa7c3aa6SEmmanuel Vadot 202aa7c3aa6SEmmanuel Vadot static driver_t usb_nop_xceiv_driver = { 203aa7c3aa6SEmmanuel Vadot "usb_nop_xceiv", 204aa7c3aa6SEmmanuel Vadot usb_nop_xceiv_methods, 205aa7c3aa6SEmmanuel Vadot sizeof(struct usb_nop_xceiv_softc), 206aa7c3aa6SEmmanuel Vadot }; 207aa7c3aa6SEmmanuel Vadot 208aa7c3aa6SEmmanuel Vadot EARLY_DRIVER_MODULE(usb_nop_xceiv, simplebus, usb_nop_xceiv_driver, 209*bc9372d7SJohn Baldwin 0, 0, BUS_PASS_SUPPORTDEV + BUS_PASS_ORDER_MIDDLE); 210