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