136c1a376SWojciech Macek /*- 236c1a376SWojciech Macek * Copyright (c) 2019 Juniper Networks, Inc. 336c1a376SWojciech Macek * Copyright (c) 2019 Semihalf. 436c1a376SWojciech Macek * All rights reserved. 536c1a376SWojciech Macek * 636c1a376SWojciech Macek * Redistribution and use in source and binary forms, with or without 736c1a376SWojciech Macek * modification, are permitted provided that the following conditions 836c1a376SWojciech Macek * are met: 936c1a376SWojciech Macek * 1. Redistributions of source code must retain the above copyright 1036c1a376SWojciech Macek * notice, this list of conditions and the following disclaimer. 1136c1a376SWojciech Macek * 2. Redistributions in binary form must reproduce the above copyright 1236c1a376SWojciech Macek * notice, this list of conditions and the following disclaimer in the 1336c1a376SWojciech Macek * documentation and/or other materials provided with the distribution. 1436c1a376SWojciech Macek * 1536c1a376SWojciech Macek * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 1636c1a376SWojciech Macek * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 1736c1a376SWojciech Macek * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 1836c1a376SWojciech Macek * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, 1936c1a376SWojciech Macek * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 2036c1a376SWojciech Macek * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 2136c1a376SWojciech Macek * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2236c1a376SWojciech Macek * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 2336c1a376SWojciech Macek * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 2436c1a376SWojciech Macek * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 2536c1a376SWojciech Macek * POSSIBILITY OF SUCH DAMAGE. 2636c1a376SWojciech Macek */ 2736c1a376SWojciech Macek 2836c1a376SWojciech Macek #include <sys/cdefs.h> 2936c1a376SWojciech Macek __FBSDID("$FreeBSD$"); 3036c1a376SWojciech Macek 3136c1a376SWojciech Macek #include <sys/param.h> 3236c1a376SWojciech Macek #include <sys/bus.h> 3336c1a376SWojciech Macek #include <sys/kernel.h> 3436c1a376SWojciech Macek #include <sys/module.h> 3536c1a376SWojciech Macek #include <sys/rman.h> 3636c1a376SWojciech Macek #include <sys/systm.h> 3736c1a376SWojciech Macek 3836c1a376SWojciech Macek #include <dev/fdt/simplebus.h> 3936c1a376SWojciech Macek #include <dev/ofw/ofw_bus_subr.h> 4036c1a376SWojciech Macek #include <dev/ofw/ofw_bus.h> 4136c1a376SWojciech Macek 4236c1a376SWojciech Macek #include <machine/bus.h> 4336c1a376SWojciech Macek #include <machine/resource.h> 4436c1a376SWojciech Macek 4536c1a376SWojciech Macek #include "mdio_if.h" 4636c1a376SWojciech Macek 4736c1a376SWojciech Macek MALLOC_DEFINE(M_BRCM_IPROC_NEXUS, "Broadcom IPROC MDIO NEXUS", 4836c1a376SWojciech Macek "Broadcom IPROC MDIO NEXUS dynamic memory"); 4936c1a376SWojciech Macek 5036c1a376SWojciech Macek struct brcm_mdionexus_softc { 5136c1a376SWojciech Macek struct simplebus_softc simplebus_sc; 5236c1a376SWojciech Macek uint32_t mux_id; 5336c1a376SWojciech Macek }; 5436c1a376SWojciech Macek 5536c1a376SWojciech Macek /* OFW bus interface */ 5636c1a376SWojciech Macek struct brcm_mdionexus_ofw_devinfo { 5736c1a376SWojciech Macek struct ofw_bus_devinfo di_dinfo; 5836c1a376SWojciech Macek struct resource_list di_rl; 5936c1a376SWojciech Macek }; 6036c1a376SWojciech Macek 6136c1a376SWojciech Macek static device_probe_t brcm_mdionexus_fdt_probe; 6236c1a376SWojciech Macek static device_attach_t brcm_mdionexus_fdt_attach; 6336c1a376SWojciech Macek 6436c1a376SWojciech Macek static const struct ofw_bus_devinfo * brcm_mdionexus_ofw_get_devinfo(device_t, 6536c1a376SWojciech Macek device_t); 6636c1a376SWojciech Macek static int brcm_mdionexus_mdio_readreg(device_t, int, int); 6736c1a376SWojciech Macek static int brcm_mdionexus_mdio_writereg(device_t, int, int, int); 6836c1a376SWojciech Macek 6936c1a376SWojciech Macek static device_method_t brcm_mdionexus_fdt_methods[] = { 7036c1a376SWojciech Macek /* Device interface */ 7136c1a376SWojciech Macek DEVMETHOD(device_probe, brcm_mdionexus_fdt_probe), 7236c1a376SWojciech Macek DEVMETHOD(device_attach, brcm_mdionexus_fdt_attach), 7336c1a376SWojciech Macek 7436c1a376SWojciech Macek /* Bus interface */ 7536c1a376SWojciech Macek DEVMETHOD(bus_alloc_resource, bus_generic_alloc_resource), 7636c1a376SWojciech Macek DEVMETHOD(bus_release_resource, bus_generic_release_resource), 7736c1a376SWojciech Macek DEVMETHOD(bus_activate_resource, bus_generic_activate_resource), 7836c1a376SWojciech Macek 7936c1a376SWojciech Macek /* ofw_bus interface */ 8036c1a376SWojciech Macek DEVMETHOD(ofw_bus_get_devinfo, brcm_mdionexus_ofw_get_devinfo), 8136c1a376SWojciech Macek DEVMETHOD(ofw_bus_get_compat, ofw_bus_gen_get_compat), 8236c1a376SWojciech Macek DEVMETHOD(ofw_bus_get_model, ofw_bus_gen_get_model), 8336c1a376SWojciech Macek DEVMETHOD(ofw_bus_get_name, ofw_bus_gen_get_name), 8436c1a376SWojciech Macek DEVMETHOD(ofw_bus_get_node, ofw_bus_gen_get_node), 8536c1a376SWojciech Macek DEVMETHOD(ofw_bus_get_type, ofw_bus_gen_get_type), 8636c1a376SWojciech Macek 8736c1a376SWojciech Macek /* MDIO interface */ 8836c1a376SWojciech Macek /* MDIO interface */ 8936c1a376SWojciech Macek DEVMETHOD(mdio_readreg, brcm_mdionexus_mdio_readreg), 9036c1a376SWojciech Macek DEVMETHOD(mdio_writereg, brcm_mdionexus_mdio_writereg), 9136c1a376SWojciech Macek 9236c1a376SWojciech Macek DEVMETHOD_END 9336c1a376SWojciech Macek }; 9436c1a376SWojciech Macek 9536c1a376SWojciech Macek DEFINE_CLASS_0(brcm_mdionexus, brcm_mdionexus_fdt_driver, brcm_mdionexus_fdt_methods, 9636c1a376SWojciech Macek sizeof(struct brcm_mdionexus_softc)); 9736c1a376SWojciech Macek 9836c1a376SWojciech Macek static driver_t brcm_mdionexus_driver = { 9936c1a376SWojciech Macek "brcm_mdionexus", 10036c1a376SWojciech Macek brcm_mdionexus_fdt_methods, 10136c1a376SWojciech Macek sizeof(struct brcm_mdionexus_softc) 10236c1a376SWojciech Macek }; 103*82d4dc06SJohn Baldwin 10436c1a376SWojciech Macek EARLY_DRIVER_MODULE(brcm_mdionexus, brcm_iproc_mdio, brcm_mdionexus_driver, 105*82d4dc06SJohn Baldwin NULL, NULL, BUS_PASS_BUS + BUS_PASS_ORDER_MIDDLE); 10636c1a376SWojciech Macek 10736c1a376SWojciech Macek static int brcm_mdionexus_ofw_bus_attach(device_t); 10836c1a376SWojciech Macek 10936c1a376SWojciech Macek static int 11036c1a376SWojciech Macek brcm_mdionexus_mdio_readreg(device_t dev, int phy, int reg) 11136c1a376SWojciech Macek { 11236c1a376SWojciech Macek struct brcm_mdionexus_softc *sc; 11336c1a376SWojciech Macek 11436c1a376SWojciech Macek sc = device_get_softc(dev); 11536c1a376SWojciech Macek 11636c1a376SWojciech Macek return (MDIO_READREG_MUX(device_get_parent(dev), 11736c1a376SWojciech Macek sc->mux_id, phy, reg)); 11836c1a376SWojciech Macek } 11936c1a376SWojciech Macek 12036c1a376SWojciech Macek static int 12136c1a376SWojciech Macek brcm_mdionexus_mdio_writereg(device_t dev, int phy, int reg, int val) 12236c1a376SWojciech Macek { 12336c1a376SWojciech Macek struct brcm_mdionexus_softc *sc; 12436c1a376SWojciech Macek 12536c1a376SWojciech Macek sc = device_get_softc(dev); 12636c1a376SWojciech Macek 12736c1a376SWojciech Macek return (MDIO_WRITEREG_MUX(device_get_parent(dev), 12836c1a376SWojciech Macek sc->mux_id, phy, reg, val)); 12936c1a376SWojciech Macek } 13036c1a376SWojciech Macek 13136c1a376SWojciech Macek static __inline void 13236c1a376SWojciech Macek get_addr_size_cells(phandle_t node, pcell_t *addr_cells, pcell_t *size_cells) 13336c1a376SWojciech Macek { 13436c1a376SWojciech Macek 13536c1a376SWojciech Macek *addr_cells = 2; 13636c1a376SWojciech Macek /* Find address cells if present */ 13736c1a376SWojciech Macek OF_getencprop(node, "#address-cells", addr_cells, sizeof(*addr_cells)); 13836c1a376SWojciech Macek 13936c1a376SWojciech Macek *size_cells = 2; 14036c1a376SWojciech Macek /* Find size cells if present */ 14136c1a376SWojciech Macek OF_getencprop(node, "#size-cells", size_cells, sizeof(*size_cells)); 14236c1a376SWojciech Macek } 14336c1a376SWojciech Macek 14436c1a376SWojciech Macek static int 14536c1a376SWojciech Macek brcm_mdionexus_fdt_probe(device_t dev) 14636c1a376SWojciech Macek { 14736c1a376SWojciech Macek 14836c1a376SWojciech Macek if (!ofw_bus_status_okay(dev)) 14936c1a376SWojciech Macek return (ENXIO); 15036c1a376SWojciech Macek device_set_desc(dev, "Broadcom MDIO nexus"); 15136c1a376SWojciech Macek return (BUS_PROBE_SPECIFIC); 15236c1a376SWojciech Macek } 15336c1a376SWojciech Macek 15436c1a376SWojciech Macek static int 15536c1a376SWojciech Macek brcm_mdionexus_fdt_attach(device_t dev) 15636c1a376SWojciech Macek { 15736c1a376SWojciech Macek struct brcm_mdionexus_softc *sc; 15836c1a376SWojciech Macek int err; 15936c1a376SWojciech Macek pcell_t addr_cells, size_cells, buf[2]; 16036c1a376SWojciech Macek phandle_t node; 16136c1a376SWojciech Macek 16236c1a376SWojciech Macek sc = device_get_softc(dev); 16336c1a376SWojciech Macek 16436c1a376SWojciech Macek node = ofw_bus_get_node(dev); 16536c1a376SWojciech Macek get_addr_size_cells(node, &addr_cells, &size_cells); 16636c1a376SWojciech Macek if ((addr_cells != 1) || (size_cells != 0)) { 16736c1a376SWojciech Macek device_printf(dev, "Only addr_cells=1 and size_cells=0 are supported\n"); 16836c1a376SWojciech Macek return (EINVAL); 16936c1a376SWojciech Macek } 17036c1a376SWojciech Macek 17136c1a376SWojciech Macek if (OF_getencprop(node, "reg", buf, sizeof(pcell_t)) < 0) 17236c1a376SWojciech Macek return (ENXIO); 17336c1a376SWojciech Macek 17436c1a376SWojciech Macek sc->mux_id = buf[0]; 17536c1a376SWojciech Macek 17636c1a376SWojciech Macek err = brcm_mdionexus_ofw_bus_attach(dev); 17736c1a376SWojciech Macek if (err != 0) 17836c1a376SWojciech Macek return (err); 17936c1a376SWojciech Macek 18036c1a376SWojciech Macek return (bus_generic_attach(dev)); 18136c1a376SWojciech Macek } 18236c1a376SWojciech Macek 18336c1a376SWojciech Macek static const struct ofw_bus_devinfo * 18436c1a376SWojciech Macek brcm_mdionexus_ofw_get_devinfo(device_t bus __unused, device_t child) 18536c1a376SWojciech Macek { 18636c1a376SWojciech Macek struct brcm_mdionexus_ofw_devinfo *di; 18736c1a376SWojciech Macek 18836c1a376SWojciech Macek di = device_get_ivars(child); 18936c1a376SWojciech Macek return (&di->di_dinfo); 19036c1a376SWojciech Macek } 19136c1a376SWojciech Macek 19236c1a376SWojciech Macek static int 19336c1a376SWojciech Macek brcm_mdionexus_ofw_bus_attach(device_t dev) 19436c1a376SWojciech Macek { 19536c1a376SWojciech Macek struct simplebus_softc *sc; 19636c1a376SWojciech Macek struct brcm_mdionexus_ofw_devinfo *di; 19736c1a376SWojciech Macek device_t child; 19836c1a376SWojciech Macek phandle_t parent, node; 19936c1a376SWojciech Macek 20036c1a376SWojciech Macek parent = ofw_bus_get_node(dev); 20136c1a376SWojciech Macek simplebus_init(dev, parent); 20236c1a376SWojciech Macek 20336c1a376SWojciech Macek sc = (struct simplebus_softc *)device_get_softc(dev); 20436c1a376SWojciech Macek 20536c1a376SWojciech Macek /* Iterate through all bus subordinates */ 20636c1a376SWojciech Macek for (node = OF_child(parent); node > 0; node = OF_peer(node)) { 20736c1a376SWojciech Macek /* Allocate and populate devinfo. */ 20836c1a376SWojciech Macek di = malloc(sizeof(*di), M_BRCM_IPROC_NEXUS, M_WAITOK | M_ZERO); 20936c1a376SWojciech Macek if (ofw_bus_gen_setup_devinfo(&di->di_dinfo, node) != 0) { 21036c1a376SWojciech Macek free(di, M_BRCM_IPROC_NEXUS); 21136c1a376SWojciech Macek continue; 21236c1a376SWojciech Macek } 21336c1a376SWojciech Macek 21436c1a376SWojciech Macek /* Initialize and populate resource list. */ 21536c1a376SWojciech Macek resource_list_init(&di->di_rl); 21636c1a376SWojciech Macek ofw_bus_reg_to_rl(dev, node, sc->acells, sc->scells, 21736c1a376SWojciech Macek &di->di_rl); 21836c1a376SWojciech Macek ofw_bus_intr_to_rl(dev, node, &di->di_rl, NULL); 21936c1a376SWojciech Macek 22036c1a376SWojciech Macek /* Add newbus device for this FDT node */ 22136c1a376SWojciech Macek child = device_add_child(dev, NULL, -1); 22236c1a376SWojciech Macek if (child == NULL) { 22336c1a376SWojciech Macek resource_list_free(&di->di_rl); 22436c1a376SWojciech Macek ofw_bus_gen_destroy_devinfo(&di->di_dinfo); 22536c1a376SWojciech Macek free(di, M_BRCM_IPROC_NEXUS); 22636c1a376SWojciech Macek continue; 22736c1a376SWojciech Macek } 22836c1a376SWojciech Macek 22936c1a376SWojciech Macek device_set_ivars(child, di); 23036c1a376SWojciech Macek } 23136c1a376SWojciech Macek 23236c1a376SWojciech Macek return (0); 23336c1a376SWojciech Macek } 234