Please note that diffs are not public domain; they are subject to the copyright notices on the relevant files. =================================================================== RCS file: /ftp/cvs/cvsroot/src/sys/arch/i386/mca/mca_machdep.c,v rcsdiff: /ftp/cvs/cvsroot/src/sys/arch/i386/mca/mca_machdep.c,v: warning: Unknown phrases like `commitid ...;' are present. retrieving revision 1.4.6.1 retrieving revision 1.4.6.2 diff -u -p -r1.4.6.1 -r1.4.6.2 --- src/sys/arch/i386/mca/mca_machdep.c 2001/06/21 19:26:04 1.4.6.1 +++ src/sys/arch/i386/mca/mca_machdep.c 2002/01/08 00:25:39 1.4.6.2 @@ -1,12 +1,13 @@ -/* $NetBSD: mca_machdep.c,v 1.4.6.1 2001/06/21 19:26:04 nathanw Exp $ */ +/* $NetBSD: mca_machdep.c,v 1.4.6.2 2002/01/08 00:25:39 nathanw Exp $ */ /*- - * Copyright (c) 2000 The NetBSD Foundation, Inc. + * Copyright (c) 2000, 2001 The NetBSD Foundation, Inc. * Copyright (c) 1996-1999 Scott D. Telford. * All rights reserved. * * This code is derived from software contributed to The NetBSD Foundation - * by Scott Telford . + * by Scott Telford and Jaromir Dolecek + * . * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -41,12 +42,17 @@ * Machine-specific functions for MCA autoconfiguration. */ +#include +__KERNEL_RCSID(0, "$NetBSD: mca_machdep.c,v 1.4.6.2 2002/01/08 00:25:39 nathanw Exp $"); + #include #include -#include -#include #include +#include +#include #include +#include +#include #include #include @@ -78,36 +84,108 @@ struct bios_config { #define FEATURE_RTC 0x20 /* Real-time clock present */ #define FEATURE_IC2 0x40 /* Second interrupt chip present */ #define FEATURE_DMA3 0x80 /* DMA channel 3 used by hard disk BIOS */ - u_int8_t pad[10]; + u_int8_t feature2; + u_int8_t pad[9]; } __attribute__ ((packed)); +/* + * Used to encode DMA channel into ISA DMA cookie. We use upper 4 bits of + * ISA DMA cookie id_flags, it's unused. + */ +struct i386_isa_dma_cookie { + int id_flags; + /* We don't care about rest */ +}; + +/* ISA DMA stuff - see i386/isa/isa_machdep.c */ +int _isa_bus_dmamap_create __P((bus_dma_tag_t, bus_size_t, int, + bus_size_t, bus_size_t, int, bus_dmamap_t *)); +void _isa_bus_dmamap_destroy __P((bus_dma_tag_t, bus_dmamap_t)); +int _isa_bus_dmamap_load __P((bus_dma_tag_t, bus_dmamap_t, void *, + bus_size_t, struct proc *, int)); +void _isa_bus_dmamap_unload __P((bus_dma_tag_t, bus_dmamap_t)); +void _isa_bus_dmamap_sync __P((bus_dma_tag_t, bus_dmamap_t, + bus_addr_t, bus_size_t, int)); + +int _isa_bus_dmamem_alloc __P((bus_dma_tag_t, bus_size_t, bus_size_t, + bus_size_t, bus_dma_segment_t *, int, int *, int)); + +static void _mca_bus_dmamap_sync __P((bus_dma_tag_t, bus_dmamap_t, + bus_addr_t, bus_size_t, int)); +static int _mca_bus_dmamap_load_mbuf __P((bus_dma_tag_t, bus_dmamap_t, + struct mbuf *, int)); +static int _mca_bus_dmamap_load_uio __P((bus_dma_tag_t, bus_dmamap_t, + struct uio *, int)); +static int _mca_bus_dmamap_load_raw __P((bus_dma_tag_t, bus_dmamap_t, + bus_dma_segment_t *, int, bus_size_t, int)); + +/* + * For now, we use MCA DMA to 0-16M always. Some IBM PS/2 have 32bit MCA bus, + * but majority of them have 24bit only. + */ +#define MCA_DMA_BOUNCE_THRESHOLD (16 * 1024 * 1024) struct i386_bus_dma_tag mca_bus_dma_tag = { - NULL, /* _cookie */ - _bus_dmamap_create, - _bus_dmamap_destroy, - _bus_dmamap_load, - _bus_dmamap_load_mbuf, - _bus_dmamap_load_uio, - _bus_dmamap_load_raw, - _bus_dmamap_unload, - NULL, /* _dmamap_sync */ - _bus_dmamem_alloc, + MCA_DMA_BOUNCE_THRESHOLD, /* _bounce_thresh */ + _isa_bus_dmamap_create, + _isa_bus_dmamap_destroy, + _isa_bus_dmamap_load, + _mca_bus_dmamap_load_mbuf, + _mca_bus_dmamap_load_uio, + _mca_bus_dmamap_load_raw, + _isa_bus_dmamap_unload, + _mca_bus_dmamap_sync, + _isa_bus_dmamem_alloc, _bus_dmamem_free, _bus_dmamem_map, _bus_dmamem_unmap, _bus_dmamem_mmap, }; -/* setup by mca_busprobe() */ -int MCA_system = 0; /* Updated in mca_busprobe() if appropriate. */ +/* Updated in mca_busprobe() if appropriate. */ +int MCA_system = 0; + +/* Used to kick MCA DMA controller */ +#define DMA_CMD 0x18 /* command the controller */ +#define DMA_EXEC 0x1A /* tell controller how to do things */ +static bus_space_handle_t dmaiot, dmacmdh, dmaexech; + +/* + * MCA DMA controller commands. The exact sense of individual bits + * are from Tymm Twillman , who worked on Linux MCA DMA + * support. + */ +#define DMACMD_SET_IO 0x00 /* set port (16bit) for i/o transfer */ +#define DMACMD_SET_ADDR 0x20 /* set addr (24bit) for i/o transfer */ +#define DMACMD_GET_ADDR 0x30 /* get addr (24bit) for i/o transfer */ +#define DMACMD_SET_CNT 0x40 /* set memory size for DMA (16b) */ +#define DMACMD_GET_CNT 0x50 /* get count of remaining bytes in DMA*/ +#define DMACMD_GET_STATUS 0x60 /* ?? */ +#define DMACMD_SET_MODE 0x70 /* set DMA mode */ +# define DMACMD_MODE_XFER 0x04 /* do transfer, read by default */ +# define DMACMD_MODE_READ 0x08 /* read transfer */ +# define DMACMD_MODE_WRITE 0x00 /* write transfer */ +# define DMACMD_MODE_IOPORT 0x01 /* DMA from/to IO register */ +# define DMACMD_MODE_16BIT 0x40 /* 16bit transfers (default 8bit) */ +#define DMACMD_SET_ARBUS 0x80 /* ?? */ +#define DMACMD_MASK 0x90 /* command mask */ +#define DMACMD_RESET_MASK 0xA0 /* reset */ +#define DMACMD_MASTER_CLEAR 0xD0 /* ?? */ +/* + * Map the MCA DMA controller registers. + */ void mca_attach_hook(parent, self, mba) struct device *parent, *self; struct mcabus_attach_args *mba; { - /* Nothing */ + dmaiot = mba->mba_iot; + + if (bus_space_map(dmaiot, DMA_CMD, 1, 0, &dmacmdh) + || bus_space_map(dmaiot, DMA_EXEC, 1, 0, &dmaexech)) + panic("%s: couldn't map DMA registers", + mba->mba_busname); } /* @@ -205,11 +283,9 @@ mca_nmi() outb(MCA_MB_SETUP_REG, 0xff); /* find if an MCA slot has the CHCK bit asserted (low) in POS 5 */ - for(slot=0; slot 0 if (!mcanmi) { /* no CHCK bits asserted, assume ISA NMI */ return (isa_nmi()); } else -#endif return(0); } @@ -266,7 +340,7 @@ mca_busprobe() scp = (struct bios_config *)ISA_HOLE_VADDR(paddr); #if 1 /* MCAVERBOSE */ - bitmask_snprintf(scp->feature1, + bitmask_snprintf(((scp->feature2 & 1)<< 8) | scp->feature1, "\20" "\01MCA+ISA" "\02MCA" @@ -275,10 +349,11 @@ mca_busprobe() "\05KBDINT" "\06RTC" "\07IC2" - "\010DMA3B\n", + "\010DMA3B" + "\011DMA32\n", buf, sizeof(buf)); - printf("BIOS CFG: Model-SubMod-Rev: %02x-%02x-%02x, 0x%s\n", + printf("BIOS CFG: Model-SubM-Rev: %02x-%02x-%02x, 0x%s\n", scp->model, scp->submodel, scp->bios_rev, buf); #endif @@ -305,3 +380,214 @@ mca_disk_unbusy(void) { outb(PORT_DISKLED, inb(PORT_DISKLED) & ~DISKLED_ON); } + +/* + * -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- + * MCA DMA specific stuff. We use ISA routines for bulk of the work, + * since MCA shares much of the charasteristics with it. We just hook + * the DMA channel initialization and kick MCA DMA controller appropriately. + * -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- + */ + +/* + * Like _mca_bus_dmamap_load(), but for mbufs. + */ +static int +_mca_bus_dmamap_load_mbuf(t, map, m0, flags) + bus_dma_tag_t t; + bus_dmamap_t map; + struct mbuf *m0; + int flags; +{ + + panic("_mca_bus_dmamap_load_mbuf: not implemented"); +} + +/* + * Like _mca_bus_dmamap_load(), but for uios. + */ +static int +_mca_bus_dmamap_load_uio(t, map, uio, flags) + bus_dma_tag_t t; + bus_dmamap_t map; + struct uio *uio; + int flags; +{ + + panic("_mca_bus_dmamap_load_uio: not implemented"); +} + +/* + * Like _mca_bus_dmamap_load(), but for raw memory allocated with + * bus_dmamem_alloc(). + */ +static int +_mca_bus_dmamap_load_raw(t, map, segs, nsegs, size, flags) + bus_dma_tag_t t; + bus_dmamap_t map; + bus_dma_segment_t *segs; + int nsegs; + bus_size_t size; + int flags; +{ + + panic("_mca_bus_dmamap_load_raw: not implemented"); +} + +/* + * Synchronize a MCA DMA map. + */ +static void +_mca_bus_dmamap_sync(t, map, offset, len, ops) + bus_dma_tag_t t; + bus_dmamap_t map; + bus_addr_t offset; + bus_size_t len; + int ops; +{ + struct i386_isa_dma_cookie *cookie; + bus_addr_t phys; + bus_size_t cnt; + int dmach, mode; + + _isa_bus_dmamap_sync(t, map, offset, len, ops); + + /* + * Don't do anything if not using the DMA controller. + */ + if ((map->_dm_flags & _MCABUS_DMA_USEDMACTRL) == 0) + return; + + /* + * Don't do anything if not PRE* operation, allow only + * one of PREREAD and PREWRITE. + */ + if (ops != BUS_DMASYNC_PREREAD && ops != BUS_DMASYNC_PREWRITE) + return; + + cookie = (struct i386_isa_dma_cookie *)map->_dm_cookie; + dmach = (cookie->id_flags & 0xf0) >> 4; + + phys = map->dm_segs[0].ds_addr; + cnt = map->dm_segs[0].ds_len; + + mode = DMACMD_MODE_XFER; + mode |= (ops == BUS_DMASYNC_PREREAD) + ? DMACMD_MODE_READ : DMACMD_MODE_WRITE; + if (map->_dm_flags & MCABUS_DMA_IOPORT) + mode |= DMACMD_MODE_IOPORT; + + /* Use 16bit DMA if requested */ + if (map->_dm_flags & MCABUS_DMA_16BIT) { +#ifdef DIAGNOSTIC + if ((cnt % 2) != 0) { + panic("_mca_bus_dmamap_sync: 16bit DMA and cnt %lu odd", + cnt); + } +#endif + mode |= DMACMD_MODE_16BIT; + cnt /= 2; + } + + /* + * Initialize the MCA DMA controller appropriately. The exact + * sequence to setup the controller is taken from Minix. + */ + + /* Disable access to dma channel. */ + bus_space_write_1(dmaiot, dmacmdh, 0, DMACMD_MASK | dmach); + + /* Set the transfer mode. */ + bus_space_write_1(dmaiot, dmacmdh, 0, DMACMD_SET_MODE | dmach); + bus_space_write_1(dmaiot, dmaexech, 0, mode); + + /* Set the address byte pointer. */ + bus_space_write_1(dmaiot, dmacmdh, 0, DMACMD_SET_ADDR | dmach); + /* address bits 0..7 */ + bus_space_write_1(dmaiot, dmaexech, 0, (phys >> 0) & 0xff); + /* address bits 8..15 */ + bus_space_write_1(dmaiot, dmaexech, 0, (phys >> 8) & 0xff); + /* address bits 16..23 */ + bus_space_write_1(dmaiot, dmaexech, 0, (phys >> 16) & 0xff); + + /* Set the count byte pointer */ + bus_space_write_1(dmaiot, dmacmdh, 0, DMACMD_SET_CNT | dmach); + /* count bits 0..7 */ + bus_space_write_1(dmaiot, dmaexech, 0, ((cnt - 1) >> 0) & 0xff); + /* count bits 8..15 */ + bus_space_write_1(dmaiot, dmaexech, 0, ((cnt - 1) >> 8) & 0xff); + + /* Enable access to dma channel. */ + bus_space_write_1(dmaiot, dmacmdh, 0, DMACMD_RESET_MASK | dmach); +} + +/* + * Allocate a dma map, and set up dma channel. + */ +int +mca_dmamap_create(t, size, flags, dmamp, dmach) + bus_dma_tag_t t; + bus_size_t size; + int flags; + bus_dmamap_t *dmamp; + int dmach; +{ + int error; + struct i386_isa_dma_cookie *cookie; + +#ifdef DEBUG + /* Sanity check */ + if (dmach < 0 || dmach >= 16) { + printf("mcadma_create: invalid DMA channel %d\n", + dmach); + return (EINVAL); + } + + if (size > 65536) { + panic("mca_dmamap_create: dmamap sz %ld > 65536", + (long) size); + } +#endif + + /* + * MCA DMA transfer can be maximum 65536 bytes long and must + * be in one chunk. No specific boundary constraints are present. + */ + if ((error = bus_dmamap_create(t, size, 1, 65536, 0, flags, dmamp))) + return (error); + + /* Encode DMA channel */ + cookie = (struct i386_isa_dma_cookie *) (*dmamp)->_dm_cookie; + cookie->id_flags &= 0x0f; + cookie->id_flags |= dmach << 4; + + /* Mark the dmamap as using DMA controller. Some devices + * drive DMA themselves, and don't need the MCA DMA controller. + * To distinguish the two, use a flag for dmamaps which use the DMA + * controller. + */ + (*dmamp)->_dm_flags |= _MCABUS_DMA_USEDMACTRL; + + return (0); +} + +/* + * Set I/O port for DMA. Implemented separately from _mca_bus_dmamap_sync() + * so that it's available for one-shot setup. + */ +void +mca_dma_set_ioport(dma, port) + int dma; + u_int16_t port; +{ + /* Disable access to dma channel. */ + bus_space_write_1(dmaiot, dmacmdh, 0, DMACMD_MASK | dma); + + /* Set I/O port to use for DMA */ + bus_space_write_1(dmaiot, dmacmdh, 0, DMACMD_SET_IO | dma); + bus_space_write_1(dmaiot, dmaexech, 0, port & 0xff); + bus_space_write_1(dmaiot, dmaexech, 0, (port >> 8) & 0xff); + + /* Enable access to dma channel. */ + bus_space_write_1(dmaiot, dmacmdh, 0, DMACMD_RESET_MASK | dma); +}