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/cdefs.h> 29 __FBSDID("$FreeBSD$"); 30 31 #include <sys/param.h> 32 #include <sys/bus.h> 33 #include <sys/kernel.h> 34 #include <sys/module.h> 35 #include <sys/rman.h> 36 #include <sys/systm.h> 37 38 #include <dev/fdt/simplebus.h> 39 #include <dev/ofw/ofw_bus_subr.h> 40 #include <dev/ofw/ofw_bus.h> 41 42 #include <machine/bus.h> 43 #include <machine/resource.h> 44 45 #include "mdio_if.h" 46 47 #define REG_BASE_RID 0 48 49 #define MDIO_RATE_ADJ_EXT_OFFSET 0x000 50 #define MDIO_RATE_ADJ_INT_OFFSET 0x004 51 #define MDIO_RATE_ADJ_DIVIDENT_SHIFT 16 52 53 #define MDIO_SCAN_CTRL_OFFSET 0x008 54 #define MDIO_SCAN_CTRL_OVRIDE_EXT_MSTR 28 55 56 #define MDIO_PARAM_OFFSET 0x23c 57 #define MDIO_PARAM_MIIM_CYCLE 29 58 #define MDIO_PARAM_INTERNAL_SEL 25 59 #define MDIO_PARAM_BUS_ID 22 60 #define MDIO_PARAM_C45_SEL 21 61 #define MDIO_PARAM_PHY_ID 16 62 #define MDIO_PARAM_PHY_DATA 0 63 64 #define MDIO_READ_OFFSET 0x240 65 #define MDIO_READ_DATA_MASK 0xffff 66 #define MDIO_ADDR_OFFSET 0x244 67 68 #define MDIO_CTRL_OFFSET 0x248 69 #define MDIO_CTRL_WRITE_OP 0x1 70 #define MDIO_CTRL_READ_OP 0x2 71 72 #define MDIO_STAT_OFFSET 0x24c 73 #define MDIO_STAT_DONE 1 74 75 #define BUS_MAX_ADDR 32 76 #define EXT_BUS_START_ADDR 16 77 78 #define MDIO_REG_ADDR_SPACE_SIZE 0x250 79 80 #define MDIO_OPERATING_FREQUENCY 11000000 81 #define MDIO_RATE_ADJ_DIVIDENT 1 82 83 #define MII_ADDR_C45 (1<<30) 84 85 static int brcm_iproc_mdio_probe(device_t); 86 static int brcm_iproc_mdio_attach(device_t); 87 static int brcm_iproc_mdio_detach(device_t); 88 89 /* OFW bus interface */ 90 struct brcm_mdio_ofw_devinfo { 91 struct ofw_bus_devinfo di_dinfo; 92 struct resource_list di_rl; 93 }; 94 95 struct brcm_iproc_mdio_softc { 96 struct simplebus_softc sbus; 97 device_t dev; 98 struct resource * reg_base; 99 uint32_t clock_rate; 100 }; 101 102 MALLOC_DEFINE(M_BRCM_IPROC_MDIO, "Broadcom IPROC MDIO", 103 "Broadcom IPROC MDIO dynamic memory"); 104 105 static int brcm_iproc_config(struct brcm_iproc_mdio_softc*); 106 static const struct ofw_bus_devinfo * 107 brcm_iproc_mdio_get_devinfo(device_t, device_t); 108 static int brcm_iproc_mdio_write_mux(device_t, int, int, int, int); 109 static int brcm_iproc_mdio_read_mux(device_t, int, int, int); 110 111 static device_method_t brcm_iproc_mdio_fdt_methods[] = { 112 /* Device interface */ 113 DEVMETHOD(device_probe, brcm_iproc_mdio_probe), 114 DEVMETHOD(device_attach, brcm_iproc_mdio_attach), 115 DEVMETHOD(device_detach, brcm_iproc_mdio_detach), 116 117 /* Bus interface */ 118 DEVMETHOD(bus_alloc_resource, bus_generic_alloc_resource), 119 DEVMETHOD(bus_release_resource, bus_generic_release_resource), 120 DEVMETHOD(bus_activate_resource, bus_generic_activate_resource), 121 122 /* ofw_bus interface */ 123 DEVMETHOD(ofw_bus_get_devinfo, brcm_iproc_mdio_get_devinfo), 124 DEVMETHOD(ofw_bus_get_compat, ofw_bus_gen_get_compat), 125 DEVMETHOD(ofw_bus_get_model, ofw_bus_gen_get_model), 126 DEVMETHOD(ofw_bus_get_name, ofw_bus_gen_get_name), 127 DEVMETHOD(ofw_bus_get_node, ofw_bus_gen_get_node), 128 DEVMETHOD(ofw_bus_get_type, ofw_bus_gen_get_type), 129 130 /* MDIO interface */ 131 DEVMETHOD(mdio_writereg_mux, brcm_iproc_mdio_write_mux), 132 DEVMETHOD(mdio_readreg_mux, brcm_iproc_mdio_read_mux), 133 134 /* End */ 135 DEVMETHOD_END 136 }; 137 138 DEFINE_CLASS_0(brcm_iproc_mdio, brcm_iproc_mdio_driver, 139 brcm_iproc_mdio_fdt_methods, sizeof(struct brcm_iproc_mdio_softc)); 140 141 EARLY_DRIVER_MODULE(brcm_iproc_mdio, ofwbus, brcm_iproc_mdio_driver, 0, 0, 142 BUS_PASS_BUS + BUS_PASS_ORDER_MIDDLE); 143 EARLY_DRIVER_MODULE(brcm_iproc_mdio, simplebus, brcm_iproc_mdio_driver, 0, 0, 144 BUS_PASS_BUS + BUS_PASS_ORDER_MIDDLE); 145 146 static struct ofw_compat_data mdio_compat_data[] = { 147 {"brcm,mdio-mux-iproc", true}, 148 {NULL, false} 149 }; 150 151 static int 152 brcm_iproc_switch(struct brcm_iproc_mdio_softc *sc, int child) 153 { 154 uint32_t param, bus_id; 155 uint32_t bus_dir; 156 157 /* select bus and its properties */ 158 bus_dir = (child < EXT_BUS_START_ADDR); 159 bus_id = bus_dir ? child : (child - EXT_BUS_START_ADDR); 160 161 param = (bus_dir ? 1 : 0) << MDIO_PARAM_INTERNAL_SEL; 162 param |= (bus_id << MDIO_PARAM_BUS_ID); 163 164 bus_write_4(sc->reg_base, MDIO_PARAM_OFFSET, param); 165 166 return (0); 167 } 168 169 static int 170 iproc_mdio_wait_for_idle(struct brcm_iproc_mdio_softc *sc, uint32_t result) 171 { 172 unsigned int timeout = 1000; /* loop for 1s */ 173 uint32_t val; 174 175 do { 176 val = bus_read_4(sc->reg_base, MDIO_STAT_OFFSET); 177 if ((val & MDIO_STAT_DONE) == result) 178 return (0); 179 180 pause("BRCM MDIO SLEEP", 1000 / hz); 181 } while (timeout--); 182 183 return (ETIMEDOUT); 184 } 185 186 /* start_miim_ops- Program and start MDIO transaction over mdio bus. 187 * @base: Base address 188 * @phyid: phyid of the selected bus. 189 * @reg: register offset to be read/written. 190 * @val :0 if read op else value to be written in @reg; 191 * @op: Operation that need to be carried out. 192 * MDIO_CTRL_READ_OP: Read transaction. 193 * MDIO_CTRL_WRITE_OP: Write transaction. 194 * 195 * Return value: Successful Read operation returns read reg values and write 196 * operation returns 0. Failure operation returns negative error code. 197 */ 198 static int 199 brcm_iproc_mdio_op(struct brcm_iproc_mdio_softc *sc, 200 uint16_t phyid, uint32_t reg, uint32_t val, uint32_t op) 201 { 202 uint32_t param; 203 int ret; 204 205 bus_write_4(sc->reg_base, MDIO_CTRL_OFFSET, 0); 206 bus_read_4(sc->reg_base, MDIO_STAT_OFFSET); 207 ret = iproc_mdio_wait_for_idle(sc, 0); 208 if (ret) 209 goto err; 210 211 param = bus_read_4(sc->reg_base, MDIO_PARAM_OFFSET); 212 param |= phyid << MDIO_PARAM_PHY_ID; 213 param |= val << MDIO_PARAM_PHY_DATA; 214 if (reg & MII_ADDR_C45) 215 param |= (1 << MDIO_PARAM_C45_SEL); 216 217 bus_write_4(sc->reg_base, MDIO_PARAM_OFFSET, param); 218 219 bus_write_4(sc->reg_base, MDIO_ADDR_OFFSET, reg); 220 221 bus_write_4(sc->reg_base, MDIO_CTRL_OFFSET, op); 222 223 ret = iproc_mdio_wait_for_idle(sc, 1); 224 if (ret) 225 goto err; 226 227 if (op == MDIO_CTRL_READ_OP) 228 ret = bus_read_4(sc->reg_base, MDIO_READ_OFFSET) & MDIO_READ_DATA_MASK; 229 err: 230 return ret; 231 } 232 233 static int 234 brcm_iproc_config(struct brcm_iproc_mdio_softc *sc) 235 { 236 uint32_t divisor; 237 uint32_t val; 238 239 /* Disable external mdio master access */ 240 val = bus_read_4(sc->reg_base, MDIO_SCAN_CTRL_OFFSET); 241 val |= 1 << MDIO_SCAN_CTRL_OVRIDE_EXT_MSTR; 242 bus_write_4(sc->reg_base, MDIO_SCAN_CTRL_OFFSET, val); 243 244 if (sc->clock_rate) { 245 /* use rate adjust regs to derrive the mdio's operating 246 * frequency from the specified core clock 247 */ 248 divisor = sc->clock_rate / MDIO_OPERATING_FREQUENCY; 249 divisor = divisor / (MDIO_RATE_ADJ_DIVIDENT + 1); 250 val = divisor; 251 val |= MDIO_RATE_ADJ_DIVIDENT << MDIO_RATE_ADJ_DIVIDENT_SHIFT; 252 bus_write_4(sc->reg_base, MDIO_RATE_ADJ_EXT_OFFSET, val); 253 bus_write_4(sc->reg_base, MDIO_RATE_ADJ_INT_OFFSET, val); 254 } 255 256 return (0); 257 } 258 259 static int 260 brcm_iproc_mdio_write_mux(device_t dev, int bus, int phy, int reg, int val) 261 { 262 struct brcm_iproc_mdio_softc *sc; 263 264 sc = device_get_softc(dev); 265 266 if (brcm_iproc_switch(sc, bus) != 0) { 267 device_printf(dev, "Failed to set BUS MUX\n"); 268 return (EINVAL); 269 } 270 271 return (brcm_iproc_mdio_op(sc, phy, reg, val, MDIO_CTRL_WRITE_OP)); 272 } 273 274 static int 275 brcm_iproc_mdio_read_mux(device_t dev, int bus, int phy, int reg) 276 { 277 struct brcm_iproc_mdio_softc *sc; 278 279 sc = device_get_softc(dev); 280 281 if (brcm_iproc_switch(sc, bus) != 0) { 282 device_printf(dev, "Failed to set BUS MUX\n"); 283 return (EINVAL); 284 } 285 286 return (brcm_iproc_mdio_op(sc, phy, reg, 0, MDIO_CTRL_READ_OP)); 287 } 288 289 static int 290 brcm_iproc_mdio_probe(device_t dev) 291 { 292 293 if (!ofw_bus_status_okay(dev)) 294 return (ENXIO); 295 if (!ofw_bus_search_compatible(dev, mdio_compat_data)->ocd_data) 296 return (ENXIO); 297 298 device_set_desc(dev, "Broadcom MDIO MUX driver"); 299 return (BUS_PROBE_DEFAULT); 300 } 301 302 static int 303 brcm_iproc_mdio_attach(device_t dev) 304 { 305 struct brcm_iproc_mdio_softc *sc; 306 phandle_t node, parent; 307 struct brcm_mdio_ofw_devinfo *di; 308 int rid; 309 device_t child; 310 311 sc = device_get_softc(dev); 312 sc->dev = dev; 313 314 /* Allocate memory resources */ 315 rid = REG_BASE_RID; 316 sc->reg_base = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, 317 RF_ACTIVE); 318 if (sc->reg_base == NULL) { 319 device_printf(dev, "Could not allocate memory\n"); 320 return (ENXIO); 321 } 322 323 /* Configure MDIO controlled */ 324 if (brcm_iproc_config(sc) < 0) { 325 device_printf(dev, "Unable to initialize IPROC MDIO\n"); 326 goto error; 327 } 328 329 parent = ofw_bus_get_node(dev); 330 simplebus_init(dev, parent); 331 332 /* Iterate through all bus subordinates */ 333 for (node = OF_child(parent); node > 0; node = OF_peer(node)) { 334 /* Allocate and populate devinfo. */ 335 di = malloc(sizeof(*di), M_BRCM_IPROC_MDIO, M_WAITOK | M_ZERO); 336 if (ofw_bus_gen_setup_devinfo(&di->di_dinfo, node) != 0) { 337 free(di, M_BRCM_IPROC_MDIO); 338 continue; 339 } 340 341 /* Initialize and populate resource list. */ 342 resource_list_init(&di->di_rl); 343 ofw_bus_reg_to_rl(dev, node, sc->sbus.acells, sc->sbus.scells, 344 &di->di_rl); 345 ofw_bus_intr_to_rl(dev, node, &di->di_rl, NULL); 346 347 /* Add newbus device for this FDT node */ 348 child = device_add_child(dev, NULL, -1); 349 if (child == NULL) { 350 printf("Failed to add child\n"); 351 resource_list_free(&di->di_rl); 352 ofw_bus_gen_destroy_devinfo(&di->di_dinfo); 353 free(di, M_BRCM_IPROC_MDIO); 354 continue; 355 } 356 357 device_set_ivars(child, di); 358 } 359 360 /* 361 * Register device to this node/xref. 362 * Thanks to that we will be able to retrieve device_t structure 363 * while holding only node reference acquired from FDT. 364 */ 365 node = ofw_bus_get_node(dev); 366 OF_device_register_xref(OF_xref_from_node(node), dev); 367 368 return (bus_generic_attach(dev)); 369 370 error: 371 brcm_iproc_mdio_detach(dev); 372 return (ENXIO); 373 } 374 375 static const struct ofw_bus_devinfo * 376 brcm_iproc_mdio_get_devinfo(device_t bus __unused, device_t child) 377 { 378 struct brcm_mdio_ofw_devinfo *di; 379 380 di = device_get_ivars(child); 381 return (&di->di_dinfo); 382 } 383 384 static int 385 brcm_iproc_mdio_detach(device_t dev) 386 { 387 struct brcm_iproc_mdio_softc *sc; 388 389 sc = device_get_softc(dev); 390 391 if (sc->reg_base != NULL) { 392 bus_release_resource(dev, SYS_RES_MEMORY, REG_BASE_RID, 393 sc->reg_base); 394 } 395 396 return (0); 397 } 398