]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/sparc64/ebus/ebus.c
This commit was generated by cvs2svn to compensate for changes in r172314,
[FreeBSD/FreeBSD.git] / sys / sparc64 / ebus / ebus.c
1 /*-
2  * Copyright (c) 1999, 2000 Matthew R. Green
3  * Copyright (c) 2001 Thomas Moestl <tmm@FreeBSD.org>
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  * 3. The name of the author may not be used to endorse or promote products
15  *    derived from this software without specific prior written permission.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
22  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
23  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
24  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
25  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27  * SUCH DAMAGE.
28  *
29  *      from: NetBSD: ebus.c,v 1.26 2001/09/10 16:27:53 eeh Exp
30  */
31
32 #include <sys/cdefs.h>
33 __FBSDID("$FreeBSD$");
34
35 /*
36  * UltraSPARC 5 and beyond Ebus support.
37  *
38  * note that this driver is not complete:
39  *      - ebus2 dma code is completely unwritten
40  *      - interrupt establish is written and appears to work
41  *      - bus map code is written and appears to work
42  * XXX: This is PCI specific, however, there exist SBus-to-EBus bridges...
43  * XXX: The EBus was designed to allow easy adaption of ISA devices to it - a
44  * compatability layer for ISA devices might be nice, although probably not
45  * easily possible because of some cruft (like in[bwl]/out[bwl] and friends).
46  * Additionally, the existing ISA code is limited to one ISA bus, however,
47  * there are machines with both ISA and EBus.
48  */
49
50 #include <sys/param.h>
51 #include <sys/systm.h>
52 #include <sys/bus.h>
53 #include <sys/kernel.h>
54 #include <sys/malloc.h>
55 #include <sys/module.h>
56
57 #include <machine/bus.h>
58
59 #include <sys/rman.h>
60
61 #include <dev/ofw/ofw_bus.h>
62 #include <dev/ofw/ofw_bus_subr.h>
63 #include <dev/ofw/openfirm.h>
64
65 #include <machine/ofw_bus.h>
66 #include <machine/resource.h>
67
68 #include <dev/pci/pcireg.h>
69 #include <dev/pci/pcivar.h>
70
71 #include <sparc64/pci/ofw_pci.h>
72
73 /*
74  * The register, ranges and interrupt map properties are identical to the ISA
75  * ones.
76  */
77 #include <sparc64/isa/ofw_isa.h>
78
79 struct ebus_devinfo {
80         struct ofw_bus_devinfo  edi_obdinfo;
81         struct resource_list    edi_rl;
82 };
83
84 struct ebus_rinfo {
85         int                     eri_rtype;
86         struct rman             eri_rman;
87         struct resource         *eri_res;
88 };
89
90 struct ebus_softc {
91         phandle_t               sc_node;
92
93         struct isa_ranges       *sc_range;
94         struct ebus_rinfo       *sc_rinfo;
95
96         int                     sc_nrange;
97
98         struct ofw_bus_iinfo    sc_iinfo;
99 };
100
101 static device_probe_t ebus_probe;
102 static device_attach_t ebus_attach;
103 static bus_print_child_t ebus_print_child;
104 static bus_probe_nomatch_t ebus_probe_nomatch;
105 static bus_alloc_resource_t ebus_alloc_resource;
106 static bus_release_resource_t ebus_release_resource;
107 static bus_get_resource_list_t ebus_get_resource_list;
108 static ofw_bus_get_devinfo_t ebus_get_devinfo;
109
110 static struct ebus_devinfo *ebus_setup_dinfo(device_t, struct ebus_softc *,
111     phandle_t);
112 static void ebus_destroy_dinfo(struct ebus_devinfo *);
113 static int ebus_print_res(struct ebus_devinfo *);
114
115 static device_method_t ebus_methods[] = {
116         /* Device interface */
117         DEVMETHOD(device_probe,         ebus_probe),
118         DEVMETHOD(device_attach,        ebus_attach),
119         DEVMETHOD(device_shutdown,      bus_generic_shutdown),
120         DEVMETHOD(device_suspend,       bus_generic_suspend),
121         DEVMETHOD(device_resume,        bus_generic_resume),
122
123         /* Bus interface */
124         DEVMETHOD(bus_print_child,      ebus_print_child),
125         DEVMETHOD(bus_probe_nomatch,    ebus_probe_nomatch),
126         DEVMETHOD(bus_setup_intr,       bus_generic_setup_intr),
127         DEVMETHOD(bus_teardown_intr,    bus_generic_teardown_intr),
128         DEVMETHOD(bus_alloc_resource,   ebus_alloc_resource),
129         DEVMETHOD(bus_get_resource_list, ebus_get_resource_list),
130         DEVMETHOD(bus_activate_resource, bus_generic_activate_resource),
131         DEVMETHOD(bus_deactivate_resource, bus_generic_deactivate_resource),
132         DEVMETHOD(bus_release_resource, ebus_release_resource),
133         DEVMETHOD(bus_get_resource,     bus_generic_rl_get_resource),
134
135         /* ofw_bus interface */
136         DEVMETHOD(ofw_bus_get_devinfo,  ebus_get_devinfo),
137         DEVMETHOD(ofw_bus_get_compat,   ofw_bus_gen_get_compat),
138         DEVMETHOD(ofw_bus_get_model,    ofw_bus_gen_get_model),
139         DEVMETHOD(ofw_bus_get_name,     ofw_bus_gen_get_name),
140         DEVMETHOD(ofw_bus_get_node,     ofw_bus_gen_get_node),
141         DEVMETHOD(ofw_bus_get_type,     ofw_bus_gen_get_type),
142
143         { 0, 0 }
144 };
145
146 static driver_t ebus_driver = {
147         "ebus",
148         ebus_methods,
149         sizeof(struct ebus_softc),
150 };
151
152 static devclass_t ebus_devclass;
153
154 DRIVER_MODULE(ebus, pci, ebus_driver, ebus_devclass, 0, 0);
155
156 static int
157 ebus_probe(device_t dev)
158 {
159
160         if (pci_get_class(dev) != PCIC_BRIDGE ||
161             pci_get_vendor(dev) != 0x108e ||
162             strcmp(ofw_bus_get_name(dev), "ebus") != 0)
163                 return (ENXIO);
164
165         if (pci_get_device(dev) == 0x1000)
166                 device_set_desc(dev, "PCI-EBus2 bridge");
167         else if (pci_get_device(dev) == 0x1100)
168                 device_set_desc(dev, "PCI-EBus3 bridge");
169         else
170                 return (ENXIO);
171         return (0);
172 }
173
174 static int
175 ebus_attach(device_t dev)
176 {
177         struct ebus_softc *sc;
178         struct ebus_devinfo *edi;
179         struct ebus_rinfo *eri;
180         struct resource *res;
181         device_t cdev;
182         phandle_t node;
183         int i, rnum, rid;
184
185         sc = device_get_softc(dev);
186         sc->sc_node = node = ofw_bus_get_node(dev);
187
188         sc->sc_nrange = OF_getprop_alloc(node, "ranges",
189             sizeof(*sc->sc_range), (void **)&sc->sc_range);
190         if (sc->sc_nrange == -1) {
191                 printf("ebus_attach: could not get ranges property\n");
192                 return (ENXIO);
193         }
194
195         sc->sc_rinfo = malloc(sizeof(*sc->sc_rinfo) * sc->sc_nrange, M_DEVBUF,
196             M_WAITOK | M_ZERO);
197
198         /* For every range, there must be a matching resource. */
199         for (rnum = 0; rnum < sc->sc_nrange; rnum++) {
200                 eri = &sc->sc_rinfo[rnum];
201                 eri->eri_rtype = ofw_isa_range_restype(&sc->sc_range[rnum]);
202                 rid = PCIR_BAR(rnum);
203                 res = bus_alloc_resource_any(dev, eri->eri_rtype, &rid,
204                     RF_ACTIVE);
205                 if (res == NULL) {
206                         printf("ebus_attach: failed to allocate range "
207                             "resource!\n");
208                         goto fail;
209                 }
210                 eri->eri_res = res;
211                 eri->eri_rman.rm_type = RMAN_ARRAY;
212                 eri->eri_rman.rm_descr = "EBus range";
213                 if (rman_init(&eri->eri_rman) != 0) {
214                         printf("ebus_attach: failed to initialize rman!");
215                         goto fail;
216                 }
217                 if (rman_manage_region(&eri->eri_rman, rman_get_start(res),
218                      rman_get_end(res)) != 0) {
219                         printf("ebus_attach: failed to register region!");
220                         rman_fini(&eri->eri_rman);
221                         goto fail;
222                 }
223         }
224
225         ofw_bus_setup_iinfo(node, &sc->sc_iinfo, sizeof(ofw_isa_intr_t));
226
227         /*
228          * Now attach our children.
229          */
230         for (node = OF_child(node); node > 0; node = OF_peer(node)) {
231                 if ((edi = ebus_setup_dinfo(dev, sc, node)) == NULL)
232                         continue;
233                 if ((cdev = device_add_child(dev, NULL, -1)) == NULL) {
234                         device_printf(dev, "<%s>: device_add_child failed\n",
235                             edi->edi_obdinfo.obd_name);
236                         ebus_destroy_dinfo(edi);
237                         continue;
238                 }
239                 device_set_ivars(cdev, edi);
240         }
241         return (bus_generic_attach(dev));
242
243 fail:
244         for (i = rnum; i >= 0; i--) {
245                 eri = &sc->sc_rinfo[i];
246                 if (i < rnum)
247                         rman_fini(&eri->eri_rman);
248                 if (eri->eri_res != 0) {
249                         bus_release_resource(dev, eri->eri_rtype,
250                             PCIR_BAR(rnum), eri->eri_res);
251                 }
252         }
253         free(sc->sc_rinfo, M_DEVBUF);
254         free(sc->sc_range, M_OFWPROP);
255         return (ENXIO);
256 }
257
258 static int
259 ebus_print_child(device_t dev, device_t child)
260 {
261         int retval;
262
263         retval = bus_print_child_header(dev, child);
264         retval += ebus_print_res(device_get_ivars(child));
265         retval += bus_print_child_footer(dev, child);
266         return (retval);
267 }
268
269 static void
270 ebus_probe_nomatch(device_t dev, device_t child)
271 {
272
273         device_printf(dev, "<%s>", ofw_bus_get_name(child));
274         ebus_print_res(device_get_ivars(child));
275         printf(" (no driver attached)\n");
276 }
277
278 static struct resource *
279 ebus_alloc_resource(device_t bus, device_t child, int type, int *rid,
280     u_long start, u_long end, u_long count, u_int flags)
281 {
282         struct ebus_softc *sc;
283         struct resource_list *rl;
284         struct resource_list_entry *rle = NULL;
285         struct resource *res;
286         struct ebus_rinfo *ri;
287         bus_space_tag_t bt;
288         bus_space_handle_t bh;
289         int passthrough = (device_get_parent(child) != bus);
290         int isdefault = (start == 0UL && end == ~0UL);
291         int ridx, rv;
292
293         sc = (struct ebus_softc *)device_get_softc(bus);
294         rl = BUS_GET_RESOURCE_LIST(bus, child);
295         /*
296          * Map ebus ranges to PCI ranges. This may include changing the
297          * allocation type.
298          */
299         switch (type) {
300         case SYS_RES_MEMORY:
301                 KASSERT(!(isdefault && passthrough),
302                     ("ebus_alloc_resource: passthrough of default alloc"));
303                 if (!passthrough) {
304                         rle = resource_list_find(rl, type, *rid);
305                         if (rle == NULL)
306                                 return (NULL);
307                         KASSERT(rle->res == NULL,
308                             ("ebus_alloc_resource: resource entry is busy"));
309                         if (isdefault) {
310                                 start = rle->start;
311                                 count = ulmax(count, rle->count);
312                                 end = ulmax(rle->end, start + count - 1);
313                         }
314                 }
315
316                 (void)ofw_isa_range_map(sc->sc_range, sc->sc_nrange,
317                     &start, &end, &ridx);
318
319                 ri = &sc->sc_rinfo[ridx];
320                 res = rman_reserve_resource(&ri->eri_rman, start, end, count,
321                     flags, child);
322                 if (res == NULL)
323                         return (NULL);
324                 rman_set_rid(res, *rid);
325                 bt = rman_get_bustag(ri->eri_res);
326                 rman_set_bustag(res, bt);
327                 rv = bus_space_subregion(bt, rman_get_bushandle(ri->eri_res),
328                     rman_get_start(res) - rman_get_start(ri->eri_res), count,
329                     &bh);
330                 if (rv != 0) {
331                         rman_release_resource(res);
332                         return (NULL);
333                 }
334                 rman_set_bushandle(res, bh);
335                 if (!passthrough)
336                         rle->res = res;
337                 return (res);
338         case SYS_RES_IRQ:
339                 return (resource_list_alloc(rl, bus, child, type, rid, start,
340                     end, count, flags));
341         }
342
343         return (NULL);
344 }
345
346 int
347 ebus_release_resource(device_t bus, device_t child, int type, int rid,
348     struct resource *res)
349 {
350         struct resource_list *rl;
351         struct resource_list_entry *rle;
352         int passthrough = (device_get_parent(child) != bus);
353         int rv;
354
355         rl = BUS_GET_RESOURCE_LIST(bus, child);
356         switch (type) {
357         case SYS_RES_MEMORY:
358                 if ((rv = rman_release_resource(res)) != 0)
359                         return (rv);
360                 if (!passthrough) {
361                         rle = resource_list_find(rl, type, rid);
362                         KASSERT(rle != NULL, ("ebus_release_resource: "
363                             "resource entry not found!"));
364                         KASSERT(rle->res != NULL, ("ebus_alloc_resource: "
365                             "resource entry is not busy"));
366                         rle->res = NULL;
367                 }
368                 break;
369         case SYS_RES_IRQ:
370                 return (resource_list_release(rl, bus, child, type, rid, res));
371         default:
372                 panic("ebus_release_resource: unsupported resource type %d",
373                     type);
374         }
375         return (0);
376 }
377
378 static struct resource_list *
379 ebus_get_resource_list(device_t dev, device_t child)
380 {
381         struct ebus_devinfo *edi;
382
383         edi = device_get_ivars(child);
384         return (&edi->edi_rl);
385 }
386
387 static const struct ofw_bus_devinfo *
388 ebus_get_devinfo(device_t bus, device_t dev)
389 {
390         struct ebus_devinfo *edi;
391
392         edi = device_get_ivars(dev);
393         return (&edi->edi_obdinfo);
394 }
395
396 static struct ebus_devinfo *
397 ebus_setup_dinfo(device_t dev, struct ebus_softc *sc, phandle_t node)
398 {
399         struct ebus_devinfo *edi;
400         struct isa_regs *reg;
401         ofw_isa_intr_t *intrs;
402         ofw_pci_intr_t rintr;
403         u_int64_t start;
404         int nreg, nintr, i;
405
406         edi = malloc(sizeof(*edi), M_DEVBUF, M_ZERO | M_WAITOK);
407         if (ofw_bus_gen_setup_devinfo(&edi->edi_obdinfo, node) != 0) {
408                 free(edi, M_DEVBUF);
409                 return (NULL);
410         }
411         resource_list_init(&edi->edi_rl);
412         nreg = OF_getprop_alloc(node, "reg", sizeof(*reg), (void **)&reg);
413         if (nreg == -1) {
414                 device_printf(dev, "<%s>: incomplete\n",
415                     edi->edi_obdinfo.obd_name);
416                 goto fail;
417         }
418         for (i = 0; i < nreg; i++) {
419                 start = ISA_REG_PHYS(reg + i);
420                 resource_list_add(&edi->edi_rl, SYS_RES_MEMORY, i,
421                     start, start + reg[i].size - 1, reg[i].size);
422         }
423         free(reg, M_OFWPROP);
424
425         nintr = OF_getprop_alloc(node, "interrupts",  sizeof(*intrs),
426             (void **)&intrs);
427         for (i = 0; i < nintr; i++) {
428                 rintr = ofw_isa_route_intr(dev, node, &sc->sc_iinfo, intrs[i]);
429                 if (rintr == PCI_INVALID_IRQ) {
430                         device_printf(dev,
431                             "<%s>: could not map EBus interrupt %d\n",
432                             edi->edi_obdinfo.obd_name, intrs[i]);
433                         free(intrs, M_OFWPROP);
434                         goto fail;
435                 }
436                 resource_list_add(&edi->edi_rl, SYS_RES_IRQ, i,
437                     rintr, rintr, 1);
438         }
439         free(intrs, M_OFWPROP);
440
441         return (edi);
442
443 fail:
444         ebus_destroy_dinfo(edi);
445         return (NULL);
446 }
447
448 static void
449 ebus_destroy_dinfo(struct ebus_devinfo *edi)
450 {
451
452         resource_list_free(&edi->edi_rl);
453         ofw_bus_gen_destroy_devinfo(&edi->edi_obdinfo);
454         free(edi, M_DEVBUF);
455 }
456
457 static int
458 ebus_print_res(struct ebus_devinfo *edi)
459 {
460         int retval;
461
462         retval = 0;
463         retval += resource_list_print_type(&edi->edi_rl, "addr", SYS_RES_MEMORY,
464             "%#lx");
465         retval += resource_list_print_type(&edi->edi_rl, "irq", SYS_RES_IRQ,
466             "%ld");
467         return (retval);
468 }