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 static devclass_t brcm_iproc_mdio_fdt_devclass; 142 143 EARLY_DRIVER_MODULE(brcm_iproc_mdio, ofwbus, brcm_iproc_mdio_driver, 144 brcm_iproc_mdio_fdt_devclass, 0, 0, BUS_PASS_BUS + BUS_PASS_ORDER_MIDDLE); 145 EARLY_DRIVER_MODULE(brcm_iproc_mdio, simplebus, brcm_iproc_mdio_driver, 146 brcm_iproc_mdio_fdt_devclass, 0, 0, BUS_PASS_BUS + BUS_PASS_ORDER_MIDDLE); 147 148 static struct ofw_compat_data mdio_compat_data[] = { 149 {"brcm,mdio-mux-iproc", true}, 150 {NULL, false} 151 }; 152 153 static int 154 brcm_iproc_switch(struct brcm_iproc_mdio_softc *sc, int child) 155 { 156 uint32_t param, bus_id; 157 uint32_t bus_dir; 158 159 /* select bus and its properties */ 160 bus_dir = (child < EXT_BUS_START_ADDR); 161 bus_id = bus_dir ? child : (child - EXT_BUS_START_ADDR); 162 163 param = (bus_dir ? 1 : 0) << MDIO_PARAM_INTERNAL_SEL; 164 param |= (bus_id << MDIO_PARAM_BUS_ID); 165 166 bus_write_4(sc->reg_base, MDIO_PARAM_OFFSET, param); 167 168 return (0); 169 } 170 171 static int 172 iproc_mdio_wait_for_idle(struct brcm_iproc_mdio_softc *sc, uint32_t result) 173 { 174 unsigned int timeout = 1000; /* loop for 1s */ 175 uint32_t val; 176 177 do { 178 val = bus_read_4(sc->reg_base, MDIO_STAT_OFFSET); 179 if ((val & MDIO_STAT_DONE) == result) 180 return (0); 181 182 pause("BRCM MDIO SLEEP", 1000 / hz); 183 } while (timeout--); 184 185 return (ETIMEDOUT); 186 } 187 188 /* start_miim_ops- Program and start MDIO transaction over mdio bus. 189 * @base: Base address 190 * @phyid: phyid of the selected bus. 191 * @reg: register offset to be read/written. 192 * @val :0 if read op else value to be written in @reg; 193 * @op: Operation that need to be carried out. 194 * MDIO_CTRL_READ_OP: Read transaction. 195 * MDIO_CTRL_WRITE_OP: Write transaction. 196 * 197 * Return value: Successful Read operation returns read reg values and write 198 * operation returns 0. Failure operation returns negative error code. 199 */ 200 static int 201 brcm_iproc_mdio_op(struct brcm_iproc_mdio_softc *sc, 202 uint16_t phyid, uint32_t reg, uint32_t val, uint32_t op) 203 { 204 uint32_t param; 205 int ret; 206 207 bus_write_4(sc->reg_base, MDIO_CTRL_OFFSET, 0); 208 bus_read_4(sc->reg_base, MDIO_STAT_OFFSET); 209 ret = iproc_mdio_wait_for_idle(sc, 0); 210 if (ret) 211 goto err; 212 213 param = bus_read_4(sc->reg_base, MDIO_PARAM_OFFSET); 214 param |= phyid << MDIO_PARAM_PHY_ID; 215 param |= val << MDIO_PARAM_PHY_DATA; 216 if (reg & MII_ADDR_C45) 217 param |= (1 << MDIO_PARAM_C45_SEL); 218 219 bus_write_4(sc->reg_base, MDIO_PARAM_OFFSET, param); 220 221 bus_write_4(sc->reg_base, MDIO_ADDR_OFFSET, reg); 222 223 bus_write_4(sc->reg_base, MDIO_CTRL_OFFSET, op); 224 225 ret = iproc_mdio_wait_for_idle(sc, 1); 226 if (ret) 227 goto err; 228 229 if (op == MDIO_CTRL_READ_OP) 230 ret = bus_read_4(sc->reg_base, MDIO_READ_OFFSET) & MDIO_READ_DATA_MASK; 231 err: 232 return ret; 233 } 234 235 static int 236 brcm_iproc_config(struct brcm_iproc_mdio_softc *sc) 237 { 238 uint32_t divisor; 239 uint32_t val; 240 241 /* Disable external mdio master access */ 242 val = bus_read_4(sc->reg_base, MDIO_SCAN_CTRL_OFFSET); 243 val |= 1 << MDIO_SCAN_CTRL_OVRIDE_EXT_MSTR; 244 bus_write_4(sc->reg_base, MDIO_SCAN_CTRL_OFFSET, val); 245 246 if (sc->clock_rate) { 247 /* use rate adjust regs to derrive the mdio's operating 248 * frequency from the specified core clock 249 */ 250 divisor = sc->clock_rate / MDIO_OPERATING_FREQUENCY; 251 divisor = divisor / (MDIO_RATE_ADJ_DIVIDENT + 1); 252 val = divisor; 253 val |= MDIO_RATE_ADJ_DIVIDENT << MDIO_RATE_ADJ_DIVIDENT_SHIFT; 254 bus_write_4(sc->reg_base, MDIO_RATE_ADJ_EXT_OFFSET, val); 255 bus_write_4(sc->reg_base, MDIO_RATE_ADJ_INT_OFFSET, val); 256 } 257 258 return (0); 259 } 260 261 static int 262 brcm_iproc_mdio_write_mux(device_t dev, int bus, int phy, int reg, int val) 263 { 264 struct brcm_iproc_mdio_softc *sc; 265 266 sc = device_get_softc(dev); 267 268 if (brcm_iproc_switch(sc, bus) != 0) { 269 device_printf(dev, "Failed to set BUS MUX\n"); 270 return (EINVAL); 271 } 272 273 return (brcm_iproc_mdio_op(sc, phy, reg, val, MDIO_CTRL_WRITE_OP)); 274 } 275 276 static int 277 brcm_iproc_mdio_read_mux(device_t dev, int bus, int phy, int reg) 278 { 279 struct brcm_iproc_mdio_softc *sc; 280 281 sc = device_get_softc(dev); 282 283 if (brcm_iproc_switch(sc, bus) != 0) { 284 device_printf(dev, "Failed to set BUS MUX\n"); 285 return (EINVAL); 286 } 287 288 return (brcm_iproc_mdio_op(sc, phy, reg, 0, MDIO_CTRL_READ_OP)); 289 } 290 291 static int 292 brcm_iproc_mdio_probe(device_t dev) 293 { 294 295 if (!ofw_bus_status_okay(dev)) 296 return (ENXIO); 297 if (!ofw_bus_search_compatible(dev, mdio_compat_data)->ocd_data) 298 return (ENXIO); 299 300 device_set_desc(dev, "Broadcom MDIO MUX driver"); 301 return (BUS_PROBE_DEFAULT); 302 } 303 304 static int 305 brcm_iproc_mdio_attach(device_t dev) 306 { 307 struct brcm_iproc_mdio_softc *sc; 308 phandle_t node, parent; 309 struct brcm_mdio_ofw_devinfo *di; 310 int rid; 311 device_t child; 312 313 sc = device_get_softc(dev); 314 sc->dev = dev; 315 316 /* Allocate memory resources */ 317 rid = REG_BASE_RID; 318 sc->reg_base = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, 319 RF_ACTIVE); 320 if (sc->reg_base == NULL) { 321 device_printf(dev, "Could not allocate memory\n"); 322 return (ENXIO); 323 } 324 325 /* Configure MDIO controlled */ 326 if (brcm_iproc_config(sc) < 0) { 327 device_printf(dev, "Unable to initialize IPROC MDIO\n"); 328 goto error; 329 } 330 331 parent = ofw_bus_get_node(dev); 332 simplebus_init(dev, parent); 333 334 /* Iterate through all bus subordinates */ 335 for (node = OF_child(parent); node > 0; node = OF_peer(node)) { 336 /* Allocate and populate devinfo. */ 337 di = malloc(sizeof(*di), M_BRCM_IPROC_MDIO, M_WAITOK | M_ZERO); 338 if (ofw_bus_gen_setup_devinfo(&di->di_dinfo, node) != 0) { 339 free(di, M_BRCM_IPROC_MDIO); 340 continue; 341 } 342 343 /* Initialize and populate resource list. */ 344 resource_list_init(&di->di_rl); 345 ofw_bus_reg_to_rl(dev, node, sc->sbus.acells, sc->sbus.scells, 346 &di->di_rl); 347 ofw_bus_intr_to_rl(dev, node, &di->di_rl, NULL); 348 349 /* Add newbus device for this FDT node */ 350 child = device_add_child(dev, NULL, -1); 351 if (child == NULL) { 352 printf("Failed to add child\n"); 353 resource_list_free(&di->di_rl); 354 ofw_bus_gen_destroy_devinfo(&di->di_dinfo); 355 free(di, M_BRCM_IPROC_MDIO); 356 continue; 357 } 358 359 device_set_ivars(child, di); 360 } 361 362 /* 363 * Register device to this node/xref. 364 * Thanks to that we will be able to retrieve device_t structure 365 * while holding only node reference acquired from FDT. 366 */ 367 node = ofw_bus_get_node(dev); 368 OF_device_register_xref(OF_xref_from_node(node), dev); 369 370 return (bus_generic_attach(dev)); 371 372 error: 373 brcm_iproc_mdio_detach(dev); 374 return (ENXIO); 375 } 376 377 static const struct ofw_bus_devinfo * 378 brcm_iproc_mdio_get_devinfo(device_t bus __unused, device_t child) 379 { 380 struct brcm_mdio_ofw_devinfo *di; 381 382 di = device_get_ivars(child); 383 return (&di->di_dinfo); 384 } 385 386 static int 387 brcm_iproc_mdio_detach(device_t dev) 388 { 389 struct brcm_iproc_mdio_softc *sc; 390 391 sc = device_get_softc(dev); 392 393 if (sc->reg_base != NULL) { 394 bus_release_resource(dev, SYS_RES_MEMORY, REG_BASE_RID, 395 sc->reg_base); 396 } 397 398 return (0); 399 } 400