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