1 /*- 2 * Copyright (c) 2019 Juniper Networks, Inc. 3 * Copyright (c) 2019 Semihalf. 4 * All rights reserved. 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 ``AS IS'' AND ANY EXPRESS OR 16 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 17 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 18 * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, 19 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 20 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 21 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 23 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 24 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 25 * POSSIBILITY OF SUCH DAMAGE. 26 */ 27 28 #include <sys/param.h> 29 #include <sys/bus.h> 30 #include <sys/kernel.h> 31 #include <sys/module.h> 32 #include <sys/rman.h> 33 #include <sys/systm.h> 34 35 #include <dev/fdt/simplebus.h> 36 #include <dev/ofw/ofw_bus_subr.h> 37 #include <dev/ofw/ofw_bus.h> 38 39 #include <machine/bus.h> 40 #include <machine/resource.h> 41 42 #include "mdio_if.h" 43 44 MALLOC_DEFINE(M_BRCM_IPROC_NEXUS, "Broadcom IPROC MDIO NEXUS", 45 "Broadcom IPROC MDIO NEXUS dynamic memory"); 46 47 struct brcm_mdionexus_softc { 48 uint32_t mux_id; 49 }; 50 51 /* OFW bus interface */ 52 struct brcm_mdionexus_ofw_devinfo { 53 struct ofw_bus_devinfo di_dinfo; 54 struct resource_list di_rl; 55 }; 56 57 static device_probe_t brcm_mdionexus_fdt_probe; 58 static device_attach_t brcm_mdionexus_fdt_attach; 59 60 static const struct ofw_bus_devinfo * brcm_mdionexus_ofw_get_devinfo(device_t, 61 device_t); 62 static int brcm_mdionexus_mdio_readreg(device_t, int, int); 63 static int brcm_mdionexus_mdio_writereg(device_t, int, int, int); 64 65 static device_method_t brcm_mdionexus_fdt_methods[] = { 66 /* Device interface */ 67 DEVMETHOD(device_probe, brcm_mdionexus_fdt_probe), 68 DEVMETHOD(device_attach, brcm_mdionexus_fdt_attach), 69 70 /* Bus interface */ 71 DEVMETHOD(bus_alloc_resource, bus_generic_alloc_resource), 72 DEVMETHOD(bus_release_resource, bus_generic_release_resource), 73 DEVMETHOD(bus_activate_resource, bus_generic_activate_resource), 74 75 /* ofw_bus interface */ 76 DEVMETHOD(ofw_bus_get_devinfo, brcm_mdionexus_ofw_get_devinfo), 77 DEVMETHOD(ofw_bus_get_compat, ofw_bus_gen_get_compat), 78 DEVMETHOD(ofw_bus_get_model, ofw_bus_gen_get_model), 79 DEVMETHOD(ofw_bus_get_name, ofw_bus_gen_get_name), 80 DEVMETHOD(ofw_bus_get_node, ofw_bus_gen_get_node), 81 DEVMETHOD(ofw_bus_get_type, ofw_bus_gen_get_type), 82 83 /* MDIO interface */ 84 /* MDIO interface */ 85 DEVMETHOD(mdio_readreg, brcm_mdionexus_mdio_readreg), 86 DEVMETHOD(mdio_writereg, brcm_mdionexus_mdio_writereg), 87 88 DEVMETHOD_END 89 }; 90 91 DEFINE_CLASS_0(brcm_mdionexus, brcm_mdionexus_fdt_driver, brcm_mdionexus_fdt_methods, 92 sizeof(struct brcm_mdionexus_softc)); 93 94 static driver_t brcm_mdionexus_driver = { 95 "brcm_mdionexus", 96 brcm_mdionexus_fdt_methods, 97 sizeof(struct brcm_mdionexus_softc) 98 }; 99 100 EARLY_DRIVER_MODULE(brcm_mdionexus, brcm_iproc_mdio, brcm_mdionexus_driver, 101 NULL, NULL, BUS_PASS_BUS + BUS_PASS_ORDER_MIDDLE); 102 103 static int brcm_mdionexus_ofw_bus_attach(device_t); 104 105 static int 106 brcm_mdionexus_mdio_readreg(device_t dev, int phy, int reg) 107 { 108 struct brcm_mdionexus_softc *sc; 109 110 sc = device_get_softc(dev); 111 112 return (MDIO_READREG_MUX(device_get_parent(dev), 113 sc->mux_id, phy, reg)); 114 } 115 116 static int 117 brcm_mdionexus_mdio_writereg(device_t dev, int phy, int reg, int val) 118 { 119 struct brcm_mdionexus_softc *sc; 120 121 sc = device_get_softc(dev); 122 123 return (MDIO_WRITEREG_MUX(device_get_parent(dev), 124 sc->mux_id, phy, reg, val)); 125 } 126 127 static __inline void 128 get_addr_size_cells(phandle_t node, pcell_t *addr_cells, pcell_t *size_cells) 129 { 130 131 *addr_cells = 2; 132 /* Find address cells if present */ 133 OF_getencprop(node, "#address-cells", addr_cells, sizeof(*addr_cells)); 134 135 *size_cells = 2; 136 /* Find size cells if present */ 137 OF_getencprop(node, "#size-cells", size_cells, sizeof(*size_cells)); 138 } 139 140 static int 141 brcm_mdionexus_fdt_probe(device_t dev) 142 { 143 144 if (!ofw_bus_status_okay(dev)) 145 return (ENXIO); 146 device_set_desc(dev, "Broadcom MDIO nexus"); 147 return (BUS_PROBE_SPECIFIC); 148 } 149 150 static int 151 brcm_mdionexus_fdt_attach(device_t dev) 152 { 153 struct brcm_mdionexus_softc *sc; 154 int err; 155 pcell_t addr_cells, size_cells, buf[2]; 156 phandle_t node; 157 158 sc = device_get_softc(dev); 159 160 node = ofw_bus_get_node(dev); 161 get_addr_size_cells(node, &addr_cells, &size_cells); 162 if ((addr_cells != 1) || (size_cells != 0)) { 163 device_printf(dev, "Only addr_cells=1 and size_cells=0 are supported\n"); 164 return (EINVAL); 165 } 166 167 if (OF_getencprop(node, "reg", buf, sizeof(pcell_t)) < 0) 168 return (ENXIO); 169 170 sc->mux_id = buf[0]; 171 172 err = brcm_mdionexus_ofw_bus_attach(dev); 173 if (err != 0) 174 return (err); 175 176 bus_attach_children(dev); 177 return (0); 178 } 179 180 static const struct ofw_bus_devinfo * 181 brcm_mdionexus_ofw_get_devinfo(device_t bus __unused, device_t child) 182 { 183 struct brcm_mdionexus_ofw_devinfo *di; 184 185 di = device_get_ivars(child); 186 return (&di->di_dinfo); 187 } 188 189 static int 190 brcm_mdionexus_ofw_bus_attach(device_t dev) 191 { 192 struct simplebus_softc *sc; 193 struct brcm_mdionexus_ofw_devinfo *di; 194 device_t child; 195 phandle_t parent, node; 196 197 parent = ofw_bus_get_node(dev); 198 simplebus_init(dev, parent); 199 200 sc = device_get_softc_class(dev, &simplebus_driver); 201 202 /* Iterate through all bus subordinates */ 203 for (node = OF_child(parent); node > 0; node = OF_peer(node)) { 204 /* Allocate and populate devinfo. */ 205 di = malloc(sizeof(*di), M_BRCM_IPROC_NEXUS, M_WAITOK | M_ZERO); 206 if (ofw_bus_gen_setup_devinfo(&di->di_dinfo, node) != 0) { 207 free(di, M_BRCM_IPROC_NEXUS); 208 continue; 209 } 210 211 /* Initialize and populate resource list. */ 212 resource_list_init(&di->di_rl); 213 ofw_bus_reg_to_rl(dev, node, sc->acells, sc->scells, 214 &di->di_rl); 215 ofw_bus_intr_to_rl(dev, node, &di->di_rl, NULL); 216 217 /* Add newbus device for this FDT node */ 218 child = device_add_child(dev, NULL, DEVICE_UNIT_ANY); 219 if (child == NULL) { 220 resource_list_free(&di->di_rl); 221 ofw_bus_gen_destroy_devinfo(&di->di_dinfo); 222 free(di, M_BRCM_IPROC_NEXUS); 223 continue; 224 } 225 226 device_set_ivars(child, di); 227 } 228 229 return (0); 230 } 231