Home | History | Annotate | Line # | Download | only in mca
      1 /*	$NetBSD: mca_machdep.c,v 1.6 2024/07/02 06:07:12 rin 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 (at) ed.ac.uk> and Jaromir Dolecek
     10  * <jdolecek (at) 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.6 2024/07/02 06:07:12 rin Exp $");
     40 
     41 #include <sys/types.h>
     42 #include <sys/param.h>
     43 #include <sys/device.h>
     44 #include <sys/kmem.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 <sys/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 (at) 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(device_t parent, device_t self, struct mcabus_attach_args *mba)
    119 {
    120 #if 0
    121 	dmaiot = mba->mba_iot;
    122 
    123 	if (bus_space_map(dmaiot, DMA_CMD, 1, 0, &dmacmdh)
    124 	    || bus_space_map(dmaiot, DMA_EXEC, 1, 0, &dmaexech))
    125 		panic("mca: couldn't map DMA registers");
    126 #endif
    127 }
    128 
    129 /*
    130  * Read value of MCA POS register "reg" in slot "slot".
    131  */
    132 
    133 int
    134 mca_conf_read(mca_chipset_tag_t mc, int slot, int reg)
    135 {
    136 	int	data;
    137 
    138 	slot &= 15;	/* slot must be in range 0-15 */
    139 	data = inb(RS6000_BUS_SPACE_IO + MCA_POS_REG(reg) + (slot<<16));
    140 	return data;
    141 }
    142 
    143 
    144 /*
    145  * Write "data" to MCA POS register "reg" in slot "slot".
    146  */
    147 
    148 void
    149 mca_conf_write(mca_chipset_tag_t mc, int slot, int reg, int data)
    150 {
    151 	slot &= 15;	/* slot must be in range 0-15 */
    152 	outb(RS6000_BUS_SPACE_IO + MCA_POS_REG(reg) + (slot<<16), data);
    153 }
    154 
    155 
    156 void *
    157 mca_intr_establish(mca_chipset_tag_t mc, mca_intr_handle_t ih,
    158     int level, int (*func)(void *), void *arg)
    159 {
    160 	if (ih == 0 || ih >= ICU_LEN)
    161 		panic("mca_intr_establish: bogus handle 0x%x", ih);
    162 
    163 	/* MCA interrupts are always level-triggered */
    164 	return intr_establish(ih, IST_LEVEL, level, func, arg);
    165 }
    166 
    167 void
    168 mca_intr_disestablish(mca_chipset_tag_t mc, void *cookie)
    169 {
    170 	intr_disestablish(cookie);
    171 }
    172 
    173 
    174 /*
    175  * GCC 12 blames pointer reference to 0-th page, [0, 0xfff].
    176  * XXX map to higher address as done for, e.g., arm by devmap?
    177  */
    178 #pragma GCC diagnostic push					/* XXX { */
    179 #pragma GCC diagnostic ignored "-Warray-bounds"
    180 
    181 /*
    182  * Handle a NMI.
    183  * return true to panic system, false to ignore.
    184  */
    185 int
    186 mca_nmi(void)
    187 {
    188 	/*
    189 	* PS/2 MCA devices can generate NMIs - we can find out which
    190 	* slot generated it from the POS registers.
    191 	*/
    192 
    193 	int 	slot, mcanmi=0;
    194 
    195 	/* if there is no MCA bus, call x86_nmi() */
    196 	if (!MCA_system)
    197 		goto out;
    198 
    199 	/* ensure motherboard setup is disabled */
    200 	outb(MCA_MB_SETUP_REG, 0xff);
    201 
    202 	/* find if an MCA slot has the CHCK bit asserted (low) in POS 5 */
    203 	for(slot=0; slot<MCA_MAX_SLOTS; slot++) {
    204 		outb(MCA_ADAP_SETUP_REG, slot | MCA_ADAP_SET);
    205 		if ((inb(MCA_POS_REG(5)) & MCA_POS5_CHCK) == 0) {
    206 			mcanmi = 1;
    207 			/* find if CHCK status is available in POS 6/7 */
    208 			if((inb(MCA_POS_REG(5)) & MCA_POS5_CHCK_STAT) == 0)
    209 				log(LOG_CRIT, "MCA NMI: slot %d, POS6=0x%02x, POS7=0x%02x\n",
    210 					slot+1, inb(MCA_POS_REG(6)),
    211 						inb(MCA_POS_REG(7)));
    212 			else
    213 				log(LOG_CRIT, "MCA NMI: slot %d\n", slot+1);
    214 		}
    215 	}
    216 	outb(MCA_ADAP_SETUP_REG, 0);
    217 
    218    out:
    219 	if (!mcanmi) {
    220 		/* no CHCK bits asserted, assume ISA NMI */
    221 		//return (x86_nmi());
    222 		return 0;
    223 	} else
    224 		return(0);
    225 }
    226 
    227 /*
    228  * Realistically, we should probe for the presence of an MCA bus here, and
    229  * return a reasonable value.  However, this port is never expected to run
    230  * on anything other than MCA, so rather than write a bunch of complex code
    231  * to find that we indeed have a bus, lets just assume we do.
    232  */
    233 void
    234 mca_busprobe(void)
    235 {
    236 	MCA_system = 1;
    237 }
    238 
    239 #define PORT_DISKLED	0x92
    240 #define DISKLED_ON	0x40
    241 
    242 /*
    243  * Light disk busy LED on IBM PS/2.
    244  */
    245 void
    246 mca_disk_busy(void)
    247 {
    248 	outb(PORT_DISKLED, inb(PORT_DISKLED) | DISKLED_ON);
    249 }
    250 
    251 /*
    252  * Turn off disk LED on IBM PS/2.
    253  */
    254 void
    255 mca_disk_unbusy(void)
    256 {
    257 	outb(PORT_DISKLED, inb(PORT_DISKLED) & ~DISKLED_ON);
    258 }
    259 
    260 #pragma GCC diagnostic pop					/* XXX } */
    261 
    262 /*
    263  * -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
    264  * MCA DMA specific stuff. We use ISA routines for bulk of the work,
    265  * since MCA shares much of the charasteristics with it. We just hook
    266  * the DMA channel initialization and kick MCA DMA controller appropriately.
    267  * -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
    268  */
    269 
    270 #ifdef NOTYET
    271 /*
    272  * Synchronize a MCA DMA map.
    273  */
    274 static void
    275 _mca_bus_dmamap_sync(bus_dma_tag_t t, bus_dmamap_t map, bus_addr_t offset,
    276     bus_size_t len, int ops)
    277 {
    278 	struct rs6000_dma_cookie *cookie;
    279 	bus_addr_t phys;
    280 	bus_size_t cnt;
    281 	int dmach, mode;
    282 
    283 	_bus_dmamap_sync(t, map, offset, len, ops);
    284 
    285 	/*
    286 	 * Don't do anything if not using the DMA controller.
    287 	 */
    288 	if ((map->_dm_flags & _MCABUS_DMA_USEDMACTRL) == 0)
    289 		return;
    290 
    291 	/*
    292 	 * Don't do anything if not PRE* operation, allow only
    293 	 * one of PREREAD and PREWRITE.
    294 	 */
    295 	if (ops != BUS_DMASYNC_PREREAD && ops != BUS_DMASYNC_PREWRITE)
    296 		return;
    297 
    298 	cookie = (struct rs6000_dma_cookie *)map->_dm_cookie;
    299 	dmach = (cookie->id_flags & 0xf0) >> 4;
    300 
    301 	phys = map->dm_segs[0].ds_addr;
    302 	cnt = map->dm_segs[0].ds_len;
    303 
    304 	mode = DMACMD_MODE_XFER;
    305 	mode |= (ops == BUS_DMASYNC_PREREAD)
    306 			? DMACMD_MODE_READ : DMACMD_MODE_WRITE;
    307 	if (map->_dm_flags & MCABUS_DMA_IOPORT)
    308 		mode |= DMACMD_MODE_IOPORT;
    309 
    310 	/* Use 16bit DMA if requested */
    311 	if (map->_dm_flags & MCABUS_DMA_16BIT) {
    312 #ifdef DIAGNOSTIC
    313 		if ((cnt % 2) != 0) {
    314 			panic("_mca_bus_dmamap_sync: 16bit DMA and cnt %lu odd",
    315 				cnt);
    316 		}
    317 #endif
    318 		mode |= DMACMD_MODE_16BIT;
    319 		cnt /= 2;
    320 	}
    321 
    322 	/*
    323 	 * Initialize the MCA DMA controller appropriately. The exact
    324 	 * sequence to setup the controller is taken from Minix.
    325 	 */
    326 
    327 	/* Disable access to DMA channel. */
    328 	bus_space_write_1(dmaiot, dmacmdh, 0, DMACMD_MASK | dmach);
    329 
    330 	/* Set the transfer mode. */
    331 	bus_space_write_1(dmaiot, dmacmdh, 0, DMACMD_SET_MODE | dmach);
    332 	bus_space_write_1(dmaiot, dmaexech, 0, mode);
    333 
    334 	/* Set the address byte pointer. */
    335 	bus_space_write_1(dmaiot, dmacmdh, 0, DMACMD_SET_ADDR | dmach);
    336 	/* address bits 0..7   */
    337 	bus_space_write_1(dmaiot, dmaexech, 0, (phys >> 0) & 0xff);
    338 	/* address bits 8..15  */
    339 	bus_space_write_1(dmaiot, dmaexech, 0, (phys >> 8) & 0xff);
    340 	/* address bits 16..23  */
    341 	bus_space_write_1(dmaiot, dmaexech, 0, (phys >> 16) & 0xff);
    342 
    343 	/* Set the count byte pointer */
    344 	bus_space_write_1(dmaiot, dmacmdh, 0, DMACMD_SET_CNT | dmach);
    345 	/* count bits 0..7     */
    346 	bus_space_write_1(dmaiot, dmaexech, 0, ((cnt - 1) >> 0) & 0xff);
    347 	/* count bits 8..15    */
    348 	bus_space_write_1(dmaiot, dmaexech, 0, ((cnt - 1) >> 8) & 0xff);
    349 
    350 	/* Enable access to DMA channel. */
    351 	bus_space_write_1(dmaiot, dmacmdh, 0, DMACMD_RESET_MASK | dmach);
    352 }
    353 #endif
    354 
    355 /*
    356  * Allocate a DMA map, and set up DMA channel.
    357  */
    358 int
    359 mca_dmamap_create(bus_dma_tag_t t, bus_size_t size, int flags,
    360     bus_dmamap_t *dmamp, int dmach)
    361 {
    362 #if 0
    363 	int error;
    364 	struct rs6000_dma_cookie *cookie;
    365 
    366 #ifdef DEBUG
    367 	/* Sanity check */
    368 	if (dmach < 0 || dmach >= MAX_DMA_CHANNELS) {
    369 		printf("mcadma_create: invalid DMA channel %d\n",
    370 			dmach);
    371 		return (EINVAL);
    372 	}
    373 
    374 	if (size > 65536) {
    375 		panic("mca_dmamap_create: dmamap sz %ld > 65536",
    376 		    (long) size);
    377 	}
    378 #endif
    379 
    380 	/*
    381 	 * MCA DMA transfer can be maximum 65536 bytes long and must
    382 	 * be in one chunk. No specific boundary constraints are present.
    383 	 */
    384 	if ((error = _bus_dmamap_create(t, size, 1, 65536, 0, flags, dmamp)))
    385 		return (error);
    386 
    387 	cookie = (struct rs6000_dma_cookie *) (*dmamp)->_dm_cookie;
    388 
    389 	if (cookie == NULL) {
    390 		/*
    391 		 * Allocate our cookie if not yet done.
    392 		 */
    393 		cookie = kmem_zalloc(sizeof(struct rs6000_dma_cookie),
    394 		    ((flags & BUS_DMA_NOWAIT) ? KM_SLEEP : KM_NOSLEEP));
    395 		if (cookie == NULL) {
    396 
    397 			return ENOMEM;
    398 		}
    399 		(*dmamp)->_dm_cookie = cookie;
    400 	}
    401 
    402 
    403 	/* Encode DMA channel */
    404 	cookie->id_flags &= 0x0f;
    405 	cookie->id_flags |= dmach << 4;
    406 
    407 	/* Mark the dmamap as using DMA controller. Some devices
    408 	 * drive DMA themselves, and don't need the MCA DMA controller.
    409 	 * To distinguish the two, use a flag for dmamaps which use the DMA
    410 	 * controller.
    411  	 */
    412 	(*dmamp)->_dm_flags |= _MCABUS_DMA_USEDMACTRL;
    413 #endif
    414 	return (0);
    415 }
    416 
    417 /*
    418  * Set I/O port for DMA. Implemented separately from _mca_bus_dmamap_sync()
    419  * so that it's available for one-shot setup.
    420  */
    421 void
    422 mca_dma_set_ioport(int dma, uint16_t port)
    423 {
    424 #if 0
    425 	/* Disable access to dma channel. */
    426 	bus_space_write_1(dmaiot, dmacmdh, 0, DMACMD_MASK | dma);
    427 
    428 	/* Set I/O port to use for DMA */
    429 	bus_space_write_1(dmaiot, dmacmdh, 0, DMACMD_SET_IO | dma);
    430 	bus_space_write_1(dmaiot, dmaexech, 0, port & 0xff);
    431 	bus_space_write_1(dmaiot, dmaexech, 0, (port >> 8) & 0xff);
    432 
    433 	/* Enable access to DMA channel. */
    434 	bus_space_write_1(dmaiot, dmacmdh, 0, DMACMD_RESET_MASK | dma);
    435 #endif
    436 }
    437