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