1 /* $NetBSD: mca_machdep.c,v 1.2 2008/04/28 20:23:34 martin 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.2 2008/04/28 20:23:34 martin 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 <powerpc/pio.h> 51 #define _POWERPC_BUS_DMA_PRIVATE 52 #include <machine/bus.h> 53 54 #include <dev/mca/mcavar.h> 55 #include <dev/mca/mcareg.h> 56 57 #include "opt_mcaverbose.h" 58 59 #ifdef UNUSED 60 static void _mca_bus_dmamap_sync(bus_dma_tag_t, bus_dmamap_t, 61 bus_addr_t, bus_size_t, int); 62 #endif 63 64 /* 65 * For now, we use MCA DMA to 0-16M always. Some IBM PS/2 have 32bit MCA bus, 66 * but majority of them have 24bit only. 67 */ 68 #define MCA_DMA_BOUNCE_THRESHOLD (16 * 1024 * 1024) 69 70 /* Updated in mca_busprobe() if appropriate. */ 71 int MCA_system = 0; 72 73 //static bus_space_handle_t dmaiot, dmacmdh, dmaexech; 74 75 #define MAX_SLAVE_CHANNELS 8 76 #define MAX_DMA_CHANNELS 16 77 78 #define INIT_DMA_CHN_BITMASK() (0xFFFFFFFF << (32 - MAX_DMA_CHANS)) 79 #define INIT_SLAVE_CHN_BITMASK(slaves) (0xFFFFFFFF << (32 - (slaves)) 80 81 #define DMA_AVAIL(chn, bitmask) ((bitmask) & (1 << (31 - (chn)))) 82 #define DMA_ALLOC(chn, bitmask) ((bitmask) &= ~(1 << (31 - (chn)))) 83 #define DMA_FREE(chn, bitmask) ((bitmask) |= (1 << (31 - (chn)))) 84 85 /* 86 * MCA DMA controller commands. The exact sense of individual bits 87 * are from Tymm Twillman <tymm@computer.org>, who worked on Linux MCA DMA 88 * support. 89 */ 90 #define DMACMD_SET_IO 0x00 /* set port (16bit) for i/o transfer */ 91 #define DMACMD_SET_ADDR 0x20 /* set addr (24bit) for i/o transfer */ 92 #define DMACMD_GET_ADDR 0x30 /* get addr (24bit) for i/o transfer */ 93 #define DMACMD_SET_CNT 0x40 /* set memory size for DMA (16b) */ 94 #define DMACMD_GET_CNT 0x50 /* get count of remaining bytes in DMA*/ 95 #define DMACMD_GET_STATUS 0x60 /* ?? */ 96 #define DMACMD_SET_MODE 0x70 /* set DMA mode */ 97 # define DMACMD_MODE_XFER 0x04 /* do transfer, read by default */ 98 # define DMACMD_MODE_READ 0x08 /* read transfer */ 99 # define DMACMD_MODE_WRITE 0x00 /* write transfer */ 100 # define DMACMD_MODE_IOPORT 0x01 /* DMA from/to IO register */ 101 # define DMACMD_MODE_16BIT 0x40 /* 16bit transfers (default 8bit) */ 102 #define DMACMD_SET_ARBUS 0x80 /* ?? */ 103 #define DMACMD_MASK 0x90 /* command mask */ 104 #define DMACMD_RESET_MASK 0xA0 /* reset */ 105 #define DMACMD_MASTER_CLEAR 0xD0 /* ?? */ 106 107 const struct evcnt * 108 mca_intr_evcnt(mca_chipset_tag_t ic, int irq) 109 { 110 /* XXX for now, no evcnt parent reported */ 111 return NULL; 112 } 113 114 /* 115 * Map the MCA DMA controller registers. 116 */ 117 void 118 mca_attach_hook(struct device *parent, struct device *self, 119 struct mcabus_attach_args *mba) 120 { 121 #if 0 122 dmaiot = mba->mba_iot; 123 124 if (bus_space_map(dmaiot, DMA_CMD, 1, 0, &dmacmdh) 125 || bus_space_map(dmaiot, DMA_EXEC, 1, 0, &dmaexech)) 126 panic("mca: couldn't map DMA registers"); 127 #endif 128 } 129 130 /* 131 * Read value of MCA POS register "reg" in slot "slot". 132 */ 133 134 int 135 mca_conf_read(mca_chipset_tag_t mc, int slot, int reg) 136 { 137 int data; 138 139 slot &= 15; /* slot must be in range 0-15 */ 140 data = inb(RS6000_BUS_SPACE_IO + MCA_POS_REG(reg) + (slot<<16)); 141 return data; 142 } 143 144 145 /* 146 * Write "data" to MCA POS register "reg" in slot "slot". 147 */ 148 149 void 150 mca_conf_write(mca_chipset_tag_t mc, int slot, int reg, int data) 151 { 152 slot &= 15; /* slot must be in range 0-15 */ 153 outb(RS6000_BUS_SPACE_IO + MCA_POS_REG(reg) + (slot<<16), data); 154 } 155 156 157 void * 158 mca_intr_establish(mca_chipset_tag_t mc, mca_intr_handle_t ih, 159 int level, int (*func)(void *), void *arg) 160 { 161 if (ih == 0 || ih >= ICU_LEN) 162 panic("mca_intr_establish: bogus handle 0x%x", ih); 163 164 /* MCA interrupts are always level-triggered */ 165 return intr_establish(ih, IST_LEVEL, level, func, arg); 166 } 167 168 void 169 mca_intr_disestablish(mca_chipset_tag_t mc, void *cookie) 170 { 171 intr_disestablish(cookie); 172 } 173 174 175 /* 176 * Handle a NMI. 177 * return true to panic system, false to ignore. 178 */ 179 int 180 mca_nmi(void) 181 { 182 /* 183 * PS/2 MCA devices can generate NMIs - we can find out which 184 * slot generated it from the POS registers. 185 */ 186 187 int slot, mcanmi=0; 188 189 /* if there is no MCA bus, call x86_nmi() */ 190 if (!MCA_system) 191 goto out; 192 193 /* ensure motherboard setup is disabled */ 194 outb(MCA_MB_SETUP_REG, 0xff); 195 196 /* find if an MCA slot has the CHCK bit asserted (low) in POS 5 */ 197 for(slot=0; slot<MCA_MAX_SLOTS; slot++) { 198 outb(MCA_ADAP_SETUP_REG, slot | MCA_ADAP_SET); 199 if ((inb(MCA_POS_REG(5)) & MCA_POS5_CHCK) == 0) { 200 mcanmi = 1; 201 /* find if CHCK status is available in POS 6/7 */ 202 if((inb(MCA_POS_REG(5)) & MCA_POS5_CHCK_STAT) == 0) 203 log(LOG_CRIT, "MCA NMI: slot %d, POS6=0x%02x, POS7=0x%02x\n", 204 slot+1, inb(MCA_POS_REG(6)), 205 inb(MCA_POS_REG(7))); 206 else 207 log(LOG_CRIT, "MCA NMI: slot %d\n", slot+1); 208 } 209 } 210 outb(MCA_ADAP_SETUP_REG, 0); 211 212 out: 213 if (!mcanmi) { 214 /* no CHCK bits asserted, assume ISA NMI */ 215 //return (x86_nmi()); 216 return 0; 217 } else 218 return(0); 219 } 220 221 /* 222 * Realistically, we should probe for the presence of an MCA bus here, and 223 * return a reasonable value. However, this port is never expected to run 224 * on anything other than MCA, so rather than write a bunch of complex code 225 * to find that we indeed have a bus, lets just assume we do. 226 */ 227 void 228 mca_busprobe(void) 229 { 230 MCA_system = 1; 231 } 232 233 #define PORT_DISKLED 0x92 234 #define DISKLED_ON 0x40 235 236 /* 237 * Light disk busy LED on IBM PS/2. 238 */ 239 void 240 mca_disk_busy(void) 241 { 242 outb(PORT_DISKLED, inb(PORT_DISKLED) | DISKLED_ON); 243 } 244 245 /* 246 * Turn off disk LED on IBM PS/2. 247 */ 248 void 249 mca_disk_unbusy(void) 250 { 251 outb(PORT_DISKLED, inb(PORT_DISKLED) & ~DISKLED_ON); 252 } 253 254 /* 255 * -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- 256 * MCA DMA specific stuff. We use ISA routines for bulk of the work, 257 * since MCA shares much of the charasteristics with it. We just hook 258 * the DMA channel initialization and kick MCA DMA controller appropriately. 259 * -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- 260 */ 261 262 #ifdef NOTYET 263 /* 264 * Synchronize a MCA DMA map. 265 */ 266 static void 267 _mca_bus_dmamap_sync(bus_dma_tag_t t, bus_dmamap_t map, bus_addr_t offset, 268 bus_size_t len, int ops) 269 { 270 struct rs6000_dma_cookie *cookie; 271 bus_addr_t phys; 272 bus_size_t cnt; 273 int dmach, mode; 274 275 _bus_dmamap_sync(t, map, offset, len, ops); 276 277 /* 278 * Don't do anything if not using the DMA controller. 279 */ 280 if ((map->_dm_flags & _MCABUS_DMA_USEDMACTRL) == 0) 281 return; 282 283 /* 284 * Don't do anything if not PRE* operation, allow only 285 * one of PREREAD and PREWRITE. 286 */ 287 if (ops != BUS_DMASYNC_PREREAD && ops != BUS_DMASYNC_PREWRITE) 288 return; 289 290 cookie = (struct rs6000_dma_cookie *)map->_dm_cookie; 291 dmach = (cookie->id_flags & 0xf0) >> 4; 292 293 phys = map->dm_segs[0].ds_addr; 294 cnt = map->dm_segs[0].ds_len; 295 296 mode = DMACMD_MODE_XFER; 297 mode |= (ops == BUS_DMASYNC_PREREAD) 298 ? DMACMD_MODE_READ : DMACMD_MODE_WRITE; 299 if (map->_dm_flags & MCABUS_DMA_IOPORT) 300 mode |= DMACMD_MODE_IOPORT; 301 302 /* Use 16bit DMA if requested */ 303 if (map->_dm_flags & MCABUS_DMA_16BIT) { 304 #ifdef DIAGNOSTIC 305 if ((cnt % 2) != 0) { 306 panic("_mca_bus_dmamap_sync: 16bit DMA and cnt %lu odd", 307 cnt); 308 } 309 #endif 310 mode |= DMACMD_MODE_16BIT; 311 cnt /= 2; 312 } 313 314 /* 315 * Initialize the MCA DMA controller appropriately. The exact 316 * sequence to setup the controller is taken from Minix. 317 */ 318 319 /* Disable access to DMA channel. */ 320 bus_space_write_1(dmaiot, dmacmdh, 0, DMACMD_MASK | dmach); 321 322 /* Set the transfer mode. */ 323 bus_space_write_1(dmaiot, dmacmdh, 0, DMACMD_SET_MODE | dmach); 324 bus_space_write_1(dmaiot, dmaexech, 0, mode); 325 326 /* Set the address byte pointer. */ 327 bus_space_write_1(dmaiot, dmacmdh, 0, DMACMD_SET_ADDR | dmach); 328 /* address bits 0..7 */ 329 bus_space_write_1(dmaiot, dmaexech, 0, (phys >> 0) & 0xff); 330 /* address bits 8..15 */ 331 bus_space_write_1(dmaiot, dmaexech, 0, (phys >> 8) & 0xff); 332 /* address bits 16..23 */ 333 bus_space_write_1(dmaiot, dmaexech, 0, (phys >> 16) & 0xff); 334 335 /* Set the count byte pointer */ 336 bus_space_write_1(dmaiot, dmacmdh, 0, DMACMD_SET_CNT | dmach); 337 /* count bits 0..7 */ 338 bus_space_write_1(dmaiot, dmaexech, 0, ((cnt - 1) >> 0) & 0xff); 339 /* count bits 8..15 */ 340 bus_space_write_1(dmaiot, dmaexech, 0, ((cnt - 1) >> 8) & 0xff); 341 342 /* Enable access to DMA channel. */ 343 bus_space_write_1(dmaiot, dmacmdh, 0, DMACMD_RESET_MASK | dmach); 344 } 345 #endif 346 347 /* 348 * Allocate a DMA map, and set up DMA channel. 349 */ 350 int 351 mca_dmamap_create(bus_dma_tag_t t, bus_size_t size, int flags, 352 bus_dmamap_t *dmamp, int dmach) 353 { 354 #if 0 355 int error; 356 struct rs6000_dma_cookie *cookie; 357 358 #ifdef DEBUG 359 /* Sanity check */ 360 if (dmach < 0 || dmach >= MAX_DMA_CHANNELS) { 361 printf("mcadma_create: invalid DMA channel %d\n", 362 dmach); 363 return (EINVAL); 364 } 365 366 if (size > 65536) { 367 panic("mca_dmamap_create: dmamap sz %ld > 65536", 368 (long) size); 369 } 370 #endif 371 372 /* 373 * MCA DMA transfer can be maximum 65536 bytes long and must 374 * be in one chunk. No specific boundary constraints are present. 375 */ 376 if ((error = _bus_dmamap_create(t, size, 1, 65536, 0, flags, dmamp))) 377 return (error); 378 379 cookie = (struct rs6000_dma_cookie *) (*dmamp)->_dm_cookie; 380 381 if (cookie == NULL) { 382 /* 383 * Allocate our cookie if not yet done. 384 */ 385 cookie = malloc(sizeof(struct rs6000_dma_cookie), M_DMAMAP, 386 ((flags & BUS_DMA_NOWAIT) ? M_NOWAIT : M_WAITOK) | M_ZERO); 387 if (cookie == NULL) { 388 389 return ENOMEM; 390 } 391 (*dmamp)->_dm_cookie = cookie; 392 } 393 394 395 /* Encode DMA channel */ 396 cookie->id_flags &= 0x0f; 397 cookie->id_flags |= dmach << 4; 398 399 /* Mark the dmamap as using DMA controller. Some devices 400 * drive DMA themselves, and don't need the MCA DMA controller. 401 * To distinguish the two, use a flag for dmamaps which use the DMA 402 * controller. 403 */ 404 (*dmamp)->_dm_flags |= _MCABUS_DMA_USEDMACTRL; 405 #endif 406 return (0); 407 } 408 409 /* 410 * Set I/O port for DMA. Implemented separately from _mca_bus_dmamap_sync() 411 * so that it's available for one-shot setup. 412 */ 413 void 414 mca_dma_set_ioport(int dma, uint16_t port) 415 { 416 #if 0 417 /* Disable access to dma channel. */ 418 bus_space_write_1(dmaiot, dmacmdh, 0, DMACMD_MASK | dma); 419 420 /* Set I/O port to use for DMA */ 421 bus_space_write_1(dmaiot, dmacmdh, 0, DMACMD_SET_IO | dma); 422 bus_space_write_1(dmaiot, dmaexech, 0, port & 0xff); 423 bus_space_write_1(dmaiot, dmaexech, 0, (port >> 8) & 0xff); 424 425 /* Enable access to DMA channel. */ 426 bus_space_write_1(dmaiot, dmacmdh, 0, DMACMD_RESET_MASK | dma); 427 #endif 428 } 429