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/param.h>
29 #include <sys/bus.h>
30 #include <sys/kernel.h>
31 #include <sys/module.h>
32 #include <sys/rman.h>
33 #include <sys/systm.h>
34
35 #include <dev/fdt/simplebus.h>
36 #include <dev/ofw/ofw_bus_subr.h>
37 #include <dev/ofw/ofw_bus.h>
38
39 #include <machine/bus.h>
40 #include <machine/resource.h>
41
42 #include "mdio_if.h"
43
44 MALLOC_DEFINE(M_BRCM_IPROC_NEXUS, "Broadcom IPROC MDIO NEXUS",
45 "Broadcom IPROC MDIO NEXUS dynamic memory");
46
47 struct brcm_mdionexus_softc {
48 struct simplebus_softc simplebus_sc;
49 uint32_t mux_id;
50 };
51
52 /* OFW bus interface */
53 struct brcm_mdionexus_ofw_devinfo {
54 struct ofw_bus_devinfo di_dinfo;
55 struct resource_list di_rl;
56 };
57
58 static device_probe_t brcm_mdionexus_fdt_probe;
59 static device_attach_t brcm_mdionexus_fdt_attach;
60
61 static const struct ofw_bus_devinfo * brcm_mdionexus_ofw_get_devinfo(device_t,
62 device_t);
63 static int brcm_mdionexus_mdio_readreg(device_t, int, int);
64 static int brcm_mdionexus_mdio_writereg(device_t, int, int, int);
65
66 static device_method_t brcm_mdionexus_fdt_methods[] = {
67 /* Device interface */
68 DEVMETHOD(device_probe, brcm_mdionexus_fdt_probe),
69 DEVMETHOD(device_attach, brcm_mdionexus_fdt_attach),
70
71 /* Bus interface */
72 DEVMETHOD(bus_alloc_resource, bus_generic_alloc_resource),
73 DEVMETHOD(bus_release_resource, bus_generic_release_resource),
74 DEVMETHOD(bus_activate_resource, bus_generic_activate_resource),
75
76 /* ofw_bus interface */
77 DEVMETHOD(ofw_bus_get_devinfo, brcm_mdionexus_ofw_get_devinfo),
78 DEVMETHOD(ofw_bus_get_compat, ofw_bus_gen_get_compat),
79 DEVMETHOD(ofw_bus_get_model, ofw_bus_gen_get_model),
80 DEVMETHOD(ofw_bus_get_name, ofw_bus_gen_get_name),
81 DEVMETHOD(ofw_bus_get_node, ofw_bus_gen_get_node),
82 DEVMETHOD(ofw_bus_get_type, ofw_bus_gen_get_type),
83
84 /* MDIO interface */
85 /* MDIO interface */
86 DEVMETHOD(mdio_readreg, brcm_mdionexus_mdio_readreg),
87 DEVMETHOD(mdio_writereg, brcm_mdionexus_mdio_writereg),
88
89 DEVMETHOD_END
90 };
91
92 DEFINE_CLASS_0(brcm_mdionexus, brcm_mdionexus_fdt_driver, brcm_mdionexus_fdt_methods,
93 sizeof(struct brcm_mdionexus_softc));
94
95 static driver_t brcm_mdionexus_driver = {
96 "brcm_mdionexus",
97 brcm_mdionexus_fdt_methods,
98 sizeof(struct brcm_mdionexus_softc)
99 };
100
101 EARLY_DRIVER_MODULE(brcm_mdionexus, brcm_iproc_mdio, brcm_mdionexus_driver,
102 NULL, NULL, BUS_PASS_BUS + BUS_PASS_ORDER_MIDDLE);
103
104 static int brcm_mdionexus_ofw_bus_attach(device_t);
105
106 static int
brcm_mdionexus_mdio_readreg(device_t dev,int phy,int reg)107 brcm_mdionexus_mdio_readreg(device_t dev, int phy, int reg)
108 {
109 struct brcm_mdionexus_softc *sc;
110
111 sc = device_get_softc(dev);
112
113 return (MDIO_READREG_MUX(device_get_parent(dev),
114 sc->mux_id, phy, reg));
115 }
116
117 static int
brcm_mdionexus_mdio_writereg(device_t dev,int phy,int reg,int val)118 brcm_mdionexus_mdio_writereg(device_t dev, int phy, int reg, int val)
119 {
120 struct brcm_mdionexus_softc *sc;
121
122 sc = device_get_softc(dev);
123
124 return (MDIO_WRITEREG_MUX(device_get_parent(dev),
125 sc->mux_id, phy, reg, val));
126 }
127
128 static __inline void
get_addr_size_cells(phandle_t node,pcell_t * addr_cells,pcell_t * size_cells)129 get_addr_size_cells(phandle_t node, pcell_t *addr_cells, pcell_t *size_cells)
130 {
131
132 *addr_cells = 2;
133 /* Find address cells if present */
134 OF_getencprop(node, "#address-cells", addr_cells, sizeof(*addr_cells));
135
136 *size_cells = 2;
137 /* Find size cells if present */
138 OF_getencprop(node, "#size-cells", size_cells, sizeof(*size_cells));
139 }
140
141 static int
brcm_mdionexus_fdt_probe(device_t dev)142 brcm_mdionexus_fdt_probe(device_t dev)
143 {
144
145 if (!ofw_bus_status_okay(dev))
146 return (ENXIO);
147 device_set_desc(dev, "Broadcom MDIO nexus");
148 return (BUS_PROBE_SPECIFIC);
149 }
150
151 static int
brcm_mdionexus_fdt_attach(device_t dev)152 brcm_mdionexus_fdt_attach(device_t dev)
153 {
154 struct brcm_mdionexus_softc *sc;
155 int err;
156 pcell_t addr_cells, size_cells, buf[2];
157 phandle_t node;
158
159 sc = device_get_softc(dev);
160
161 node = ofw_bus_get_node(dev);
162 get_addr_size_cells(node, &addr_cells, &size_cells);
163 if ((addr_cells != 1) || (size_cells != 0)) {
164 device_printf(dev, "Only addr_cells=1 and size_cells=0 are supported\n");
165 return (EINVAL);
166 }
167
168 if (OF_getencprop(node, "reg", buf, sizeof(pcell_t)) < 0)
169 return (ENXIO);
170
171 sc->mux_id = buf[0];
172
173 err = brcm_mdionexus_ofw_bus_attach(dev);
174 if (err != 0)
175 return (err);
176
177 bus_attach_children(dev);
178 return (0);
179 }
180
181 static const struct ofw_bus_devinfo *
brcm_mdionexus_ofw_get_devinfo(device_t bus __unused,device_t child)182 brcm_mdionexus_ofw_get_devinfo(device_t bus __unused, device_t child)
183 {
184 struct brcm_mdionexus_ofw_devinfo *di;
185
186 di = device_get_ivars(child);
187 return (&di->di_dinfo);
188 }
189
190 static int
brcm_mdionexus_ofw_bus_attach(device_t dev)191 brcm_mdionexus_ofw_bus_attach(device_t dev)
192 {
193 struct simplebus_softc *sc;
194 struct brcm_mdionexus_ofw_devinfo *di;
195 device_t child;
196 phandle_t parent, node;
197
198 parent = ofw_bus_get_node(dev);
199 simplebus_init(dev, parent);
200
201 sc = (struct simplebus_softc *)device_get_softc(dev);
202
203 /* Iterate through all bus subordinates */
204 for (node = OF_child(parent); node > 0; node = OF_peer(node)) {
205 /* Allocate and populate devinfo. */
206 di = malloc(sizeof(*di), M_BRCM_IPROC_NEXUS, M_WAITOK | M_ZERO);
207 if (ofw_bus_gen_setup_devinfo(&di->di_dinfo, node) != 0) {
208 free(di, M_BRCM_IPROC_NEXUS);
209 continue;
210 }
211
212 /* Initialize and populate resource list. */
213 resource_list_init(&di->di_rl);
214 ofw_bus_reg_to_rl(dev, node, sc->acells, sc->scells,
215 &di->di_rl);
216 ofw_bus_intr_to_rl(dev, node, &di->di_rl, NULL);
217
218 /* Add newbus device for this FDT node */
219 child = device_add_child(dev, NULL, DEVICE_UNIT_ANY);
220 if (child == NULL) {
221 resource_list_free(&di->di_rl);
222 ofw_bus_gen_destroy_devinfo(&di->di_dinfo);
223 free(di, M_BRCM_IPROC_NEXUS);
224 continue;
225 }
226
227 device_set_ivars(child, di);
228 }
229
230 return (0);
231 }
232