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/param.h>
2936c1a376SWojciech Macek #include <sys/bus.h>
3036c1a376SWojciech Macek #include <sys/kernel.h>
3136c1a376SWojciech Macek #include <sys/module.h>
3236c1a376SWojciech Macek #include <sys/rman.h>
3336c1a376SWojciech Macek #include <sys/systm.h>
3436c1a376SWojciech Macek
3536c1a376SWojciech Macek #include <dev/fdt/simplebus.h>
3636c1a376SWojciech Macek #include <dev/ofw/ofw_bus_subr.h>
3736c1a376SWojciech Macek #include <dev/ofw/ofw_bus.h>
3836c1a376SWojciech Macek
3936c1a376SWojciech Macek #include <machine/bus.h>
4036c1a376SWojciech Macek #include <machine/resource.h>
4136c1a376SWojciech Macek
4236c1a376SWojciech Macek #include "mdio_if.h"
4336c1a376SWojciech Macek
4436c1a376SWojciech Macek MALLOC_DEFINE(M_BRCM_IPROC_NEXUS, "Broadcom IPROC MDIO NEXUS",
4536c1a376SWojciech Macek "Broadcom IPROC MDIO NEXUS dynamic memory");
4636c1a376SWojciech Macek
4736c1a376SWojciech Macek struct brcm_mdionexus_softc {
4836c1a376SWojciech Macek struct simplebus_softc simplebus_sc;
4936c1a376SWojciech Macek uint32_t mux_id;
5036c1a376SWojciech Macek };
5136c1a376SWojciech Macek
5236c1a376SWojciech Macek /* OFW bus interface */
5336c1a376SWojciech Macek struct brcm_mdionexus_ofw_devinfo {
5436c1a376SWojciech Macek struct ofw_bus_devinfo di_dinfo;
5536c1a376SWojciech Macek struct resource_list di_rl;
5636c1a376SWojciech Macek };
5736c1a376SWojciech Macek
5836c1a376SWojciech Macek static device_probe_t brcm_mdionexus_fdt_probe;
5936c1a376SWojciech Macek static device_attach_t brcm_mdionexus_fdt_attach;
6036c1a376SWojciech Macek
6136c1a376SWojciech Macek static const struct ofw_bus_devinfo * brcm_mdionexus_ofw_get_devinfo(device_t,
6236c1a376SWojciech Macek device_t);
6336c1a376SWojciech Macek static int brcm_mdionexus_mdio_readreg(device_t, int, int);
6436c1a376SWojciech Macek static int brcm_mdionexus_mdio_writereg(device_t, int, int, int);
6536c1a376SWojciech Macek
6636c1a376SWojciech Macek static device_method_t brcm_mdionexus_fdt_methods[] = {
6736c1a376SWojciech Macek /* Device interface */
6836c1a376SWojciech Macek DEVMETHOD(device_probe, brcm_mdionexus_fdt_probe),
6936c1a376SWojciech Macek DEVMETHOD(device_attach, brcm_mdionexus_fdt_attach),
7036c1a376SWojciech Macek
7136c1a376SWojciech Macek /* Bus interface */
7236c1a376SWojciech Macek DEVMETHOD(bus_alloc_resource, bus_generic_alloc_resource),
7336c1a376SWojciech Macek DEVMETHOD(bus_release_resource, bus_generic_release_resource),
7436c1a376SWojciech Macek DEVMETHOD(bus_activate_resource, bus_generic_activate_resource),
7536c1a376SWojciech Macek
7636c1a376SWojciech Macek /* ofw_bus interface */
7736c1a376SWojciech Macek DEVMETHOD(ofw_bus_get_devinfo, brcm_mdionexus_ofw_get_devinfo),
7836c1a376SWojciech Macek DEVMETHOD(ofw_bus_get_compat, ofw_bus_gen_get_compat),
7936c1a376SWojciech Macek DEVMETHOD(ofw_bus_get_model, ofw_bus_gen_get_model),
8036c1a376SWojciech Macek DEVMETHOD(ofw_bus_get_name, ofw_bus_gen_get_name),
8136c1a376SWojciech Macek DEVMETHOD(ofw_bus_get_node, ofw_bus_gen_get_node),
8236c1a376SWojciech Macek DEVMETHOD(ofw_bus_get_type, ofw_bus_gen_get_type),
8336c1a376SWojciech Macek
8436c1a376SWojciech Macek /* MDIO interface */
8536c1a376SWojciech Macek /* MDIO interface */
8636c1a376SWojciech Macek DEVMETHOD(mdio_readreg, brcm_mdionexus_mdio_readreg),
8736c1a376SWojciech Macek DEVMETHOD(mdio_writereg, brcm_mdionexus_mdio_writereg),
8836c1a376SWojciech Macek
8936c1a376SWojciech Macek DEVMETHOD_END
9036c1a376SWojciech Macek };
9136c1a376SWojciech Macek
9236c1a376SWojciech Macek DEFINE_CLASS_0(brcm_mdionexus, brcm_mdionexus_fdt_driver, brcm_mdionexus_fdt_methods,
9336c1a376SWojciech Macek sizeof(struct brcm_mdionexus_softc));
9436c1a376SWojciech Macek
9536c1a376SWojciech Macek static driver_t brcm_mdionexus_driver = {
9636c1a376SWojciech Macek "brcm_mdionexus",
9736c1a376SWojciech Macek brcm_mdionexus_fdt_methods,
9836c1a376SWojciech Macek sizeof(struct brcm_mdionexus_softc)
9936c1a376SWojciech Macek };
10082d4dc06SJohn Baldwin
10136c1a376SWojciech Macek EARLY_DRIVER_MODULE(brcm_mdionexus, brcm_iproc_mdio, brcm_mdionexus_driver,
10282d4dc06SJohn Baldwin NULL, NULL, BUS_PASS_BUS + BUS_PASS_ORDER_MIDDLE);
10336c1a376SWojciech Macek
10436c1a376SWojciech Macek static int brcm_mdionexus_ofw_bus_attach(device_t);
10536c1a376SWojciech Macek
10636c1a376SWojciech Macek static int
brcm_mdionexus_mdio_readreg(device_t dev,int phy,int reg)10736c1a376SWojciech Macek brcm_mdionexus_mdio_readreg(device_t dev, int phy, int reg)
10836c1a376SWojciech Macek {
10936c1a376SWojciech Macek struct brcm_mdionexus_softc *sc;
11036c1a376SWojciech Macek
11136c1a376SWojciech Macek sc = device_get_softc(dev);
11236c1a376SWojciech Macek
11336c1a376SWojciech Macek return (MDIO_READREG_MUX(device_get_parent(dev),
11436c1a376SWojciech Macek sc->mux_id, phy, reg));
11536c1a376SWojciech Macek }
11636c1a376SWojciech Macek
11736c1a376SWojciech Macek static int
brcm_mdionexus_mdio_writereg(device_t dev,int phy,int reg,int val)11836c1a376SWojciech Macek brcm_mdionexus_mdio_writereg(device_t dev, int phy, int reg, int val)
11936c1a376SWojciech Macek {
12036c1a376SWojciech Macek struct brcm_mdionexus_softc *sc;
12136c1a376SWojciech Macek
12236c1a376SWojciech Macek sc = device_get_softc(dev);
12336c1a376SWojciech Macek
12436c1a376SWojciech Macek return (MDIO_WRITEREG_MUX(device_get_parent(dev),
12536c1a376SWojciech Macek sc->mux_id, phy, reg, val));
12636c1a376SWojciech Macek }
12736c1a376SWojciech Macek
12836c1a376SWojciech Macek static __inline void
get_addr_size_cells(phandle_t node,pcell_t * addr_cells,pcell_t * size_cells)12936c1a376SWojciech Macek get_addr_size_cells(phandle_t node, pcell_t *addr_cells, pcell_t *size_cells)
13036c1a376SWojciech Macek {
13136c1a376SWojciech Macek
13236c1a376SWojciech Macek *addr_cells = 2;
13336c1a376SWojciech Macek /* Find address cells if present */
13436c1a376SWojciech Macek OF_getencprop(node, "#address-cells", addr_cells, sizeof(*addr_cells));
13536c1a376SWojciech Macek
13636c1a376SWojciech Macek *size_cells = 2;
13736c1a376SWojciech Macek /* Find size cells if present */
13836c1a376SWojciech Macek OF_getencprop(node, "#size-cells", size_cells, sizeof(*size_cells));
13936c1a376SWojciech Macek }
14036c1a376SWojciech Macek
14136c1a376SWojciech Macek static int
brcm_mdionexus_fdt_probe(device_t dev)14236c1a376SWojciech Macek brcm_mdionexus_fdt_probe(device_t dev)
14336c1a376SWojciech Macek {
14436c1a376SWojciech Macek
14536c1a376SWojciech Macek if (!ofw_bus_status_okay(dev))
14636c1a376SWojciech Macek return (ENXIO);
14736c1a376SWojciech Macek device_set_desc(dev, "Broadcom MDIO nexus");
14836c1a376SWojciech Macek return (BUS_PROBE_SPECIFIC);
14936c1a376SWojciech Macek }
15036c1a376SWojciech Macek
15136c1a376SWojciech Macek static int
brcm_mdionexus_fdt_attach(device_t dev)15236c1a376SWojciech Macek brcm_mdionexus_fdt_attach(device_t dev)
15336c1a376SWojciech Macek {
15436c1a376SWojciech Macek struct brcm_mdionexus_softc *sc;
15536c1a376SWojciech Macek int err;
15636c1a376SWojciech Macek pcell_t addr_cells, size_cells, buf[2];
15736c1a376SWojciech Macek phandle_t node;
15836c1a376SWojciech Macek
15936c1a376SWojciech Macek sc = device_get_softc(dev);
16036c1a376SWojciech Macek
16136c1a376SWojciech Macek node = ofw_bus_get_node(dev);
16236c1a376SWojciech Macek get_addr_size_cells(node, &addr_cells, &size_cells);
16336c1a376SWojciech Macek if ((addr_cells != 1) || (size_cells != 0)) {
16436c1a376SWojciech Macek device_printf(dev, "Only addr_cells=1 and size_cells=0 are supported\n");
16536c1a376SWojciech Macek return (EINVAL);
16636c1a376SWojciech Macek }
16736c1a376SWojciech Macek
16836c1a376SWojciech Macek if (OF_getencprop(node, "reg", buf, sizeof(pcell_t)) < 0)
16936c1a376SWojciech Macek return (ENXIO);
17036c1a376SWojciech Macek
17136c1a376SWojciech Macek sc->mux_id = buf[0];
17236c1a376SWojciech Macek
17336c1a376SWojciech Macek err = brcm_mdionexus_ofw_bus_attach(dev);
17436c1a376SWojciech Macek if (err != 0)
17536c1a376SWojciech Macek return (err);
17636c1a376SWojciech Macek
177*18250ec6SJohn Baldwin bus_attach_children(dev);
178*18250ec6SJohn Baldwin return (0);
17936c1a376SWojciech Macek }
18036c1a376SWojciech Macek
18136c1a376SWojciech Macek static const struct ofw_bus_devinfo *
brcm_mdionexus_ofw_get_devinfo(device_t bus __unused,device_t child)18236c1a376SWojciech Macek brcm_mdionexus_ofw_get_devinfo(device_t bus __unused, device_t child)
18336c1a376SWojciech Macek {
18436c1a376SWojciech Macek struct brcm_mdionexus_ofw_devinfo *di;
18536c1a376SWojciech Macek
18636c1a376SWojciech Macek di = device_get_ivars(child);
18736c1a376SWojciech Macek return (&di->di_dinfo);
18836c1a376SWojciech Macek }
18936c1a376SWojciech Macek
19036c1a376SWojciech Macek static int
brcm_mdionexus_ofw_bus_attach(device_t dev)19136c1a376SWojciech Macek brcm_mdionexus_ofw_bus_attach(device_t dev)
19236c1a376SWojciech Macek {
19336c1a376SWojciech Macek struct simplebus_softc *sc;
19436c1a376SWojciech Macek struct brcm_mdionexus_ofw_devinfo *di;
19536c1a376SWojciech Macek device_t child;
19636c1a376SWojciech Macek phandle_t parent, node;
19736c1a376SWojciech Macek
19836c1a376SWojciech Macek parent = ofw_bus_get_node(dev);
19936c1a376SWojciech Macek simplebus_init(dev, parent);
20036c1a376SWojciech Macek
20136c1a376SWojciech Macek sc = (struct simplebus_softc *)device_get_softc(dev);
20236c1a376SWojciech Macek
20336c1a376SWojciech Macek /* Iterate through all bus subordinates */
20436c1a376SWojciech Macek for (node = OF_child(parent); node > 0; node = OF_peer(node)) {
20536c1a376SWojciech Macek /* Allocate and populate devinfo. */
20636c1a376SWojciech Macek di = malloc(sizeof(*di), M_BRCM_IPROC_NEXUS, M_WAITOK | M_ZERO);
20736c1a376SWojciech Macek if (ofw_bus_gen_setup_devinfo(&di->di_dinfo, node) != 0) {
20836c1a376SWojciech Macek free(di, M_BRCM_IPROC_NEXUS);
20936c1a376SWojciech Macek continue;
21036c1a376SWojciech Macek }
21136c1a376SWojciech Macek
21236c1a376SWojciech Macek /* Initialize and populate resource list. */
21336c1a376SWojciech Macek resource_list_init(&di->di_rl);
21436c1a376SWojciech Macek ofw_bus_reg_to_rl(dev, node, sc->acells, sc->scells,
21536c1a376SWojciech Macek &di->di_rl);
21636c1a376SWojciech Macek ofw_bus_intr_to_rl(dev, node, &di->di_rl, NULL);
21736c1a376SWojciech Macek
21836c1a376SWojciech Macek /* Add newbus device for this FDT node */
2195b56413dSWarner Losh child = device_add_child(dev, NULL, DEVICE_UNIT_ANY);
22036c1a376SWojciech Macek if (child == NULL) {
22136c1a376SWojciech Macek resource_list_free(&di->di_rl);
22236c1a376SWojciech Macek ofw_bus_gen_destroy_devinfo(&di->di_dinfo);
22336c1a376SWojciech Macek free(di, M_BRCM_IPROC_NEXUS);
22436c1a376SWojciech Macek continue;
22536c1a376SWojciech Macek }
22636c1a376SWojciech Macek
22736c1a376SWojciech Macek device_set_ivars(child, di);
22836c1a376SWojciech Macek }
22936c1a376SWojciech Macek
23036c1a376SWojciech Macek return (0);
23136c1a376SWojciech Macek }
232