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 struct simplebus_softc simplebus_sc; 49 uint32_t mux_id; 50 }; 51 52 /* OFW bus interface */ 53 struct brcm_mdionexus_ofw_devinfo { 54 struct ofw_bus_devinfo di_dinfo; 55 struct resource_list di_rl; 56 }; 57 58 static device_probe_t brcm_mdionexus_fdt_probe; 59 static device_attach_t brcm_mdionexus_fdt_attach; 60 61 static const struct ofw_bus_devinfo * brcm_mdionexus_ofw_get_devinfo(device_t, 62 device_t); 63 static int brcm_mdionexus_mdio_readreg(device_t, int, int); 64 static int brcm_mdionexus_mdio_writereg(device_t, int, int, int); 65 66 static device_method_t brcm_mdionexus_fdt_methods[] = { 67 /* Device interface */ 68 DEVMETHOD(device_probe, brcm_mdionexus_fdt_probe), 69 DEVMETHOD(device_attach, brcm_mdionexus_fdt_attach), 70 71 /* Bus interface */ 72 DEVMETHOD(bus_alloc_resource, bus_generic_alloc_resource), 73 DEVMETHOD(bus_release_resource, bus_generic_release_resource), 74 DEVMETHOD(bus_activate_resource, bus_generic_activate_resource), 75 76 /* ofw_bus interface */ 77 DEVMETHOD(ofw_bus_get_devinfo, brcm_mdionexus_ofw_get_devinfo), 78 DEVMETHOD(ofw_bus_get_compat, ofw_bus_gen_get_compat), 79 DEVMETHOD(ofw_bus_get_model, ofw_bus_gen_get_model), 80 DEVMETHOD(ofw_bus_get_name, ofw_bus_gen_get_name), 81 DEVMETHOD(ofw_bus_get_node, ofw_bus_gen_get_node), 82 DEVMETHOD(ofw_bus_get_type, ofw_bus_gen_get_type), 83 84 /* MDIO interface */ 85 /* MDIO interface */ 86 DEVMETHOD(mdio_readreg, brcm_mdionexus_mdio_readreg), 87 DEVMETHOD(mdio_writereg, brcm_mdionexus_mdio_writereg), 88 89 DEVMETHOD_END 90 }; 91 92 DEFINE_CLASS_0(brcm_mdionexus, brcm_mdionexus_fdt_driver, brcm_mdionexus_fdt_methods, 93 sizeof(struct brcm_mdionexus_softc)); 94 95 static driver_t brcm_mdionexus_driver = { 96 "brcm_mdionexus", 97 brcm_mdionexus_fdt_methods, 98 sizeof(struct brcm_mdionexus_softc) 99 }; 100 101 EARLY_DRIVER_MODULE(brcm_mdionexus, brcm_iproc_mdio, brcm_mdionexus_driver, 102 NULL, NULL, BUS_PASS_BUS + BUS_PASS_ORDER_MIDDLE); 103 104 static int brcm_mdionexus_ofw_bus_attach(device_t); 105 106 static int 107 brcm_mdionexus_mdio_readreg(device_t dev, int phy, int reg) 108 { 109 struct brcm_mdionexus_softc *sc; 110 111 sc = device_get_softc(dev); 112 113 return (MDIO_READREG_MUX(device_get_parent(dev), 114 sc->mux_id, phy, reg)); 115 } 116 117 static int 118 brcm_mdionexus_mdio_writereg(device_t dev, int phy, int reg, int val) 119 { 120 struct brcm_mdionexus_softc *sc; 121 122 sc = device_get_softc(dev); 123 124 return (MDIO_WRITEREG_MUX(device_get_parent(dev), 125 sc->mux_id, phy, reg, val)); 126 } 127 128 static __inline void 129 get_addr_size_cells(phandle_t node, pcell_t *addr_cells, pcell_t *size_cells) 130 { 131 132 *addr_cells = 2; 133 /* Find address cells if present */ 134 OF_getencprop(node, "#address-cells", addr_cells, sizeof(*addr_cells)); 135 136 *size_cells = 2; 137 /* Find size cells if present */ 138 OF_getencprop(node, "#size-cells", size_cells, sizeof(*size_cells)); 139 } 140 141 static int 142 brcm_mdionexus_fdt_probe(device_t dev) 143 { 144 145 if (!ofw_bus_status_okay(dev)) 146 return (ENXIO); 147 device_set_desc(dev, "Broadcom MDIO nexus"); 148 return (BUS_PROBE_SPECIFIC); 149 } 150 151 static int 152 brcm_mdionexus_fdt_attach(device_t dev) 153 { 154 struct brcm_mdionexus_softc *sc; 155 int err; 156 pcell_t addr_cells, size_cells, buf[2]; 157 phandle_t node; 158 159 sc = device_get_softc(dev); 160 161 node = ofw_bus_get_node(dev); 162 get_addr_size_cells(node, &addr_cells, &size_cells); 163 if ((addr_cells != 1) || (size_cells != 0)) { 164 device_printf(dev, "Only addr_cells=1 and size_cells=0 are supported\n"); 165 return (EINVAL); 166 } 167 168 if (OF_getencprop(node, "reg", buf, sizeof(pcell_t)) < 0) 169 return (ENXIO); 170 171 sc->mux_id = buf[0]; 172 173 err = brcm_mdionexus_ofw_bus_attach(dev); 174 if (err != 0) 175 return (err); 176 177 return (bus_generic_attach(dev)); 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 = (struct simplebus_softc *)device_get_softc(dev); 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