]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/sparc64/fhc/fhc.c
Import DTS from Linux 4.20
[FreeBSD/FreeBSD.git] / sys / sparc64 / fhc / fhc.c
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3  *
4  * Copyright (c) 2003 Jake Burkholder.
5  * Copyright (c) 2005 Marius Strobl <marius@FreeBSD.org>
6  * All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25  * LIABILITY, 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
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/malloc.h>
38 #include <sys/module.h>
39 #include <sys/pcpu.h>
40
41 #include <dev/led/led.h>
42 #include <dev/ofw/ofw_bus.h>
43 #include <dev/ofw/ofw_bus_subr.h>
44 #include <dev/ofw/openfirm.h>
45
46 #include <machine/bus.h>
47 #include <machine/bus_common.h>
48 #include <machine/resource.h>
49
50 #include <sys/rman.h>
51
52 #include <sparc64/fhc/fhcreg.h>
53 #include <sparc64/sbus/ofw_sbus.h>
54
55 struct fhc_devinfo {
56         struct ofw_bus_devinfo  fdi_obdinfo;
57         struct resource_list    fdi_rl;
58 };
59
60 struct fhc_softc {
61         struct resource         *sc_memres[FHC_NREG];
62         int                     sc_nrange;
63         struct sbus_ranges      *sc_ranges;
64         int                     sc_ign;
65         struct cdev             *sc_led_dev;
66 };
67
68 static device_probe_t fhc_probe;
69 static device_attach_t fhc_attach;
70 static bus_print_child_t fhc_print_child;
71 static bus_probe_nomatch_t fhc_probe_nomatch;
72 static bus_setup_intr_t fhc_setup_intr;
73 static bus_alloc_resource_t fhc_alloc_resource;
74 static bus_adjust_resource_t fhc_adjust_resource;
75 static bus_get_resource_list_t fhc_get_resource_list;
76 static ofw_bus_get_devinfo_t fhc_get_devinfo;
77
78 static void fhc_intr_enable(void *);
79 static void fhc_intr_disable(void *);
80 static void fhc_intr_assign(void *);
81 static void fhc_intr_clear(void *);
82 static void fhc_led_func(void *, int);
83 static int fhc_print_res(struct fhc_devinfo *);
84
85 static device_method_t fhc_methods[] = {
86         /* Device interface */
87         DEVMETHOD(device_probe,         fhc_probe),
88         DEVMETHOD(device_attach,        fhc_attach),
89         DEVMETHOD(device_shutdown,      bus_generic_shutdown),
90         DEVMETHOD(device_suspend,       bus_generic_suspend),
91         DEVMETHOD(device_resume,        bus_generic_resume),
92
93         /* Bus interface */
94         DEVMETHOD(bus_print_child,      fhc_print_child),
95         DEVMETHOD(bus_probe_nomatch,    fhc_probe_nomatch),
96         DEVMETHOD(bus_alloc_resource,   fhc_alloc_resource),
97         DEVMETHOD(bus_activate_resource, bus_generic_activate_resource),
98         DEVMETHOD(bus_deactivate_resource, bus_generic_deactivate_resource),
99         DEVMETHOD(bus_adjust_resource,  fhc_adjust_resource),
100         DEVMETHOD(bus_release_resource, bus_generic_rl_release_resource),
101         DEVMETHOD(bus_setup_intr,       fhc_setup_intr),
102         DEVMETHOD(bus_teardown_intr,    bus_generic_teardown_intr),
103         DEVMETHOD(bus_get_resource,     bus_generic_rl_get_resource),
104         DEVMETHOD(bus_get_resource_list, fhc_get_resource_list),
105         DEVMETHOD(bus_child_pnpinfo_str, ofw_bus_gen_child_pnpinfo_str),
106
107         /* ofw_bus interface */
108         DEVMETHOD(ofw_bus_get_devinfo,  fhc_get_devinfo),
109         DEVMETHOD(ofw_bus_get_compat,   ofw_bus_gen_get_compat),
110         DEVMETHOD(ofw_bus_get_model,    ofw_bus_gen_get_model),
111         DEVMETHOD(ofw_bus_get_name,     ofw_bus_gen_get_name),
112         DEVMETHOD(ofw_bus_get_node,     ofw_bus_gen_get_node),
113         DEVMETHOD(ofw_bus_get_type,     ofw_bus_gen_get_type),
114
115         DEVMETHOD_END
116 };
117
118 static driver_t fhc_driver = {
119         "fhc",
120         fhc_methods,
121         sizeof(struct fhc_softc),
122 };
123
124 static devclass_t fhc_devclass;
125
126 EARLY_DRIVER_MODULE(fhc, central, fhc_driver, fhc_devclass, 0, 0,
127     BUS_PASS_BUS);
128 MODULE_DEPEND(fhc, central, 1, 1, 1);
129 EARLY_DRIVER_MODULE(fhc, nexus, fhc_driver, fhc_devclass, 0, 0,
130     BUS_PASS_BUS);
131 MODULE_DEPEND(fhc, nexus, 1, 1, 1);
132 MODULE_VERSION(fhc, 1);
133
134 static const struct intr_controller fhc_ic = {
135         fhc_intr_enable,
136         fhc_intr_disable,
137         fhc_intr_assign,
138         fhc_intr_clear
139 };
140
141 struct fhc_icarg {
142         struct fhc_softc        *fica_sc;
143         struct resource         *fica_memres;
144 };
145
146 static int
147 fhc_probe(device_t dev)
148 {
149
150         if (strcmp(ofw_bus_get_name(dev), "fhc") == 0) {
151                 device_set_desc(dev, "fhc");
152                 return (0);
153         }
154         return (ENXIO);
155 }
156
157 static int
158 fhc_attach(device_t dev)
159 {
160         char ledname[sizeof("boardXX")];
161         struct fhc_devinfo *fdi;
162         struct fhc_icarg *fica;
163         struct fhc_softc *sc;
164         struct sbus_regs *reg;
165         phandle_t child;
166         phandle_t node;
167         device_t cdev;
168         uint32_t board;
169         uint32_t ctrl;
170         uint32_t *intr;
171         uint32_t iv;
172         char *name;
173         int central;
174         int error;
175         int i;
176         int j;
177
178         sc = device_get_softc(dev);
179         node = ofw_bus_get_node(dev);
180
181         central = 0;
182         if (strcmp(device_get_name(device_get_parent(dev)), "central") == 0)
183                 central = 1;
184
185         for (i = 0; i < FHC_NREG; i++) {
186                 j = i;
187                 sc->sc_memres[i] = bus_alloc_resource_any(dev, SYS_RES_MEMORY,
188                     &j, RF_ACTIVE);
189                 if (sc->sc_memres[i] == NULL) {
190                         device_printf(dev, "cannot allocate resource %d\n", i);
191                         error = ENXIO;
192                         goto fail_memres;
193                 }
194         }
195
196         if (central != 0) {
197                 board = bus_read_4(sc->sc_memres[FHC_INTERNAL], FHC_BSR);
198                 board = ((board >> 16) & 0x1) | ((board >> 12) & 0xe);
199         } else {
200                 if (OF_getprop(node, "board#", &board, sizeof(board)) == -1) {
201                         device_printf(dev, "cannot get board number\n");
202                         error = ENXIO;
203                         goto fail_memres;
204                 }
205         }
206
207         device_printf(dev, "board %d, ", board);
208         if (OF_getprop_alloc(node, "board-model", (void **)&name) != -1) {
209                 printf("model %s\n", name);
210                 OF_prop_free(name);
211         } else
212                 printf("model unknown\n");
213
214         for (i = FHC_FANFAIL; i <= FHC_TOD; i++) {
215                 bus_write_4(sc->sc_memres[i], FHC_ICLR, INTCLR_IDLE);
216                 (void)bus_read_4(sc->sc_memres[i], FHC_ICLR);
217         }
218
219         sc->sc_ign = board << 1;
220         bus_write_4(sc->sc_memres[FHC_IGN], 0x0, sc->sc_ign);
221         sc->sc_ign = bus_read_4(sc->sc_memres[FHC_IGN], 0x0);
222
223         ctrl = bus_read_4(sc->sc_memres[FHC_INTERNAL], FHC_CTRL);
224         if (central == 0)
225                 ctrl |= FHC_CTRL_IXIST;
226         ctrl &= ~(FHC_CTRL_AOFF | FHC_CTRL_BOFF | FHC_CTRL_SLINE);
227         bus_write_4(sc->sc_memres[FHC_INTERNAL], FHC_CTRL, ctrl);
228         (void)bus_read_4(sc->sc_memres[FHC_INTERNAL], FHC_CTRL);
229
230         sc->sc_nrange = OF_getprop_alloc_multi(node, "ranges",
231             sizeof(*sc->sc_ranges), (void **)&sc->sc_ranges);
232         if (sc->sc_nrange == -1) {
233                 device_printf(dev, "cannot get ranges\n");
234                 error = ENXIO;
235                 goto fail_memres;
236         }
237
238         /*
239          * Apparently only the interrupt controller of boards hanging off
240          * of central(4) is indented to be used, otherwise we would have
241          * conflicts registering the interrupt controllers for all FHC
242          * boards as the board number and thus the IGN isn't unique.
243          */
244         if (central == 1) {
245                 /*
246                  * Hunt through all the interrupt mapping regs and register
247                  * our interrupt controller for the corresponding interrupt
248                  * vectors.  We do this early in order to be able to catch
249                  * stray interrupts.
250                  */
251                 for (i = FHC_FANFAIL; i <= FHC_TOD; i++) {
252                         fica = malloc(sizeof(*fica), M_DEVBUF, M_NOWAIT);
253                         if (fica == NULL)
254                                 panic("%s: could not allocate interrupt "
255                                     "controller argument", __func__);
256                         fica->fica_sc = sc;
257                         fica->fica_memres = sc->sc_memres[i];
258 #ifdef FHC_DEBUG
259                         device_printf(dev, "intr map %d: %#lx, clr: %#lx\n", i,
260                             (u_long)bus_read_4(fica->fica_memres, FHC_IMAP),
261                             (u_long)bus_read_4(fica->fica_memres, FHC_ICLR));
262 #endif
263                         /*
264                          * XXX we only pick the INO rather than the INR
265                          * from the IMR since the firmware may not provide
266                          * the IGN and the IGN is constant for all devices
267                          * on that FireHose controller.
268                          */
269                         j = intr_controller_register(INTMAP_VEC(sc->sc_ign,
270                             INTINO(bus_read_4(fica->fica_memres, FHC_IMAP))),
271                             &fhc_ic, fica);
272                         if (j != 0)
273                                 device_printf(dev, "could not register "
274                                     "interrupt controller for map %d (%d)\n",
275                                     i, j);
276                 }
277         } else {
278                 snprintf(ledname, sizeof(ledname), "board%d", board);
279                 sc->sc_led_dev = led_create(fhc_led_func, sc, ledname);
280         }
281
282         for (child = OF_child(node); child != 0; child = OF_peer(child)) {
283                 fdi = malloc(sizeof(*fdi), M_DEVBUF, M_WAITOK | M_ZERO);
284                 if (ofw_bus_gen_setup_devinfo(&fdi->fdi_obdinfo, child) != 0) {
285                         free(fdi, M_DEVBUF);
286                         continue;
287                 }
288                 i = OF_getprop_alloc_multi(child, "reg", sizeof(*reg),
289                     (void **)&reg);
290                 if (i == -1) {
291                         device_printf(dev, "<%s>: incomplete\n",
292                             fdi->fdi_obdinfo.obd_name);
293                         ofw_bus_gen_destroy_devinfo(&fdi->fdi_obdinfo);
294                         free(fdi, M_DEVBUF);
295                         continue;
296                 }
297                 resource_list_init(&fdi->fdi_rl);
298                 for (j = 0; j < i; j++)
299                         resource_list_add(&fdi->fdi_rl, SYS_RES_MEMORY, j,
300                             reg[j].sbr_offset, reg[j].sbr_offset +
301                             reg[j].sbr_size, reg[j].sbr_size);
302                 OF_prop_free(reg);
303                 if (central == 1) {
304                         i = OF_getprop_alloc_multi(child, "interrupts",
305                             sizeof(*intr), (void **)&intr);
306                         if (i != -1) {
307                                 for (j = 0; j < i; j++) {
308                                         iv = INTMAP_VEC(sc->sc_ign, intr[j]);
309                                         resource_list_add(&fdi->fdi_rl,
310                                             SYS_RES_IRQ, j, iv, iv, 1);
311                                 }
312                                 OF_prop_free(intr);
313                         }
314                 }
315                 cdev = device_add_child(dev, NULL, -1);
316                 if (cdev == NULL) {
317                         device_printf(dev, "<%s>: device_add_child failed\n",
318                             fdi->fdi_obdinfo.obd_name);
319                         resource_list_free(&fdi->fdi_rl);
320                         ofw_bus_gen_destroy_devinfo(&fdi->fdi_obdinfo);
321                         free(fdi, M_DEVBUF);
322                         continue;
323                 }
324                 device_set_ivars(cdev, fdi);
325         }
326
327         return (bus_generic_attach(dev));
328
329  fail_memres:
330         for (i = 0; i < FHC_NREG; i++)
331                 if (sc->sc_memres[i] != NULL)
332                         bus_release_resource(dev, SYS_RES_MEMORY,
333                             rman_get_rid(sc->sc_memres[i]), sc->sc_memres[i]);
334         return (error);
335 }
336
337 static int
338 fhc_print_child(device_t dev, device_t child)
339 {
340         int rv;
341
342         rv = bus_print_child_header(dev, child);
343         rv += fhc_print_res(device_get_ivars(child));
344         rv += bus_print_child_footer(dev, child);
345         return (rv);
346 }
347
348 static void
349 fhc_probe_nomatch(device_t dev, device_t child)
350 {
351         const char *type;
352
353         device_printf(dev, "<%s>", ofw_bus_get_name(child));
354         fhc_print_res(device_get_ivars(child));
355         type = ofw_bus_get_type(child);
356         printf(" type %s (no driver attached)\n",
357             type != NULL ? type : "unknown");
358 }
359
360 static void
361 fhc_intr_enable(void *arg)
362 {
363         struct intr_vector *iv = arg;
364         struct fhc_icarg *fica = iv->iv_icarg;
365
366         bus_write_4(fica->fica_memres, FHC_IMAP,
367             INTMAP_ENABLE(iv->iv_vec, iv->iv_mid));
368         (void)bus_read_4(fica->fica_memres, FHC_IMAP);
369 }
370
371 static void
372 fhc_intr_disable(void *arg)
373 {
374         struct intr_vector *iv = arg;
375         struct fhc_icarg *fica = iv->iv_icarg;
376
377         bus_write_4(fica->fica_memres, FHC_IMAP, iv->iv_vec);
378         (void)bus_read_4(fica->fica_memres, FHC_IMAP);
379 }
380
381 static void
382 fhc_intr_assign(void *arg)
383 {
384         struct intr_vector *iv = arg;
385         struct fhc_icarg *fica = iv->iv_icarg;
386
387         bus_write_4(fica->fica_memres, FHC_IMAP, INTMAP_TID(
388             bus_read_4(fica->fica_memres, FHC_IMAP), iv->iv_mid));
389         (void)bus_read_4(fica->fica_memres, FHC_IMAP);
390 }
391
392 static void
393 fhc_intr_clear(void *arg)
394 {
395         struct intr_vector *iv = arg;
396         struct fhc_icarg *fica = iv->iv_icarg;
397
398         bus_write_4(fica->fica_memres, FHC_ICLR, INTCLR_IDLE);
399         (void)bus_read_4(fica->fica_memres, FHC_ICLR);
400 }
401
402 static int
403 fhc_setup_intr(device_t bus, device_t child, struct resource *r, int flags,
404     driver_filter_t *filt, driver_intr_t *func, void *arg, void **cookiep)
405 {
406         struct fhc_softc *sc;
407         u_long vec;
408
409         sc = device_get_softc(bus);
410         /*
411          * Make sure the vector is fully specified and we registered
412          * our interrupt controller for it.
413          */
414         vec = rman_get_start(r);
415         if (INTIGN(vec) != sc->sc_ign || intr_vectors[vec].iv_ic != &fhc_ic) {
416                 device_printf(bus, "invalid interrupt vector 0x%lx\n", vec);
417                 return (EINVAL);
418         }
419         return (bus_generic_setup_intr(bus, child, r, flags, filt, func,
420             arg, cookiep));
421 }
422
423 static struct resource *
424 fhc_alloc_resource(device_t bus, device_t child, int type, int *rid,
425     rman_res_t start, rman_res_t end, rman_res_t count, u_int flags)
426 {
427         struct resource_list *rl;
428         struct resource_list_entry *rle;
429         struct fhc_softc *sc;
430         struct resource *res;
431         bus_addr_t coffset;
432         bus_addr_t cend;
433         bus_addr_t phys;
434         int isdefault;
435         int passthrough;
436         int i;
437
438         isdefault = RMAN_IS_DEFAULT_RANGE(start, end);
439         passthrough = (device_get_parent(child) != bus);
440         res = NULL;
441         rle = NULL;
442         rl = BUS_GET_RESOURCE_LIST(bus, child);
443         sc = device_get_softc(bus);
444         switch (type) {
445         case SYS_RES_IRQ:
446                 return (resource_list_alloc(rl, bus, child, type, rid, start,
447                     end, count, flags));
448         case SYS_RES_MEMORY:
449                 if (!passthrough) {
450                         rle = resource_list_find(rl, type, *rid);
451                         if (rle == NULL)
452                                 return (NULL);
453                         if (rle->res != NULL)
454                                 panic("%s: resource entry is busy", __func__);
455                         if (isdefault) {
456                                 start = rle->start;
457                                 count = ulmax(count, rle->count);
458                                 end = ulmax(rle->end, start + count - 1);
459                         }
460                 }
461                 for (i = 0; i < sc->sc_nrange; i++) {
462                         coffset = sc->sc_ranges[i].coffset;
463                         cend = coffset + sc->sc_ranges[i].size - 1;
464                         if (start >= coffset && end <= cend) {
465                                 start -= coffset;
466                                 end -= coffset;
467                                 phys = sc->sc_ranges[i].poffset |
468                                     ((bus_addr_t)sc->sc_ranges[i].pspace << 32);
469                                 res = bus_generic_alloc_resource(bus, child,
470                                     type, rid, phys + start, phys + end,
471                                     count, flags);
472                                 if (!passthrough)
473                                         rle->res = res;
474                                 break;
475                         }
476                 }
477                 break;
478         }
479         return (res);
480 }
481
482 static int
483 fhc_adjust_resource(device_t bus __unused, device_t child __unused,
484     int type __unused, struct resource *r __unused, rman_res_t start __unused,
485     rman_res_t end __unused)
486 {
487
488         return (ENXIO);
489 }
490
491 static struct resource_list *
492 fhc_get_resource_list(device_t bus, device_t child)
493 {
494         struct fhc_devinfo *fdi;
495
496         fdi = device_get_ivars(child);
497         return (&fdi->fdi_rl);
498 }
499
500 static const struct ofw_bus_devinfo *
501 fhc_get_devinfo(device_t bus, device_t child)
502 {
503         struct fhc_devinfo *fdi;
504
505         fdi = device_get_ivars(child);
506         return (&fdi->fdi_obdinfo);
507 }
508
509 static void
510 fhc_led_func(void *arg, int onoff)
511 {
512         struct fhc_softc *sc;
513         uint32_t ctrl;
514
515         sc = (struct fhc_softc *)arg;
516
517         ctrl = bus_read_4(sc->sc_memres[FHC_INTERNAL], FHC_CTRL);
518         if (onoff)
519                 ctrl |= FHC_CTRL_RLED;
520         else
521                 ctrl &= ~FHC_CTRL_RLED;
522         ctrl &= ~(FHC_CTRL_AOFF | FHC_CTRL_BOFF | FHC_CTRL_SLINE);
523         bus_write_4(sc->sc_memres[FHC_INTERNAL], FHC_CTRL, ctrl);
524         (void)bus_read_4(sc->sc_memres[FHC_INTERNAL], FHC_CTRL);
525 }
526
527 static int
528 fhc_print_res(struct fhc_devinfo *fdi)
529 {
530         int rv;
531
532         rv = 0;
533         rv += resource_list_print_type(&fdi->fdi_rl, "mem", SYS_RES_MEMORY,
534             "%#jx");
535         rv += resource_list_print_type(&fdi->fdi_rl, "irq", SYS_RES_IRQ, "%jd");
536         return (rv);
537 }