vme_pcc.c revision 1.6.24.2 1 /* $NetBSD: vme_pcc.c,v 1.6.24.2 2000/03/13 19:09:03 scw Exp $ */
2
3 /*-
4 * Copyright (c) 1996-2000 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 and Steve C. Woodford.
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 * 3. All advertising materials mentioning features or use of this software
19 * must display the following acknowledgement:
20 * This product includes software developed by the NetBSD
21 * Foundation, Inc. and its contributors.
22 * 4. Neither the name of The NetBSD Foundation nor the names of its
23 * contributors may be used to endorse or promote products derived
24 * from this software without specific prior written permission.
25 *
26 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
27 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
28 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
29 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
30 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
31 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
32 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
33 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
34 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
35 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
36 * POSSIBILITY OF SUCH DAMAGE.
37 */
38
39 /*
40 * VME support specific to the Type 1 VMEchip found on the
41 * MVME-147.
42 *
43 * For a manual on the MVME-147, call: 408.991.8634. (Yes, this
44 * is the Sunnyvale sales office.)
45 */
46
47 #include <sys/param.h>
48 #include <sys/kernel.h>
49 #include <sys/systm.h>
50 #include <sys/device.h>
51 #include <sys/malloc.h>
52 #include <sys/kcore.h>
53
54 #include <machine/cpu.h>
55 #include <machine/bus.h>
56
57 #include <dev/vme/vmereg.h>
58 #include <dev/vme/vmevar.h>
59
60 #include <mvme68k/dev/pccvar.h>
61 #include <mvme68k/dev/vme_pccreg.h>
62 #include <mvme68k/dev/vme_pccvar.h>
63
64
65 static int vme_pcc_match __P((struct device *, struct cfdata *, void *));
66 static void vme_pcc_attach __P((struct device *, struct device *, void *));
67
68 struct cfattach vmepcc_ca = {
69 sizeof(struct vme_pcc_softc), vme_pcc_match, vme_pcc_attach
70 };
71
72 extern struct cfdriver vmepcc_cd;
73
74 extern phys_ram_seg_t mem_clusters[];
75 static int vme_pcc_attached;
76
77 const char *_vme_mod_string __P((vme_addr_t, vme_size_t,
78 vme_am_t, vme_datasize_t));
79
80 /*
81 * Describe the VMEbus ranges available from the MVME147
82 */
83 struct vme_pcc_range {
84 vme_am_t pr_am; /* Address Modifier for this range */
85 vme_datasize_t pr_datasize; /* Usable Data Sizes (D8, D16, D32) */
86 vme_addr_t pr_start; /* Local-bus start address of range */
87 vme_addr_t pr_end; /* Local-bus end address of range */
88 };
89
90 static struct vme_pcc_range vme_pcc_ranges[] = {
91 {VME_AM_A24, VME_D32|VME_D16|VME_D8, VME1_A24D32_START, VME1_A24D32_END},
92 {VME_AM_A32, VME_D32|VME_D16|VME_D8, VME1_A32D32_START, VME1_A32D32_END},
93 {VME_AM_A24, VME_D16|VME_D8, VME1_A24D16_START, VME1_A24D16_END},
94 {VME_AM_A32, VME_D16|VME_D8, VME1_A32D16_START, VME1_A32D16_END},
95 {VME_AM_A16, VME_D16|VME_D8, VME1_A16D16_START, VME1_A16D16_END}
96 };
97 #define VME1_NRANGES (sizeof(vme_pcc_ranges)/sizeof(struct vme_pcc_range))
98
99
100 static int
101 vme_pcc_match(parent, cf, aux)
102 struct device *parent;
103 struct cfdata *cf;
104 void *aux;
105 {
106 struct pcc_attach_args *pa = aux;
107
108 /* Only one VME chip, please. */
109 if (vme_pcc_attached)
110 return (0);
111
112 if (strcmp(pa->pa_name, vmepcc_cd.cd_name))
113 return (0);
114
115 return (1);
116 }
117
118 static void
119 vme_pcc_attach(parent, self, aux)
120 struct device *parent, *self;
121 void *aux;
122 {
123 struct pcc_attach_args *pa;
124 struct vme_pcc_softc *sc;
125 struct vmebus_attach_args vaa;
126 u_int8_t reg;
127 char pbuf[9];
128
129 sc = (struct vme_pcc_softc *) self;
130 pa = (struct pcc_attach_args *) aux;
131
132 sc->sc_dmat = pa->pa_dmat;
133 sc->sc_bust = pa->pa_bust;
134 sc->sc_vmet = MVME68K_VME_BUS_SPACE;
135
136 bus_space_map(sc->sc_bust, pa->pa_offset, VME1REG_SIZE,0, &sc->sc_bush);
137
138 /* Initialize the chip. */
139 reg = vme1_reg_read(sc, VME1REG_SCON) & ~VME1_SCON_SYSFAIL;
140 vme1_reg_write(sc, VME1REG_SCON, reg);
141
142 printf(": Type 1 VMEchip, scon jumper %s\n",
143 (reg & VME1_SCON_SWITCH) ? "enabled" : "disabled");
144 format_bytes(pbuf, sizeof(pbuf), VMEIOMAPSIZE);
145 printf("%s: VMEbus map size: %s\n", sc->sc_dev.dv_xname, pbuf);
146
147 sc->sc_vct.cookie = self;
148 sc->sc_vct.vct_probe = _vme_pcc_probe;
149 sc->sc_vct.vct_map = _vme_pcc_map;
150 sc->sc_vct.vct_unmap = _vme_pcc_unmap;
151 sc->sc_vct.vct_int_map = _vme_pcc_intmap;
152 sc->sc_vct.vct_int_establish = _vme_pcc_intr_establish;
153 sc->sc_vct.vct_int_disestablish = _vme_pcc_intr_disestablish;
154 sc->sc_vct.vct_dmamap_create = _vme_pcc_dmamap_create;
155 sc->sc_vct.vct_dmamap_destroy = _vme_pcc_dmamap_destroy;
156 sc->sc_vct.vct_dmamem_alloc = _vme_pcc_dmamem_alloc;
157 sc->sc_vct.vct_dmamem_free = _vme_pcc_dmamem_free;
158
159 /*
160 * Adjust the start address of the first range in vme_pcc_ranges[]
161 * according to how much onboard memory exists. Disable the first
162 * range if onboard memory >= 16Mb, and adjust the start of the
163 * second range (A32D32).
164 */
165 vme_pcc_ranges[0].pr_start = (vme_addr_t) mem_clusters[0].size;
166 if ( mem_clusters[0].size >= 0x01000000 ) {
167 vme_pcc_ranges[0].pr_am = (vme_am_t) -1;
168 vme_pcc_ranges[1].pr_start +=
169 (vme_addr_t) (mem_clusters[0].size - 0x01000000);
170 }
171
172 /*
173 * We keep A16 space permanently mapped into KVA to avoid
174 * problems when drivers try to bus_space_map() register ranges
175 * which are closer than NBPG to each other (highly likely in
176 * A16 address space).
177 * The _vme_bus_map() function re-uses this mapping to satisfy
178 * requests to map A16 ranges.
179 */
180 if ( bus_space_map(sc->sc_vmet, VME1_A16D16_START, 0x10000,
181 0, &sc->sc_a16bush) < 0 ) {
182 panic("vme_pcc_attach: failed to map A16 VMEbus space");
183 /* NOTREACHED */
184 }
185
186 vaa.va_vct = &(sc->sc_vct);
187 vaa.va_bdt = sc->sc_dmat;
188 vaa.va_slaveconfig = NULL;
189
190 vme_pcc_attached = 1;
191
192 /* Attach the MI VMEbus glue. */
193 config_found(self, &vaa, 0);
194 }
195
196 int
197 _vme_pcc_map(vsc, vmeaddr, len, am, datasize, swap, tag, handle, resc)
198 void *vsc;
199 vme_addr_t vmeaddr;
200 vme_size_t len;
201 vme_am_t am;
202 vme_datasize_t datasize;
203 vme_swap_t swap;
204 bus_space_tag_t *tag;
205 bus_space_handle_t *handle;
206 vme_mapresc_t *resc;
207 {
208 struct vme_pcc_softc *sc;
209 struct vme_pcc_mapresc_t *pm;
210 struct vme_pcc_range *pr;
211 vme_addr_t end, mask;
212 paddr_t paddr;
213 int rv;
214 int i;
215
216 sc = (struct vme_pcc_softc *) vsc;
217
218 end = (vmeaddr + len) - 1;
219 mask = 0;
220 paddr = 0;
221
222 switch ( am & VME_AM_ADRSIZEMASK ) {
223 case VME_AM_A32:
224 mask = 0xffffffffu;
225 break;
226
227 case VME_AM_A24:
228 mask = 0x00ffffffu;
229 break;
230
231 case VME_AM_A16:
232 mask = 0x0000ffffu;
233 break;
234
235 case VME_AM_USERDEF:
236 printf("%s: User-defined address modifiers not supported\n",
237 sc->sc_dev.dv_xname);
238 return EINVAL;
239 }
240
241 for (i = 0, pr = &vme_pcc_ranges[0]; i < VME1_NRANGES; i++, pr++) {
242 /* Ignore if range is disabled */
243 if ( pr->pr_am == (vme_addr_t) -1 )
244 continue;
245
246 /*
247 * Accept the range if it matches the constraints
248 */
249 if ( (am & VME_AM_ADRSIZEMASK) == pr->pr_am &&
250 datasize <= pr->pr_datasize &&
251 vmeaddr >= (pr->pr_start & mask) &&
252 end <= (pr->pr_end & mask) ) {
253 /*
254 * We have a match.
255 */
256 paddr = pr->pr_start + vmeaddr;
257 break;
258 }
259 }
260
261 if ( paddr == 0 ) {
262 #ifdef DIAGNOSTIC
263 printf("%s: Unable to map %s\n", sc->sc_dev.dv_xname,
264 _vme_mod_string(vmeaddr, len, am, datasize));
265 #endif
266 return ENOMEM;
267 }
268
269 if ( (am & VME_AM_ADRSIZEMASK) == VME_AM_A16 )
270 bus_space_subregion(sc->sc_vmet, sc->sc_a16bush,
271 vmeaddr, len, handle);
272 else
273 if ( (rv = bus_space_map(sc->sc_vmet, paddr, len, 0, handle)) != 0 )
274 return rv;
275
276 if ( (pm = malloc(sizeof(*pm), M_DEVBUF, M_NOWAIT)) == NULL ) {
277 if ( (am & VME_AM_ADRSIZEMASK) != VME_AM_A16 )
278 bus_space_unmap(sc->sc_vmet, *handle, len);
279 return ENOMEM;
280 }
281
282 *tag = sc->sc_vmet;
283 pm->pm_am = am;
284 pm->pm_datasize = datasize;
285 pm->pm_addr = vmeaddr;
286 pm->pm_size = len;
287 pm->pm_handle = *handle;
288 *resc = (vme_mapresc_t *) pm;
289
290 return 0;
291 }
292
293 void
294 _vme_pcc_unmap(vsc, resc)
295 void *vsc;
296 vme_mapresc_t resc;
297 {
298 struct vme_pcc_softc *sc;
299 struct vme_pcc_mapresc_t *pm;
300
301 sc = (struct vme_pcc_softc *) vsc;
302 pm = (struct vme_pcc_mapresc_t *) resc;
303
304 if ( (pm->pm_am & VME_AM_ADRSIZEMASK) != VME_AM_A16 )
305 bus_space_unmap(sc->sc_vmet, pm->pm_handle, pm->pm_size);
306
307 free(pm, M_DEVBUF);
308 }
309
310 int
311 _vme_pcc_probe(vsc, vmeaddr, len, am, datasize, callback, arg)
312 void *vsc;
313 vme_addr_t vmeaddr;
314 vme_size_t len;
315 vme_am_t am;
316 vme_datasize_t datasize;
317 int (*callback) __P((void *, bus_space_tag_t, bus_space_handle_t));
318 void *arg;
319 {
320 bus_space_tag_t tag;
321 bus_space_handle_t handle;
322 vme_mapresc_t resc;
323 int rv;
324
325 rv = _vme_pcc_map(vsc, vmeaddr, len, am, datasize,
326 0, &tag, &handle, &resc);
327 if ( rv )
328 return rv;
329
330 if ( callback )
331 rv = (*callback)(arg, tag, handle);
332 else {
333 /*
334 * FIXME: datasize is fixed by hardware, so using badaddr() in
335 * this way may cause several accesses to each VMEbus address.
336 * Also, using 'handle' in this way is a bit presumptuous...
337 */
338 rv = badaddr((caddr_t) handle, (int) len) ? EIO : 0;
339 }
340
341 _vme_pcc_unmap(vsc, resc);
342
343 return rv;
344 }
345
346 int
347 _vme_pcc_intmap(vsc, level, vector, handlep)
348 void *vsc;
349 int level, vector;
350 vme_intr_handle_t *handlep;
351 {
352 if ( level < 0x80 )
353 return EINVAL;
354
355 /* This is rather gross */
356 *handlep = (void *)(int)((level << 8) | vector);
357
358 return 0;
359 }
360
361 void *
362 _vme_pcc_intr_establish(vsc, handle, prior, func, arg)
363 void *vsc;
364 vme_intr_handle_t handle;
365 int prior;
366 int (*func) __P((void *));
367 void *arg;
368 {
369 struct vme_pcc_softc *sc;
370 int level, vector;
371
372 sc = (struct vme_pcc_softc *) vsc;
373 level = ((int)handle) >> 8;
374 vector = ((int)handle) & 0xff;
375
376 isrlink_vectored(func, arg, level, vector);
377 sc->sc_irqref[level]++;
378
379 /*
380 * There had better not be another VMEbus master responding
381 * to this interrupt level...
382 */
383 vme1_reg_write(sc, VME1REG_IRQEN,
384 vme1_reg_read(sc, VME1REG_IRQEN) | VME1_IRQ_VME(level));
385 }
386
387 void
388 _vme_pcc_intr_disestablish(vsc, handle)
389 void *vsc;
390 vme_intr_handle_t handle;
391 {
392 struct vme_pcc_softc *sc;
393 int level, vector;
394
395 sc = (struct vme_pcc_softc *) vsc;
396 level = ((int)handle) >> 8;
397 vector = ((int)handle) & 0xff;
398
399 isrunlink_vectored(vector);
400
401 /* Disable VME IRQ if possible. */
402 switch (sc->sc_irqref[level]) {
403 case 0:
404 printf("vme_pcc_intr_disestablish: nothing using IRQ %d\n",
405 level);
406 panic("vme_pcc_intr_disestablish");
407 /* NOTREACHED */
408
409 case 1:
410 vme1_reg_write(sc, VME1REG_IRQEN,
411 vme1_reg_read(sc, VME1REG_IRQEN) & ~VME1_IRQ_VME(level));
412 /* FALLTHROUGH */
413
414 default:
415 sc->sc_irqref[level]--;
416 }
417 }
418
419 int
420 _vme_pcc_dmamap_create(vsc, len, am, datasize, swap, nsegs,
421 segsz, bound, flags, mapp)
422 void *vsc;
423 vme_size_t len;
424 vme_am_t am;
425 vme_datasize_t datasize;
426 vme_swap_t swap;
427 int nsegs;
428 vme_size_t segsz;
429 vme_addr_t bound;
430 int flags;
431 bus_dmamap_t *mapp;
432 {
433 return (EINVAL);
434 }
435
436 void
437 _vme_pcc_dmamap_destroy(vsc, map)
438 void *vsc;
439 bus_dmamap_t map;
440 {
441 }
442
443 int
444 _vme_pcc_dmamem_alloc(vsc, len, am, datasizes, swap,
445 segs, nsegs, rsegs, flags)
446 void *vsc;
447 vme_size_t len;
448 vme_am_t am;
449 vme_datasize_t datasizes;
450 vme_swap_t swap;
451 bus_dma_segment_t *segs;
452 int nsegs;
453 int *rsegs;
454 int flags;
455 {
456 return (EINVAL);
457 }
458
459 void
460 _vme_pcc_dmamem_free(vsc, segs, nsegs)
461 void *vsc;
462 bus_dma_segment_t *segs;
463 int nsegs;
464 {
465 }
466
467 const char *
468 _vme_mod_string(addr, len, am, ds)
469 vme_addr_t addr;
470 vme_size_t len;
471 vme_am_t am;
472 vme_datasize_t ds;
473 {
474 static const char *mode[] = {"BLT64)", "DATA)", "PROG)", "BLT32)"};
475 static char mstring[40];
476 static char mdata[10];
477 char *fmt;
478
479 mdata[0] = '\0';
480 if ( ds & VME_D32 )
481 strcat(mdata, "D32");
482 if ( ds & VME_D16 )
483 strcat(mdata, mdata[0] == '\0' ? "D16" : "|D16");
484 if ( ds & VME_D8 )
485 strcat(mdata, mdata[0] == '\0' ? "D8" : "|D8");
486
487 switch ( am & VME_AM_ADRSIZEMASK ) {
488 case VME_AM_A32:
489 fmt = "A24%s:%08x-%08x";
490 break;
491
492 case VME_AM_A24:
493 fmt = "A24%s:%06x-%06x";
494 break;
495
496 case VME_AM_A16:
497 fmt = "A16%s:%04x-%04x";
498 break;
499
500 case VME_AM_USERDEF:
501 fmt = "USR%s:%08x-%08x";
502 break;
503 }
504
505 sprintf(mstring, fmt, mdata, addr, addr + len - 1);
506 strcat(mstring, ((am & VME_AM_PRIVMASK) == VME_AM_USER) ?
507 " (USER," : " (SUPER,");
508 strcat(mstring, mode[am & VME_AM_MODEMASK]);
509
510 return (mstring);
511 }
512