1 /*-
2 * Copyright (c) 2015 Broadcom Corporation
3 * (based on sys/dev/fdt/simplebus.c)
4 * Copyright (c) 2013 Nathan Whitehorn
5 * All rights reserved.
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
29 #include <sys/cdefs.h>
30 #include <sys/param.h>
31 #include <sys/systm.h>
32 #include <sys/module.h>
33 #include <sys/bus.h>
34 #include <sys/conf.h>
35 #include <sys/kernel.h>
36 #include <sys/rman.h>
37
38 #include <vm/vm.h>
39 #include <vm/vm_param.h>
40 #include <vm/pmap.h>
41
42 #include <machine/bus.h>
43 #include <machine/intr_machdep.h>
44
45 #include <mips/nlm/hal/haldefs.h>
46 #include <mips/nlm/interrupt.h>
47 #include <mips/nlm/hal/iomap.h>
48 #include <mips/nlm/hal/mips-extns.h>
49 #include <mips/nlm/hal/pcibus.h>
50 #include <mips/nlm/xlp.h>
51
52 #include <dev/ofw/openfirm.h>
53 #include <dev/ofw/ofw_bus.h>
54 #include <dev/ofw/ofw_bus_subr.h>
55
56 #include <dev/fdt/simplebus.h>
57
58 /* flash memory region for chipselects */
59 #define GBU_MEM_BASE 0x16000000UL
60 #define GBU_MEM_LIMIT 0x17ffffffUL
61
62 /*
63 * Device registers in pci ecfg memory region for devices without regular PCI BARs
64 */
65 #define PCI_ECFG_BASE XLP_DEFAULT_IO_BASE
66 #define PCI_ECFG_LIMIT (XLP_DEFAULT_IO_BASE + 0x0fffffff)
67
68 /*
69 * Bus interface.
70 */
71 static int xlp_simplebus_probe(device_t dev);
72 static struct resource *xlp_simplebus_alloc_resource(device_t, device_t, int,
73 int *, rman_res_t, rman_res_t, rman_res_t, u_int);
74 static int xlp_simplebus_activate_resource(device_t, device_t, int,
75 int, struct resource *);
76 static int xlp_simplebus_setup_intr(device_t, device_t,
77 struct resource *, int, driver_filter_t *, driver_intr_t *, void *, void **);
78
79 /*
80 * ofw_bus interface
81 */
82 static int xlp_simplebus_ofw_map_intr(device_t, device_t, phandle_t,
83 int, pcell_t *);
84
85 static devclass_t simplebus_devclass;
86 static device_method_t xlp_simplebus_methods[] = {
87 /* Device interface */
88 DEVMETHOD(device_probe, xlp_simplebus_probe),
89
90 DEVMETHOD(bus_alloc_resource, xlp_simplebus_alloc_resource),
91 DEVMETHOD(bus_activate_resource, xlp_simplebus_activate_resource),
92 DEVMETHOD(bus_setup_intr, xlp_simplebus_setup_intr),
93
94 DEVMETHOD(ofw_bus_map_intr, xlp_simplebus_ofw_map_intr),
95 DEVMETHOD_END
96 };
97
98 DEFINE_CLASS_1(simplebus, xlp_simplebus_driver, xlp_simplebus_methods,
99 sizeof(struct simplebus_softc), simplebus_driver);
100 DRIVER_MODULE(xlp_simplebus, ofwbus, xlp_simplebus_driver, simplebus_devclass,
101 0, 0);
102
103 static struct rman irq_rman, port_rman, mem_rman, pci_ecfg_rman, gbu_rman;
104
105 static void
xlp_simplebus_init_resources(void)106 xlp_simplebus_init_resources(void)
107 {
108 irq_rman.rm_start = 0;
109 irq_rman.rm_end = 255;
110 irq_rman.rm_type = RMAN_ARRAY;
111 irq_rman.rm_descr = "PCI Mapped Interrupts";
112 if (rman_init(&irq_rman)
113 || rman_manage_region(&irq_rman, 0, 255))
114 panic("xlp_simplebus_init_resources irq_rman");
115
116 port_rman.rm_type = RMAN_ARRAY;
117 port_rman.rm_descr = "I/O ports";
118 if (rman_init(&port_rman)
119 || rman_manage_region(&port_rman, PCIE_IO_BASE, PCIE_IO_LIMIT))
120 panic("xlp_simplebus_init_resources port_rman");
121
122 mem_rman.rm_type = RMAN_ARRAY;
123 mem_rman.rm_descr = "I/O memory";
124 if (rman_init(&mem_rman)
125 || rman_manage_region(&mem_rman, PCIE_MEM_BASE, PCIE_MEM_LIMIT))
126 panic("xlp_simplebus_init_resources mem_rman");
127
128 pci_ecfg_rman.rm_type = RMAN_ARRAY;
129 pci_ecfg_rman.rm_descr = "PCI ECFG IO";
130 if (rman_init(&pci_ecfg_rman) || rman_manage_region(&pci_ecfg_rman,
131 PCI_ECFG_BASE, PCI_ECFG_LIMIT))
132 panic("xlp_simplebus_init_resources pci_ecfg_rman");
133
134 gbu_rman.rm_type = RMAN_ARRAY;
135 gbu_rman.rm_descr = "Flash region";
136 if (rman_init(&gbu_rman)
137 || rman_manage_region(&gbu_rman, GBU_MEM_BASE, GBU_MEM_LIMIT))
138 panic("xlp_simplebus_init_resources gbu_rman");
139 }
140
141 static int
xlp_simplebus_probe(device_t dev)142 xlp_simplebus_probe(device_t dev)
143 {
144
145 if (!ofw_bus_status_okay(dev))
146 return (ENXIO);
147
148 /*
149 * FDT data puts a "simple-bus" compatible string on many things that
150 * have children but aren't really busses in our world. Without a
151 * ranges property we will fail to attach, so just fail to probe too.
152 */
153 if (!(ofw_bus_is_compatible(dev, "simple-bus") &&
154 ofw_bus_has_prop(dev, "ranges")) &&
155 (ofw_bus_get_type(dev) == NULL || strcmp(ofw_bus_get_type(dev),
156 "soc") != 0))
157 return (ENXIO);
158
159 xlp_simplebus_init_resources();
160 device_set_desc(dev, "XLP SoC bus");
161
162 return (BUS_PROBE_SPECIFIC);
163 }
164
165 static struct resource *
xlp_simplebus_alloc_resource(device_t bus,device_t child,int type,int * rid,rman_res_t start,rman_res_t end,rman_res_t count,u_int flags)166 xlp_simplebus_alloc_resource(device_t bus, device_t child, int type, int *rid,
167 rman_res_t start, rman_res_t end, rman_res_t count, u_int flags)
168 {
169 struct rman *rm;
170 struct resource *rv;
171 struct resource_list_entry *rle;
172 struct simplebus_softc *sc;
173 struct simplebus_devinfo *di;
174 bus_space_tag_t bustag;
175 int j, isdefault, passthrough, needsactivate;
176
177 passthrough = (device_get_parent(child) != bus);
178 needsactivate = flags & RF_ACTIVE;
179 sc = device_get_softc(bus);
180 di = device_get_ivars(child);
181 rle = NULL;
182 bustag = NULL;
183
184 if (!passthrough) {
185 isdefault = RMAN_IS_DEFAULT_RANGE(start, end);
186 if (isdefault) {
187 rle = resource_list_find(&di->rl, type, *rid);
188 if (rle == NULL)
189 return (NULL);
190 if (rle->res != NULL)
191 panic("%s: resource entry is busy", __func__);
192 start = rle->start;
193 count = ulmax(count, rle->count);
194 end = ulmax(rle->end, start + count - 1);
195 }
196 if (type == SYS_RES_MEMORY) {
197 /* Remap through ranges property */
198 for (j = 0; j < sc->nranges; j++) {
199 if (start >= sc->ranges[j].bus && end <
200 sc->ranges[j].bus + sc->ranges[j].size) {
201 start -= sc->ranges[j].bus;
202 start += sc->ranges[j].host;
203 end -= sc->ranges[j].bus;
204 end += sc->ranges[j].host;
205 break;
206 }
207 }
208 if (j == sc->nranges && sc->nranges != 0) {
209 if (bootverbose)
210 device_printf(bus, "Could not map resource "
211 "%#jx-%#jx\n", start, end);
212 return (NULL);
213 }
214 }
215 }
216 switch (type) {
217 case SYS_RES_IRQ:
218 rm = &irq_rman;
219 break;
220 case SYS_RES_IOPORT:
221 rm = &port_rman;
222 bustag = rmi_bus_space;
223 break;
224 case SYS_RES_MEMORY:
225 if (start >= GBU_MEM_BASE && end <= GBU_MEM_LIMIT) {
226 rm = &gbu_rman;
227 bustag = rmi_bus_space;
228 } else if (start >= PCI_ECFG_BASE && end <= PCI_ECFG_LIMIT) {
229 rm = &pci_ecfg_rman;
230 bustag = rmi_uart_bus_space;
231 } else if (start >= PCIE_MEM_BASE && end <= PCIE_MEM_LIMIT) {
232 rm = &mem_rman;
233 bustag = rmi_bus_space;
234 } else {
235 if (bootverbose)
236 device_printf(bus, "Invalid MEM range"
237 "%#jx-%#jx\n", start, end);
238 return (NULL);
239 }
240 break;
241 default:
242 return (NULL);
243 }
244
245 rv = rman_reserve_resource(rm, start, end, count, flags, child);
246 if (rv == NULL) {
247 device_printf(bus, "%s: could not reserve resource for %s\n",
248 __func__, device_get_nameunit(child));
249 return (NULL);
250 }
251
252 rman_set_rid(rv, *rid);
253 if (bustag != NULL)
254 rman_set_bustag(rv, bustag);
255
256 if (needsactivate) {
257 if (bus_activate_resource(child, type, *rid, rv)) {
258 device_printf(bus, "%s: could not activate resource\n",
259 __func__);
260 rman_release_resource(rv);
261 return (NULL);
262 }
263 }
264
265 return (rv);
266 }
267
268 static int
xlp_simplebus_activate_resource(device_t bus,device_t child,int type,int rid,struct resource * r)269 xlp_simplebus_activate_resource(device_t bus, device_t child, int type, int rid,
270 struct resource *r)
271 {
272 void *vaddr;
273 vm_paddr_t paddr;
274 vm_size_t psize;
275
276 /*
277 * If this is a memory resource, use pmap_mapdev to map it.
278 */
279 if (type == SYS_RES_MEMORY || type == SYS_RES_IOPORT) {
280 paddr = rman_get_start(r);
281 psize = rman_get_size(r);
282 vaddr = pmap_mapdev(paddr, psize);
283
284 rman_set_virtual(r, vaddr);
285 rman_set_bushandle(r, (bus_space_handle_t)(uintptr_t)vaddr);
286 }
287
288 return (rman_activate_resource(r));
289 }
290
291 static int
xlp_simplebus_setup_intr(device_t dev,device_t child,struct resource * res,int flags,driver_filter_t * filt,driver_intr_t * intr,void * arg,void ** cookiep)292 xlp_simplebus_setup_intr(device_t dev, device_t child, struct resource *res, int flags,
293 driver_filter_t *filt, driver_intr_t *intr, void *arg, void **cookiep)
294 {
295 register_t s;
296 int irq;
297
298 /* setup irq */
299 s = intr_disable();
300 irq = rman_get_start(res);
301 cpu_establish_hardintr(device_get_nameunit(child), filt, intr, arg,
302 irq, flags, cookiep);
303 intr_restore(s);
304 return (0);
305 }
306
307 static int
xlp_simplebus_ofw_map_intr(device_t dev,device_t child,phandle_t iparent,int icells,pcell_t * irq)308 xlp_simplebus_ofw_map_intr(device_t dev, device_t child, phandle_t iparent, int icells,
309 pcell_t *irq)
310 {
311
312 return ((int)irq[0]);
313 }
314