]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/powerpc/powerpc/nexus.c
powerpc: fix warning: a function declaration without a prototype is deprecated in...
[FreeBSD/FreeBSD.git] / sys / powerpc / powerpc / nexus.c
1 /*-
2  * Copyright 1998 Massachusetts Institute of Technology
3  * Copyright 2001 by Thomas Moestl <tmm@FreeBSD.org>.
4  * Copyright 2006 by Marius Strobl <marius@FreeBSD.org>.
5  * All rights reserved.
6  *
7  * Permission to use, copy, modify, and distribute this software and
8  * its documentation for any purpose and without fee is hereby
9  * granted, provided that both the above copyright notice and this
10  * permission notice appear in all copies, that both the above
11  * copyright notice and this permission notice appear in all
12  * supporting documentation, and that the name of M.I.T. not be used
13  * in advertising or publicity pertaining to distribution of the
14  * software without specific, written prior permission.  M.I.T. makes
15  * no representations about the suitability of this software for any
16  * purpose.  It is provided "as is" without express or implied
17  * warranty.
18  *
19  * THIS SOFTWARE IS PROVIDED BY M.I.T. ``AS IS''.  M.I.T. DISCLAIMS
20  * ALL EXPRESS OR IMPLIED WARRANTIES WITH REGARD TO THIS SOFTWARE,
21  * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
22  * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT
23  * SHALL M.I.T. BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
26  * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
27  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
28  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
29  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30  * SUCH DAMAGE.
31  *
32  *      from: FreeBSD: src/sys/i386/i386/nexus.c,v 1.43 2001/02/09
33  */
34
35 #include <sys/cdefs.h>
36 __FBSDID("$FreeBSD$");
37
38 #include <sys/endian.h>
39 #include <sys/param.h>
40 #include <sys/systm.h>
41 #include <sys/bus.h>
42 #include <sys/kdb.h>
43 #include <sys/kernel.h>
44 #include <sys/malloc.h>
45 #include <sys/module.h>
46 #include <sys/pcpu.h>
47 #include <sys/rman.h>
48 #include <sys/smp.h>
49
50 #include <vm/vm.h>
51 #include <vm/pmap.h>
52
53 #include <dev/ofw/ofw_bus.h>
54 #include <dev/ofw/ofw_bus_subr.h>
55 #include <dev/ofw/openfirm.h>
56
57 #include <machine/bus.h>
58 #include <machine/intr_machdep.h>
59 #include <machine/resource.h>
60
61 /*
62  * The nexus handles root-level resource allocation requests and interrupt
63  * mapping. All direct subdevices of nexus are attached by DEVICE_IDENTIFY().
64  */
65
66 static struct rman intr_rman;
67 static struct rman mem_rman;
68
69 static device_probe_t nexus_probe;
70 static device_attach_t nexus_attach;
71 static bus_setup_intr_t nexus_setup_intr;
72 static bus_teardown_intr_t nexus_teardown_intr;
73 static bus_alloc_resource_t nexus_alloc_resource;
74 static bus_activate_resource_t nexus_activate_resource;
75 static bus_deactivate_resource_t nexus_deactivate_resource;
76 static bus_adjust_resource_t nexus_adjust_resource;
77 static bus_release_resource_t nexus_release_resource;
78 static  int nexus_map_resource(device_t bus, device_t child, int type,
79                                struct resource *r,
80                                struct resource_map_request *argsp,
81                                struct resource_map *map);
82 static  int nexus_unmap_resource(device_t bus, device_t child, int type,
83                                struct resource *r, struct resource_map *map);
84
85 static bus_space_tag_t nexus_get_bus_tag(device_t, device_t);
86 #ifdef SMP
87 static bus_bind_intr_t nexus_bind_intr;
88 #endif
89 static bus_config_intr_t nexus_config_intr;
90 static ofw_bus_map_intr_t nexus_ofw_map_intr;
91
92 static device_method_t nexus_methods[] = {
93         /* Device interface */
94         DEVMETHOD(device_probe,         nexus_probe),
95         DEVMETHOD(device_attach,        nexus_attach),
96
97         /* Bus interface */
98         DEVMETHOD(bus_add_child,        bus_generic_add_child),
99         DEVMETHOD(bus_alloc_resource,   nexus_alloc_resource),
100         DEVMETHOD(bus_activate_resource,        nexus_activate_resource),
101         DEVMETHOD(bus_deactivate_resource,      nexus_deactivate_resource),
102         DEVMETHOD(bus_adjust_resource,  nexus_adjust_resource),
103         DEVMETHOD(bus_release_resource, nexus_release_resource),
104         DEVMETHOD(bus_map_resource,     nexus_map_resource),
105         DEVMETHOD(bus_unmap_resource,   nexus_unmap_resource),
106         DEVMETHOD(bus_setup_intr,       nexus_setup_intr),
107         DEVMETHOD(bus_teardown_intr,    nexus_teardown_intr),
108 #ifdef SMP
109         DEVMETHOD(bus_bind_intr,        nexus_bind_intr),
110 #endif
111         DEVMETHOD(bus_config_intr,      nexus_config_intr),
112         DEVMETHOD(bus_get_bus_tag,      nexus_get_bus_tag),
113
114         /* ofw_bus interface */
115         DEVMETHOD(ofw_bus_map_intr,     nexus_ofw_map_intr),
116
117         DEVMETHOD_END
118 };
119
120 DEFINE_CLASS_0(nexus, nexus_driver, nexus_methods, 1);
121 EARLY_DRIVER_MODULE(nexus, root, nexus_driver, 0, 0, BUS_PASS_BUS);
122 MODULE_VERSION(nexus, 1);
123
124 static int
125 nexus_probe(device_t dev)
126 {
127         
128         device_quiet(dev);      /* suppress attach message for neatness */
129
130         return (BUS_PROBE_DEFAULT);
131 }
132
133 static int
134 nexus_attach(device_t dev)
135 {
136
137         intr_rman.rm_type = RMAN_ARRAY;
138         intr_rman.rm_descr = "Interrupts";
139         mem_rman.rm_type = RMAN_ARRAY;
140         mem_rman.rm_descr = "I/O memory addresses";
141         if (rman_init(&intr_rman) != 0 || rman_init(&mem_rman) != 0 ||
142             rman_manage_region(&intr_rman, 0, ~0) != 0 ||
143             rman_manage_region(&mem_rman, 0, BUS_SPACE_MAXADDR) != 0)
144                 panic("%s: failed to set up rmans.", __func__);
145
146         /* Add ofwbus0. */
147         device_add_child(dev, "ofwbus", 0);
148
149         /* Now, probe children. */
150         bus_generic_probe(dev);
151         bus_generic_attach(dev);
152
153         return (0);
154 }
155
156 static int
157 nexus_setup_intr(device_t bus __unused, device_t child, struct resource *r,
158     int flags, driver_filter_t *filt, driver_intr_t *intr, void *arg,
159     void **cookiep)
160 {
161         int error, domain;
162
163         if (r == NULL)
164                 panic("%s: NULL interrupt resource!", __func__);
165
166         if (cookiep != NULL)
167                 *cookiep = NULL;
168         if ((rman_get_flags(r) & RF_SHAREABLE) == 0)
169                 flags |= INTR_EXCL;
170
171         /* We depend here on rman_activate_resource() being idempotent. */
172         error = rman_activate_resource(r);
173         if (error)
174                 return (error);
175
176         if (bus_get_domain(child, &domain) != 0) {
177                 if(bootverbose)
178                         device_printf(child, "no domain found\n");
179                 domain = 0;
180         }
181         error = powerpc_setup_intr(device_get_nameunit(child),
182             rman_get_start(r), filt, intr, arg, flags, cookiep, domain);
183
184         return (error);
185 }
186
187 static int
188 nexus_teardown_intr(device_t bus __unused, device_t child __unused,
189     struct resource *r, void *ih)
190 {
191         
192         if (r == NULL)
193                 return (EINVAL);
194
195         return (powerpc_teardown_intr(ih));
196 }
197
198 static bus_space_tag_t
199 nexus_get_bus_tag(device_t bus __unused, device_t child __unused)
200 {
201
202 #if BYTE_ORDER == LITTLE_ENDIAN
203         return(&bs_le_tag);
204 #else
205         return(&bs_be_tag);
206 #endif
207 }
208
209 #ifdef SMP
210 static int
211 nexus_bind_intr(device_t bus __unused, device_t child __unused,
212     struct resource *r, int cpu)
213 {
214
215         return (powerpc_bind_intr(rman_get_start(r), cpu));
216 }
217 #endif
218
219 static int
220 nexus_config_intr(device_t dev, int irq, enum intr_trigger trig,
221     enum intr_polarity pol)
222 {
223
224         return (powerpc_config_intr(irq, trig, pol));
225
226
227 static int
228 nexus_ofw_map_intr(device_t dev, device_t child, phandle_t iparent, int icells,
229     pcell_t *irq)
230 {
231         u_int intr = MAP_IRQ(iparent, irq[0]);
232         if (icells > 1)
233                 powerpc_fw_config_intr(intr, irq[1]);
234         return (intr);
235 }
236
237 /*
238  * Allocate a resource on behalf of child.  NB: child is usually going to be a
239  * child of one of our descendants, not a direct child of nexus0.
240  */
241 static struct resource *
242 nexus_alloc_resource(device_t bus, device_t child, int type, int *rid,
243     rman_res_t start, rman_res_t end, rman_res_t count, u_int flags)
244 {
245         struct rman *rm;
246         struct resource *rv;
247
248         switch (type) {
249         case SYS_RES_IRQ:
250                 rm = &intr_rman;
251                 break;
252         case SYS_RES_MEMORY:
253                 rm = &mem_rman;
254                 break;
255         default:
256                 return (NULL);
257         }
258
259         rv = rman_reserve_resource(rm, start, end, count, flags & ~RF_ACTIVE,
260             child);
261         if (rv == NULL)
262                 return (NULL);
263         rman_set_rid(rv, *rid);
264
265         if ((flags & RF_ACTIVE) != 0) {
266                 if (bus_activate_resource(child, type, *rid, rv) != 0) {
267                         rman_release_resource(rv);
268                         return (NULL);
269                 }
270         }
271
272         return (rv);
273 }
274
275 static int
276 nexus_activate_resource(device_t bus __unused, device_t child __unused,
277     int type, int rid __unused, struct resource *r)
278 {
279
280         if (type == SYS_RES_MEMORY) {
281                 vm_paddr_t start;
282                 void *p;
283
284                 start = (vm_paddr_t) rman_get_start(r);
285                 if (bootverbose)
286                         printf("nexus mapdev: start %jx, len %jd\n",
287                             (uintmax_t)start, rman_get_size(r));
288
289                 p = pmap_mapdev(start, (vm_size_t) rman_get_size(r));
290                 if (p == NULL)
291                         return (ENOMEM);
292                 rman_set_virtual(r, p);
293                 rman_set_bustag(r, &bs_be_tag);
294                 rman_set_bushandle(r, (u_long)p);
295         }
296         return (rman_activate_resource(r));
297 }
298
299 static int
300 nexus_deactivate_resource(device_t bus __unused, device_t child __unused,
301     int type __unused, int rid __unused, struct resource *r)
302 {
303
304         /*
305          * If this is a memory resource, unmap it.
306          */
307         if ((type == SYS_RES_MEMORY) || (type == SYS_RES_IOPORT)) {
308                 bus_size_t psize;
309
310                 psize = rman_get_size(r);
311                 pmap_unmapdev(rman_get_virtual(r), psize);
312         }
313
314         return (rman_deactivate_resource(r));
315 }
316
317 static int
318 nexus_adjust_resource(device_t bus, device_t child __unused, int type,
319     struct resource *r, rman_res_t start, rman_res_t end)
320 {
321         struct rman *rm;
322
323         switch (type) {
324         case SYS_RES_IRQ:
325                 rm = &intr_rman;
326                 break;
327         case SYS_RES_MEMORY:
328                 rm = &mem_rman;
329                 break;
330         default:
331                 return (EINVAL);
332         }
333         if (rm == NULL)
334                 return (ENXIO);
335         if (rman_is_region_manager(r, rm) == 0)
336                 return (EINVAL);
337         return (rman_adjust_resource(r, start, end));
338 }
339
340 static int
341 nexus_release_resource(device_t bus, device_t child, int type,
342     int rid, struct resource *r)
343 {
344         int error;
345
346         if ((rman_get_flags(r) & RF_ACTIVE) != 0) {
347                 error = bus_deactivate_resource(child, type, rid, r);
348                 if (error)
349                         return (error);
350         }
351         return (rman_release_resource(r));
352 }
353
354 static int
355 nexus_map_resource(device_t bus, device_t child, int type, struct resource *r,
356     struct resource_map_request *argsp, struct resource_map *map)
357 {
358
359         struct resource_map_request args;
360         rman_res_t end, length, start;
361
362         /* Resources must be active to be mapped. */
363         if (!(rman_get_flags(r) & RF_ACTIVE))
364                 return (ENXIO);
365
366         /* Mappings are only supported on I/O and memory resources. */
367         switch (type) {
368         case SYS_RES_IOPORT:
369         case SYS_RES_MEMORY:
370                 break;
371         default:
372                 return (EINVAL);
373         }
374
375         resource_init_map_request(&args);
376         if (argsp != NULL)
377                 bcopy(argsp, &args, imin(argsp->size, args.size));
378
379         start = rman_get_start(r) + args.offset;
380         if (args.length == 0)
381                 length = rman_get_size(r);
382         else
383                 length = args.length;
384
385         end = start + length - 1;
386         if (start > rman_get_end(r) || start < rman_get_start(r))
387                 return (EINVAL);
388
389         if (end > rman_get_end(r) || end < start)
390                 return (EINVAL);
391
392         /*
393          * If this is a memory resource, map it into the kernel.
394          */
395         switch (type) {
396         case SYS_RES_IOPORT:
397                 panic("%s:%d SYS_RES_IOPORT handling not implemented", __func__, __LINE__);
398                 /*   XXX: untested
399                 map->r_bushandle = start;
400                 map->r_bustag = nexus_get_bus_tag(NULL, NULL);
401                 map->r_size = length;
402                 map->r_vaddr = NULL;
403                 */
404                 break;
405         case SYS_RES_MEMORY:
406                 map->r_vaddr = pmap_mapdev_attr(start, length, args.memattr);
407                 map->r_bustag = nexus_get_bus_tag(NULL, NULL);
408                 map->r_size = length;
409                 map->r_bushandle = (bus_space_handle_t)map->r_vaddr;
410                 break;
411         }
412
413         return (0);
414
415 }
416
417 static int
418 nexus_unmap_resource(device_t bus, device_t child, int type, struct resource *r,
419     struct resource_map *map)
420 {
421
422         /*
423          * If this is a memory resource, unmap it.
424          */
425         switch (type) {
426         case SYS_RES_MEMORY:
427                 pmap_unmapdev(map->r_vaddr, map->r_size);
428                 /* FALLTHROUGH */
429         case SYS_RES_IOPORT:
430                 break;
431         default:
432                 return (EINVAL);
433         }
434
435         return (0);
436
437 }