1 /*        $NetBSD: frodo.c,v 1.36 2024/01/16 03:44:43 thorpej Exp $   */
2 
3 /*-
4  * Copyright (c) 1997, 1998, 1999 The NetBSD Foundation, Inc.
5  * All rights reserved.
6  *
7  * This code is derived from software contributed to The NetBSD Foundation
8  * by Jason R. Thorpe.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer.
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in the
17  *    documentation and/or other materials provided with the distribution.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29  * POSSIBILITY OF SUCH DAMAGE.
30  */
31 
32 /*
33  * Copyright (c) 1997 Michael Smith.  All rights reserved.
34  *
35  * Redistribution and use in source and binary forms, with or without
36  * modification, are permitted provided that the following conditions
37  * are met:
38  * 1. Redistributions of source code must retain the above copyright
39  *    notice, this list of conditions and the following disclaimer.
40  * 2. Redistributions in binary form must reproduce the above copyright
41  *    notice, this list of conditions and the following disclaimer in the
42  *    documentation and/or other materials provided with the distribution.
43  *
44  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
45  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
46  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
47  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
48  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
49  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
50  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
51  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
52  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
53  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
54  * SUCH DAMAGE.
55  */
56 
57 /*
58  * Support for the "Frodo" (a.k.a. "Apollo Utility") chip found
59  * in HP Apollo 9000/4xx workstations, as well as 9000/382 controllers.
60  */
61 
62 #include <sys/cdefs.h>
63 __KERNEL_RCSID(0, "$NetBSD: frodo.c,v 1.36 2024/01/16 03:44:43 thorpej Exp $");
64 
65 #define   _M68K_INTR_PRIVATE
66 
67 #include <sys/param.h>
68 #include <sys/systm.h>
69 #include <sys/kernel.h>
70 #include <sys/syslog.h>
71 #include <sys/device.h>
72 
73 #include <machine/bus.h>
74 #include <machine/cpu.h>
75 #include <machine/intr.h>
76 #include <machine/hp300spu.h>
77 
78 #include <hp300/dev/intiovar.h>
79 
80 #include <hp300/dev/frodoreg.h>
81 #include <hp300/dev/frodovar.h>
82 
83 /*
84  * Description of a Frodo interrupt handler.
85  */
86 struct frodo_interhand {
87           int       (*ih_fn)(void *);
88           void      *ih_arg;
89           int       ih_isrpri;
90 };
91 
92 struct frodo_softc {
93           device_t  sc_dev;             /* generic device glue */
94           volatile uint8_t *sc_regs;    /* register base */
95           struct frodo_interhand sc_intr[FRODO_NINTR]; /* interrupt handlers */
96           int                 sc_ipl;
97           void                *sc_ih;             /* out interrupt cookie */
98           int                 sc_refcnt;          /* number of interrupt refs */
99           struct bus_space_tag sc_tag;  /* bus space tag */
100 };
101 
102 static int          frodomatch(device_t, cfdata_t, void *);
103 static void         frodoattach(device_t, device_t, void *);
104 
105 static int          frodoprint(void *, const char *);
106 static int          frodosubmatch(device_t, cfdata_t, const int *, void *);
107 
108 static int          frodointr(void *);
109 
110 static void         frodo_imask(struct frodo_softc *, uint16_t, uint16_t);
111 
112 CFATTACH_DECL_NEW(frodo, sizeof(struct frodo_softc),
113     frodomatch, frodoattach, NULL, NULL);
114 
115 static const struct frodo_device frodo_subdevs[] = {
116           { "dnkbd",          FRODO_APCI_OFFSET(0),         FRODO_INTR_APCI0 },
117           { "com",  FRODO_APCI_OFFSET(1),         FRODO_INTR_APCI1 },
118           { "com",  FRODO_APCI_OFFSET(2),         FRODO_INTR_APCI2 },
119           { "com",  FRODO_APCI_OFFSET(3),         FRODO_INTR_APCI3 },
120           { "mcclock",        FRODO_CALENDAR,               FRODO_INTR_CALENDAR },
121           { NULL,             0,                            0 }
122 };
123 
124 static int
frodomatch(device_t parent,cfdata_t cf,void * aux)125 frodomatch(device_t parent, cfdata_t cf, void *aux)
126 {
127           struct intio_attach_args *ia = aux;
128           static int frodo_matched = 0;
129 
130           /* only allow one instance */
131           if (frodo_matched)
132                     return 0;
133 
134           if (strcmp(ia->ia_modname, "frodo") != 0)
135                     return 0;
136 
137           if (badaddr((void *)ia->ia_addr))
138                     return 0;
139 
140           frodo_matched = 1;
141           return 1;
142 }
143 
144 static void
frodoattach(device_t parent,device_t self,void * aux)145 frodoattach(device_t parent, device_t self, void *aux)
146 {
147           struct frodo_softc *sc = device_private(self);
148           struct intio_attach_args *ia = aux;
149           bus_space_tag_t bst = &sc->sc_tag;
150           const struct frodo_device *fd;
151           struct frodo_attach_args fa;
152 
153           sc->sc_dev = self;
154           sc->sc_regs = (volatile uint8_t *)ia->ia_addr;
155           sc->sc_ipl = ia->ia_ipl;
156 
157           if ((FRODO_READ(sc, FRODO_IISR) & FRODO_IISR_SERVICE) == 0)
158                     aprint_normal(": service mode enabled");
159           aprint_normal("\n");
160 
161           /* Initialize bus_space_tag_t for frodo */
162           frodo_init_bus_space(bst);
163 
164           /* Clear all of the interrupt handlers. */
165           memset(sc->sc_intr, 0, sizeof(sc->sc_intr));
166           sc->sc_refcnt = 0;
167 
168           /*
169            * Disable all of the interrupt lines; we reenable them
170            * as subdevices attach.
171            */
172           frodo_imask(sc, 0, 0xffff);
173 
174           /* Clear any pending interrupts. */
175           FRODO_WRITE(sc, FRODO_PIC_PU, 0xff);
176           FRODO_WRITE(sc, FRODO_PIC_PL, 0xff);
177 
178           /* Set interrupt polarities. */
179           FRODO_WRITE(sc, FRODO_PIO_IPR, 0x10);
180 
181           /* ...and configure for edge triggering. */
182           FRODO_WRITE(sc, FRODO_PIO_IELR, 0xcf);
183 
184           /*
185            * We defer hooking up our interrupt handler until
186            * a subdevice hooks up theirs.
187            */
188           sc->sc_ih = NULL;
189 
190           /* ... and attach subdevices. */
191           for (fd = frodo_subdevs; fd->fd_name != NULL; fd++) {
192                     /*
193                      * Skip the first serial port if we're not a 425e;
194                      * it's mapped to the DCA at select code 9 on all
195                      * other models.
196                      */
197                     if (fd->fd_offset == FRODO_APCI_OFFSET(1) &&
198                         mmuid != MMUID_425_E)
199                               continue;
200                     /*
201                      * The mcclock is available only on a 425e.
202                      */
203                     if (fd->fd_offset == FRODO_CALENDAR && mmuid != MMUID_425_E)
204                               continue;
205                     fa.fa_name = fd->fd_name;
206                     fa.fa_bst = bst;
207                     fa.fa_base = ia->ia_iobase;
208                     fa.fa_offset = fd->fd_offset;
209                     fa.fa_line = fd->fd_line;
210                     config_found(self, &fa, frodoprint,
211                         CFARGS(.submatch = frodosubmatch));
212           }
213 }
214 
215 static int
frodosubmatch(device_t parent,cfdata_t cf,const int * ldesc,void * aux)216 frodosubmatch(device_t parent, cfdata_t cf, const int *ldesc, void *aux)
217 {
218           struct frodo_attach_args *fa = aux;
219 
220           if (cf->frodocf_offset != FRODO_UNKNOWN_OFFSET &&
221               cf->frodocf_offset != fa->fa_offset)
222                     return 0;
223 
224           return config_match(parent, cf, aux);
225 }
226 
227 static int
frodoprint(void * aux,const char * pnp)228 frodoprint(void *aux, const char *pnp)
229 {
230           struct frodo_attach_args *fa = aux;
231 
232           if (pnp)
233                     aprint_normal("%s at %s", fa->fa_name, pnp);
234           aprint_normal(" offset 0x%x", fa->fa_offset);
235           return UNCONF;
236 }
237 
238 void
frodo_intr_establish(device_t frdev,int (* func)(void *),void * arg,int line,int isrpri)239 frodo_intr_establish(device_t frdev, int (*func)(void *), void *arg,
240     int line, int isrpri)
241 {
242           struct frodo_softc *sc = device_private(frdev);
243           struct m68k_intrhand *ih = sc->sc_ih;
244 
245           if (line < 0 || line >= FRODO_NINTR) {
246                     aprint_error_dev(frdev, "bad interrupt line %d\n", line);
247                     goto lose;
248           }
249           if (sc->sc_intr[line].ih_fn != NULL) {
250                     aprint_error_dev(frdev, "interrupt line %d already used\n",
251                         line);
252                     goto lose;
253           }
254 
255           /* Install the handler. */
256           sc->sc_intr[line].ih_fn = func;
257           sc->sc_intr[line].ih_arg = arg;
258           sc->sc_intr[line].ih_isrpri = isrpri;
259 
260           /*
261            * If this is the first one, establish the frodo
262            * interrupt handler.  If not, reestablish at a
263            * higher priority if necessary.
264            */
265           if (ih == NULL || ih->ih_isrpri < isrpri) {
266                     if (ih != NULL)
267                               intr_disestablish(ih);
268                     sc->sc_ih = intr_establish(frodointr, sc, sc->sc_ipl, isrpri);
269           }
270 
271           sc->sc_refcnt++;
272 
273           /* Enable the interrupt line. */
274           frodo_imask(sc, (1 << line), 0);
275           return;
276  lose:
277           panic("frodo_intr_establish");
278 }
279 
280 void
frodo_intr_disestablish(device_t frdev,int line)281 frodo_intr_disestablish(device_t frdev, int line)
282 {
283           struct frodo_softc *sc = device_private(frdev);
284           struct m68k_intrhand *ih = sc->sc_ih;
285           int newpri;
286 
287           if (sc->sc_intr[line].ih_fn == NULL) {
288                     printf("%s: no handler for line %d\n",
289                         device_xname(sc->sc_dev), line);
290                     panic("frodo_intr_disestablish");
291           }
292 
293           sc->sc_intr[line].ih_fn = NULL;
294           frodo_imask(sc, 0, (1 << line));
295 
296           /* If this was the last, unhook ourselves. */
297           if (sc->sc_refcnt-- == 1) {
298                     intr_disestablish(ih);
299                     return;
300           }
301 
302           /* Lower our priority, if appropriate. */
303           for (newpri = 0, line = 0; line < FRODO_NINTR; line++)
304                     if (sc->sc_intr[line].ih_fn != NULL &&
305                         sc->sc_intr[line].ih_isrpri > newpri)
306                               newpri = sc->sc_intr[line].ih_isrpri;
307 
308           if (newpri != ih->ih_isrpri) {
309                     intr_disestablish(ih);
310                     sc->sc_ih = intr_establish(frodointr, sc, sc->sc_ipl, newpri);
311           }
312 }
313 
314 static int
frodointr(void * arg)315 frodointr(void *arg)
316 {
317           struct frodo_softc *sc = arg;
318           struct frodo_interhand *fih;
319           int line, taken = 0;
320 
321           /* Any interrupts pending? */
322           if (FRODO_GETPEND(sc) == 0)
323                     return 0;
324 
325           do {
326                     /*
327                      * Get pending interrupt; this also clears it for us.
328                      */
329                     line = FRODO_IPEND(sc);
330                     fih = &sc->sc_intr[line];
331                     if (fih->ih_fn == NULL ||
332                         (*fih->ih_fn)(fih->ih_arg) == 0)
333                               printf("%s: spurious interrupt on line %d\n",
334                                   device_xname(sc->sc_dev), line);
335                     if (taken++ > 100)
336                               panic("frodointr: looping!");
337           } while (FRODO_GETPEND(sc) != 0);
338 
339           return 1;
340 }
341 
342 static void
frodo_imask(struct frodo_softc * sc,uint16_t set,uint16_t clear)343 frodo_imask(struct frodo_softc *sc, uint16_t set, uint16_t clear)
344 {
345           uint16_t imask;
346 
347           imask = FRODO_GETMASK(sc);
348 
349           imask |= set;
350           imask &= ~clear;
351 
352           FRODO_SETMASK(sc, imask);
353 }
354 
355 /*
356  * frodo chip specific bus_space(9) support functions.
357  */
358 static uint8_t frodo_bus_space_read_sparse_1(bus_space_tag_t,
359     bus_space_handle_t, bus_size_t);
360 static void frodo_bus_space_write_sparse_1(bus_space_tag_t,
361     bus_space_handle_t, bus_size_t, uint8_t);
362 
363 static void frodo_bus_space_read_multi_sparse_1(bus_space_tag_t,
364     bus_space_handle_t, bus_size_t, uint8_t *, bus_size_t);
365 static void frodo_bus_space_write_multi_sparse_1(bus_space_tag_t,
366     bus_space_handle_t, bus_size_t, const uint8_t *, bus_size_t);
367 
368 static void frodo_bus_space_read_region_sparse_1(bus_space_tag_t,
369     bus_space_handle_t, bus_size_t, uint8_t *, bus_size_t);
370 static void frodo_bus_space_write_region_sparse_1(bus_space_tag_t,
371     bus_space_handle_t, bus_size_t, const uint8_t *, bus_size_t);
372 
373 static void frodo_bus_space_set_multi_sparse_1(bus_space_tag_t,
374     bus_space_handle_t, bus_size_t, uint8_t, bus_size_t);
375 
376 static void frodo_bus_space_set_region_sparse_1(bus_space_tag_t,
377     bus_space_handle_t, bus_size_t, uint8_t, bus_size_t);
378 
379 /*
380  * frodo_init_bus_space():
381  *        Initialize bus_space functions in bus_space_tag_t
382  *        for frodo devices which have sparse address space.
383  */
384 void
frodo_init_bus_space(bus_space_tag_t bst)385 frodo_init_bus_space(bus_space_tag_t bst)
386 {
387 
388           memset(bst, 0, sizeof(struct bus_space_tag));
389           bst->bustype = HP300_BUS_SPACE_INTIO;
390 
391           bst->bsr1 = frodo_bus_space_read_sparse_1;
392           bst->bsw1 = frodo_bus_space_write_sparse_1;
393 
394           bst->bsrm1 = frodo_bus_space_read_multi_sparse_1;
395           bst->bswm1 = frodo_bus_space_write_multi_sparse_1;
396 
397           bst->bsrr1 = frodo_bus_space_read_region_sparse_1;
398           bst->bswr1 = frodo_bus_space_write_region_sparse_1;
399 
400           bst->bssm1 = frodo_bus_space_set_multi_sparse_1;
401 
402           bst->bssr1 = frodo_bus_space_set_region_sparse_1;
403 }
404 
405 static uint8_t
frodo_bus_space_read_sparse_1(bus_space_tag_t bst,bus_space_handle_t bsh,bus_size_t offset)406 frodo_bus_space_read_sparse_1(bus_space_tag_t bst, bus_space_handle_t bsh,
407     bus_size_t offset)
408 {
409 
410           return *(volatile uint8_t *)(bsh + (offset << 2));
411 }
412 
413 static void
frodo_bus_space_write_sparse_1(bus_space_tag_t bst,bus_space_handle_t bsh,bus_size_t offset,uint8_t val)414 frodo_bus_space_write_sparse_1(bus_space_tag_t bst, bus_space_handle_t bsh,
415     bus_size_t offset, uint8_t val)
416 {
417 
418           *(volatile uint8_t *)(bsh + (offset << 2)) = val;
419 }
420 
421 static void
frodo_bus_space_read_multi_sparse_1(bus_space_tag_t bst,bus_space_handle_t bsh,bus_size_t offset,uint8_t * addr,bus_size_t len)422 frodo_bus_space_read_multi_sparse_1(bus_space_tag_t bst, bus_space_handle_t bsh,
423     bus_size_t offset, uint8_t *addr, bus_size_t len)
424 {
425 
426           __asm volatile (
427           "         movl      %0,%%a0             ;\n"
428           "         movl      %1,%%a1             ;\n"
429           "         movl      %2,%%d0             ;\n"
430           "1:       movb      %%a0@,%%a1@+        ;\n"
431           "         subql     #1,%%d0             ;\n"
432           "         jne       1b"
433               :
434               : "r" (bsh + (offset << 2)), "g" (addr), "g" (len)
435               : "%a0","%a1","%d0");
436 }
437 
438 static void
frodo_bus_space_write_multi_sparse_1(bus_space_tag_t bst,bus_space_handle_t bsh,bus_size_t offset,const uint8_t * addr,bus_size_t len)439 frodo_bus_space_write_multi_sparse_1(bus_space_tag_t bst,
440     bus_space_handle_t bsh, bus_size_t offset, const uint8_t *addr,
441     bus_size_t len)
442 {
443 
444           __asm volatile (
445           "         movl      %0,%%a0             ;\n"
446           "         movl      %1,%%a1             ;\n"
447           "         movl      %2,%%d0             ;\n"
448           "1:       movb      %%a1@+,%%a0@        ;\n"
449           "         subql     #1,%%d0             ;\n"
450           "         jne       1b"
451               :
452               : "r" (bsh + (offset << 2)), "g" (addr), "g" (len)
453               : "%a0","%a1","%d0");
454 }
455 
456 static void
frodo_bus_space_read_region_sparse_1(bus_space_tag_t bst,bus_space_handle_t bsh,bus_size_t offset,uint8_t * addr,bus_size_t len)457 frodo_bus_space_read_region_sparse_1(bus_space_tag_t bst,
458     bus_space_handle_t bsh, bus_size_t offset, uint8_t *addr, bus_size_t len)
459 {
460           __asm volatile (
461           "         movl      %0,%%a0             ;\n"
462           "         movl      %1,%%a1             ;\n"
463           "         movl      %2,%%d0             ;\n"
464           "1:       movb      %%a0@,%%a1@+        ;\n"
465           "         addql     #4,%%a0             ;\n"
466           "         subql     #1,%%d0             ;\n"
467           "         jne       1b"
468               :
469               : "r" (bsh + (offset << 2)), "g" (addr), "g" (len)
470               : "%a0","%a1","%d0");
471 }
472 
473 static void
frodo_bus_space_write_region_sparse_1(bus_space_tag_t bst,bus_space_handle_t bsh,bus_size_t offset,const uint8_t * addr,bus_size_t len)474 frodo_bus_space_write_region_sparse_1(bus_space_tag_t bst,
475     bus_space_handle_t bsh, bus_size_t offset, const uint8_t *addr,
476     bus_size_t len)
477 {
478 
479           __asm volatile (
480           "         movl      %0,%%a0             ;\n"
481           "         movl      %1,%%a1             ;\n"
482           "         movl      %2,%%d0             ;\n"
483           "1:       movb      %%a1@+,%%a0@        ;\n"
484           "         addql     #4,%%a0             ;\n"
485           "         subql     #1,%%d0             ;\n"
486           "         jne       1b"
487               :
488               : "r" (bsh + (offset << 2)), "g" (addr), "g" (len)
489               : "%a0","%a1","%d0");
490 }
491 
492 static void
frodo_bus_space_set_multi_sparse_1(bus_space_tag_t bst,bus_space_handle_t bsh,bus_size_t offset,uint8_t val,bus_size_t count)493 frodo_bus_space_set_multi_sparse_1(bus_space_tag_t bst, bus_space_handle_t bsh,
494     bus_size_t offset, uint8_t val, bus_size_t count)
495 {
496           __asm volatile (
497           "         movl      %0,%%a0             ;\n"
498           "         movl      %1,%%d1             ;\n"
499           "         movl      %2,%%d0             ;\n"
500           "1:       movb      %%d1,%%a0@          ;\n"
501           "         subql     #1,%%d0             ;\n"
502           "         jne       1b"
503               :
504               : "r" (bsh + (offset << 2)), "g" (val), "g" (count)
505               : "%a0","%d0","%d1");
506 }
507 
508 static void
frodo_bus_space_set_region_sparse_1(bus_space_tag_t bst,bus_space_handle_t bsh,bus_size_t offset,uint8_t val,bus_size_t count)509 frodo_bus_space_set_region_sparse_1(bus_space_tag_t bst, bus_space_handle_t bsh,
510     bus_size_t offset, uint8_t val, bus_size_t count)
511 {
512 
513           __asm volatile (
514           "         movl      %0,%%a0             ;\n"
515           "         movl      %1,%%d1             ;\n"
516           "         movl      %2,%%d0             ;\n"
517           "1:       movb      %%d1,%%a0@          ;\n"
518           "         addql     #4,%%a0             ;\n"
519           "         subql     #1,%%d0             ;\n"
520           "         jne       1b"
521               :
522               : "r" (bsh + (offset << 2)), "g" (val), "g" (count)
523               : "%a0","%d0","%d1");
524 }
525