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