]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/dev/vnic/mrml_bridge.c
Allow for overlapping quirk device ranges. Prior to this patch only
[FreeBSD/FreeBSD.git] / sys / dev / vnic / mrml_bridge.c
1 /*
2  * Copyright (C) 2016 Cavium Inc.
3  * All rights reserved.
4  *
5  * Developed by Semihalf.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26  * SUCH DAMAGE.
27  */
28 #include "opt_platform.h"
29
30 #include <sys/cdefs.h>
31 __FBSDID("$FreeBSD$");
32
33 #include <sys/param.h>
34 #include <sys/systm.h>
35 #include <sys/bus.h>
36 #include <sys/kernel.h>
37 #include <sys/module.h>
38 #include <sys/resource.h>
39 #include <sys/rman.h>
40 #include <sys/socket.h>
41 #include <sys/queue.h>
42
43 #include <dev/ofw/ofw_bus.h>
44 #include <dev/ofw/ofw_bus_subr.h>
45 #include <dev/fdt/simplebus.h>
46
47 #include <machine/bus.h>
48 #include <machine/resource.h>
49
50 static MALLOC_DEFINE(M_MRMLB, "MRML bridge", "Cavium MRML bridge");
51
52 static device_probe_t mrmlb_fdt_probe;
53 static device_attach_t mrmlb_fdt_attach;
54
55 static struct resource * mrmlb_ofw_bus_alloc_res(device_t, device_t, int, int *,
56     rman_res_t, rman_res_t, rman_res_t, u_int);
57
58 static const struct ofw_bus_devinfo * mrmlb_ofw_get_devinfo(device_t, device_t);
59
60 static device_method_t mrmlbus_fdt_methods[] = {
61         /* Device interface */
62         DEVMETHOD(device_probe,         mrmlb_fdt_probe),
63         DEVMETHOD(device_attach,        mrmlb_fdt_attach),
64
65         /* Bus interface */
66         DEVMETHOD(bus_alloc_resource,           mrmlb_ofw_bus_alloc_res),
67         DEVMETHOD(bus_release_resource,         bus_generic_release_resource),
68         DEVMETHOD(bus_activate_resource,        bus_generic_activate_resource),
69
70         /* ofw_bus interface */
71         DEVMETHOD(ofw_bus_get_devinfo,  mrmlb_ofw_get_devinfo),
72         DEVMETHOD(ofw_bus_get_compat,   ofw_bus_gen_get_compat),
73         DEVMETHOD(ofw_bus_get_model,    ofw_bus_gen_get_model),
74         DEVMETHOD(ofw_bus_get_name,     ofw_bus_gen_get_name),
75         DEVMETHOD(ofw_bus_get_node,     ofw_bus_gen_get_node),
76         DEVMETHOD(ofw_bus_get_type,     ofw_bus_gen_get_type),
77
78         DEVMETHOD_END
79 };
80
81 DEFINE_CLASS_0(mrmlbus, mrmlbus_fdt_driver, mrmlbus_fdt_methods,
82     sizeof(struct simplebus_softc));
83
84 static devclass_t mrmlbus_fdt_devclass;
85
86 EARLY_DRIVER_MODULE(mrmlbus, pcib, mrmlbus_fdt_driver, mrmlbus_fdt_devclass, 0, 0,
87     BUS_PASS_BUS + BUS_PASS_ORDER_MIDDLE);
88
89 static int mrmlb_ofw_fill_ranges(phandle_t, struct simplebus_softc *);
90 static int mrmlb_ofw_bus_attach(device_t);
91
92 static int
93 mrmlb_fdt_probe(device_t dev)
94 {
95
96         if (!ofw_bus_status_okay(dev))
97                 return (ENXIO);
98
99         if (!ofw_bus_is_compatible(dev, "cavium,thunder-8890-mrml-bridge"))
100                 return (ENXIO);
101
102         device_set_desc(dev, "Cavium ThunderX MRML bridge");
103         return (BUS_PROBE_SPECIFIC);
104 }
105
106 static int
107 mrmlb_fdt_attach(device_t dev)
108 {
109         int err;
110
111         err = mrmlb_ofw_bus_attach(dev);
112         if (err != 0)
113                 return (err);
114
115         return (bus_generic_attach(dev));
116 }
117
118 /* OFW bus interface */
119 struct mrmlb_ofw_devinfo {
120         struct ofw_bus_devinfo  di_dinfo;
121         struct resource_list    di_rl;
122 };
123
124 static const struct ofw_bus_devinfo *
125 mrmlb_ofw_get_devinfo(device_t bus __unused, device_t child)
126 {
127         struct mrmlb_ofw_devinfo *di;
128
129         di = device_get_ivars(child);
130         return (&di->di_dinfo);
131 }
132
133 static struct resource *
134 mrmlb_ofw_bus_alloc_res(device_t bus, device_t child, int type, int *rid,
135     rman_res_t start, rman_res_t end, rman_res_t count, u_int flags)
136 {
137         struct simplebus_softc *sc;
138         struct mrmlb_ofw_devinfo *di;
139         struct resource_list_entry *rle;
140         int i;
141
142         if (RMAN_IS_DEFAULT_RANGE(start, end)) {
143                 if ((di = device_get_ivars(child)) == NULL)
144                         return (NULL);
145                 if (type == SYS_RES_IOPORT)
146                     type = SYS_RES_MEMORY;
147
148                 /* Find defaults for this rid */
149                 rle = resource_list_find(&di->di_rl, type, *rid);
150                 if (rle == NULL)
151                         return (NULL);
152
153                 start = rle->start;
154                 end = rle->end;
155                 count = rle->count;
156         }
157
158         sc = device_get_softc(bus);
159
160         if (type == SYS_RES_MEMORY) {
161                 /* Remap through ranges property */
162                 for (i = 0; i < sc->nranges; i++) {
163                         if (start >= sc->ranges[i].bus && end <
164                             sc->ranges[i].bus + sc->ranges[i].size) {
165                                 start -= sc->ranges[i].bus;
166                                 start += sc->ranges[i].host;
167                                 end -= sc->ranges[i].bus;
168                                 end += sc->ranges[i].host;
169                                 break;
170                         }
171                 }
172
173                 if (i == sc->nranges && sc->nranges != 0) {
174                         device_printf(bus, "Could not map resource "
175                             "%#lx-%#lx\n", start, end);
176                         return (NULL);
177                 }
178         }
179
180         return (bus_generic_alloc_resource(bus, child, type, rid, start, end,
181             count, flags));
182 }
183
184 /* Helper functions */
185
186 static int
187 mrmlb_ofw_fill_ranges(phandle_t node, struct simplebus_softc *sc)
188 {
189         int host_address_cells;
190         cell_t *base_ranges;
191         ssize_t nbase_ranges;
192         int err;
193         int i, j, k;
194
195         err = OF_searchencprop(OF_parent(node), "#address-cells",
196             &host_address_cells, sizeof(host_address_cells));
197         if (err <= 0)
198                 return (-1);
199
200         nbase_ranges = OF_getproplen(node, "ranges");
201         if (nbase_ranges < 0)
202                 return (-1);
203         sc->nranges = nbase_ranges / sizeof(cell_t) /
204             (sc->acells + host_address_cells + sc->scells);
205         if (sc->nranges == 0)
206                 return (0);
207
208         sc->ranges = malloc(sc->nranges * sizeof(sc->ranges[0]),
209             M_MRMLB, M_WAITOK);
210         base_ranges = malloc(nbase_ranges, M_MRMLB, M_WAITOK);
211         OF_getencprop(node, "ranges", base_ranges, nbase_ranges);
212
213         for (i = 0, j = 0; i < sc->nranges; i++) {
214                 sc->ranges[i].bus = 0;
215                 for (k = 0; k < sc->acells; k++) {
216                         sc->ranges[i].bus <<= 32;
217                         sc->ranges[i].bus |= base_ranges[j++];
218                 }
219                 sc->ranges[i].host = 0;
220                 for (k = 0; k < host_address_cells; k++) {
221                         sc->ranges[i].host <<= 32;
222                         sc->ranges[i].host |= base_ranges[j++];
223                 }
224                 sc->ranges[i].size = 0;
225                 for (k = 0; k < sc->scells; k++) {
226                         sc->ranges[i].size <<= 32;
227                         sc->ranges[i].size |= base_ranges[j++];
228                 }
229         }
230
231         free(base_ranges, M_MRMLB);
232         return (sc->nranges);
233 }
234
235 static int
236 mrmlb_ofw_bus_attach(device_t dev)
237 {
238         struct simplebus_softc *sc;
239         struct mrmlb_ofw_devinfo *di;
240         device_t child;
241         phandle_t parent, node;
242
243         parent = ofw_bus_get_node(dev);
244         simplebus_init(dev, parent);
245
246         sc = device_get_softc(dev);
247
248         if (mrmlb_ofw_fill_ranges(parent, sc) < 0) {
249                 device_printf(dev, "could not get ranges\n");
250                 return (ENXIO);
251         }
252         /* Iterate through all bus subordinates */
253         for (node = OF_child(parent); node > 0; node = OF_peer(node)) {
254                 /* Allocate and populate devinfo. */
255                 di = malloc(sizeof(*di), M_MRMLB, M_WAITOK | M_ZERO);
256                 if (ofw_bus_gen_setup_devinfo(&di->di_dinfo, node) != 0) {
257                         free(di, M_MRMLB);
258                         continue;
259                 }
260
261                 /* Initialize and populate resource list. */
262                 resource_list_init(&di->di_rl);
263                 ofw_bus_reg_to_rl(dev, node, sc->acells, sc->scells,
264                     &di->di_rl);
265                 ofw_bus_intr_to_rl(dev, node, &di->di_rl, NULL);
266
267                 /* Add newbus device for this FDT node */
268                 child = device_add_child(dev, NULL, -1);
269                 if (child == NULL) {
270                         resource_list_free(&di->di_rl);
271                         ofw_bus_gen_destroy_devinfo(&di->di_dinfo);
272                         free(di, M_MRMLB);
273                         continue;
274                 }
275
276                 device_set_ivars(child, di);
277         }
278
279         return (0);
280 }