xref: /netbsd-src/sys/arch/i386/mca/mca_machdep.c (revision 23c8222edbfb0f0932d88a8351d3a0cf817dfb9e)
1 /*	$NetBSD: mca_machdep.c,v 1.23 2004/08/30 15:05:17 drochner Exp $	*/
2 
3 /*-
4  * Copyright (c) 2000, 2001 The NetBSD Foundation, Inc.
5  * Copyright (c) 1996-1999 Scott D. Telford.
6  * All rights reserved.
7  *
8  * This code is derived from software contributed to The NetBSD Foundation
9  * by Scott Telford <s.telford@ed.ac.uk> and Jaromir Dolecek
10  * <jdolecek@NetBSD.org>.
11  *
12  * Redistribution and use in source and binary forms, with or without
13  * modification, are permitted provided that the following conditions
14  * are met:
15  * 1. Redistributions of source code must retain the above copyright
16  *    notice, this list of conditions and the following disclaimer.
17  * 2. Redistributions in binary form must reproduce the above copyright
18  *    notice, this list of conditions and the following disclaimer in the
19  *    documentation and/or other materials provided with the distribution.
20  * 3. All advertising materials mentioning features or use of this software
21  *    must display the following acknowledgement:
22  *	This product includes software developed by the NetBSD
23  *	Foundation, Inc. and its contributors.
24  * 4. Neither the name of The NetBSD Foundation nor the names of its
25  *    contributors may be used to endorse or promote products derived
26  *    from this software without specific prior written permission.
27  *
28  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
29  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
30  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
31  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
32  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
33  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
34  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
35  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
36  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
37  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
38  * POSSIBILITY OF SUCH DAMAGE.
39  */
40 
41 /*
42  * Machine-specific functions for MCA autoconfiguration.
43  */
44 
45 #include <sys/cdefs.h>
46 __KERNEL_RCSID(0, "$NetBSD: mca_machdep.c,v 1.23 2004/08/30 15:05:17 drochner Exp $");
47 
48 #include <sys/types.h>
49 #include <sys/param.h>
50 #include <sys/device.h>
51 #include <sys/malloc.h>
52 #include <sys/systm.h>
53 #include <sys/syslog.h>
54 #include <sys/time.h>
55 #include <sys/kernel.h>
56 
57 #include <machine/bioscall.h>
58 #include <machine/psl.h>
59 
60 #define _X86_BUS_DMA_PRIVATE
61 #include <machine/bus.h>
62 
63 #include <dev/isa/isavar.h>
64 #include <dev/isa/isareg.h>
65 #include <dev/mca/mcavar.h>
66 #include <dev/mca/mcareg.h>
67 
68 #include "isa.h"
69 #include "opt_mcaverbose.h"
70 
71 /* System Configuration Block - this info is returned by the BIOS call */
72 struct bios_config {
73 	u_int16_t	count;
74 	u_int8_t	model;
75 	u_int8_t	submodel;
76 	u_int8_t	bios_rev;
77 	u_int8_t	feature1;
78 #define FEATURE_MCAISA	0x01	/* Machine contains both MCA and ISA bus */
79 #define FEATURE_MCABUS	0x02	/* Machine has MCA bus instead of ISA	*/
80 #define FEATURE_EBDA	0x04	/* Extended BIOS data area allocated	*/
81 #define FEATURE_WAITEV	0x08	/* Wait for external event is supported	*/
82 #define FEATURE_KBDINT	0x10	/* Keyboard intercept called by Int 09h	*/
83 #define FEATURE_RTC	0x20	/* Real-time clock present		*/
84 #define FEATURE_IC2	0x40	/* Second interrupt chip present	*/
85 #define FEATURE_DMA3	0x80	/* DMA channel 3 used by hard disk BIOS	*/
86 	u_int8_t	feature2;
87 	u_int8_t	pad[9];
88 } __attribute__ ((packed));
89 
90 /*
91  * Used to encode DMA channel into ISA DMA cookie. We use upper 4 bits of
92  * ISA DMA cookie id_flags, it's unused.
93  */
94 struct x86_isa_dma_cookie {
95 	int id_flags;
96 	/* We don't care about rest */
97 };
98 
99 #ifdef UNUSED
100 static void	_mca_bus_dmamap_sync __P((bus_dma_tag_t, bus_dmamap_t,
101 		    bus_addr_t, bus_size_t, int));
102 #endif
103 
104 /*
105  * For now, we use MCA DMA to 0-16M always. Some IBM PS/2 have 32bit MCA bus,
106  * but majority of them have 24bit only.
107  */
108 #define	MCA_DMA_BOUNCE_THRESHOLD	(16 * 1024 * 1024)
109 
110 struct x86_bus_dma_tag mca_bus_dma_tag = {
111 	MCA_DMA_BOUNCE_THRESHOLD,		/* _bounce_thresh */
112 	0,					/* _bounce_alloc_lo */
113 	MCA_DMA_BOUNCE_THRESHOLD,		/* _bounce_alloc_hi */
114 	NULL,					/* _may_bounce */
115 	_bus_dmamap_create,
116 	_bus_dmamap_destroy,
117 	_bus_dmamap_load,
118 	_bus_dmamap_load_mbuf,
119 	_bus_dmamap_load_uio,
120 	_bus_dmamap_load_raw,
121 	_bus_dmamap_unload,
122 	_bus_dmamap_sync,
123 	_bus_dmamem_alloc,
124 	_bus_dmamem_free,
125 	_bus_dmamem_map,
126 	_bus_dmamem_unmap,
127 	_bus_dmamem_mmap,
128 };
129 
130 /* Updated in mca_busprobe() if appropriate. */
131 int MCA_system = 0;
132 
133 /* Used to kick MCA DMA controller */
134 #define DMA_CMD		0x18		/* command the controller */
135 #define DMA_EXEC	0x1A		/* tell controller how to do things */
136 static bus_space_handle_t dmaiot, dmacmdh, dmaexech;
137 
138 /*
139  * MCA DMA controller commands. The exact sense of individual bits
140  * are from Tymm Twillman <tymm@computer.org>, who worked on Linux MCA DMA
141  * support.
142  */
143 #define DMACMD_SET_IO		0x00	/* set port (16bit) for i/o transfer */
144 #define DMACMD_SET_ADDR		0x20	/* set addr (24bit) for i/o transfer */
145 #define DMACMD_GET_ADDR		0x30	/* get addr (24bit) for i/o transfer */
146 #define	DMACMD_SET_CNT		0x40	/* set memory size for DMA (16b) */
147 #define DMACMD_GET_CNT		0x50	/* get count of remaining bytes in DMA*/
148 #define DMACMD_GET_STATUS	0x60	/* ?? */
149 #define DMACMD_SET_MODE		0x70	/* set DMA mode */
150 # define DMACMD_MODE_XFER	0x04	/* do transfer, read by default */
151 # define DMACMD_MODE_READ	0x08	/* read transfer */
152 # define DMACMD_MODE_WRITE	0x00	/* write transfer */
153 # define DMACMD_MODE_IOPORT	0x01	/* DMA from/to IO register */
154 # define DMACMD_MODE_16BIT	0x40	/* 16bit transfers (default 8bit) */
155 #define DMACMD_SET_ARBUS	0x80	/* ?? */
156 #define DMACMD_MASK		0x90	/* command mask */
157 #define DMACMD_RESET_MASK	0xA0	/* reset */
158 #define DMACMD_MASTER_CLEAR	0xD0	/* ?? */
159 
160 /*
161  * Map the MCA DMA controller registers.
162  */
163 void
164 mca_attach_hook(parent, self, mba)
165 	struct device *parent, *self;
166 	struct mcabus_attach_args *mba;
167 {
168 	dmaiot = mba->mba_iot;
169 
170 	if (bus_space_map(dmaiot, DMA_CMD, 1, 0, &dmacmdh)
171 	    || bus_space_map(dmaiot, DMA_EXEC, 1, 0, &dmaexech))
172 		panic("mca: couldn't map DMA registers");
173 }
174 
175 /*
176  * Read value of MCA POS register "reg" in slot "slot".
177  */
178 
179 int
180 mca_conf_read(mc, slot, reg)
181 	mca_chipset_tag_t mc;
182 	int slot, reg;
183 {
184 	int	data;
185 
186 	slot &= 7;	/* slot must be in range 0-7 */
187 	outb(MCA_MB_SETUP_REG, 0xff); /* ensure m/board setup is disabled */
188 	outb(MCA_ADAP_SETUP_REG, slot | MCA_ADAP_SET);
189 	data = inb(MCA_POS_REG(reg));
190 	outb(MCA_ADAP_SETUP_REG, 0);
191 	return data;
192 }
193 
194 
195 /*
196  * Write "data" to MCA POS register "reg" in slot "slot".
197  */
198 
199 void
200 mca_conf_write(mc, slot, reg, data)
201 	mca_chipset_tag_t mc;
202 	int slot, reg, data;
203 {
204 	slot&=7;	/* slot must be in range 0-7 */
205 	outb(MCA_MB_SETUP_REG, 0xff); /* ensure m/board setup is disabled */
206 	outb(MCA_ADAP_SETUP_REG, slot | MCA_ADAP_SET);
207 	outb(MCA_POS_REG(reg), data);
208 	outb(MCA_ADAP_SETUP_REG, 0);
209 }
210 
211 #if NISA <= 0
212 #error mca_intr_(dis)establish: needs ISA to be configured into kernel
213 #endif
214 
215 #if 0
216 const struct evcnt *
217 mca_intr_establish(mca_chipset_tag_t mc, mca_intr_handle_t ih)
218 {
219 
220 	/* XXX for now, no evcnt parent reported */
221 	return NULL;
222 }
223 #endif
224 
225 void *
226 mca_intr_establish(mc, ih, level, func, arg)
227 	mca_chipset_tag_t mc;
228 	mca_intr_handle_t ih;
229 	int level, (*func) __P((void *));
230 	void *arg;
231 {
232 	if (ih == 0 || ih >= NUM_LEGACY_IRQS || ih == 2)
233 		panic("mca_intr_establish: bogus handle 0x%x", ih);
234 
235 	/* MCA interrupts are always level-triggered */
236 	return isa_intr_establish(NULL, ih, IST_LEVEL, level, func, arg);
237 }
238 
239 void
240 mca_intr_disestablish(mc, cookie)
241 	mca_chipset_tag_t mc;
242 	void *cookie;
243 {
244 	isa_intr_disestablish(NULL, cookie);
245 }
246 
247 
248 /*
249  * Handle a NMI.
250  * return true to panic system, false to ignore.
251  */
252 int
253 mca_nmi()
254 {
255 	/*
256 	* PS/2 MCA devices can generate NMIs - we can find out which
257 	* slot generated it from the POS registers.
258 	*/
259 
260 	int 	slot, mcanmi=0;
261 
262 	/* if there is no MCA bus, call x86_nmi() */
263 	if (!MCA_system)
264 		goto out;
265 
266 	/* ensure motherboard setup is disabled */
267 	outb(MCA_MB_SETUP_REG, 0xff);
268 
269 	/* find if an MCA slot has the CHCK bit asserted (low) in POS 5 */
270 	for(slot=0; slot<MCA_MAX_SLOTS; slot++) {
271 		outb(MCA_ADAP_SETUP_REG, slot | MCA_ADAP_SET);
272 		if ((inb(MCA_POS_REG(5)) & MCA_POS5_CHCK) == 0) {
273 			mcanmi = 1;
274 			/* find if CHCK status is available in POS 6/7 */
275 			if((inb(MCA_POS_REG(5)) & MCA_POS5_CHCK_STAT) == 0)
276 				log(LOG_CRIT, "MCA NMI: slot %d, POS6=0x%02x, POS7=0x%02x\n",
277 					slot+1, inb(MCA_POS_REG(6)),
278 						inb(MCA_POS_REG(7)));
279 			else
280 				log(LOG_CRIT, "MCA NMI: slot %d\n", slot+1);
281 		}
282 	}
283 	outb(MCA_ADAP_SETUP_REG, 0);
284 
285    out:
286 	if (!mcanmi) {
287 		/* no CHCK bits asserted, assume ISA NMI */
288 		return (x86_nmi());
289 	} else
290 		return(0);
291 }
292 
293 /*
294  * We can obtain the information about MCA bus presence via
295  * GET CONFIGURATION BIOS call - int 0x15, function 0xc0.
296  * The call returns a pointer to memory place with the configuration block
297  * in es:bx (on AT-compatible, e.g. all we care about, computers).
298  *
299  * Configuration block contains block length (2 bytes), model
300  * number (1 byte), submodel number (1 byte), BIOS revision
301  * (1 byte) and up to 5 feature bytes. We only care about
302  * first feature byte.
303  */
304 void
305 mca_busprobe()
306 {
307 	struct bioscallregs regs;
308 	struct bios_config *scp;
309 	paddr_t             paddr;
310 	char buf[80];
311 
312 	memset(&regs, 0, sizeof(regs));
313 	regs.AH = 0xc0;
314 	bioscall(0x15, &regs);
315 
316 	if ((regs.EFLAGS & PSL_C) || regs.AH != 0) {
317 #ifdef DEBUG
318 		printf("BIOS CFG: Not supported. Not AT-compatible?\n");
319 #endif
320 		return;
321 	}
322 
323 	paddr = (regs.ES << 4) + regs.BX;
324 	scp = (struct bios_config *)ISA_HOLE_VADDR(paddr);
325 
326 #if 1 /* MCAVERBOSE */
327 	bitmask_snprintf((scp->feature2 << 8) | scp->feature1,
328 		"\20"
329 		"\01MCA+ISA"
330 		"\02MCA"
331 		"\03EBDA"
332 		"\04WAITEV"
333 		"\05KBDINT"
334 		"\06RTC"
335 		"\07IC2"
336 		"\010DMA3B"
337 		"\011res"
338 		"\012DSTR"
339 		"\013n8042"
340 		"\014CPUF"
341 		"\015MMF"
342 		"\016GPDF"
343 		"\017KBDF"
344 		"\020DMA32\n",
345 		buf, sizeof(buf));
346 
347 	printf("BIOS CFG: Model-SubM-Rev: %02x-%02x-%02x, 0x%s\n",
348 		scp->model, scp->submodel, scp->bios_rev, buf);
349 #endif
350 
351 	MCA_system = (scp->feature1 & FEATURE_MCABUS) ? 1 : 0;
352 }
353 
354 #define PORT_DISKLED	0x92
355 #define DISKLED_ON	0x40
356 
357 /*
358  * Light disk busy LED on IBM PS/2.
359  */
360 void
361 mca_disk_busy(void)
362 {
363 	outb(PORT_DISKLED, inb(PORT_DISKLED) | DISKLED_ON);
364 }
365 
366 /*
367  * Turn off disk LED on IBM PS/2.
368  */
369 void
370 mca_disk_unbusy(void)
371 {
372 	outb(PORT_DISKLED, inb(PORT_DISKLED) & ~DISKLED_ON);
373 }
374 
375 /*
376  * -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
377  * MCA DMA specific stuff. We use ISA routines for bulk of the work,
378  * since MCA shares much of the charasteristics with it. We just hook
379  * the DMA channel initialization and kick MCA DMA controller appropriately.
380  * -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
381  */
382 
383 #ifdef UNUSED
384 /*
385  * Synchronize a MCA DMA map.
386  */
387 static void
388 _mca_bus_dmamap_sync(t, map, offset, len, ops)
389 	bus_dma_tag_t t;
390 	bus_dmamap_t map;
391 	bus_addr_t offset;
392 	bus_size_t len;
393 	int ops;
394 {
395 	struct x86_isa_dma_cookie *cookie;
396 	bus_addr_t phys;
397 	bus_size_t cnt;
398 	int dmach, mode;
399 
400 	_bus_dmamap_sync(t, map, offset, len, ops);
401 
402 	/*
403 	 * Don't do anything if not using the DMA controller.
404 	 */
405 	if ((map->_dm_flags & _MCABUS_DMA_USEDMACTRL) == 0)
406 		return;
407 
408 	/*
409 	 * Don't do anything if not PRE* operation, allow only
410 	 * one of PREREAD and PREWRITE.
411 	 */
412 	if (ops != BUS_DMASYNC_PREREAD && ops != BUS_DMASYNC_PREWRITE)
413 		return;
414 
415 	cookie = (struct x86_isa_dma_cookie *)map->_dm_cookie;
416 	dmach = (cookie->id_flags & 0xf0) >> 4;
417 
418 	phys = map->dm_segs[0].ds_addr;
419 	cnt = map->dm_segs[0].ds_len;
420 
421 	mode = DMACMD_MODE_XFER;
422 	mode |= (ops == BUS_DMASYNC_PREREAD)
423 			? DMACMD_MODE_READ : DMACMD_MODE_WRITE;
424 	if (map->_dm_flags & MCABUS_DMA_IOPORT)
425 		mode |= DMACMD_MODE_IOPORT;
426 
427 	/* Use 16bit DMA if requested */
428 	if (map->_dm_flags & MCABUS_DMA_16BIT) {
429 #ifdef DIAGNOSTIC
430 		if ((cnt % 2) != 0) {
431 			panic("_mca_bus_dmamap_sync: 16bit DMA and cnt %lu odd",
432 				cnt);
433 		}
434 #endif
435 		mode |= DMACMD_MODE_16BIT;
436 		cnt /= 2;
437 	}
438 
439 	/*
440 	 * Initialize the MCA DMA controller appropriately. The exact
441 	 * sequence to setup the controller is taken from Minix.
442 	 */
443 
444 	/* Disable access to DMA channel. */
445 	bus_space_write_1(dmaiot, dmacmdh, 0, DMACMD_MASK | dmach);
446 
447 	/* Set the transfer mode. */
448 	bus_space_write_1(dmaiot, dmacmdh, 0, DMACMD_SET_MODE | dmach);
449 	bus_space_write_1(dmaiot, dmaexech, 0, mode);
450 
451 	/* Set the address byte pointer. */
452 	bus_space_write_1(dmaiot, dmacmdh, 0, DMACMD_SET_ADDR | dmach);
453 	/* address bits 0..7   */
454 	bus_space_write_1(dmaiot, dmaexech, 0, (phys >> 0) & 0xff);
455 	/* address bits 8..15  */
456 	bus_space_write_1(dmaiot, dmaexech, 0, (phys >> 8) & 0xff);
457 	/* address bits 16..23  */
458 	bus_space_write_1(dmaiot, dmaexech, 0, (phys >> 16) & 0xff);
459 
460 	/* Set the count byte pointer */
461 	bus_space_write_1(dmaiot, dmacmdh, 0, DMACMD_SET_CNT | dmach);
462 	/* count bits 0..7     */
463 	bus_space_write_1(dmaiot, dmaexech, 0, ((cnt - 1) >> 0) & 0xff);
464 	/* count bits 8..15    */
465 	bus_space_write_1(dmaiot, dmaexech, 0, ((cnt - 1) >> 8) & 0xff);
466 
467 	/* Enable access to DMA channel. */
468 	bus_space_write_1(dmaiot, dmacmdh, 0, DMACMD_RESET_MASK | dmach);
469 }
470 #endif
471 
472 /*
473  * Allocate a DMA map, and set up DMA channel.
474  */
475 int
476 mca_dmamap_create(t, size, flags, dmamp, dmach)
477 	bus_dma_tag_t t;
478 	bus_size_t size;
479 	int flags;
480 	bus_dmamap_t *dmamp;
481 	int dmach;
482 {
483 	int error;
484 	struct x86_isa_dma_cookie *cookie;
485 
486 #ifdef DEBUG
487 	/* Sanity check */
488 	if (dmach < 0 || dmach >= 16) {
489 		printf("mcadma_create: invalid DMA channel %d\n",
490 			dmach);
491 		return (EINVAL);
492 	}
493 
494 	if (size > 65536) {
495 		panic("mca_dmamap_create: dmamap sz %ld > 65536",
496 		    (long) size);
497 	}
498 #endif
499 
500 	/*
501 	 * MCA DMA transfer can be maximum 65536 bytes long and must
502 	 * be in one chunk. No specific boundary constraints are present.
503 	 */
504 	if ((error = _bus_dmamap_create(t, size, 1, 65536, 0, flags, dmamp)))
505 		return (error);
506 
507 	cookie = (struct x86_isa_dma_cookie *) (*dmamp)->_dm_cookie;
508 
509 	if (cookie == NULL) {
510 		/*
511 		 * Allocate our cookie if not yet done.
512 		 */
513 		cookie = malloc(sizeof(struct x86_bus_dma_cookie), M_DMAMAP,
514 		    ((flags & BUS_DMA_NOWAIT) ? M_NOWAIT : M_WAITOK) | M_ZERO);
515 		if (cookie == NULL) {
516 
517 			return ENOMEM;
518 		}
519 		(*dmamp)->_dm_cookie = cookie;
520 	}
521 
522 
523 	/* Encode DMA channel */
524 	cookie->id_flags &= 0x0f;
525 	cookie->id_flags |= dmach << 4;
526 
527 	/* Mark the dmamap as using DMA controller. Some devices
528 	 * drive DMA themselves, and don't need the MCA DMA controller.
529 	 * To distinguish the two, use a flag for dmamaps which use the DMA
530 	 * controller.
531  	 */
532 	(*dmamp)->_dm_flags |= _MCABUS_DMA_USEDMACTRL;
533 
534 	return (0);
535 }
536 
537 /*
538  * Set I/O port for DMA. Implemented separately from _mca_bus_dmamap_sync()
539  * so that it's available for one-shot setup.
540  */
541 void
542 mca_dma_set_ioport(dma, port)
543 	int dma;
544 	u_int16_t port;
545 {
546 	/* Disable access to dma channel. */
547 	bus_space_write_1(dmaiot, dmacmdh, 0, DMACMD_MASK | dma);
548 
549 	/* Set I/O port to use for DMA */
550 	bus_space_write_1(dmaiot, dmacmdh, 0, DMACMD_SET_IO | dma);
551 	bus_space_write_1(dmaiot, dmaexech, 0, port & 0xff);
552 	bus_space_write_1(dmaiot, dmaexech, 0, (port >> 8) & 0xff);
553 
554 	/* Enable access to DMA channel. */
555 	bus_space_write_1(dmaiot, dmacmdh, 0, DMACMD_RESET_MASK | dma);
556 }
557