1 /*- 2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD 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 __FBSDID("$FreeBSD$"); 31 32 #include <sys/param.h> 33 #include <sys/systm.h> 34 #include <sys/bus.h> 35 36 #include <sys/kernel.h> 37 #include <sys/module.h> 38 #include <sys/rman.h> 39 #include <sys/lock.h> 40 #include <sys/mutex.h> 41 42 #include <machine/bus.h> 43 44 #include <dev/ofw/ofw_bus.h> 45 #include <dev/ofw/ofw_bus_subr.h> 46 47 #include <dev/extres/clk/clk.h> 48 #include <dev/extres/regulator/regulator.h> 49 #include <dev/extres/phy/phy_usb.h> 50 51 #include "phynode_if.h" 52 53 struct usb_nop_xceiv_softc { 54 device_t dev; 55 regulator_t vcc_supply; 56 clk_t clk; 57 uint32_t clk_freq; 58 }; 59 60 static struct ofw_compat_data compat_data[] = { 61 {"usb-nop-xceiv", 1}, 62 {NULL, 0} 63 }; 64 65 /* Phy class and methods. */ 66 static int usb_nop_xceiv_phy_enable(struct phynode *phy, bool enable); 67 static phynode_usb_method_t usb_nop_xceiv_phynode_methods[] = { 68 PHYNODEMETHOD(phynode_enable, usb_nop_xceiv_phy_enable), 69 70 PHYNODEMETHOD_END 71 }; 72 DEFINE_CLASS_1(usb_nop_xceiv_phynode, usb_nop_xceiv_phynode_class, 73 usb_nop_xceiv_phynode_methods, 74 sizeof(struct phynode_usb_sc), phynode_usb_class); 75 76 static int 77 usb_nop_xceiv_phy_enable(struct phynode *phynode, bool enable) 78 { 79 struct usb_nop_xceiv_softc *sc; 80 device_t dev; 81 intptr_t phy; 82 int error; 83 84 dev = phynode_get_device(phynode); 85 phy = phynode_get_id(phynode); 86 sc = device_get_softc(dev); 87 88 if (phy != 0) 89 return (ERANGE); 90 91 /* Enable the phy clock */ 92 if (sc->clk_freq != 0) { 93 if (enable) { 94 error = clk_set_freq(sc->clk, sc->clk_freq, 95 CLK_SET_ROUND_ANY); 96 if (error != 0) { 97 device_printf(dev, "Cannot set clock to %dMhz\n", 98 sc->clk_freq); 99 goto fail; 100 } 101 102 error = clk_enable(sc->clk); 103 } else 104 error = clk_disable(sc->clk); 105 106 if (error != 0) { 107 device_printf(dev, "Cannot %sable the clock\n", 108 enable ? "En" : "Dis"); 109 goto fail; 110 } 111 } 112 if (sc->vcc_supply) { 113 if (enable) 114 error = regulator_enable(sc->vcc_supply); 115 else 116 error = regulator_disable(sc->vcc_supply); 117 if (error != 0) { 118 device_printf(dev, "Cannot %sable the regulator\n", 119 enable ? "En" : "Dis"); 120 goto fail; 121 } 122 } 123 124 return (0); 125 126 fail: 127 return (ENXIO); 128 } 129 130 static int 131 usb_nop_xceiv_probe(device_t dev) 132 { 133 134 if (!ofw_bus_status_okay(dev)) 135 return (ENXIO); 136 137 if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0) 138 return (ENXIO); 139 140 device_set_desc(dev, "USB NOP PHY"); 141 return (BUS_PROBE_DEFAULT); 142 } 143 144 static int 145 usb_nop_xceiv_attach(device_t dev) 146 { 147 struct usb_nop_xceiv_softc *sc; 148 struct phynode *phynode; 149 struct phynode_init_def phy_init; 150 phandle_t node; 151 int error; 152 153 sc = device_get_softc(dev); 154 sc->dev = dev; 155 node = ofw_bus_get_node(dev); 156 157 /* Parse the optional properties */ 158 OF_getencprop(node, "clock-frequency", &sc->clk_freq, sizeof(uint32_t)); 159 160 error = clk_get_by_ofw_name(dev, node, "main_clk", &sc->clk); 161 if (error != 0 && sc->clk_freq != 0) { 162 device_printf(dev, "clock property is mandatory if clock-frequency is present\n"); 163 return (ENXIO); 164 } 165 166 regulator_get_by_ofw_property(dev, node, "vcc-supply", &sc->vcc_supply); 167 168 phy_init.id = 0; 169 phy_init.ofw_node = node; 170 phynode = phynode_create(dev, &usb_nop_xceiv_phynode_class, 171 &phy_init); 172 if (phynode == NULL) { 173 device_printf(dev, "failed to create USB NOP PHY\n"); 174 return (ENXIO); 175 } 176 if (phynode_register(phynode) == NULL) { 177 device_printf(dev, "failed to create USB NOP PHY\n"); 178 return (ENXIO); 179 } 180 181 OF_device_register_xref(OF_xref_from_node(node), dev); 182 183 return (0); 184 } 185 186 static int 187 usb_nop_xceiv_detach(device_t dev) 188 { 189 190 return (EBUSY); 191 } 192 193 static device_method_t usb_nop_xceiv_methods[] = { 194 /* Device interface */ 195 DEVMETHOD(device_probe, usb_nop_xceiv_probe), 196 DEVMETHOD(device_attach, usb_nop_xceiv_attach), 197 DEVMETHOD(device_detach, usb_nop_xceiv_detach), 198 199 DEVMETHOD_END 200 }; 201 202 static devclass_t usb_nop_xceiv_devclass; 203 204 static driver_t usb_nop_xceiv_driver = { 205 "usb_nop_xceiv", 206 usb_nop_xceiv_methods, 207 sizeof(struct usb_nop_xceiv_softc), 208 }; 209 210 EARLY_DRIVER_MODULE(usb_nop_xceiv, simplebus, usb_nop_xceiv_driver, 211 usb_nop_xceiv_devclass, 0, 0, BUS_PASS_SUPPORTDEV + BUS_PASS_ORDER_MIDDLE); 212