xref: /freebsd/sys/arm64/broadcom/brcmmdio/mdio_nexus_iproc.c (revision 5b56413d04e608379c9a306373554a8e4d321bc0)
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
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
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
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
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
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 
17736c1a376SWojciech Macek 	return (bus_generic_attach(dev));
17836c1a376SWojciech Macek }
17936c1a376SWojciech Macek 
18036c1a376SWojciech Macek static const struct ofw_bus_devinfo *
18136c1a376SWojciech Macek brcm_mdionexus_ofw_get_devinfo(device_t bus __unused, device_t child)
18236c1a376SWojciech Macek {
18336c1a376SWojciech Macek 	struct brcm_mdionexus_ofw_devinfo *di;
18436c1a376SWojciech Macek 
18536c1a376SWojciech Macek 	di = device_get_ivars(child);
18636c1a376SWojciech Macek 	return (&di->di_dinfo);
18736c1a376SWojciech Macek }
18836c1a376SWojciech Macek 
18936c1a376SWojciech Macek static int
19036c1a376SWojciech Macek brcm_mdionexus_ofw_bus_attach(device_t dev)
19136c1a376SWojciech Macek {
19236c1a376SWojciech Macek 	struct simplebus_softc *sc;
19336c1a376SWojciech Macek 	struct brcm_mdionexus_ofw_devinfo *di;
19436c1a376SWojciech Macek 	device_t child;
19536c1a376SWojciech Macek 	phandle_t parent, node;
19636c1a376SWojciech Macek 
19736c1a376SWojciech Macek 	parent = ofw_bus_get_node(dev);
19836c1a376SWojciech Macek 	simplebus_init(dev, parent);
19936c1a376SWojciech Macek 
20036c1a376SWojciech Macek 	sc = (struct simplebus_softc *)device_get_softc(dev);
20136c1a376SWojciech Macek 
20236c1a376SWojciech Macek 	/* Iterate through all bus subordinates */
20336c1a376SWojciech Macek 	for (node = OF_child(parent); node > 0; node = OF_peer(node)) {
20436c1a376SWojciech Macek 		/* Allocate and populate devinfo. */
20536c1a376SWojciech Macek 		di = malloc(sizeof(*di), M_BRCM_IPROC_NEXUS, M_WAITOK | M_ZERO);
20636c1a376SWojciech Macek 		if (ofw_bus_gen_setup_devinfo(&di->di_dinfo, node) != 0) {
20736c1a376SWojciech Macek 			free(di, M_BRCM_IPROC_NEXUS);
20836c1a376SWojciech Macek 			continue;
20936c1a376SWojciech Macek 		}
21036c1a376SWojciech Macek 
21136c1a376SWojciech Macek 		/* Initialize and populate resource list. */
21236c1a376SWojciech Macek 		resource_list_init(&di->di_rl);
21336c1a376SWojciech Macek 		ofw_bus_reg_to_rl(dev, node, sc->acells, sc->scells,
21436c1a376SWojciech Macek 		    &di->di_rl);
21536c1a376SWojciech Macek 		ofw_bus_intr_to_rl(dev, node, &di->di_rl, NULL);
21636c1a376SWojciech Macek 
21736c1a376SWojciech Macek 		/* Add newbus device for this FDT node */
218*5b56413dSWarner Losh 		child = device_add_child(dev, NULL, DEVICE_UNIT_ANY);
21936c1a376SWojciech Macek 		if (child == NULL) {
22036c1a376SWojciech Macek 			resource_list_free(&di->di_rl);
22136c1a376SWojciech Macek 			ofw_bus_gen_destroy_devinfo(&di->di_dinfo);
22236c1a376SWojciech Macek 			free(di, M_BRCM_IPROC_NEXUS);
22336c1a376SWojciech Macek 			continue;
22436c1a376SWojciech Macek 		}
22536c1a376SWojciech Macek 
22636c1a376SWojciech Macek 		device_set_ivars(child, di);
22736c1a376SWojciech Macek 	}
22836c1a376SWojciech Macek 
22936c1a376SWojciech Macek 	return (0);
23036c1a376SWojciech Macek }
231