xref: /netbsd-src/sys/arch/i386/mca/mca_machdep.c (revision 8b0f9554ff8762542c4defc4f70e1eb76fb508fa)
1 /*	$NetBSD: mca_machdep.c,v 1.34 2007/12/01 16:49:56 ad 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.34 2007/12/01 16:49:56 ad 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 		aprint_verbose("BIOS CFG: Not supported. Not AT-compatible?\n");
311 		return;
312 	}
313 
314 	paddr = (regs.ES << 4) + regs.BX;
315 	scp = (struct bios_config *)ISA_HOLE_VADDR(paddr);
316 
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 	aprint_verbose("BIOS CFG: Model-SubM-Rev: %02x-%02x-%02x, 0x%s\n",
338 		scp->model, scp->submodel, scp->bios_rev, buf);
339 
340 	MCA_system = (scp->feature1 & FEATURE_MCABUS) ? 1 : 0;
341 }
342 
343 #define PORT_DISKLED	0x92
344 #define DISKLED_ON	0x40
345 
346 /*
347  * Light disk busy LED on IBM PS/2.
348  */
349 void
350 mca_disk_busy(void)
351 {
352 	outb(PORT_DISKLED, inb(PORT_DISKLED) | DISKLED_ON);
353 }
354 
355 /*
356  * Turn off disk LED on IBM PS/2.
357  */
358 void
359 mca_disk_unbusy(void)
360 {
361 	outb(PORT_DISKLED, inb(PORT_DISKLED) & ~DISKLED_ON);
362 }
363 
364 /*
365  * -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
366  * MCA DMA specific stuff. We use ISA routines for bulk of the work,
367  * since MCA shares much of the charasteristics with it. We just hook
368  * the DMA channel initialization and kick MCA DMA controller appropriately.
369  * -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
370  */
371 
372 #ifdef UNUSED
373 /*
374  * Synchronize a MCA DMA map.
375  */
376 static void
377 _mca_bus_dmamap_sync(bus_dma_tag_t t, bus_dmamap_t map, bus_addr_t offset,
378     bus_size_t len, int ops)
379 {
380 	struct x86_isa_dma_cookie *cookie;
381 	bus_addr_t phys;
382 	bus_size_t cnt;
383 	int dmach, mode;
384 
385 	_bus_dmamap_sync(t, map, offset, len, ops);
386 
387 	/*
388 	 * Don't do anything if not using the DMA controller.
389 	 */
390 	if ((map->_dm_flags & _MCABUS_DMA_USEDMACTRL) == 0)
391 		return;
392 
393 	/*
394 	 * Don't do anything if not PRE* operation, allow only
395 	 * one of PREREAD and PREWRITE.
396 	 */
397 	if (ops != BUS_DMASYNC_PREREAD && ops != BUS_DMASYNC_PREWRITE)
398 		return;
399 
400 	cookie = (struct x86_isa_dma_cookie *)map->_dm_cookie;
401 	dmach = (cookie->id_flags & 0xf0) >> 4;
402 
403 	phys = map->dm_segs[0].ds_addr;
404 	cnt = map->dm_segs[0].ds_len;
405 
406 	mode = DMACMD_MODE_XFER;
407 	mode |= (ops == BUS_DMASYNC_PREREAD)
408 			? DMACMD_MODE_READ : DMACMD_MODE_WRITE;
409 	if (map->_dm_flags & MCABUS_DMA_IOPORT)
410 		mode |= DMACMD_MODE_IOPORT;
411 
412 	/* Use 16bit DMA if requested */
413 	if (map->_dm_flags & MCABUS_DMA_16BIT) {
414 #ifdef DIAGNOSTIC
415 		if ((cnt % 2) != 0) {
416 			panic("_mca_bus_dmamap_sync: 16bit DMA and cnt %lu odd",
417 				cnt);
418 		}
419 #endif
420 		mode |= DMACMD_MODE_16BIT;
421 		cnt /= 2;
422 	}
423 
424 	/*
425 	 * Initialize the MCA DMA controller appropriately. The exact
426 	 * sequence to setup the controller is taken from Minix.
427 	 */
428 
429 	/* Disable access to DMA channel. */
430 	bus_space_write_1(dmaiot, dmacmdh, 0, DMACMD_MASK | dmach);
431 
432 	/* Set the transfer mode. */
433 	bus_space_write_1(dmaiot, dmacmdh, 0, DMACMD_SET_MODE | dmach);
434 	bus_space_write_1(dmaiot, dmaexech, 0, mode);
435 
436 	/* Set the address byte pointer. */
437 	bus_space_write_1(dmaiot, dmacmdh, 0, DMACMD_SET_ADDR | dmach);
438 	/* address bits 0..7   */
439 	bus_space_write_1(dmaiot, dmaexech, 0, (phys >> 0) & 0xff);
440 	/* address bits 8..15  */
441 	bus_space_write_1(dmaiot, dmaexech, 0, (phys >> 8) & 0xff);
442 	/* address bits 16..23  */
443 	bus_space_write_1(dmaiot, dmaexech, 0, (phys >> 16) & 0xff);
444 
445 	/* Set the count byte pointer */
446 	bus_space_write_1(dmaiot, dmacmdh, 0, DMACMD_SET_CNT | dmach);
447 	/* count bits 0..7     */
448 	bus_space_write_1(dmaiot, dmaexech, 0, ((cnt - 1) >> 0) & 0xff);
449 	/* count bits 8..15    */
450 	bus_space_write_1(dmaiot, dmaexech, 0, ((cnt - 1) >> 8) & 0xff);
451 
452 	/* Enable access to DMA channel. */
453 	bus_space_write_1(dmaiot, dmacmdh, 0, DMACMD_RESET_MASK | dmach);
454 }
455 #endif
456 
457 /*
458  * Allocate a DMA map, and set up DMA channel.
459  */
460 int
461 mca_dmamap_create(bus_dma_tag_t t, bus_size_t size, int flags,
462     bus_dmamap_t *dmamp, int dmach)
463 {
464 	int error;
465 	struct x86_isa_dma_cookie *cookie;
466 
467 #ifdef DEBUG
468 	/* Sanity check */
469 	if (dmach < 0 || dmach >= 16) {
470 		printf("mcadma_create: invalid DMA channel %d\n",
471 			dmach);
472 		return (EINVAL);
473 	}
474 
475 	if (size > 65536) {
476 		panic("mca_dmamap_create: dmamap sz %ld > 65536",
477 		    (long) size);
478 	}
479 #endif
480 
481 	/*
482 	 * MCA DMA transfer can be maximum 65536 bytes long and must
483 	 * be in one chunk. No specific boundary constraints are present.
484 	 */
485 	if ((error = _bus_dmamap_create(t, size, 1, 65536, 0, flags, dmamp)))
486 		return (error);
487 
488 	cookie = (struct x86_isa_dma_cookie *) (*dmamp)->_dm_cookie;
489 
490 	if (cookie == NULL) {
491 		/*
492 		 * Allocate our cookie if not yet done.
493 		 */
494 		cookie = malloc(sizeof(struct x86_bus_dma_cookie), M_DMAMAP,
495 		    ((flags & BUS_DMA_NOWAIT) ? M_NOWAIT : M_WAITOK) | M_ZERO);
496 		if (cookie == NULL) {
497 
498 			return ENOMEM;
499 		}
500 		(*dmamp)->_dm_cookie = cookie;
501 	}
502 
503 
504 	/* Encode DMA channel */
505 	cookie->id_flags &= 0x0f;
506 	cookie->id_flags |= dmach << 4;
507 
508 	/* Mark the dmamap as using DMA controller. Some devices
509 	 * drive DMA themselves, and don't need the MCA DMA controller.
510 	 * To distinguish the two, use a flag for dmamaps which use the DMA
511 	 * controller.
512  	 */
513 	(*dmamp)->_dm_flags |= _MCABUS_DMA_USEDMACTRL;
514 
515 	return (0);
516 }
517 
518 /*
519  * Set I/O port for DMA. Implemented separately from _mca_bus_dmamap_sync()
520  * so that it's available for one-shot setup.
521  */
522 void
523 mca_dma_set_ioport(int dma, uint16_t port)
524 {
525 	/* Disable access to dma channel. */
526 	bus_space_write_1(dmaiot, dmacmdh, 0, DMACMD_MASK | dma);
527 
528 	/* Set I/O port to use for DMA */
529 	bus_space_write_1(dmaiot, dmacmdh, 0, DMACMD_SET_IO | dma);
530 	bus_space_write_1(dmaiot, dmaexech, 0, port & 0xff);
531 	bus_space_write_1(dmaiot, dmaexech, 0, (port >> 8) & 0xff);
532 
533 	/* Enable access to DMA channel. */
534 	bus_space_write_1(dmaiot, dmacmdh, 0, DMACMD_RESET_MASK | dma);
535 }
536