1 /*        $NetBSD: dio.c,v 1.43 2024/01/16 07:06:59 thorpej Exp $     */
2 
3 /*-
4  * Copyright (c) 1996, 1997, 1998 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  * Autoconfiguration and mapping support for the DIO bus.
34  */
35 
36 #include <sys/cdefs.h>
37 __KERNEL_RCSID(0, "$NetBSD: dio.c,v 1.43 2024/01/16 07:06:59 thorpej Exp $");
38 
39 #define   _M68K_INTR_PRIVATE
40 
41 #include <sys/param.h>
42 #include <sys/systm.h>
43 #include <sys/kernel.h>
44 #include <sys/device.h>
45 
46 #include <machine/bus.h>
47 
48 #include <uvm/uvm_extern.h>
49 
50 #include <machine/autoconf.h>
51 #include <machine/cpu.h>
52 #include <machine/hp300spu.h>
53 
54 #include <hp300/dev/dmavar.h>
55 
56 #include <hp300/dev/dioreg.h>
57 #include <hp300/dev/diovar.h>
58 
59 #include <hp300/dev/diodevs.h>
60 #include <hp300/dev/diodevs_data.h>
61 
62 #include "locators.h"
63 #define        diocf_scode             cf_loc[DIOCF_SCODE]
64 
65 struct dio_softc {
66           device_t sc_dev;
67           struct bus_space_tag sc_tag;
68 };
69 
70 static int          dio_scodesize(struct dio_attach_args *);
71 static const char *dio_devinfo(struct dio_attach_args *, char *, size_t);
72 
73 static int          diomatch(device_t, cfdata_t, void *);
74 static void         dioattach(device_t, device_t, void *);
75 static int          dioprint(void *, const char *);
76 static int          diosubmatch(device_t, cfdata_t, const int *, void *);
77 
78 CFATTACH_DECL_NEW(dio, sizeof(struct dio_softc),
79     diomatch, dioattach, NULL, NULL);
80 
81 static LIST_HEAD(, hp300_intrhand) dio_dma_users =
82     LIST_HEAD_INITIALIZER(dio_dma_users);
83 
84 static int
diomatch(device_t parent,cfdata_t cf,void * aux)85 diomatch(device_t parent, cfdata_t cf, void *aux)
86 {
87           static int dio_matched = 0;
88 
89           /* Allow only one instance. */
90           if (dio_matched)
91                     return 0;
92 
93           dio_matched = 1;
94           return 1;
95 }
96 
97 static void
dioattach(device_t parent,device_t self,void * aux)98 dioattach(device_t parent, device_t self, void *aux)
99 {
100           struct dio_softc *sc = device_private(self);
101           struct dio_attach_args da;
102           bus_addr_t pa;
103           void *va;
104           bus_space_tag_t bst = &sc->sc_tag;
105           bus_space_handle_t bsh;
106           int scode, scmax, scodesize;
107 
108           sc->sc_dev = self;
109           aprint_normal("\n");
110 
111           memset(bst, 0, sizeof(struct bus_space_tag));
112           bst->bustype = HP300_BUS_SPACE_DIO;
113 
114           scmax = DIO_SCMAX(machineid);
115 
116           for (scode = 0; scode < scmax; ) {
117                     if (DIO_INHOLE(scode)) {
118                               scode++;
119                               continue;
120                     }
121 
122                     /*
123                      * Temporarily map the space corresponding to
124                      * the current select code unless:
125                      */
126                     pa = (bus_addr_t)dio_scodetopa(scode);
127                     if (bus_space_map(bst, pa, PAGE_SIZE, 0, &bsh)) {
128                               aprint_error_dev(self, "can't map scode %d\n", scode);
129                               scode++;
130                               continue;
131                     }
132                     va = bus_space_vaddr(bst, bsh);
133 
134                     /* Check for hardware. */
135                     if (badaddr(va)) {
136                               bus_space_unmap(bst, bsh, PAGE_SIZE);
137                               scode++;
138                               continue;
139                     }
140 
141                     /* Fill out attach args. */
142                     memset(&da, 0, sizeof(da));
143                     da.da_bst = bst;
144                     da.da_scode = scode;
145 
146                     da.da_id = DIO_ID(va);
147                     if (DIO_ISFRAMEBUFFER(da.da_id))
148                               da.da_secid = DIO_SECID(va);
149                     da.da_addr = pa;
150                     da.da_size = DIO_SIZE(scode, va);
151                     scodesize = dio_scodesize(&da);
152                     if (DIO_ISDIO(scode))
153                               da.da_size *= scodesize;
154                     da.da_ipl = DIO_IPL(va);
155 
156                     /* No longer need the device to be mapped. */
157                     bus_space_unmap(bst, bsh, PAGE_SIZE);
158 
159                     /* Attach matching device. */
160                     config_found(self, &da, dioprint,
161                         CFARGS(.submatch = diosubmatch));
162                     scode += scodesize;
163           }
164 }
165 
166 static int
diosubmatch(device_t parent,cfdata_t cf,const int * ldesc,void * aux)167 diosubmatch(device_t parent, cfdata_t cf, const int *ldesc, void *aux)
168 {
169           struct dio_attach_args *da = aux;
170 
171           if (cf->diocf_scode != DIOCF_SCODE_DEFAULT &&
172               cf->diocf_scode != da->da_scode)
173                     return 0;
174 
175           return config_match(parent, cf, aux);
176 }
177 
178 static int
dioprint(void * aux,const char * pnp)179 dioprint(void *aux, const char *pnp)
180 {
181           struct dio_attach_args *da = aux;
182           char buf[128];
183 
184           if (pnp)
185                     aprint_normal("%s at %s",
186                         dio_devinfo(da, buf, sizeof(buf)), pnp);
187           aprint_normal(" scode %d ipl %d", da->da_scode, da->da_ipl);
188           return UNCONF;
189 }
190 
191 /*
192  * Convert a select code to a system physical address.
193  */
194 void *
dio_scodetopa(int scode)195 dio_scodetopa(int scode)
196 {
197           u_long rval;
198 
199           if (DIO_ISDIO(scode))
200                     rval = DIO_BASE + (scode * DIO_DEVSIZE);
201           else if (DIO_ISDIOII(scode))
202                     rval = DIOII_BASE + ((scode - DIOII_SCBASE) * DIOII_DEVSIZE);
203           else
204                     rval = 0;
205 
206           return (void *)rval;
207 }
208 
209 /*
210  * Return the select code size for this device, defaulting to 1
211  * if we don't know what kind of device we have.
212  */
213 static int
dio_scodesize(struct dio_attach_args * da)214 dio_scodesize(struct dio_attach_args *da)
215 {
216           int i;
217 
218           /*
219            * Find the dio_devdata matchind the primary id.
220            * If we're a framebuffer, we also check the secondary id.
221            */
222           for (i = 0; i < DIO_NDEVICES; i++) {
223                     if (da->da_id == dio_devdatas[i].dd_id) {
224                               if (DIO_ISFRAMEBUFFER(da->da_id)) {
225                                         if (da->da_secid == dio_devdatas[i].dd_secid) {
226                                                   goto foundit;
227                                         }
228                               } else {
229  foundit:
230                                         return dio_devdatas[i].dd_nscode;
231                               }
232                     }
233           }
234 
235           /*
236            * Device is unknown.  Print a warning and assume a default.
237            */
238           aprint_error("WARNING: select code size unknown "
239               "for id = 0x%x secid = 0x%x\n",
240               da->da_id, da->da_secid);
241           return 1;
242 }
243 
244 /*
245  * Return a reasonable description of a DIO device.
246  */
247 static const char *
dio_devinfo(struct dio_attach_args * da,char * buf,size_t buflen)248 dio_devinfo(struct dio_attach_args *da, char *buf, size_t buflen)
249 {
250 #ifdef DIOVERBOSE
251           int i;
252 #endif
253 
254           memset(buf, 0, buflen);
255 
256 #ifdef DIOVERBOSE
257           /*
258            * Find the description matching our primary id.
259            * If we're a framebuffer, we also check the secondary id.
260            */
261           for (i = 0; i < DIO_NDEVICES; i++) {
262                     if (da->da_id == dio_devdescs[i].dd_id) {
263                               if (DIO_ISFRAMEBUFFER(da->da_id)) {
264                                         if (da->da_secid == dio_devdescs[i].dd_secid) {
265                                                   goto foundit;
266                                         }
267                               } else {
268                               foundit:
269                                         snprintf(buf, buflen, "%s",
270                                             dio_devdescs[i].dd_desc);
271                                         return buf;
272                               }
273                     }
274           }
275 #endif /* DIOVERBOSE */
276 
277           /*
278            * Device is unknown.  Construct something reasonable.
279            */
280           snprintf(buf, buflen, "device id = 0x%x secid = 0x%x",
281               da->da_id, da->da_secid);
282           return buf;
283 }
284 
285 /*
286  * Enumerate the list of DMA controller users (the HPIB and SCSI
287  * controllers, essentially), and calculate what the highest
288  * auto-vector level used by those devices is.  Then tell the DMA
289  * controller to interrupt at that level.
290  */
291 static void
dio_dma_users_changed(void)292 dio_dma_users_changed(void)
293 {
294           struct hp300_intrhand *ih;
295           int ipl = 0;
296 
297           LIST_FOREACH(ih, &dio_dma_users, ih_dio_link) {
298                     KASSERT(ih->ih_super.ih_isrpri == ISRPRI_BIO);
299                     if (ih->ih_super.ih_ipl > ipl) {
300                               ipl = ih->ih_super.ih_ipl;
301                     }
302           }
303           dmaupdateipl(ipl);
304 }
305 
306 /*
307  * Establish an interrupt handler for a DIO device.
308  */
309 void *
dio_intr_establish(int (* func)(void *),void * arg,int ipl,int isrpri)310 dio_intr_establish(int (*func)(void *), void *arg, int ipl, int isrpri)
311 {
312           struct hp300_intrhand *ih;
313 
314           ih = intr_establish(func, arg, ipl, isrpri);
315 
316           if (ih != NULL && isrpri == ISRPRI_BIO) {
317                     LIST_INSERT_HEAD(&dio_dma_users, ih, ih_dio_link);
318                     dio_dma_users_changed();
319           }
320 
321           return ih;
322 }
323 
324 /*
325  * Remove an interrupt handler for a DIO device.
326  */
327 void
dio_intr_disestablish(void * arg)328 dio_intr_disestablish(void *arg)
329 {
330           struct hp300_intrhand *ih = arg;
331           int isrpri = ih->ih_super.ih_isrpri;
332 
333           if (isrpri == ISRPRI_BIO) {
334                     LIST_REMOVE(ih, ih_dio_link);
335           }
336 
337           intr_disestablish(arg);
338 
339           if (isrpri == ISRPRI_BIO) {
340                     dio_dma_users_changed();
341           }
342 }
343 
344 /*
345  * DIO specific bus_space(9) support functions.
346  */
347 static uint8_t dio_bus_space_read_oddbyte_1(bus_space_tag_t,
348     bus_space_handle_t, bus_size_t);
349 static void dio_bus_space_write_oddbyte_1(bus_space_tag_t,
350     bus_space_handle_t, bus_size_t, uint8_t);
351 
352 static void dio_bus_space_read_multi_oddbyte_1(bus_space_tag_t,
353     bus_space_handle_t, bus_size_t, uint8_t *, bus_size_t);
354 static void dio_bus_space_write_multi_oddbyte_1(bus_space_tag_t,
355     bus_space_handle_t, bus_size_t, const uint8_t *, bus_size_t);
356 
357 static void dio_bus_space_read_region_oddbyte_1(bus_space_tag_t,
358     bus_space_handle_t, bus_size_t, uint8_t *, bus_size_t);
359 static void dio_bus_space_write_region_oddbyte_1(bus_space_tag_t,
360     bus_space_handle_t, bus_size_t, const uint8_t *, bus_size_t);
361 
362 static void dio_bus_space_set_multi_oddbyte_1(bus_space_tag_t,
363     bus_space_handle_t, bus_size_t, uint8_t, bus_size_t);
364 
365 static void dio_bus_space_set_region_oddbyte_1(bus_space_tag_t,
366     bus_space_handle_t, bus_size_t, uint8_t, bus_size_t);
367 
368 /*
369  * dio_set_bus_space_oddbyte():
370  *        Override bus_space functions in bus_space_tag_t
371  *        for devices which have odd byte address space.
372  */
373 void
dio_set_bus_space_oddbyte(bus_space_tag_t bst)374 dio_set_bus_space_oddbyte(bus_space_tag_t bst)
375 {
376 
377           /* XXX only 1-byte functions for now */
378           bst->bsr1 = dio_bus_space_read_oddbyte_1;
379           bst->bsw1 = dio_bus_space_write_oddbyte_1;
380 
381           bst->bsrm1 = dio_bus_space_read_multi_oddbyte_1;
382           bst->bswm1 = dio_bus_space_write_multi_oddbyte_1;
383 
384           bst->bsrr1 = dio_bus_space_read_region_oddbyte_1;
385           bst->bswr1 = dio_bus_space_write_region_oddbyte_1;
386 
387           bst->bssm1 = dio_bus_space_set_multi_oddbyte_1;
388 
389           bst->bssr1 = dio_bus_space_set_region_oddbyte_1;
390 }
391 
392 static uint8_t
dio_bus_space_read_oddbyte_1(bus_space_tag_t bst,bus_space_handle_t bsh,bus_size_t offset)393 dio_bus_space_read_oddbyte_1(bus_space_tag_t bst, bus_space_handle_t bsh,
394     bus_size_t offset)
395 {
396 
397           return *(volatile uint8_t *)(bsh + (offset << 1) + 1);
398 }
399 
400 static void
dio_bus_space_write_oddbyte_1(bus_space_tag_t bst,bus_space_handle_t bsh,bus_size_t offset,uint8_t val)401 dio_bus_space_write_oddbyte_1(bus_space_tag_t bst, bus_space_handle_t bsh,
402     bus_size_t offset, uint8_t val)
403 {
404 
405           *(volatile uint8_t *)(bsh + (offset << 1) + 1) = val;
406 }
407 
408 static void
dio_bus_space_read_multi_oddbyte_1(bus_space_tag_t bst,bus_space_handle_t bsh,bus_size_t offset,uint8_t * addr,bus_size_t len)409 dio_bus_space_read_multi_oddbyte_1(bus_space_tag_t bst, bus_space_handle_t bsh,
410     bus_size_t offset, uint8_t *addr, bus_size_t len)
411 {
412 
413           __asm volatile (
414           "         movl      %0,%%a0             ;\n"
415           "         movl      %1,%%a1             ;\n"
416           "         movl      %2,%%d0             ;\n"
417           "1:       movb      %%a0@,%%a1@+        ;\n"
418           "         subql     #1,%%d0             ;\n"
419           "         jne       1b"
420               :
421               : "r" (bsh + (offset << 1) + 1), "g" (addr), "g" (len)
422               : "%a0","%a1","%d0");
423 }
424 
425 static void
dio_bus_space_write_multi_oddbyte_1(bus_space_tag_t bst,bus_space_handle_t bsh,bus_size_t offset,const uint8_t * addr,bus_size_t len)426 dio_bus_space_write_multi_oddbyte_1(bus_space_tag_t bst, bus_space_handle_t bsh,
427     bus_size_t offset, const uint8_t *addr, bus_size_t len)
428 {
429 
430           __asm volatile (
431           "         movl      %0,%%a0             ;\n"
432           "         movl      %1,%%a1             ;\n"
433           "         movl      %2,%%d0             ;\n"
434           "1:       movb      %%a1@+,%%a0@        ;\n"
435           "         subql     #1,%%d0             ;\n"
436           "         jne       1b"
437               :
438               : "r" (bsh + (offset << 1) + 1), "g" (addr), "g" (len)
439               : "%a0","%a1","%d0");
440 }
441 
442 static void
dio_bus_space_read_region_oddbyte_1(bus_space_tag_t bst,bus_space_handle_t bsh,bus_size_t offset,uint8_t * addr,bus_size_t len)443 dio_bus_space_read_region_oddbyte_1(bus_space_tag_t bst, bus_space_handle_t bsh,
444     bus_size_t offset, uint8_t *addr, bus_size_t len)
445 {
446           __asm volatile (
447           "         movl      %0,%%a0             ;\n"
448           "         movl      %1,%%a1             ;\n"
449           "         movl      %2,%%d0             ;\n"
450           "1:       movb      %%a0@,%%a1@+        ;\n"
451           "         addql     #2,%%a0             ;\n"
452           "         subql     #1,%%d0             ;\n"
453           "         jne       1b"
454               :
455               : "r" (bsh + (offset << 1) + 1), "g" (addr), "g" (len)
456               : "%a0","%a1","%d0");
457 }
458 
459 static void
dio_bus_space_write_region_oddbyte_1(bus_space_tag_t bst,bus_space_handle_t bsh,bus_size_t offset,const uint8_t * addr,bus_size_t len)460 dio_bus_space_write_region_oddbyte_1(bus_space_tag_t bst,
461     bus_space_handle_t bsh, bus_size_t offset, const uint8_t *addr,
462     bus_size_t len)
463 {
464 
465           __asm volatile (
466           "         movl      %0,%%a0             ;\n"
467           "         movl      %1,%%a1             ;\n"
468           "         movl      %2,%%d0             ;\n"
469           "1:       movb      %%a1@+,%%a0@        ;\n"
470           "         addql     #2,%%a0             ;\n"
471           "         subql     #1,%%d0             ;\n"
472           "         jne       1b"
473               :
474               : "r" (bsh + (offset << 1) + 1), "g" (addr), "g" (len)
475               : "%a0","%a1","%d0");
476 }
477 
478 static void
dio_bus_space_set_multi_oddbyte_1(bus_space_tag_t bst,bus_space_handle_t bsh,bus_size_t offset,uint8_t val,bus_size_t count)479 dio_bus_space_set_multi_oddbyte_1(bus_space_tag_t bst, bus_space_handle_t bsh,
480     bus_size_t offset, uint8_t val, bus_size_t count)
481 {
482           __asm volatile (
483           "         movl      %0,%%a0             ;\n"
484           "         movl      %1,%%d1             ;\n"
485           "         movl      %2,%%d0             ;\n"
486           "1:       movb      %%d1,%%a0@          ;\n"
487           "         subql     #1,%%d0             ;\n"
488           "         jne       1b"
489               :
490               : "r" (bsh + (offset << 1) + 1), "g" (val), "g" (count)
491               : "%a0","%d0","%d1");
492 }
493 
494 static void
dio_bus_space_set_region_oddbyte_1(bus_space_tag_t bst,bus_space_handle_t bsh,bus_size_t offset,uint8_t val,bus_size_t count)495 dio_bus_space_set_region_oddbyte_1(bus_space_tag_t bst, bus_space_handle_t bsh,
496     bus_size_t offset, uint8_t val, bus_size_t count)
497 {
498 
499           __asm volatile (
500           "         movl      %0,%%a0             ;\n"
501           "         movl      %1,%%d1             ;\n"
502           "         movl      %2,%%d0             ;\n"
503           "1:       movb      %%d1,%%a0@          ;\n"
504           "         addql     #2,%%a0             ;\n"
505           "         subql     #1,%%d0             ;\n"
506           "         jne       1b"
507               :
508               : "r" (bsh + (offset << 1) + 1), "g" (val), "g" (count)
509               : "%a0","%d0","%d1");
510 }
511