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