xref: /netbsd-src/sys/arch/i386/mca/mca_machdep.c (revision aaf4ece63a859a04e37cf3a7229b5fab0157cc06)
1 /*	$NetBSD: mca_machdep.c,v 1.27 2005/12/26 19:24:00 perry 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.27 2005/12/26 19:24:00 perry 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 #include <machine/bus.h>
61 #include <machine/bus_private.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 	uint16_t	count;
74 	uint8_t		model;
75 	uint8_t		submodel;
76 	uint8_t		bios_rev;
77 	uint8_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 	uint8_t		feature2;
87 	uint8_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(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(struct device *parent, struct device *self,
165     struct mcabus_attach_args *mba)
166 {
167 	dmaiot = mba->mba_iot;
168 
169 	if (bus_space_map(dmaiot, DMA_CMD, 1, 0, &dmacmdh)
170 	    || bus_space_map(dmaiot, DMA_EXEC, 1, 0, &dmaexech))
171 		panic("mca: couldn't map DMA registers");
172 }
173 
174 /*
175  * Read value of MCA POS register "reg" in slot "slot".
176  */
177 
178 int
179 mca_conf_read(mca_chipset_tag_t mc, int slot, int reg)
180 {
181 	int	data;
182 
183 	slot &= 7;	/* slot must be in range 0-7 */
184 	outb(MCA_MB_SETUP_REG, 0xff); /* ensure m/board setup is disabled */
185 	outb(MCA_ADAP_SETUP_REG, slot | MCA_ADAP_SET);
186 	data = inb(MCA_POS_REG(reg));
187 	outb(MCA_ADAP_SETUP_REG, 0);
188 	return data;
189 }
190 
191 
192 /*
193  * Write "data" to MCA POS register "reg" in slot "slot".
194  */
195 
196 void
197 mca_conf_write(mca_chipset_tag_t mc, int slot, int reg, int data)
198 {
199 	slot&=7;	/* slot must be in range 0-7 */
200 	outb(MCA_MB_SETUP_REG, 0xff); /* ensure m/board setup is disabled */
201 	outb(MCA_ADAP_SETUP_REG, slot | MCA_ADAP_SET);
202 	outb(MCA_POS_REG(reg), data);
203 	outb(MCA_ADAP_SETUP_REG, 0);
204 }
205 
206 #if NISA <= 0
207 #error mca_intr_(dis)establish: needs ISA to be configured into kernel
208 #endif
209 
210 #if 0
211 const struct evcnt *
212 mca_intr_establish(mca_chipset_tag_t mc, mca_intr_handle_t ih)
213 {
214 
215 	/* XXX for now, no evcnt parent reported */
216 	return NULL;
217 }
218 #endif
219 
220 void *
221 mca_intr_establish(mca_chipset_tag_t mc, mca_intr_handle_t ih,
222     int level, int (*func)(void *), void *arg)
223 {
224 	if (ih == 0 || ih >= NUM_LEGACY_IRQS || ih == 2)
225 		panic("mca_intr_establish: bogus handle 0x%x", ih);
226 
227 	/* MCA interrupts are always level-triggered */
228 	return isa_intr_establish(NULL, ih, IST_LEVEL, level, func, arg);
229 }
230 
231 void
232 mca_intr_disestablish(mca_chipset_tag_t mc, void *cookie)
233 {
234 	isa_intr_disestablish(NULL, cookie);
235 }
236 
237 
238 /*
239  * Handle a NMI.
240  * return true to panic system, false to ignore.
241  */
242 int
243 mca_nmi(void)
244 {
245 	/*
246 	* PS/2 MCA devices can generate NMIs - we can find out which
247 	* slot generated it from the POS registers.
248 	*/
249 
250 	int 	slot, mcanmi=0;
251 
252 	/* if there is no MCA bus, call x86_nmi() */
253 	if (!MCA_system)
254 		goto out;
255 
256 	/* ensure motherboard setup is disabled */
257 	outb(MCA_MB_SETUP_REG, 0xff);
258 
259 	/* find if an MCA slot has the CHCK bit asserted (low) in POS 5 */
260 	for(slot=0; slot<MCA_MAX_SLOTS; slot++) {
261 		outb(MCA_ADAP_SETUP_REG, slot | MCA_ADAP_SET);
262 		if ((inb(MCA_POS_REG(5)) & MCA_POS5_CHCK) == 0) {
263 			mcanmi = 1;
264 			/* find if CHCK status is available in POS 6/7 */
265 			if((inb(MCA_POS_REG(5)) & MCA_POS5_CHCK_STAT) == 0)
266 				log(LOG_CRIT, "MCA NMI: slot %d, POS6=0x%02x, POS7=0x%02x\n",
267 					slot+1, inb(MCA_POS_REG(6)),
268 						inb(MCA_POS_REG(7)));
269 			else
270 				log(LOG_CRIT, "MCA NMI: slot %d\n", slot+1);
271 		}
272 	}
273 	outb(MCA_ADAP_SETUP_REG, 0);
274 
275    out:
276 	if (!mcanmi) {
277 		/* no CHCK bits asserted, assume ISA NMI */
278 		return (x86_nmi());
279 	} else
280 		return(0);
281 }
282 
283 /*
284  * We can obtain the information about MCA bus presence via
285  * GET CONFIGURATION BIOS call - int 0x15, function 0xc0.
286  * The call returns a pointer to memory place with the configuration block
287  * in es:bx (on AT-compatible, e.g. all we care about, computers).
288  *
289  * Configuration block contains block length (2 bytes), model
290  * number (1 byte), submodel number (1 byte), BIOS revision
291  * (1 byte) and up to 5 feature bytes. We only care about
292  * first feature byte.
293  */
294 void
295 mca_busprobe(void)
296 {
297 	struct bioscallregs regs;
298 	struct bios_config *scp;
299 	paddr_t             paddr;
300 	char buf[80];
301 
302 	memset(&regs, 0, sizeof(regs));
303 	regs.AH = 0xc0;
304 	bioscall(0x15, &regs);
305 
306 	if ((regs.EFLAGS & PSL_C) || regs.AH != 0) {
307 #ifdef DEBUG
308 		printf("BIOS CFG: Not supported. Not AT-compatible?\n");
309 #endif
310 		return;
311 	}
312 
313 	paddr = (regs.ES << 4) + regs.BX;
314 	scp = (struct bios_config *)ISA_HOLE_VADDR(paddr);
315 
316 #if 1 /* MCAVERBOSE */
317 	bitmask_snprintf((scp->feature2 << 8) | scp->feature1,
318 		"\20"
319 		"\01MCA+ISA"
320 		"\02MCA"
321 		"\03EBDA"
322 		"\04WAITEV"
323 		"\05KBDINT"
324 		"\06RTC"
325 		"\07IC2"
326 		"\010DMA3B"
327 		"\011res"
328 		"\012DSTR"
329 		"\013n8042"
330 		"\014CPUF"
331 		"\015MMF"
332 		"\016GPDF"
333 		"\017KBDF"
334 		"\020DMA32\n",
335 		buf, sizeof(buf));
336 
337 	printf("BIOS CFG: Model-SubM-Rev: %02x-%02x-%02x, 0x%s\n",
338 		scp->model, scp->submodel, scp->bios_rev, buf);
339 #endif
340 
341 	MCA_system = (scp->feature1 & FEATURE_MCABUS) ? 1 : 0;
342 }
343 
344 #define PORT_DISKLED	0x92
345 #define DISKLED_ON	0x40
346 
347 /*
348  * Light disk busy LED on IBM PS/2.
349  */
350 void
351 mca_disk_busy(void)
352 {
353 	outb(PORT_DISKLED, inb(PORT_DISKLED) | DISKLED_ON);
354 }
355 
356 /*
357  * Turn off disk LED on IBM PS/2.
358  */
359 void
360 mca_disk_unbusy(void)
361 {
362 	outb(PORT_DISKLED, inb(PORT_DISKLED) & ~DISKLED_ON);
363 }
364 
365 /*
366  * -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
367  * MCA DMA specific stuff. We use ISA routines for bulk of the work,
368  * since MCA shares much of the charasteristics with it. We just hook
369  * the DMA channel initialization and kick MCA DMA controller appropriately.
370  * -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
371  */
372 
373 #ifdef UNUSED
374 /*
375  * Synchronize a MCA DMA map.
376  */
377 static void
378 _mca_bus_dmamap_sync(bus_dma_tag_t t, bus_dmamap_t map, bus_addr_t offset,
379     bus_size_t len, int ops)
380 {
381 	struct x86_isa_dma_cookie *cookie;
382 	bus_addr_t phys;
383 	bus_size_t cnt;
384 	int dmach, mode;
385 
386 	_bus_dmamap_sync(t, map, offset, len, ops);
387 
388 	/*
389 	 * Don't do anything if not using the DMA controller.
390 	 */
391 	if ((map->_dm_flags & _MCABUS_DMA_USEDMACTRL) == 0)
392 		return;
393 
394 	/*
395 	 * Don't do anything if not PRE* operation, allow only
396 	 * one of PREREAD and PREWRITE.
397 	 */
398 	if (ops != BUS_DMASYNC_PREREAD && ops != BUS_DMASYNC_PREWRITE)
399 		return;
400 
401 	cookie = (struct x86_isa_dma_cookie *)map->_dm_cookie;
402 	dmach = (cookie->id_flags & 0xf0) >> 4;
403 
404 	phys = map->dm_segs[0].ds_addr;
405 	cnt = map->dm_segs[0].ds_len;
406 
407 	mode = DMACMD_MODE_XFER;
408 	mode |= (ops == BUS_DMASYNC_PREREAD)
409 			? DMACMD_MODE_READ : DMACMD_MODE_WRITE;
410 	if (map->_dm_flags & MCABUS_DMA_IOPORT)
411 		mode |= DMACMD_MODE_IOPORT;
412 
413 	/* Use 16bit DMA if requested */
414 	if (map->_dm_flags & MCABUS_DMA_16BIT) {
415 #ifdef DIAGNOSTIC
416 		if ((cnt % 2) != 0) {
417 			panic("_mca_bus_dmamap_sync: 16bit DMA and cnt %lu odd",
418 				cnt);
419 		}
420 #endif
421 		mode |= DMACMD_MODE_16BIT;
422 		cnt /= 2;
423 	}
424 
425 	/*
426 	 * Initialize the MCA DMA controller appropriately. The exact
427 	 * sequence to setup the controller is taken from Minix.
428 	 */
429 
430 	/* Disable access to DMA channel. */
431 	bus_space_write_1(dmaiot, dmacmdh, 0, DMACMD_MASK | dmach);
432 
433 	/* Set the transfer mode. */
434 	bus_space_write_1(dmaiot, dmacmdh, 0, DMACMD_SET_MODE | dmach);
435 	bus_space_write_1(dmaiot, dmaexech, 0, mode);
436 
437 	/* Set the address byte pointer. */
438 	bus_space_write_1(dmaiot, dmacmdh, 0, DMACMD_SET_ADDR | dmach);
439 	/* address bits 0..7   */
440 	bus_space_write_1(dmaiot, dmaexech, 0, (phys >> 0) & 0xff);
441 	/* address bits 8..15  */
442 	bus_space_write_1(dmaiot, dmaexech, 0, (phys >> 8) & 0xff);
443 	/* address bits 16..23  */
444 	bus_space_write_1(dmaiot, dmaexech, 0, (phys >> 16) & 0xff);
445 
446 	/* Set the count byte pointer */
447 	bus_space_write_1(dmaiot, dmacmdh, 0, DMACMD_SET_CNT | dmach);
448 	/* count bits 0..7     */
449 	bus_space_write_1(dmaiot, dmaexech, 0, ((cnt - 1) >> 0) & 0xff);
450 	/* count bits 8..15    */
451 	bus_space_write_1(dmaiot, dmaexech, 0, ((cnt - 1) >> 8) & 0xff);
452 
453 	/* Enable access to DMA channel. */
454 	bus_space_write_1(dmaiot, dmacmdh, 0, DMACMD_RESET_MASK | dmach);
455 }
456 #endif
457 
458 /*
459  * Allocate a DMA map, and set up DMA channel.
460  */
461 int
462 mca_dmamap_create(bus_dma_tag_t t, bus_size_t size, int flags,
463     bus_dmamap_t *dmamp, int dmach)
464 {
465 	int error;
466 	struct x86_isa_dma_cookie *cookie;
467 
468 #ifdef DEBUG
469 	/* Sanity check */
470 	if (dmach < 0 || dmach >= 16) {
471 		printf("mcadma_create: invalid DMA channel %d\n",
472 			dmach);
473 		return (EINVAL);
474 	}
475 
476 	if (size > 65536) {
477 		panic("mca_dmamap_create: dmamap sz %ld > 65536",
478 		    (long) size);
479 	}
480 #endif
481 
482 	/*
483 	 * MCA DMA transfer can be maximum 65536 bytes long and must
484 	 * be in one chunk. No specific boundary constraints are present.
485 	 */
486 	if ((error = _bus_dmamap_create(t, size, 1, 65536, 0, flags, dmamp)))
487 		return (error);
488 
489 	cookie = (struct x86_isa_dma_cookie *) (*dmamp)->_dm_cookie;
490 
491 	if (cookie == NULL) {
492 		/*
493 		 * Allocate our cookie if not yet done.
494 		 */
495 		cookie = malloc(sizeof(struct x86_bus_dma_cookie), M_DMAMAP,
496 		    ((flags & BUS_DMA_NOWAIT) ? M_NOWAIT : M_WAITOK) | M_ZERO);
497 		if (cookie == NULL) {
498 
499 			return ENOMEM;
500 		}
501 		(*dmamp)->_dm_cookie = cookie;
502 	}
503 
504 
505 	/* Encode DMA channel */
506 	cookie->id_flags &= 0x0f;
507 	cookie->id_flags |= dmach << 4;
508 
509 	/* Mark the dmamap as using DMA controller. Some devices
510 	 * drive DMA themselves, and don't need the MCA DMA controller.
511 	 * To distinguish the two, use a flag for dmamaps which use the DMA
512 	 * controller.
513  	 */
514 	(*dmamp)->_dm_flags |= _MCABUS_DMA_USEDMACTRL;
515 
516 	return (0);
517 }
518 
519 /*
520  * Set I/O port for DMA. Implemented separately from _mca_bus_dmamap_sync()
521  * so that it's available for one-shot setup.
522  */
523 void
524 mca_dma_set_ioport(int dma, uint16_t port)
525 {
526 	/* Disable access to dma channel. */
527 	bus_space_write_1(dmaiot, dmacmdh, 0, DMACMD_MASK | dma);
528 
529 	/* Set I/O port to use for DMA */
530 	bus_space_write_1(dmaiot, dmacmdh, 0, DMACMD_SET_IO | dma);
531 	bus_space_write_1(dmaiot, dmaexech, 0, port & 0xff);
532 	bus_space_write_1(dmaiot, dmaexech, 0, (port >> 8) & 0xff);
533 
534 	/* Enable access to DMA channel. */
535 	bus_space_write_1(dmaiot, dmacmdh, 0, DMACMD_RESET_MASK | dma);
536 }
537