code:
/*	$OpenBSD: macebus.c,v 1.63 2014/09/30 06:51:58 jmatthew Exp $ */

/*
 * Copyright (c) 2000-2004 Opsycon AB  (www.opsycon.se)
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 *
 */

/*
 * This is a combined macebus/crimebus driver. It handles configuration of all
 * devices on the processor bus.
 */

#include <sys/param.h>
#include <sys/systm.h>
#include <sys/kernel.h>
#include <sys/conf.h>
#include <sys/malloc.h>
#include <sys/device.h>
#include <sys/proc.h>
#include <sys/atomic.h>

#include <mips64/archtype.h>
#include <mips64/mips_cpu.h>

#include <machine/autoconf.h>
#include <machine/intr.h>

#include <sgi/localbus/crimebus.h>
#include <sgi/localbus/macebus.h>
#include <sgi/localbus/macebusvar.h>

int	 macebusmatch(struct device *, void *, void *);
void	 macebusattach(struct device *, struct device *, void *);
int	 macebusprint(void *, const char *);
int	 macebussubmatch(struct device *, void *, void *);

void	 macebus_intr_makemasks(void);
void	 macebus_splx(int);
uint32_t macebus_iointr(uint32_t, struct trap_frame *);
uint32_t macebus_aux(uint32_t, struct trap_frame *);
int	 macebus_iointr_skip(struct intrhand *, uint64_t, uint64_t);
void	 crime_setintrmask(int);

u_int8_t mace_read_1(bus_space_tag_t, bus_space_handle_t, bus_size_t);
u_int16_t mace_read_2(bus_space_tag_t, bus_space_handle_t, bus_size_t);
u_int32_t mace_read_4(bus_space_tag_t, bus_space_handle_t, bus_size_t);
u_int64_t mace_read_8(bus_space_tag_t, bus_space_handle_t, bus_size_t);

void	 mace_write_1(bus_space_tag_t, bus_space_handle_t, bus_size_t,
	    u_int8_t);
void	 mace_write_2(bus_space_tag_t, bus_space_handle_t, bus_size_t,
	    u_int16_t);
void	 mace_write_4(bus_space_tag_t, bus_space_handle_t, bus_size_t,
	    u_int32_t);
void	 mace_write_8(bus_space_tag_t, bus_space_handle_t, bus_size_t,
	    u_int64_t);

void	 mace_read_raw_2(bus_space_tag_t, bus_space_handle_t, bus_addr_t,
	    u_int8_t *, bus_size_t);
void	 mace_write_raw_2(bus_space_tag_t, bus_space_handle_t, bus_addr_t,
	    const u_int8_t *, bus_size_t);
void	 mace_read_raw_4(bus_space_tag_t, bus_space_handle_t, bus_addr_t,
	    u_int8_t *, bus_size_t);
void	 mace_write_raw_4(bus_space_tag_t, bus_space_handle_t, bus_addr_t,
	    const u_int8_t *, bus_size_t);
void	 mace_read_raw_8(bus_space_tag_t, bus_space_handle_t, bus_addr_t,
	    u_int8_t *, bus_size_t);
void	 mace_write_raw_8(bus_space_tag_t, bus_space_handle_t, bus_addr_t,
	    const u_int8_t *, bus_size_t);

int	 mace_space_map(bus_space_tag_t, bus_addr_t, bus_size_t, int,
	    bus_space_handle_t *);
void	 mace_space_unmap(bus_space_tag_t, bus_space_handle_t, bus_size_t);
int	 mace_space_region(bus_space_tag_t, bus_space_handle_t, bus_size_t,
	    bus_size_t, bus_space_handle_t *);

void	*mace_space_vaddr(bus_space_tag_t, bus_space_handle_t);
void	 mace_space_barrier(bus_space_tag_t, bus_space_handle_t, bus_size_t,
	    bus_size_t, int);

bus_addr_t macebus_pa_to_device(paddr_t);
paddr_t	 macebus_device_to_pa(bus_addr_t);

struct cfattach macebus_ca = {
	sizeof(struct device), macebusmatch, macebusattach
};

struct cfdriver macebus_cd = {
	NULL, "macebus", DV_DULL
};

bus_space_t macebus_tag = {
	PHYS_TO_XKPHYS(MACEBUS_BASE, CCA_NC),
	NULL,
	mace_read_1, mace_write_1,
	mace_read_2, mace_write_2,
	mace_read_4, mace_write_4,
	mace_read_8, mace_write_8,
	mace_read_raw_2, mace_write_raw_2,
	mace_read_raw_4, mace_write_raw_4,
	mace_read_raw_8, mace_write_raw_8,
	mace_space_map, mace_space_unmap, mace_space_region,
	mace_space_vaddr, mace_space_barrier
};

bus_space_t crimebus_tag = {
	PHYS_TO_XKPHYS(CRIMEBUS_BASE, CCA_NC),
	NULL,
	mace_read_1, mace_write_1,
	mace_read_2, mace_write_2,
	mace_read_4, mace_write_4,
	mace_read_8, mace_write_8,
	mace_read_raw_2, mace_write_raw_2,
	mace_read_raw_4, mace_write_raw_4,
	mace_read_raw_8, mace_write_raw_8,
	mace_space_map, mace_space_unmap, mace_space_region,
	mace_space_vaddr, mace_space_barrier
};

bus_space_handle_t crime_h;
bus_space_handle_t mace_h;

struct machine_bus_dma_tag mace_bus_dma_tag = {
	NULL,			/* _cookie */
	_dmamap_create,
	_dmamap_destroy,
	_dmamap_load,
	_dmamap_load_mbuf,
	_dmamap_load_uio,
	_dmamap_load_raw,
	_dmamap_load_buffer,
	_dmamap_unload,
	_dmamap_sync,
	_dmamem_alloc,
	_dmamem_free,
	_dmamem_map,
	_dmamem_unmap,
	_dmamem_mmap,
	macebus_pa_to_device,
	macebus_device_to_pa,
	CRIME_MEMORY_MASK
};

/*
 * CRIME/MACE interrupt handling declarations: 32 CRIME sources, 32 MACE
 * sources (multiplexed by CRIME); 1 level.
 * We define another level for periodic tasks as well.
 */

struct crime_intrhand {
	struct	intrhand	ih;
	uint32_t		mace_irqmask;
};
struct crime_intrhand *crime_intrhand[CRIME_NINTS];

#define	INTPRI_MACEIO	(INTPRI_CLOCK + 1)
#define	INTPRI_MACEAUX	(INTPRI_MACEIO + 1)

uint64_t crime_intem, mace_intem;
uint64_t crime_imask[NIPLS];

/*
 * List of macebus child devices.
 */

#define	MACEBUSDEV(name, addr, i, i2) \
	{ name, &macebus_tag, &macebus_tag, &mace_bus_dma_tag, addr, i, i2 }
struct macebus_attach_args macebus_children[] = {
	MACEBUSDEV("com", MACE_ISA_SER1_OFFS, 4, MACE_ISA_INT_SERIAL_1),
	MACEBUSDEV("com", MACE_ISA_SER2_OFFS, 4, MACE_ISA_INT_SERIAL_2),
	MACEBUSDEV("dsrtc", MACE_ISA_RTC_OFFS, -1, 0),
#if 0
	MACEBUSDEV("lpt", MACE_ISA_EPP_OFFS, 4, MACE_ISA_INT_PARALLEL),
#endif
	MACEBUSDEV("macepcibr", MACE_PCI_OFFS, 7, 0),
	MACEBUSDEV("mavb", MACE_IO_AUDIO_OFFS, 6, MACE_ISA_INT_AUDIO),
	MACEBUSDEV("mec", MACE_ETHERNET_OFFS, 3, 0),
	MACEBUSDEV("mkbc", MACE_IO_KBC_OFFS, 5,
	    MACE_ISA_INT_KBD | MACE_ISA_INT_KBD_POLL |
	    MACE_ISA_INT_MOUSE | MACE_ISA_INT_MOUSE_POLL),
	MACEBUSDEV("power", 0, 5, MACE_ISA_INT_RTC)
};
#undef	MACEBUSDEV

/*
 * Match bus only to targets which have this bus.
 */
int
macebusmatch(struct device *parent, void *match, void *aux)
{
	if (sys_config.system_type == SGI_O2)
		return (1);
	return (0);
}

int
macebusprint(void *aux, const char *macebus)
{
	struct macebus_attach_args *maa = aux;

	if (macebus != NULL)
		printf("%s at %s", maa->maa_name, macebus);

	if (maa->maa_baseaddr != 0)
		printf(" base 0x%08lx", maa->maa_baseaddr);
	if (maa->maa_intr >= 0)
		printf(" irq %d", maa->maa_intr);

	return (UNCONF);
}

int
macebussubmatch(struct device *parent, void *vcf, void *args)
{
	struct cfdata *cf = vcf;
	struct macebus_attach_args *maa = args;

	if (strcmp(cf->cf_driver->cd_name, maa->maa_name) != 0)
		return 0;

	if (cf->cf_loc[0] != -1 && cf->cf_loc[0] != (int)maa->maa_baseaddr)
		return 0;

	return (*cf->cf_attach->ca_match)(parent, cf, maa);
}

void
macebusattach(struct device *parent, struct device *self, void *aux)
{
	u_int32_t creg;
	uint i;

	/*
	 * Map and setup CRIME control registers.
	 */
	if (bus_space_map(&crimebus_tag, 0x00000000, 0x400, 0, &crime_h)) {
		printf(": can't map CRIME control registers\n");
		return;
	}

	creg = bus_space_read_8(&crimebus_tag, crime_h, CRIME_REVISION);
	printf(": crime rev %d.%d\n", (creg & 0xf0) >> 4, creg & 0xf);

	bus_space_write_8(&crimebus_tag, crime_h, CRIME_CPU_ERROR_STAT, 0);
	bus_space_write_8(&crimebus_tag, crime_h, CRIME_MEM_ERROR_STAT, 0);

	bus_space_write_8(&crimebus_tag, crime_h, CRIME_INT_MASK, 0);
	bus_space_write_8(&crimebus_tag, crime_h, CRIME_INT_SOFT, 0);
	bus_space_write_8(&crimebus_tag, crime_h, CRIME_INT_HARD, 0);
	bus_space_write_8(&crimebus_tag, crime_h, CRIME_INT_STAT, 0);

	/*
	 * Map and setup MACE ISA control registers.
	 */
	if (bus_space_map(&macebus_tag, MACE_ISA_OFFS, 0x400, 0, &mace_h)) {
		printf("%s: can't map MACE control registers\n",
		    self->dv_xname);
		return;
	}

	bus_space_write_8(&macebus_tag, mace_h, MACE_ISA_INT_MASK, 0);
	bus_space_write_8(&macebus_tag, mace_h, MACE_ISA_INT_STAT, 0);

	/*
	 * On O2 systems all interrupts are handled by the macebus interrupt
	 * handler. Register all except clock.
	 */
	set_intr(INTPRI_MACEIO, CR_INT_0, macebus_iointr);
	register_splx_handler(macebus_splx);

	/* Set up a handler called when clock interrupts go off. */
	set_intr(INTPRI_MACEAUX, CR_INT_5, macebus_aux);

	/*
	 * Attach subdevices.
	 */
	for (i = 0; i < nitems(macebus_children); i++)
		config_found_sm(self, macebus_children + i,
		    macebusprint, macebussubmatch);
}

/*
 * Bus access primitives. These are really ugly...
 */

u_int8_t
mace_read_1(bus_space_tag_t t, bus_space_handle_t h, bus_size_t o)
{
	return *(volatile u_int8_t *)(h + (o << 8) + 7);
}

u_int16_t
mace_read_2(bus_space_tag_t t, bus_space_handle_t h, bus_size_t o)
{
	panic(__func__);
}

u_int32_t
mace_read_4(bus_space_tag_t t, bus_space_handle_t h, bus_size_t o)
{
	return *(volatile u_int32_t *)(h + o);
}

u_int64_t
mace_read_8(bus_space_tag_t t, bus_space_handle_t h, bus_size_t o)
{
	return *(volatile u_int64_t *)(h + o);
}

void
mace_write_1(bus_space_tag_t t, bus_space_handle_t h, bus_size_t o, u_int8_t v)
{
	*(volatile u_int8_t *)(h + (o << 8) + 7) = v;
}

void
mace_write_2(bus_space_tag_t t, bus_space_handle_t h, bus_size_t o, u_int16_t v)
{
	panic(__func__);
}

void
mace_write_4(bus_space_tag_t t, bus_space_handle_t h, bus_size_t o, u_int32_t v)
{
	*(volatile u_int32_t *)(h + o) = v;
}

void
mace_write_8(bus_space_tag_t t, bus_space_handle_t h, bus_size_t o, u_int64_t v)
{
	*(volatile u_int64_t *)(h + o) = v;
}

void
mace_read_raw_2(bus_space_tag_t t, bus_space_handle_t h, bus_addr_t o,
    u_int8_t *buf, bus_size_t len)
{
	panic(__func__);
}

void
mace_write_raw_2(bus_space_tag_t t, bus_space_handle_t h, bus_addr_t o,
    const u_int8_t *buf, bus_size_t len)
{
	panic(__func__);
}

void
mace_read_raw_4(bus_space_tag_t t, bus_space_handle_t h, bus_addr_t o,
    u_int8_t *buf, bus_size_t len)
{
	volatile u_int32_t *addr = (volatile u_int32_t *)(h + o);
	len >>= 2;
	while (len-- != 0) {
		*(u_int32_t *)buf = *addr;
		buf += 4;
	}
}

void
mace_write_raw_4(bus_space_tag_t t, bus_space_handle_t h, bus_addr_t o,
    const u_int8_t *buf, bus_size_t len)
{
	volatile u_int32_t *addr = (volatile u_int32_t *)(h + o);
	len >>= 2;
	while (len-- != 0) {
		*addr = *(u_int32_t *)buf;
		buf += 4;
	}
}

void
mace_read_raw_8(bus_space_tag_t t, bus_space_handle_t h, bus_addr_t o,
    u_int8_t *buf, bus_size_t len)
{
	volatile u_int64_t *addr = (volatile u_int64_t *)(h + o);
	len >>= 3;
	while (len-- != 0) {
		*(u_int64_t *)buf = *addr;
		buf += 8;
	}
}

void
mace_write_raw_8(bus_space_tag_t t, bus_space_handle_t h, bus_addr_t o,
    const u_int8_t *buf, bus_size_t len)
{
	volatile u_int64_t *addr = (volatile u_int64_t *)(h + o);
	len >>= 3;
	while (len-- != 0) {
		*addr = *(u_int64_t *)buf;
		buf += 8;
	}
}

int
mace_space_map(bus_space_tag_t t, bus_addr_t offs, bus_size_t size,
    int flags, bus_space_handle_t *bshp)
{
	if (ISSET(flags, BUS_SPACE_MAP_CACHEABLE))
		offs +=
		    PHYS_TO_XKPHYS(0, CCA_CACHED) - PHYS_TO_XKPHYS(0, CCA_NC);
	*bshp = t->bus_base + offs;
	return 0;
}

void
mace_space_unmap(bus_space_tag_t t, bus_space_handle_t bsh, bus_size_t size)
{
}

int
mace_space_region(bus_space_tag_t t, bus_space_handle_t bsh,
    bus_size_t offset, bus_size_t size, bus_space_handle_t *nbshp)
{
	*nbshp = bsh + offset;
	return (0);
}

void *
mace_space_vaddr(bus_space_tag_t t, bus_space_handle_t h)
{
	return (void *)h;
}

void
mace_space_barrier(bus_space_tag_t t, bus_space_handle_t h, bus_size_t offs,
    bus_size_t len, int flags)
{
	mips_sync();
}

/*
 * Macebus bus_dma helpers.
 * Mace accesses memory contiguously at 0x40000000 onwards.
 */

bus_addr_t
macebus_pa_to_device(paddr_t pa)
{
	return (pa | CRIME_MEMORY_OFFSET);
}

paddr_t
macebus_device_to_pa(bus_addr_t addr)
{
	paddr_t pa = (paddr_t)addr & CRIME_MEMORY_MASK;

	if (pa >= 256 * 1024 * 1024)
		pa |= CRIME_MEMORY_OFFSET;

	return (pa);
}

/*
 * Macebus interrupt handler driver.
 */

/*
 * Establish an interrupt handler called from the dispatcher.
 * The interrupt function established should return zero if there was nothing
 * to serve (no int) and non-zero when an interrupt was serviced.
 *
 * Interrupts are numbered from 1 and up where 1 maps to HW int 0.
 * XXX There is no reason to keep this... except for hardcoded interrupts
 * XXX in kernel configuration files...
 */
void *
macebus_intr_establish(int irq, uint32_t mace_irqmask, int type, int level,
    int (*ih_fun)(void *), void *ih_arg, const char *ih_what)
{
	struct crime_intrhand **p, *q, *ih;
	int s;

#ifdef DIAGNOSTIC
	if (irq >= CRIME_NINTS || irq < 0)
		panic("intr_establish: illegal irq %d", irq);
#endif

	ih = malloc(sizeof *ih, M_DEVBUF, M_NOWAIT);
	if (ih == NULL)
		return NULL;

	ih->ih.ih_next = NULL;
	ih->ih.ih_fun = ih_fun;
	ih->ih.ih_arg = ih_arg;
	ih->ih.ih_level = level;
	ih->ih.ih_irq = irq;
	ih->mace_irqmask = mace_irqmask;
	evcount_attach(&ih->ih.ih_count, ih_what, &ih->ih.ih_irq);

	s = splhigh();

	/*
	 * Figure out where to put the handler.
	 * This is O(N^2), but we want to preserve the order, and N is
	 * generally small.
	 */
	for (p = &crime_intrhand[irq]; (q = *p) != NULL;
	    p = (struct crime_intrhand **)&q->ih.ih_next)
		;
	*p = ih;

	crime_intem |= 1UL << irq;
	macebus_intr_makemasks();

	/* enable further MACE sources if necessary */
	if (mace_irqmask != 0) {
		mace_intem |= mace_irqmask;
		bus_space_write_8(&macebus_tag, mace_h, MACE_ISA_INT_MASK,
		    mace_intem);
	}

	splx(s);	/* causes hw mask update */

	return (ih);
}

void
macebus_intr_disestablish(void *ih)
{
	/* XXX */
	panic("%s not implemented", __func__);
}

void
macebus_splx(int newipl)
{
	struct cpu_info *ci = curcpu();

	/* Update masks to new ipl. Order highly important! */
	__asm__ (".set noreorder\n");
	ci->ci_ipl = newipl;
	mips_sync();
	__asm__ (".set reorder\n");
	crime_setintrmask(newipl);
	/* If we still have softints pending trigger processing. */
	if (ci->ci_softpending != 0 && newipl < IPL_SOFTINT)
		setsoftintr0();
}

/*
 * Crime interrupt handler.
 */

#define	INTR_FUNCTIONNAME	macebus_iointr
#define	MASK_FUNCTIONNAME	macebus_intr_makemasks

#define	INTR_LOCAL_DECLS \
	uint64_t mace_isr, mace_imr;
#define	MASK_LOCAL_DECLS
#define	INTR_GETMASKS \
do { \
	isr = bus_space_read_8(&crimebus_tag, crime_h, CRIME_INT_STAT); \
	imr = bus_space_read_8(&crimebus_tag, crime_h, CRIME_INT_MASK); \
	if (((CRIME_INT_SUPER_IO | CRIME_INT_SUB_MISC | CRIME_INT_SUB_AUDIO) & \
	    isr & imr) != 0) { \
		mace_isr = bus_space_read_8(&macebus_tag, mace_h, \
		    MACE_ISA_INT_STAT); \
		mace_imr = bus_space_read_8(&macebus_tag, mace_h, \
		    MACE_ISA_INT_MASK); \
	} else \
		mace_isr = mace_imr = 0; \
	bit = 63; \
} while (0)
#define	INTR_MASKPENDING \
	bus_space_write_8(&crimebus_tag, crime_h, CRIME_INT_MASK, imr & ~isr)
#define	INTR_IMASK(ipl)		crime_imask[ipl]
#define	INTR_HANDLER(bit)	(struct intrhand *)crime_intrhand[bit]
#define	INTR_SPURIOUS(bit) \
do { \
	if (((CRIME_INT_SUPER_IO | CRIME_INT_SUB_MISC | CRIME_INT_SUB_AUDIO) & \
	    (1 << (bit))) != 0) { \
		if ((mace_isr & mace_imr) != 0) \
			printf("spurious crime interrupt %d" \
			    " mace isr %p imr %p\n", \
			    bit, (void *)mace_isr, (void *)mace_imr); \
	} else \
		printf("spurious crime interrupt %d\n", bit); \
} while (0)
#define	INTR_MASKRESTORE \
	bus_space_write_8(&crimebus_tag, crime_h, CRIME_INT_MASK, imr)
#define	INTR_MASKSIZE		CRIME_NINTS

#define	INTR_HANDLER_SKIP(ih) \
	macebus_iointr_skip((void *)ih, mace_isr, mace_imr)

#include <sgi/sgi/intr_template.c>

int
macebus_iointr_skip(struct intrhand *ih, uint64_t mace_isr, uint64_t mace_imr)
{
	struct crime_intrhand *mih = (struct crime_intrhand *)ih;

	/* do not skip pure CRIME interrupts */
	if (mih->mace_irqmask == 0)
		return 0;

	/*
	 * Several CRIME interrupts (such as superio and miscellaneous) are
	 * shared by multiple devices, so narrow the selection with the
	 * MACE interrupt status.
	 */

	if ((mace_isr & mace_imr & mih->mace_irqmask) != 0)
		return 0;

	return 1;
}

/*
 * Macebus auxilary functions run each clock interrupt.
 */
uint32_t
macebus_aux(uint32_t hwpend, struct trap_frame *cf)
{
	u_int64_t mask;

	mask = bus_space_read_8(&macebus_tag, mace_h, MACE_ISA_MISC_REG);
	mask |= MACE_ISA_MISC_RLED_OFF | MACE_ISA_MISC_GLED_OFF;

	/* GREEN - Idle */
	/* AMBER - System mode */
	/* RED   - User mode */
	if (cf->sr & SR_KSU_USER) {
		mask &= ~MACE_ISA_MISC_RLED_OFF;
	} else if (curproc == NULL ||
	    curproc == curcpu()->ci_schedstate.spc_idleproc) {
		mask &= ~MACE_ISA_MISC_GLED_OFF;
	} else {
		mask &= ~(MACE_ISA_MISC_RLED_OFF | MACE_ISA_MISC_GLED_OFF);
	}
	bus_space_write_8(&macebus_tag, mace_h, MACE_ISA_MISC_REG, mask);

	return 0;	/* Real clock int handler will claim the interrupt. */
}

void
crime_setintrmask(int level)
{
	*(volatile uint64_t *)(PHYS_TO_XKPHYS(CRIMEBUS_BASE, CCA_NC) +
	    CRIME_INT_MASK) = crime_intem & ~crime_imask[level];
}


code:
/*	$OpenBSD: macebus.h,v 1.15 2009/10/26 18:00:06 miod Exp $	*/

/*
 * Copyright (c) 2003-2004 Opsycon AB (www.opsycon.com).
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 *
 */

/*
 *  Physical address of MACEBUS.
 */
#define	MACEBUS_BASE		0x1f000000

/*
 *  Offsets for various I/O sections on MACEBUS
 */
#define	MACE_PCI_OFFS		0x00080000
#define	MACE_VIN1_OFFS		0x00100000
#define	MACE_VIN2_OFFS		0x00180000
#define	MACE_VOUT_OFFS		0x00200000
#define	MACE_ETHERNET_OFFS	0x00280000
#define	MACE_IO_OFFS		0x00300000
#define	MACE_IO_SIZE		0x00050000
#define	MACE_ISAX_OFFS		0x00380000
#define	MACE_ISAX_SIZE		0x00020000

/*
 *  PCI control registers (relative MACE_PCI_OFFS)
 */
#define	MACE_PCI_ERROR_ADDRESS	0x0000
#define	MACE_PCI_ERROR_FLAGS	0x0004
#define	MACE_PCI_CONTROL	0x0008
#define	MACE_PCI_REVISION	0x000c
#define	MACE_PCI_FLUSH		0x000c
#define	MACE_PCI_CFGADDR	0x0cf8
#define	MACE_PCI_CFGDATA	0x0cfc

#define	MACE_PCI_INTCTRL	0x000000ff	/* Interrupt control mask */

/* PCI_ERROR_FLAGS Bits */
#define	PERR_MASTER_ABORT		0x80000000
#define	PERR_TARGET_ABORT		0x40000000
#define	PERR_DATA_PARITY_ERR		0x20000000
#define	PERR_RETRY_ERR			0x10000000
#define	PERR_ILLEGAL_CMD		0x08000000
#define	PERR_SYSTEM_ERR			0x04000000
#define	PERR_INTERRUPT_TEST		0x02000000
#define	PERR_PARITY_ERR			0x01000000
#define	PERR_OVERRUN			0x00800000
#define	PERR_RSVD			0x00400000
#define	PERR_MEMORY_ADDR		0x00200000
#define	PERR_CONFIG_ADDR		0x00100000
#define	PERR_MASTER_ABORT_ADDR_VALID	0x00080000
#define	PERR_TARGET_ABORT_ADDR_VALID	0x00040000
#define	PERR_DATA_PARITY_ADDR_VALID	0x00020000
#define	PERR_RETRY_ADDR_VALID		0x00010000

/*
 * MACE IO definitions.
 */
#define	MACE_IO_AUDIO_OFFS	(MACE_IO_OFFS+0x00000000)
#define	MACE_IO_KBC_OFFS	(MACE_IO_OFFS+0x00020000)

/*
 *  MACE ISA definitions.
 */
#define	MACE_ISA_OFFS		(MACE_IO_OFFS+0x00010000)

#define MACE_ISA_RING_BASE	0x0000
#define  MACE_ISA_RING_ALIGN	0x00010000
#define	MACE_ISA_MISC_REG	0x0008	/* Various status and controls */
#define	MACE_ISA_INT_STAT	0x0010
#define	MACE_ISA_INT_MASK	0x0018

/* MACE_ISA_MISC_REG definitions */
#define	MACE_ISA_MISC_RLED_OFF	0x0010	/* Turns off RED LED */
#define	MACE_ISA_MISC_GLED_OFF	0x0020	/* Turns off GREEN LED */

/* MACE_ISA_INT_* definitions */
#define	MACE_ISA_INT_AUDIO	0x000000ff	/* Audio ints */
#define  MACE_ISA_INT_AUDIO_SC	      0x02
#define  MACE_ISA_INT_AUDIO_DMA1      0x04
#define  MACE_ISA_INT_AUDIO_DMA2      0x10
#define  MACE_ISA_INT_AUDIO_DMA3      0x40
#define	MACE_ISA_INT_RTC	0x00000100	/* RTC */
#define	MACE_ISA_INT_KBD	0x00000200	/* Keyboard */
#define	MACE_ISA_INT_KBD_POLL	0x00000400	/* Keyboard polled */
#define	MACE_ISA_INT_MOUSE	0x00000800	/* Mouse */
#define	MACE_ISA_INT_MOUSE_POLL	0x00001000	/* Mouse polled */
#define	MACE_ISA_INT_TIMER	0x0000e000	/* Timer/counter compare */
#define	MACE_ISA_INT_PARALLEL	0x000f0000	/* Parallel port */
#define	MACE_ISA_INT_SERIAL_1	0x03f00000	/* Serial port 1 */
#define	MACE_ISA_INT_SERIAL_2	0xfc000000	/* Serial port 2 */

/* ISA Peripherals */
#define	MACE_ISA_EPP_OFFS	(MACE_ISAX_OFFS+0x00000000)
#define	MACE_ISA_ECP_OFFS	(MACE_ISAX_OFFS+0x00008000)
#define	MACE_ISA_SER1_OFFS	(MACE_ISAX_OFFS+0x00010000)
#define	MACE_ISA_SER2_OFFS	(MACE_ISAX_OFFS+0x00018000)
#define	MACE_ISA_RTC_OFFS	(MACE_ISAX_OFFS+0x00020000)
#define	MACE_ISA_GAME_OFFS	(MACE_ISAX_OFFS+0x00030000)

license: unknown


code:
/*	$OpenBSD: macebusvar.h,v 1.1 2009/10/26 18:01:40 miod Exp $	*/

/*
 * Copyright (c) 2001-2003 Opsycon AB  (www.opsycon.se / www.opsycon.com)
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 *
 */

#ifndef	_MACEBUSVAR_H_
#define	_MACEBUSVAR_H_

#include <machine/bus.h>

extern bus_space_t macebus_tag;
extern struct machine_bus_dma_tag mace_bus_dma_tag;

struct macebus_attach_args {
	char		*maa_name;

	bus_space_tag_t  maa_iot;
	bus_space_tag_t  maa_memt;
	bus_dma_tag_t	 maa_dmat;
	bus_addr_t	 maa_baseaddr;
	int	 	 maa_intr;		/* crime intr bit */
	uint32_t	 maa_mace_intr;		/* narrowing mace intr mask */
};

void   *macebus_intr_establish(int, uint32_t, int, int, int (*)(void *),
	    void *, const char *);
void	macebus_intr_disestablish(void *);

#endif	/* _MACEBUSVAR_H_ */


code:
/*	$OpenBSD: tcc.c,v 1.5 2014/05/19 21:18:42 miod Exp $	*/

/*
 * Copyright (c) 2012 Miodrag Vallat.
 *
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

/*
 * POWER Indigo2 TBus Cache Controller (Stream Cache) support code.
 */

#include <sys/param.h>
#include <sys/device.h>
#include <sys/systm.h>
#include <sys/kernel.h>

#include <uvm/uvm_extern.h>

#include <mips64/archtype.h>
#include <mips64/cache.h>

#include <machine/autoconf.h>
#include <machine/bus.h>
#include <machine/cpu.h>
#include <mips64/mips_cpu.h>

#include <sgi/sgi/ip22.h>
#include <sgi/localbus/tccreg.h>
#include <sgi/localbus/tccvar.h>

int	tcc_match(struct device *, void *, void *);
void	tcc_attach(struct device *, struct device *, void *);

const struct cfattach tcc_ca = {
	sizeof(struct device), tcc_match, tcc_attach
};

struct cfdriver tcc_cd = {
	NULL, "tcc", DV_DULL
};

uint32_t tcc_bus_error(uint32_t, struct trap_frame *);

CACHE_PROTOS(tcc)

int
tcc_match(struct device *parent, void *match, void *aux)
{
	struct mainbus_attach_args *maa = (void *)aux;

	switch (sys_config.system_type) {
	case SGI_IP26:
		return strcmp(maa->maa_name, tcc_cd.cd_name) == 0;
	default:
		return 0;
	}
}

void
tcc_attach(struct device *parent, struct device *self, void *aux)
{
	uint32_t ctrl, rev;

	ctrl = (uint32_t)tcc_read(TCC_GCACHE_CTRL);
	rev = (ctrl & TCC_GCACHE_REV_MASK) >> TCC_GCACHE_REV_SHIFT;
	printf(": streaming cache revision %d\n", rev);

	tcc_bus_reset();

	/* Enable bus error and machine check interrupts. */
	set_intr(INTPRI_BUSERR_TCC, CR_BERR, tcc_bus_error);
	tcc_write(TCC_INTR, TCC_INTR_MCHECK_ENAB | TCC_INTR_BERR_ENAB);

	/* Enable all cache sets. */
	tcc_write(TCC_GCACHE_CTRL, (ctrl | TCC_GCACHE_SET_ALL) &
	    ~TCC_GCACHE_DISABLE_WB);

	/* Enable prefetching. */
	tcc_prefetch_enable();
}

void
tcc_bus_reset()
{
	tcc_write(TCC_INTR, (tcc_read(TCC_INTR) & TCC_INTR_ENABLE_MASK) |
	    TCC_INTR_MCHECK | TCC_INTR_BERR);
	tcc_write(TCC_ERROR, TCC_ERROR_NESTED_MCHECK | TCC_ERROR_NESTED_BERR);
}

uint32_t
tcc_bus_error(uint32_t hwpend, struct trap_frame *tf)
{
	uint64_t intr, error, addr, errack;

	intr = tcc_read(TCC_INTR);
	error = tcc_read(TCC_ERROR);
	addr = tcc_read(TCC_BERR_ADDR);

	printf("tcc bus error: intr %llx error %llx (%llu) addr %08llx\n",
	    intr, error, (error & TCC_ERROR_TYPE_MASK) >> TCC_ERROR_TYPE_SHIFT,
	    addr);

	/* Ack error condition */
	errack = 0;
	if (intr & TCC_INTR_MCHECK)
		errack |= TCC_ERROR_NESTED_MCHECK;
	if (intr & TCC_INTR_BERR)
		errack |= TCC_ERROR_NESTED_BERR;
	tcc_write(TCC_INTR, (intr & TCC_INTR_ENABLE_MASK) |
	    (intr & (TCC_INTR_MCHECK | TCC_INTR_BERR)));
	tcc_write(TCC_ERROR, errack);

	cp0_reset_cause(CR_BERR);
	return hwpend;
}

/*
 * Cache maintainance routines
 */

#define	tcc_cache_hit(addr,op) \
__asm__ volatile ("lw $0, %0(%1)" :: "i" (TCC_CACHEOP_HIT), \
    "r" (PHYS_TO_XKPHYS(TCC_CACHEOP_BASE | (addr) | (op), CCA_NC)) : "memory")
#define	tcc_cache_index(s,i,op) \
__asm__ volatile ("lw $0, %0(%1)" :: "i" (TCC_CACHEOP_INDEX | (op)), \
    "r" (PHYS_TO_XKPHYS(TCC_CACHEOP_BASE | ((s) << TCC_CACHEOP_SET_SHIFT) | \
	 ((i) << TCC_CACHEOP_INDEX_SHIFT), CCA_NC)) : "memory")

void tcc_virtual(struct cpu_info *, vaddr_t, vsize_t, uint64_t);

void
tcc_ConfigCache(struct cpu_info *ci)
{
	struct cache_info l2;

	l2 = ci->ci_l2;

	tfp_ConfigCache(ci);

	if (l2.size != 0) {
		ci->ci_l2 = l2;

		ci->ci_SyncCache = tcc_SyncCache;
		ci->ci_SyncDCachePage = tcc_SyncDCachePage;
		ci->ci_HitSyncDCache = tcc_HitSyncDCache;
		ci->ci_HitInvalidateDCache = tcc_HitInvalidateDCache;
		ci->ci_IOSyncDCache = tcc_IOSyncDCache;
	}
}

void
tcc_SyncCache(struct cpu_info *ci)
{
	uint64_t idx;

	mips_sync();
	tfp_InvalidateICache(ci, 0, ci->ci_l1inst.size);

	/*
	 * The following relies upon the fact that the (line, set)
	 * fields are contiguous. Therefore by pretending there is
	 * a huge number of sets and only one line, we can span the
	 * whole cache.
	 */
	idx = (uint64_t)ci->ci_l2.size / TCC_CACHE_LINE;
	while (idx != 0) {
		idx--;
		tcc_cache_index(idx, 0,
		    TCC_CACHEOP_WRITEBACK | TCC_CACHEOP_INVALIDATE);
	}
	tcc_prefetch_invalidate();
}

void
tcc_SyncDCachePage(struct cpu_info *ci, vaddr_t va, paddr_t pa)
{
	vaddr_t epa;

	mips_sync();
	epa = pa + PAGE_SIZE;
	do {
		tcc_cache_hit(pa,
		    TCC_CACHEOP_WRITEBACK | TCC_CACHEOP_INVALIDATE);
		pa += TCC_CACHE_LINE;
	} while (pa != epa);
	tcc_prefetch_invalidate();
}

void
tcc_virtual(struct cpu_info *ci, vaddr_t va, vsize_t sz, uint64_t op)
{
	paddr_t pa;

	if (IS_XKPHYS(va)) {
		pa = XKPHYS_TO_PHYS(va);

		while (sz != 0) {
			tcc_cache_hit(pa, op);
			pa += TCC_CACHE_LINE;
			sz -= TCC_CACHE_LINE;
		}
		return;
	}

	while (sz != 0) {
		/* get the proper physical address */
		if (pmap_extract(pmap_kernel(), va, &pa) == 0) {
#ifdef DIAGNOSTIC
			panic("%s: invalid va %p", __func__, (void *)va);
#else
			/* should not happen */
#endif
		}

		while (sz != 0) {
			tcc_cache_hit(pa, op);
			pa += TCC_CACHE_LINE;
			va += TCC_CACHE_LINE;
			sz -= TCC_CACHE_LINE;
			if (sz == 0)
				return;
			if ((va & PAGE_MASK) == 0)
				break;	/* need new pmap_extract() */
		}
	}
}

void
tcc_HitSyncDCache(struct cpu_info *ci, vaddr_t _va, size_t _sz)
{
	vaddr_t va;
	vsize_t sz;

	mips_sync();

	/* extend the range to integral cache lines */
	va = _va & ~(TCC_CACHE_LINE - 1);
	sz = ((_va + _sz + TCC_CACHE_LINE - 1) & ~(TCC_CACHE_LINE - 1)) - va;

	tcc_virtual(ci, va, sz, TCC_CACHEOP_WRITEBACK | TCC_CACHEOP_INVALIDATE);
	tcc_prefetch_invalidate();
}

void
tcc_HitInvalidateDCache(struct cpu_info *ci, vaddr_t _va, size_t _sz)
{
	vaddr_t va;
	vsize_t sz;

	mips_sync();

	/* extend the range to integral cache lines */
	va = _va & ~(TCC_CACHE_LINE - 1);
	sz = ((_va + _sz + TCC_CACHE_LINE - 1) & ~(TCC_CACHE_LINE - 1)) - va;

	tcc_virtual(ci, va, sz, TCC_CACHEOP_INVALIDATE);
	tcc_prefetch_invalidate();
}

void
tcc_IOSyncDCache(struct cpu_info *ci, vaddr_t _va, size_t _sz, int how)
{
	vaddr_t va;
	vsize_t sz;
	int partial_start, partial_end;

	mips_sync();

	/* extend the range to integral cache lines */
	va = _va & ~(TCC_CACHE_LINE - 1);
	sz = ((_va + _sz + TCC_CACHE_LINE - 1) & ~(TCC_CACHE_LINE - 1)) - va;

	switch (how) {
	default:
	case CACHE_SYNC_R:
		/* writeback partial cachelines */
		if (((_va | _sz) & (TCC_CACHE_LINE - 1)) != 0) {
			partial_start = va != _va;
			partial_end = va + sz != _va + _sz;
		} else {
			partial_start = partial_end = 0;
		}
		if (partial_start) {
			tcc_virtual(ci, va, TCC_CACHE_LINE,
			    TCC_CACHEOP_WRITEBACK | TCC_CACHEOP_INVALIDATE);
			va += TCC_CACHE_LINE;
			sz -= TCC_CACHE_LINE;
		}
		if (sz != 0 && partial_end) {
			sz -= TCC_CACHE_LINE;
			tcc_virtual(ci, va + sz, TCC_CACHE_LINE,
			    TCC_CACHEOP_WRITEBACK | TCC_CACHEOP_INVALIDATE);
		}
		if (sz != 0)
			tcc_virtual(ci, va, sz, TCC_CACHEOP_INVALIDATE);

		tcc_prefetch_invalidate();
		break;

	case CACHE_SYNC_X:
		tcc_virtual(ci, va, sz, TCC_CACHEOP_WRITEBACK);
		break;

	case CACHE_SYNC_W:
		tcc_virtual(ci, va, sz,
		    TCC_CACHEOP_WRITEBACK | TCC_CACHEOP_INVALIDATE);
		tcc_prefetch_invalidate();
		break;
	}
}


code:
/*	$OpenBSD: tccreg.h,v 1.1 2012/09/29 21:46:02 miod Exp $	*/

/*
 * Copyright (c) 2012 Miodrag Vallat.
 *
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

/*
 * POWER Indigo2 TBus Cache Controller (Stream Cache) support code.
 */

#define	TCC_BASE		0x0000000018000000UL

/*
 * TCC Control Registers
 */

#define	TCC_FIFO_CTRL		0x0000000

#define	TCC_GCACHE_CTRL		0x0000008
#define	TCC_GCACHE_SET_0		0x00000001 /* cache set enables */
#define	TCC_GCACHE_SET_1		0x00000002
#define	TCC_GCACHE_SET_2		0x00000004
#define	TCC_GCACHE_SET_3		0x00000008
#define	TCC_GCACHE_SET_ALL		0x0000000f
#define	TCC_GCACHE_DISABLE_WB		0x00000020 /* prevents writebacks */
#define	TCC_GCACHE_REV_MASK		0x03c00000
#define	TCC_GCACHE_REV_SHIFT		18

#define	TCC_INTR		0x0000010
#define	TCC_INTR_MASK_0			0x00000001
#define	TCC_INTR_MASK_1			0x00000002
#define	TCC_INTR_MASK_2			0x00000004
#define	TCC_INTR_MASK_3			0x00000008
#define	TCC_INTR_MASK_4			0x00000010
#define	TCC_INTR_MASK_5			0x00000020
#define	TCC_INTR_GFX_FIFO_HIWAT		0x00000040
#define	TCC_INTR_GFX_FIFO_LOWAT		0x00000080
#define	TCC_INTR_COUNTER		0x00000100
#define	TCC_INTR_BERR			0x00000200
#define	TCC_INTR_MCHECK			0x00000400
#define	TCC_INTR_PENDING_MASK		0x000007ff
#define	TCC_INTR_GFX_FIFO_HIWAT_ENAB	0x00000800
#define	TCC_INTR_GFX_FIFO_LOWAT_ENAB	0x00001000
#define	TCC_INTR_COUNTER_ENAB		0x00002000
#define	TCC_INTR_BERR_ENAB		0x00004000
#define	TCC_INTR_MCHECK_ENAB		0x00008000
#define	TCC_INTR_ENABLE_MASK		0x0000f800

#define	TCC_BERR_ADDR		0x0000018

#define	TCC_ERROR		0x0000020
#define	TCC_ERROR_NESTED_BERR		0x00000001
#define	TCC_ERROR_NESTED_MCHECK		0x00000002
#define	TCC_ERROR_TYPE_MASK		0x0000001c
#define	TCC_ERROR_TYPE_SHIFT		2

#define	TCC_ERROR_TYPE_TDB_PAR		0
#define	TCC_ERROR_TYPE_TCC_PAR		1
#define	TCC_ERROR_TYPE_CMD_PAR		2
#define	TCC_ERROR_TYPE_SYSAD		3
#define	TCC_ERROR_TYPE_GCACHE_PAR	4
#define	TCC_ERROR_TYPE_TBUS		5

#define	TCC_PARITY_CTRL		0x0000028

#define	TCC_COUNT		0x0000030

#define	TCC_COMPARE		0x0000038

#define	TCC_PREFETCH_CTRL	0x0010038

#define	TCC_PREFETCH_INVALIDATE		0x0000000000000001 /* one-shot */
#define	TCC_PREFETCH_TIMEOUT_MASK	0x000000000000001e
#define	TCC_PREFETCH_TIMEOUT_MAX	15
#define	TCC_PREFETCH_TIMEOUT_SHIFT	1
#define	TCC_PREFETCH_BUF_A_ENABLE	0x0000000000000020
#define	TCC_PREFETCH_BUF_B_ENABLE	0x0000000000000040
#define	TCC_PREFETCH_QUEUE_DEPTH_MASK	0x0000000000000380
#define	TCC_PREFETCH_QUEUE_DEPTH_MAX	7
#define	TCC_PREFETCH_QUEUE_DEPTH_SHIFT	7

/*
 * The following default prefetch settings has been borrowed from IRIX's
 * <sys/IP26.h>. We can only assume these settings are optimal for most
 * workloads.
 */
#define	TCC_PREFETCH_ENABLE \
    (TCC_PREFETCH_BUF_A_ENABLE | TCC_PREFETCH_BUF_B_ENABLE | \
     (3 << TCC_PREFETCH_TIMEOUT_SHIFT) | \
     (TCC_PREFETCH_QUEUE_DEPTH_MAX << TCC_PREFETCH_QUEUE_DEPTH_SHIFT))

/*
 * TCC Cache Operations
 */

#define	TCC_CACHEOP_BASE	0x0000000100000000UL

#define	TCC_CACHEOP_HIT			0x00000000
#define	TCC_CACHEOP_INDEX		0x00000004
#define	TCC_CACHEOP_WRITEBACK		0x00000008
#define	TCC_CACHEOP_INVALIDATE		0x00000010
#define	TCC_CACHEOP_SET_SHIFT		5
#define	TCC_CACHEOP_INDEX_SHIFT		7
#define	TCC_CACHE_SETS			4
#define	TCC_CACHE_LINE			128UL


code:
/*	$OpenBSD: tccvar.h,v 1.1 2012/09/29 21:46:02 miod Exp $	*/

/*
 * Copyright (c) 2012 Miodrag Vallat.
 *
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

#define	tcc_read(o) \
	*(volatile uint64_t *)PHYS_TO_XKPHYS(TCC_BASE + (o), CCA_NC)
#define	tcc_write(o,v) \
	*(volatile uint64_t *)PHYS_TO_XKPHYS(TCC_BASE + (o), CCA_NC) = (v)

static __inline__ void
tcc_prefetch_disable(void)
{
	tcc_write(TCC_PREFETCH_CTRL, TCC_PREFETCH_INVALIDATE);
}

static __inline__ void
tcc_prefetch_enable(void)
{
	tcc_write(TCC_PREFETCH_CTRL, TCC_PREFETCH_ENABLE);
}

static __inline__ void
tcc_prefetch_invalidate(void)
{
	tcc_write(TCC_PREFETCH_CTRL,
	    tcc_read(TCC_PREFETCH_CTRL) | TCC_PREFETCH_INVALIDATE);
}

void tcc_bus_reset(void);


code: src/sys/arch/sgi/pci

path: sys/arch/sgi/pci/CVS/Repository


code:
/*	$OpenBSD: ioc.c,v 1.39 2014/05/19 21:18:42 miod Exp $	*/

/*
 * Copyright (c) 2008 Joel Sing.
 * Copyright (c) 2008, 2009, 2010 Miodrag Vallat.
 *
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

/*
 * IOC3 device driver.
 */

#include <sys/param.h>
#include <sys/systm.h>
#include <sys/kernel.h>
#include <sys/device.h>
#include <sys/malloc.h>

#include <mips64/archtype.h>
#include <machine/autoconf.h>
#include <machine/bus.h>

#ifdef TGT_ORIGIN
#include <sgi/sgi/ip27.h>
#include <sgi/sgi/l1.h>
#endif

#include <dev/pci/pcireg.h>
#include <dev/pci/pcivar.h>
#include <dev/pci/pcidevs.h>

#include <sgi/pci/iocreg.h>
#include <sgi/pci/iocvar.h>

#include <dev/onewire/onewirereg.h>
#include <dev/onewire/onewirevar.h>

#include <sgi/dev/owmacvar.h>
#include <sgi/dev/owserialvar.h>

int	ioc_match(struct device *, void *, void *);
void	ioc_attach(struct device *, struct device *, void *);

struct ioc_intr {
	struct ioc_softc	*ii_ioc;

	int			 (*ii_func)(void *);
	void			*ii_arg;

	struct evcount		 ii_count;
	int			 ii_level;
};

struct ioc_softc {
	struct device		 sc_dev;

	bus_space_tag_t		 sc_memt;
	bus_space_handle_t	 sc_memh;
	bus_dma_tag_t		 sc_dmat;
	pci_chipset_tag_t	 sc_pc;
	pcitag_t		 sc_tag;

	void			*sc_ih_enet;	/* Ethernet interrupt */
	void			*sc_ih_superio;	/* SuperIO interrupt */
	struct ioc_intr		*sc_intr[IOC_NDEVS];

	struct onewire_bus	 sc_bus;

	struct owmac_softc	*sc_owmac;
	struct owserial_softc	*sc_owserial;

	int			 sc_attach_flags;
};

struct cfattach ioc_ca = {
	sizeof(struct ioc_softc), ioc_match, ioc_attach,
};

struct cfdriver ioc_cd = {
	NULL, "ioc", DV_DULL,
};

void	ioc_attach_child(struct ioc_softc *, const char *, bus_addr_t, int);
int	ioc_search_onewire(struct device *, void *, void *);
int	ioc_search_mundane(struct device *, void *, void *);
int	ioc_print(void *, const char *);

int	ioc_intr_dispatch(struct ioc_softc *, int);
int	ioc_intr_ethernet(void *);
int	ioc_intr_shared(void *);
int	ioc_intr_superio(void *);

int	iocow_reset(void *);
int	iocow_read_bit(struct ioc_softc *);
int	iocow_send_bit(void *, int);
int	iocow_read_byte(void *);
int	iocow_triplet(void *, int);
int	iocow_pulse(struct ioc_softc *, int, int);

#ifdef TGT_ORIGIN
/*
 * A mask of nodes on which an ioc driver has attached.
 * We use this on IP35 systems, to prevent attaching a pci IOC3 card which NIC
 * has failed, as the onboard IOC3.
 * XXX This obviously will not work in N mode... but then IP35 are supposed to
 * XXX always run in M mode.
 */
static	uint64_t ioc_nodemask = 0;
#endif

int
ioc_match(struct device *parent, void *match, void *aux)
{
	struct pci_attach_args *pa = aux;

	if (PCI_VENDOR(pa->pa_id) == PCI_VENDOR_SGI &&
	    PCI_PRODUCT(pa->pa_id) == PCI_PRODUCT_SGI_IOC3)
		return (1);

	return (0);
}

int
ioc_print(void *aux, const char *iocname)
{
	struct ioc_attach_args *iaa = aux;

	if (iocname != NULL)
		printf("%s at %s", iaa->iaa_name, iocname);

	/* no base for onewire, and don't display it for rtc */
	if ((int)iaa->iaa_base > 0 && (int)iaa->iaa_base < IOC3_BYTEBUS_0)
		printf(" base 0x%lx", iaa->iaa_base);

	return (UNCONF);
}

void
ioc_attach(struct device *parent, struct device *self, void *aux)
{
	struct ioc_softc *sc = (struct ioc_softc *)self;
	struct pci_attach_args *pa = aux;
	pci_intr_handle_t ih_enet, ih_superio;
	bus_space_tag_t memt;
	bus_space_handle_t memh;
	bus_size_t memsize;
	pcireg_t data;
	int has_superio, has_enet, is_obio;
	int subdevice_mask;
	bus_addr_t rtcbase;

	if (pci_mapreg_map(pa, PCI_MAPREG_START, PCI_MAPREG_TYPE_MEM, 0,
	    &memt, &memh, NULL, &memsize, 0)) {
		printf(": can't map mem space\n");
		return;
	}

	sc->sc_pc = pa->pa_pc;
	sc->sc_tag = pa->pa_tag;
	sc->sc_dmat = pa->pa_dmat;

	/*
	 * Initialise IOC3 ASIC.
	 */
	data = pci_conf_read(pa->pa_pc, pa->pa_tag, PCI_COMMAND_STATUS_REG);
	data |= PCI_COMMAND_MEM_ENABLE | PCI_COMMAND_PARITY_ENABLE |
	    PCI_COMMAND_SERR_ENABLE;
	data &= ~PCI_COMMAND_INTERRUPT_DISABLE;
	pci_conf_write(pa->pa_pc, pa->pa_tag, PCI_COMMAND_STATUS_REG, data);

	printf("\n");

	sc->sc_memt = memt;
	sc->sc_memh = memh;

	/*
	 * Attach the 1-Wire bus now, so that we can get our own part
	 * number and deduce which devices are really available on the
	 * board.
	 */

	bus_space_write_4(sc->sc_memt, sc->sc_memh, IOC3_GPCR_S,
	    IOC3_GPCR_MLAN);
	(void)bus_space_read_4(sc->sc_memt, sc->sc_memh, IOC3_GPCR_S);
	config_search(ioc_search_onewire, self, aux);

	/*
	 * Now figure out what our configuration is.
	 */

	has_superio = has_enet = 0;
	is_obio = 0;
	subdevice_mask = 0;
	if (sc->sc_owserial != NULL) {
		if (strncmp(sc->sc_owserial->sc_product, "030-0873-", 9) == 0) {
			/*
			 * MENET board; these attach as four ioc devices
			 * behind an xbridge. However the fourth one lacks
			 * the superio chip.
			 */
			subdevice_mask = (1 << IOCDEV_EF);
			has_enet = 1;
			if (pa->pa_device != 3) {
				subdevice_mask |= (1 << IOCDEV_SERIAL_A) |
				    (1 << IOCDEV_SERIAL_B);
				has_superio = 1;
			}
		} else
		if (strncmp(sc->sc_owserial->sc_product, "030-0891-", 9) == 0) {
			/* IP30 on-board IOC3 */
			subdevice_mask = (1 << IOCDEV_SERIAL_A) |
			    (1 << IOCDEV_SERIAL_B) | (1 << IOCDEV_LPT) |
			    (1 << IOCDEV_KBC) | (1 << IOCDEV_RTC) |
			    (1 << IOCDEV_EF);
			rtcbase = IOC3_BYTEBUS_1;
			has_superio = has_enet = 1;
			is_obio = 1;
		} else
		if (strncmp(sc->sc_owserial->sc_product, "030-1155-", 9) == 0) {
			/* CADDuo board */
			subdevice_mask = (1 << IOCDEV_KBC) | (1 << IOCDEV_EF);
			has_superio = has_enet = 1;
		} else
		if (strncmp(sc->sc_owserial->sc_product, "030-1657-", 9) == 0 ||
		    strncmp(sc->sc_owserial->sc_product, "030-1664-", 9) == 0) {
			/* PCI_SIO_UFC dual serial board */
			subdevice_mask = (1 << IOCDEV_SERIAL_A) |
			    (1 << IOCDEV_SERIAL_B);
			has_superio = 1;
		} else
			goto unknown;
	} else {
#ifdef TGT_ORIGIN
		/*
		 * If no owserial device has been found, then it is
		 * very likely that we are the on-board IOC3 found
		 * on IP27 and IP35 systems, unless we have already
		 * found an on-board IOC3 on this node.
		 *
		 * Origin 2000 (real IP27) systems are a real annoyance,
		 * because they actually have two IOC3 on their BASEIO
		 * board, with the various devices split accross them
		 * (two IOC3 chips are needed to provide the four serial
		 * ports). We can rely upon the PCI device numbers (2 and 6)
		 * to tell onboard IOC3 from PCI IOC3 devices.
		 */
		switch (sys_config.system_type) {
		case SGI_IP27:
			switch (sys_config.system_subtype) {
			case IP27_O2K:
				if (pci_get_widget(sc->sc_pc) ==
				    IP27_O2K_BRIDGE_WIDGET)
					switch (pa->pa_device) {
					case IP27_IOC_SLOTNO:
						subdevice_mask =
						    (1 << IOCDEV_SERIAL_A) |
						    (1 << IOCDEV_SERIAL_B) |
						    (1 << IOCDEV_RTC) |
						    (1 << IOCDEV_EF);
						break;
					case IP27_IOC2_SLOTNO:
						subdevice_mask =
						    (1 << IOCDEV_SERIAL_A) |
						    (1 << IOCDEV_SERIAL_B) |
						    (1 << IOCDEV_LPT) |
#if 0 /* not worth doing */
						    (1 << IOCDEV_RTC) |
#endif
						    (1 << IOCDEV_KBC);
						break;
					default:
						break;
					}
				break;
			case IP27_O200:
				if (pci_get_widget(sc->sc_pc) ==
				    IP27_O200_BRIDGE_WIDGET)
					switch (pa->pa_device) {
					case IP27_IOC_SLOTNO:
						subdevice_mask =
						    (1 << IOCDEV_SERIAL_A) |
						    (1 << IOCDEV_SERIAL_B) |
						    (1 << IOCDEV_LPT) |
						    (1 << IOCDEV_KBC) |
						    (1 << IOCDEV_RTC) |
						    (1 << IOCDEV_EF);
						break;
					default:
						break;
					}
				break;
			default:
				break;
			}
			break;
		case SGI_IP35:
			if (!ISSET(ioc_nodemask, 1UL << currentnasid)) {
				SET(ioc_nodemask, 1UL << currentnasid);

				switch (sys_config.system_subtype) {
				/*
				 * Origin 300 onboard IOC3 do not have PS/2
				 * ports; since they can only be connected to
				 * other 300 or 350 bricks (the latter using
				 * IOC4 devices), it is safe to do this
				 * regardless of the current nasid.
				 * XXX What about Onyx 300 though???
				 */
				case IP35_O300:
					subdevice_mask =
					    (1 << IOCDEV_SERIAL_A) |
					    (1 << IOCDEV_SERIAL_B) |
					    (1 << IOCDEV_LPT) |
					    (1 << IOCDEV_RTC) |
					    (1 << IOCDEV_EF);
					break;
				/*
				 * Origin 3000 I-Bricks have only one serial
				 * port, and no keyboard or parallel ports.
				 */
				case IP35_CBRICK:
					subdevice_mask =
					    (1 << IOCDEV_SERIAL_A) |
					    (1 << IOCDEV_RTC) |
					    (1 << IOCDEV_EF);
					break;
				default:
					subdevice_mask =
					    (1 << IOCDEV_SERIAL_A) |
					    (1 << IOCDEV_SERIAL_B) |
					    (1 << IOCDEV_LPT) |
					    (1 << IOCDEV_KBC) |
					    (1 << IOCDEV_RTC) |
					    (1 << IOCDEV_EF);
					break;
				}
			}
			break;
		default:
			break;
		}

		if (subdevice_mask != 0) {
			rtcbase = IOC3_BYTEBUS_0;
			has_superio = 1;
			if (ISSET(subdevice_mask, 1 << IOCDEV_EF))
				has_enet = 1;
			is_obio = 1;
		} else
#endif	/* TGT_ORIGIN */
		{
unknown:
			/*
			 * Well, we don't really know what kind of device
			 * we are.  We should probe various registers
			 * to figure out, but for now we'll just
			 * chicken out.
			 */
			printf("%s: unknown flavour\n", self->dv_xname);
			return;
		}
	}

	/*
	 * Acknowledge all pending interrupts, and disable them.
	 * Be careful not all registers may be wired depending on what
	 * devices are actually present.
	 */

	if (has_superio) {
		bus_space_write_4(sc->sc_memt, sc->sc_memh, IOC3_SIO_IEC, ~0x0);
		bus_space_write_4(sc->sc_memt, sc->sc_memh, IOC3_SIO_IES, 0x0);
		bus_space_write_4(sc->sc_memt, sc->sc_memh, IOC3_SIO_IR,
		    bus_space_read_4(sc->sc_memt, sc->sc_memh, IOC3_SIO_IR));
	}
	if (has_enet) {
		bus_space_write_4(sc->sc_memt, sc->sc_memh, IOC3_ENET_IER, 0);
	}

	/*
	 * IOC3 is not a real PCI device - it's a poor wrapper over a set
	 * of convenience chips. And when it is in full-blown configuration,
	 * it actually needs to use two interrupts, one for the superio
	 * chip, and the other for the Ethernet chip.
	 *
	 * This would not be a problem if the device advertized itself
	 * as a multifunction device. But it doesn't...
	 *
	 * Fortunately, the interrupt used are simply interrupt pins A
	 * and B; so with the help of the PCI bridge driver, we can
	 * register the two interrupts and almost pretend things are
	 * as normal as they could be.
	 */

	if (has_enet) {
		pa->pa_intrpin = PCI_INTERRUPT_PIN_A;
		if (pci_intr_map(pa, &ih_enet) != 0) {
			printf("%s: failed to map ethernet interrupt\n",
			    self->dv_xname);
			goto unmap;
		}
	}

	if (has_superio) {
		if (has_enet)
			pa->pa_intrpin =
			    is_obio ? PCI_INTERRUPT_PIN_D : PCI_INTERRUPT_PIN_B;
		else
			pa->pa_intrpin = PCI_INTERRUPT_PIN_A;

		if (pci_intr_map(pa, &ih_superio) != 0) {
			printf("%s: failed to map superio interrupt\n",
			    self->dv_xname);
			goto unmap;
		}
	}

	if (has_enet) {
		sc->sc_ih_enet = pci_intr_establish(sc->sc_pc, ih_enet,
		    IPL_NET, ioc_intr_ethernet, sc, self->dv_xname);
		if (sc->sc_ih_enet == NULL) {
			printf("%s: failed to establish ethernet interrupt "
			    "at %s\n", self->dv_xname,
			    pci_intr_string(sc->sc_pc, ih_enet));
			goto unmap;
		}
	}

	if (has_superio) {
		sc->sc_ih_superio = pci_intr_establish(sc->sc_pc, ih_superio,
		    IPL_TTY, ioc_intr_superio, sc, self->dv_xname);
		if (sc->sc_ih_superio == NULL) {
			printf("%s: failed to establish superio interrupt "
			    "at %s\n", self->dv_xname,
			    pci_intr_string(sc->sc_pc, ih_superio));
			goto unregister;
		}
	}

	if (has_enet)
		printf("%s: ethernet %s\n",
		    self->dv_xname, pci_intr_string(sc->sc_pc, ih_enet));
	if (has_superio)
		printf("%s: superio %s\n",
		    self->dv_xname, pci_intr_string(sc->sc_pc, ih_superio));

	/*
	 * Attach other sub-devices.
	 */

	sc->sc_attach_flags = is_obio ? IOC_FLAGS_OBIO : 0;

	if (ISSET(subdevice_mask, 1 << IOCDEV_SERIAL_A)) {
		/*
		 * Put serial ports in passthrough mode,
		 * to use the MI com(4) 16550 support.
		 */
		bus_space_write_4(sc->sc_memt, sc->sc_memh, IOC3_UARTA_SSCR, 0);
		bus_space_write_4(sc->sc_memt, sc->sc_memh, IOC3_UARTB_SSCR, 0);

		bus_space_write_4(sc->sc_memt, sc->sc_memh,
		    IOC3_UARTA_SHADOW, 0);
		bus_space_write_4(sc->sc_memt, sc->sc_memh,
		    IOC3_UARTB_SHADOW, 0);

		ioc_attach_child(sc, "com", IOC3_UARTA_BASE, IOCDEV_SERIAL_A);
		if (ISSET(subdevice_mask, 1 << IOCDEV_SERIAL_B))
			ioc_attach_child(sc, "com", IOC3_UARTB_BASE,
			    IOCDEV_SERIAL_B);
	}
	if (ISSET(subdevice_mask, 1 << IOCDEV_KBC))
		ioc_attach_child(sc, "iockbc", 0, IOCDEV_KBC);
	if (ISSET(subdevice_mask, 1 << IOCDEV_EF))
		ioc_attach_child(sc, "iec", 0, IOCDEV_EF);
	if (ISSET(subdevice_mask, 1 << IOCDEV_LPT))
		ioc_attach_child(sc, "lpt", 0, IOCDEV_LPT);
	if (ISSET(subdevice_mask, 1 << IOCDEV_RTC))
		ioc_attach_child(sc, "dsrtc", rtcbase, IOCDEV_RTC);

	return;

unregister:
	if (has_enet)
		pci_intr_disestablish(sc->sc_pc, sc->sc_ih_enet);
unmap:
	bus_space_unmap(memt, memh, memsize);
}

void
ioc_attach_child(struct ioc_softc *sc, const char *name, bus_addr_t base,
    int dev)
{
	struct ioc_attach_args iaa;

	memset(&iaa, 0, sizeof iaa);

	iaa.iaa_name = name;
	pci_get_device_location(sc->sc_pc, sc->sc_tag, &iaa.iaa_location);
	iaa.iaa_memt = sc->sc_memt;
	iaa.iaa_memh = sc->sc_memh;
	iaa.iaa_dmat = sc->sc_dmat;
	iaa.iaa_base = base;
	iaa.iaa_dev = dev;
	iaa.iaa_flags = sc->sc_attach_flags;

	if (dev == IOCDEV_EF) {
		if (sc->sc_owmac != NULL)
			memcpy(iaa.iaa_enaddr, sc->sc_owmac->sc_enaddr, 6);
		else {
#ifdef TGT_ORIGIN
			/*
			 * On IP35 class machines, there are no
			 * Number-In-a-Can attached to the onboard
			 * IOC3; instead, the Ethernet address is
			 * stored in the Brick EEPROM, and can be
			 * retrieved with an L1 controller query.
			 */
			if (sys_config.system_type != SGI_IP35 ||
			    l1_get_brick_ethernet_address(currentnasid,
			      iaa.iaa_enaddr) != 0)
#endif
				memset(iaa.iaa_enaddr, 0xff, 6);
		}
	}

	config_found_sm(&sc->sc_dev, &iaa, ioc_print, ioc_search_mundane);
}

int
ioc_search_mundane(struct device *parent, void *vcf, void *args)
{
	struct cfdata *cf = vcf;
	struct ioc_attach_args *iaa = (struct ioc_attach_args *)args;

	if (strcmp(cf->cf_driver->cd_name, iaa->iaa_name) != 0)
		return 0;

	if (cf->cf_loc[0] != -1 && cf->cf_loc[0] != (int)iaa->iaa_base)
		return 0;

	return (*cf->cf_attach->ca_match)(parent, cf, iaa);
}

/*
 * Number-In-a-Can access driver (1-Wire interface through IOC)
 */

int
ioc_search_onewire(struct device *parent, void *vcf, void *args)
{
	struct ioc_softc *sc = (struct ioc_softc *)parent;
	struct cfdata *cf = vcf;
	struct onewirebus_attach_args oba;
	struct device *owdev, *dev;
	extern struct cfdriver owmac_cd;
	extern struct cfdriver owserial_cd;
	struct owserial_softc *s;

	if (strcmp(cf->cf_driver->cd_name, "onewire") != 0)
		return 0;

	sc->sc_bus.bus_cookie = sc;
	sc->sc_bus.bus_reset = iocow_reset;
	sc->sc_bus.bus_bit = iocow_send_bit;
	sc->sc_bus.bus_read_byte = iocow_read_byte;
	sc->sc_bus.bus_write_byte = NULL;	/* use default routine */
	sc->sc_bus.bus_read_block = NULL;	/* use default routine */
	sc->sc_bus.bus_write_block = NULL;	/* use default routine */
	sc->sc_bus.bus_triplet = iocow_triplet;
	sc->sc_bus.bus_matchrom = NULL;		/* use default routine */
	sc->sc_bus.bus_search = NULL;		/* use default routine */

	oba.oba_bus = &sc->sc_bus;
	oba.oba_flags = ONEWIRE_SCAN_NOW | ONEWIRE_NO_PERIODIC_SCAN;

	/* In case onewire is disabled in UKC... */
	if ((*cf->cf_attach->ca_match)(parent, cf, &oba) == 0)
		return 0;

	owdev = config_attach(parent, cf, &oba, onewirebus_print);

	/*
	 * Find the first owmac child of the onewire bus, and keep
	 * a pointer to it.  This allows us to pass the ethernet
	 * address to the ethernet subdevice.
	 */
	if (owdev != NULL) {
		TAILQ_FOREACH(dev, &alldevs, dv_list)
			if (dev->dv_parent == owdev &&
			    dev->dv_cfdata->cf_driver == &owmac_cd) {
				sc->sc_owmac = (struct owmac_softc *)dev;
				break;
			}
	}

	/*
	 * Find the first owserial child of the onewire bus not
	 * reporting power supply information, and keep a pointer
	 * to it.  This is a bit overkill since we do not need to
	 * keep the pointer after attach, but it makes that kind
	 * of code contained in the same place.
	 */
	if (owdev != NULL) {
		TAILQ_FOREACH(dev, &alldevs, dv_list)
			if (dev->dv_parent == owdev &&
			    dev->dv_cfdata->cf_driver == &owserial_cd) {
				s = (struct owserial_softc *)dev;
				if (strncmp(s->sc_name, "PWR", 3) == 0)
					continue;
				sc->sc_owserial = s;
				break;
			}
	}

	return 1;
}

int
iocow_reset(void *v)
{
	struct ioc_softc *sc = v;
	return iocow_pulse(sc, 500, 65);
}

int
iocow_read_bit(struct ioc_softc *sc)
{
	return iocow_pulse(sc, 6, 13);
}

int
iocow_send_bit(void *v, int bit)
{
	struct ioc_softc *sc = v;
	int rc;

	if (bit != 0)
		rc = iocow_pulse(sc, 6, 110);
	else
		rc = iocow_pulse(sc, 80, 30);
	return rc;
}

int
iocow_read_byte(void *v)
{
	struct ioc_softc *sc = v;
	unsigned int byte = 0;
	int i;

	for (i = 0; i < 8; i++)
		byte |= iocow_read_bit(sc) << i;

	return byte;
}

int
iocow_triplet(void *v, int dir)
{
	struct ioc_softc *sc = v;
	int rc;

	rc = iocow_read_bit(sc);
	rc <<= 1;
	rc |= iocow_read_bit(sc);

	switch (rc) {
	case 0x0:
		iocow_send_bit(v, dir);
		break;
	case 0x1:
		iocow_send_bit(v, 0);
		break;
	default:
		iocow_send_bit(v, 1);
		break;
	}

	return (rc);
}

int
iocow_pulse(struct ioc_softc *sc, int pulse, int data)
{
	uint32_t mcr_value;

	mcr_value = (pulse << 10) | (data << 2);
	bus_space_write_4(sc->sc_memt, sc->sc_memh, IOC3_MCR, mcr_value);
	do {
		mcr_value =
		    bus_space_read_4(sc->sc_memt, sc->sc_memh, IOC3_MCR);
	} while ((mcr_value & 0x00000002) == 0);

	delay(500);

	return (mcr_value & 1);
}

/*
 * Interrupt handling.
 */

/*
 * List of interrupt bits to enable for each device.
 *
 * For the serial ports, we only enable the passthrough interrupt and
 * let com(4) tinker with the appropriate registers, instead of adding
 * an unnecessary layer there.
 */
static const uint32_t ioc_intrbits[IOC_NDEVS] = {
	IOC3_IRQ_UARTA,
	IOC3_IRQ_UARTB,
	IOC3_IRQ_LPT,
	IOC3_IRQ_KBC,
	0,	/* RTC */
	0	/* Ethernet, handled differently */
};

void *
ioc_intr_establish(void *cookie, u_long dev, int level, int (*func)(void *),
    void *arg, char *name)
{
	struct ioc_softc *sc = cookie;
	struct ioc_intr *ii;

	if (dev < 0 || dev >= IOC_NDEVS)
		return NULL;

	ii = (struct ioc_intr *)malloc(sizeof(*ii), M_DEVBUF, M_NOWAIT);
	if (ii == NULL)
		return NULL;

	ii->ii_ioc = sc;
	ii->ii_func = func;
	ii->ii_arg = arg;
	ii->ii_level = level;

	evcount_attach(&ii->ii_count, name, &ii->ii_level);
	sc->sc_intr[dev] = ii;

	/* enable hardware source if necessary */
	bus_space_write_4(sc->sc_memt, sc->sc_memh, IOC3_SIO_IES,
	    ioc_intrbits[dev]);

	return (ii);
}

int
ioc_intr_superio(void *v)
{
	struct ioc_softc *sc = (struct ioc_softc *)v;
	uint32_t pending, mask;
	int dev;

	pending = bus_space_read_4(sc->sc_memt, sc->sc_memh, IOC3_SIO_IR) &
	    bus_space_read_4(sc->sc_memt, sc->sc_memh, IOC3_SIO_IES);

	if (pending == 0)
		return 0;

	/* Disable pending interrupts */
	bus_space_write_4(sc->sc_memt, sc->sc_memh, IOC3_SIO_IEC, pending);

	for (dev = 0; dev < IOC_NDEVS - 1 /* skip Ethernet */; dev++) {
		mask = pending & ioc_intrbits[dev];
		if (mask != 0) {
			(void)ioc_intr_dispatch(sc, dev);

			/* Ack, then reenable, pending interrupts */
			bus_space_write_4(sc->sc_memt, sc->sc_memh,
			    IOC3_SIO_IR, mask);
			bus_space_write_4(sc->sc_memt, sc->sc_memh,
			    IOC3_SIO_IES, mask);
		}
	}

	return 1;
}

int
ioc_intr_ethernet(void *v)
{
	struct ioc_softc *sc = (struct ioc_softc *)v;

	/* This interrupt source is not shared between several devices. */
	return ioc_intr_dispatch(sc, IOCDEV_EF);
}

int
ioc_intr_shared(void *v)
{
	return ioc_intr_superio(v) | ioc_intr_ethernet(v);
}

int
ioc_intr_dispatch(struct ioc_softc *sc, int dev)
{
	struct ioc_intr *ii;
	int rc = 0;

	/* Call registered interrupt function. */
	if ((ii = sc->sc_intr[dev]) != NULL && ii->ii_func != NULL) {
		rc = (*ii->ii_func)(ii->ii_arg);
		if (rc != 0)
			ii->ii_count.ec_count++;
	}

	return rc;
}


code:
/*	$OpenBSD: iocreg.h,v 1.10 2009/11/18 19:03:27 miod Exp $	*/

/*
 * Copyright (c) 2008 Joel Sing.
 *
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

/*
 * Register definitions for SGI IOC3 ASIC.
 */

#define IOC_NDEVS		6

#define IOCDEV_SERIAL_A		0
#define IOCDEV_SERIAL_B		1
#define IOCDEV_LPT		2
#define IOCDEV_KBC		3
#define IOCDEV_RTC		4
#define IOCDEV_EF		5

/* SuperIO registers */
#define IOC3_SIO_IR		0x0000001c	/* SIO interrupt register */
#define IOC3_SIO_IES		0x00000020	/* SIO interrupt enable */
#define IOC3_SIO_IEC		0x00000024	/* SIO interrupt disable */
#define IOC3_SIO_CR		0x00000028	/* SIO control register */
#define	IOC3_MCR		0x00000030	/* MicroLan control register */

/* GPIO registers */
#define	IOC3_GPCR_S		0x00000034	/* GPIO control bit set */
#define	IOC3_GPCR_C		0x00000038	/* GPIO control bit clear */
#define	IOC3_GPDR		0x0000003c	/* GPIO data */
#define	IOC3_GPPR_BASE		0x00000040	/* 9 GPIO pin registers */
#define	IOC3_GPPR(x)		(IOC3_GPPR_BASE + (x) * 4)

/* Keyboard controller registers. */
#define	IOC3_KBC_CTRL_STATUS	0x0000009c
#define	IOC3_KBC_KBD_RX		0x000000a0
#define	IOC3_KBC_AUX_RX		0x000000a4
#define	IOC3_KBC_KBD_TX		0x000000a8
#define	IOC3_KBC_AUX_TX		0x000000ac

/* Non-16550 mode UART registers */
#define	IOC3_UARTA_SSCR		0x000000b8	/* control register */
#define	IOC3_UARTA_STPIR	0x000000bc	/* TX producer index register */
#define	IOC3_UARTA_STCIR	0x000000c0	/* TX consumer index register */
#define	IOC3_UARTA_SRPIR	0x000000c4	/* RX producer index register */
#define	IOC3_UARTA_SRCIR	0x000000c8	/* RX consumer index register */
#define	IOC3_UARTA_SRTR		0x000000cc	/* receive timer register */
#define	IOC3_UARTA_SHADOW	0x000000d0	/* 16550 shadow register */

#define	IOC3_UARTB_SSCR		0x000000d4
#define	IOC3_UARTB_STPIR	0x000000d8
#define	IOC3_UARTB_STCIR	0x000000dc
#define	IOC3_UARTB_SRPIR	0x000000e0
#define	IOC3_UARTB_SRCIR	0x000000e4
#define	IOC3_UARTB_SRTR		0x000000e8
#define	IOC3_UARTB_SHADOW	0x000000ec

/* Ethernet registers */
#define	IOC3_ENET_MCR		0x000000f0	/* Master Control Register */
#define	IOC3_ENET_ISR		0x000000f4	/* Interrupt Status Register */
#define	IOC3_ENET_IER		0x000000f8	/* Interrupt Enable Register */
#define	IOC3_ENET_RCSR		0x000000fc	/* RX Control and Status Reg. */
#define	IOC3_ENET_RBR_H		0x00000100	/* RX Base Register */
#define	IOC3_ENET_RBR_L		0x00000104
#define	IOC3_ENET_RBAR		0x00000108	/* RX Barrier Register */
#define	IOC3_ENET_RCIR		0x0000010c	/* RX Consumer Index Register */
#define	IOC3_ENET_RPIR		0x00000110	/* RX Producer Index Register */
#define	IOC3_ENET_RTR		0x00000114	/* RX Timer Register */
#define	IOC3_ENET_TCSR		0x00000118	/* TX Control and Status Reg. */
#define	IOC3_ENET_RSR		0x0000011c	/* Random Seed Register */
#define	IOC3_ENET_TCDC		0x00000120	/* TX Collision Detect Counter */
#define	IOC3_ENET_BIR		0x00000124
#define	IOC3_ENET_TBR_H		0x00000128	/* TX Base Register */
#define	IOC3_ENET_TBR_L		0x0000012c
#define	IOC3_ENET_TCIR		0x00000130	/* TX Consumer Index Register */
#define	IOC3_ENET_TPIR		0x00000134	/* TX Producer Index Register */
#define	IOC3_ENET_MAR_H		0x00000138	/* MAC Address Register */
#define	IOC3_ENET_MAR_L		0x0000013c
#define	IOC3_ENET_HAR_H		0x00000140	/* Hash filter Address Reg. */
#define	IOC3_ENET_HAR_L		0x00000144
#define	IOC3_ENET_MICR		0x00000148	/* MII Control Register */
#define	IOC3_ENET_MIDR_R	0x0000014c	/* MII Data Register (read) */
#define	IOC3_ENET_MIDR_W	0x00000150	/* MII Data Register (write) */

/* bits in the SIO interrupt register */
#define	IOC3_IRQ_UARTA		0x00000040	/* UART A passthrough */
#define	IOC3_IRQ_UARTB		0x00008000	/* UART B passthrough */
#define	IOC3_IRQ_LPT		0x00040000	/* parallel port passthrough */
#define	IOC3_IRQ_KBC		0x00400000	/* keyboard controller */

/* bits in GPCR */
#define	IOC3_GPCR_UARTA_PIO	0x00000040	/* UARTA in PIO mode */
#define	IOC3_GPCR_UARTB_PIO	0x00000080	/* UARTB in PIO mode */
#define	IOC3_GPCR_MLAN		0x00200000	/* MicroLan enable */

/* bits in SSCR */
#define	IOC3_SSCR_RESET		0x80000000

/* bits in ENET_MCR */
#define	IOC3_ENET_MCR_DUPLEX		0x00000001
#define	IOC3_ENET_MCR_PROMISC		0x00000002
#define	IOC3_ENET_MCR_PADEN		0x00000004
#define	IOC3_ENET_MCR_RXOFF_MASK	0x000001f8
#define	IOC3_ENET_MCR_RXOFF_SHIFT	3
#define	IOC3_ENET_MCR_PARITY_ENABLE	0x00000200
#define	IOC3_ENET_MCR_LARGE_SSRAM	0x00001000
#define	IOC3_ENET_MCR_TX_DMA		0x00002000
#define	IOC3_ENET_MCR_TX		0x00004000
#define	IOC3_ENET_MCR_RX_DMA		0x00008000
#define	IOC3_ENET_MCR_RX		0x00010000
#define	IOC3_ENET_MCR_LOOPBACK		0x00020000
#define	IOC3_ENET_MCR_RESET		0x80000000

/* bits in the ENET interrupt register */
#define	IOC3_ENET_ISR_RX_TIMER		0x00000001	/* RX periodic int */
#define	IOC3_ENET_ISR_RX_THRESHOLD	0x00000002	/* RX q above threshold */
#define	IOC3_ENET_ISR_RX_OFLOW		0x00000004	/* RX q overflow */
#define	IOC3_ENET_ISR_RX_BUF_OFLOW	0x00000008	/* RX buffer overflow */
#define	IOC3_ENET_ISR_RX_MEMORY		0x00000010	/* RX memory acc. err */
#define	IOC3_ENET_ISR_RX_PARITY		0x00000020	/* RX parity error */
#define	IOC3_ENET_ISR_TX_EMPTY		0x00010000	/* TX q empty */
#define	IOC3_ENET_ISR_TX_RETRY		0x00020000
#define	IOC3_ENET_ISR_TX_EXDEF		0x00040000
#define	IOC3_ENET_ISR_TX_LCOLL		0x00080000	/* TX Late Collision */
#define	IOC3_ENET_ISR_TX_GIANT		0x00100000
#define	IOC3_ENET_ISR_TX_BUF_UFLOW	0x00200000	/* TX buf underflow */
#define	IOC3_ENET_ISR_TX_EXPLICIT	0x00400000	/* TX int requested */
#define	IOC3_ENET_ISR_TX_COLL_WRAP	0x00800000
#define	IOC3_ENET_ISR_TX_DEFER_WRAP	0x01000000
#define	IOC3_ENET_ISR_TX_MEMORY		0x02000000	/* TX memory acc. err */
#define	IOC3_ENET_ISR_TX_PARITY		0x04000000	/* TX parity error */

#define	IOC3_ENET_ISR_TX_ALL		0x07ff0000	/* all TX bits */

/* bits in ENET_RCSR */
#define	IOC3_ENET_RCSR_THRESHOLD_MASK	0x000001ff

/* bits in ENET_RPIR */
#define	IOC3_ENET_PIR_SET		0x80000000	/* set new value */

/* bits in ENET_TCSR */
#define	IOC3_ENET_TCSR_IPGT_MASK	0x0000007f	/* interpacket gap */
#define	IOC3_ENET_TCSR_IPGT_SHIFT	0
#define	IOC3_ENET_TCSR_IPGR1_MASK	0x00007f00
#define	IOC3_ENET_TCSR_IPGR1_SHIFT	8
#define	IOC3_ENET_TCSR_IPGR2_MASK	0x007f0000
#define	IOC3_ENET_TCSR_IPGR2_SHIFT	16
#define	IOC3_ENET_TCSR_NOTXCLOCK	0x80000000
#define	IOC3_ENET_TCSR_FULL_DUPLEX \
	((17 << IOC3_ENET_TCSR_IPGR2_SHIFT) | \
	 (11 << IOC3_ENET_TCSR_IPGR1_SHIFT) | \
	 (21 << IOC3_ENET_TCSR_IPGT_SHIFT))
#define	IOC3_ENET_TCSR_HALF_DUPLEX \
	((21 << IOC3_ENET_TCSR_IPGR2_SHIFT) | \
	 (21 << IOC3_ENET_TCSR_IPGR1_SHIFT) | \
	 (21 << IOC3_ENET_TCSR_IPGT_SHIFT))

/* bits in ENET_TCDC */
#define	IOC3_ENET_TCDC_COLLISION_MASK	0x0000ffff
#define	IOC3_ENET_TCDC_DEFER_MASK	0xffff0000
#define	IOC3_ENET_TCDC_DEFER_SHIFT	16

/* bits in ENET_TCIR */
#define	IOC3_ENET_TCIR_IDLE		0x80000000

/* bits in ENET_MICR */
#define	IOC3_ENET_MICR_REG_MASK		0x0000001f
#define	IOC3_ENET_MICR_PHY_MASK		0x000003e0
#define	IOC3_ENET_MICR_PHY_SHIFT	5
#define	IOC3_ENET_MICR_READ		0x00000400
#define	IOC3_ENET_MICR_WRITE		0x00000000
#define	IOC3_ENET_MICR_BUSY		0x00000800

#define	IOC3_ENET_MIDR_MASK		0x0000ffff

/*
 * Offsets and sizes for subdevices handled by mi drivers.
 */

#define	IOC3_UARTA_BASE		0x00020178
#define	IOC3_UARTB_BASE		0x00020170

/*
 * Ethernet SSRAM.
 */

#define	IOC3_SSRAM_BASE		0x00040000
#define	IOC3_SSRAM_SMALL_SIZE	0x00020000
#define	IOC3_SSRAM_LARGE_SIZE	0x00040000

#define	IOC3_SSRAM_PARITY_BIT		0x00010000
#define	IOC3_SSRAM_DATA_MASK		0x0000ffff

/*
 * Offsets of devices connected to the four IOC3 `bytebus'.
 */

#define	IOC3_BYTEBUS_0		0x00080000
#define	IOC3_BYTEBUS_1		0x000a0000
#define	IOC3_BYTEBUS_2		0x000c0000
#define	IOC3_BYTEBUS_3		0x000e0000


code:
/*	$OpenBSD: iocvar.h,v 1.6 2010/04/06 19:12:34 miod Exp $	*/

/*
 * Copyright (c) 2008, 2010 Miodrag Vallat.
 *
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

struct ioc_attach_args {
	const char			*iaa_name;

	bus_space_tag_t			 iaa_memt;
	bus_space_handle_t		 iaa_memh;
	bus_dma_tag_t			 iaa_dmat;

	bus_addr_t			 iaa_base;
	int				 iaa_dev;

	uint8_t				 iaa_enaddr[6];

	int				 iaa_flags;
#define	IOC_FLAGS_OBIO			0x00000001

	struct sgi_device_location	 iaa_location;
};

void   *ioc_intr_establish(void *, u_long, int, int (*)(void *),
	    void *, char *);


code:
/*	$OpenBSD: iof.c,v 1.9 2014/05/19 21:18:42 miod Exp $	*/

/*
 * Copyright (c) 2009 Miodrag Vallat.
 *
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

/*
 * IOC4 device driver.
 */

#include <sys/param.h>
#include <sys/systm.h>
#include <sys/kernel.h>
#include <sys/device.h>
#include <sys/malloc.h>

#include <machine/autoconf.h>
#include <machine/bus.h>

#include <dev/pci/pcireg.h>
#include <dev/pci/pcivar.h>
#include <dev/pci/pcidevs.h>

#include <sgi/pci/iofreg.h>
#include <sgi/pci/iofvar.h>

int	iof_match(struct device *, void *, void *);
void	iof_attach(struct device *, struct device *, void *);
void	iof_attach_child(struct device *, const char *, bus_addr_t, uint);
int	iof_search(struct device *, void *, void *);
int	iof_print(void *, const char *);

struct iof_intr {
	struct iof_softc	*ii_iof;

	int			 (*ii_func)(void *);
	void			*ii_arg;

	struct evcount		 ii_count;
	int			 ii_level;
};

struct iof_softc {
	struct device		 sc_dev;

	bus_space_tag_t		 sc_memt;
	bus_space_handle_t	 sc_memh;
	bus_dma_tag_t		 sc_dmat;
	pci_chipset_tag_t	 sc_pc;
	pcitag_t		 sc_tag;

	uint32_t		 sc_mcr;

	void			*sc_ih;
	struct iof_intr		*sc_intr[IOC4_NDEVS];
};

struct cfattach iof_ca = {
	sizeof(struct iof_softc), iof_match, iof_attach,
};

struct cfdriver iof_cd = {
	NULL, "iof", DV_DULL,
};

int	iof_intr_dispatch(struct iof_softc *, int);
int	iof_intr(void *);

int
iof_match(struct device *parent, void *match, void *aux)
{
	struct pci_attach_args *pa = aux;

	if (PCI_VENDOR(pa->pa_id) == PCI_VENDOR_SGI &&
	    PCI_PRODUCT(pa->pa_id) == PCI_PRODUCT_SGI_IOC4)
		return 1;

	return 0;
}

int
iof_print(void *aux, const char *iofname)
{
	struct iof_attach_args *iaa = aux;

	if (iofname != NULL)
		printf("%s at %s", iaa->iaa_name, iofname);

	printf(" base 0x%lx", iaa->iaa_base);

	return UNCONF;
}

void
iof_attach(struct device *parent, struct device *self, void *aux)
{
	struct iof_softc *sc = (struct iof_softc *)self;
	struct pci_attach_args *pa = aux;
	pci_intr_handle_t ih;
	bus_space_tag_t memt;
	bus_space_handle_t memh;
	bus_size_t memsize;
	const char *intrstr;

	printf(": ");

	if (pci_mapreg_map(pa, PCI_MAPREG_START, PCI_MAPREG_TYPE_MEM, 0,
	    &memt, &memh, NULL, &memsize, 0)) {
		printf("can't map mem space\n");
		return;
	}

	sc->sc_pc = pa->pa_pc;
	sc->sc_tag = pa->pa_tag;
	sc->sc_dmat = pa->pa_dmat;

	sc->sc_memt = memt;
	sc->sc_memh = memh;

	sc->sc_mcr = bus_space_read_4(sc->sc_memt, sc->sc_memh, IOC4_MCR);

	/*
	 * Acknowledge all pending interrupts, and disable them.
	 */

	bus_space_write_4(sc->sc_memt, sc->sc_memh, IOC4_SIO_IEC, ~0x0);
	bus_space_write_4(sc->sc_memt, sc->sc_memh, IOC4_SIO_IES, 0x0);
	bus_space_write_4(sc->sc_memt, sc->sc_memh, IOC4_SIO_IR,
	    bus_space_read_4(sc->sc_memt, sc->sc_memh, IOC4_SIO_IR));

	bus_space_write_4(sc->sc_memt, sc->sc_memh, IOC4_OTHER_IEC, ~0x0);
	bus_space_write_4(sc->sc_memt, sc->sc_memh, IOC4_OTHER_IES, 0x0);
	bus_space_write_4(sc->sc_memt, sc->sc_memh, IOC4_OTHER_IR,
	    bus_space_read_4(sc->sc_memt, sc->sc_memh, IOC4_OTHER_IR));

	if (pci_intr_map(pa, &ih) != 0) {
		printf("failed to map interrupt!\n");
		goto unmap;
	}
	intrstr = pci_intr_string(sc->sc_pc, ih);

	sc->sc_ih = pci_intr_establish(sc->sc_pc, ih, IPL_TTY, iof_intr,
	    sc, self->dv_xname);
	if (sc->sc_ih == NULL) {
		printf("failed to establish interrupt at %s\n", intrstr);
		goto unmap;
	}
	printf("%s\n", intrstr);

	/*
	 * Attach other sub-devices.
	 */

	iof_attach_child(self, "com", IOC4_UARTA_BASE, IOC4DEV_SERIAL_A);
	iof_attach_child(self, "com", IOC4_UARTB_BASE, IOC4DEV_SERIAL_B);
	iof_attach_child(self, "com", IOC4_UARTC_BASE, IOC4DEV_SERIAL_C);
	iof_attach_child(self, "com", IOC4_UARTD_BASE, IOC4DEV_SERIAL_D);
	iof_attach_child(self, "iockbc", IOC4_KBC_BASE, IOC4DEV_KBC);
	iof_attach_child(self, "dsrtc", IOC4_BYTEBUS_0, IOC4DEV_RTC);

	return;

unmap:
	bus_space_unmap(memt, memh, memsize);
}

void
iof_attach_child(struct device *iof, const char *name, bus_addr_t base,
    uint dev)
{
	struct iof_softc *sc = (struct iof_softc *)iof;
	struct iof_attach_args iaa;

	iaa.iaa_name = name;
	pci_get_device_location(sc->sc_pc, sc->sc_tag, &iaa.iaa_location);
	iaa.iaa_memt = sc->sc_memt;
	iaa.iaa_memh = sc->sc_memh;
	iaa.iaa_dmat = sc->sc_dmat;
	iaa.iaa_base = base;
	iaa.iaa_dev = dev;
	iaa.iaa_clock = sc->sc_mcr & IOC4_MCR_PCI_66MHZ ?  66666667 : 33333333;

	config_found_sm(iof, &iaa, iof_print, iof_search);
}

int
iof_search(struct device *parent, void *vcf, void *args)
{
	struct cfdata *cf = vcf;
	struct iof_attach_args *iaa = (struct iof_attach_args *)args;

	if (strcmp(cf->cf_driver->cd_name, iaa->iaa_name) != 0)
		return 0;

	if (cf->cf_loc[0] != -1 && cf->cf_loc[0] != (int)iaa->iaa_base)
		return 0;

	return (*cf->cf_attach->ca_match)(parent, cf, iaa);
}

/*
 * Interrupt handling.
 */

/*
 * List of interrupt bits to enable for each device.
 *
 * For the serial ports, we only enable the passthrough interrupt and
 * let com(4) tinker with the appropriate registers, instead of adding
 * an unnecessary layer there.
 */
static const struct {
	uint32_t sio;
	uint32_t other;
} ioc4_intrbits[IOC4_NDEVS] = {
	{ IOC4_SIRQ_UARTA, 0 },
	{ IOC4_SIRQ_UARTB, 0 },
	{ IOC4_SIRQ_UARTC, 0 },
	{ IOC4_SIRQ_UARTD, 0 },
	{ 0, IOC4_OIRQ_KBC },
	{ 0, IOC4_OIRQ_ATAPI },
	{ 0, 0 }	/* no RTC interrupt */
};

void *
iof_intr_establish(void *cookie, uint dev, int level, int (*func)(void *),
    void *arg, char *name)
{
	struct iof_softc *sc = cookie;
	struct iof_intr *ii;

	if (dev < 0 || dev >= IOC4_NDEVS)
		return NULL;

	if (ioc4_intrbits[dev].sio == 0 && ioc4_intrbits[dev].other == 0)
		return NULL;

	ii = (struct iof_intr *)malloc(sizeof(*ii), M_DEVBUF, M_NOWAIT);
	if (ii == NULL)
		return NULL;

	ii->ii_iof = sc;
	ii->ii_func = func;
	ii->ii_arg = arg;
	ii->ii_level = level;

	evcount_attach(&ii->ii_count, name, &ii->ii_level);
	sc->sc_intr[dev] = ii;

	/* enable hardware source if necessary */
	bus_space_write_4(sc->sc_memt, sc->sc_memh, IOC4_SIO_IES,
	    ioc4_intrbits[dev].sio);
	bus_space_write_4(sc->sc_memt, sc->sc_memh, IOC4_OTHER_IES,
	    ioc4_intrbits[dev].other);

	return (ii);
}

int
iof_intr(void *v)
{
	struct iof_softc *sc = (struct iof_softc *)v;
	uint32_t spending, opending, mask;
	int dev;

	spending = bus_space_read_4(sc->sc_memt, sc->sc_memh, IOC4_SIO_IR) &
	    bus_space_read_4(sc->sc_memt, sc->sc_memh, IOC4_SIO_IES);
	opending = bus_space_read_4(sc->sc_memt, sc->sc_memh, IOC4_OTHER_IR) &
	    bus_space_read_4(sc->sc_memt, sc->sc_memh, IOC4_OTHER_IES);

	if (spending == 0 && opending == 0)
		return 0;

	/* Disable pending interrupts */
	bus_space_write_4(sc->sc_memt, sc->sc_memh, IOC4_SIO_IEC, spending);
	bus_space_write_4(sc->sc_memt, sc->sc_memh, IOC4_OTHER_IEC, opending);

	for (dev = 0; dev < IOC4_NDEVS; dev++) {
		mask = spending & ioc4_intrbits[dev].sio;
		if (mask != 0) {
			(void)iof_intr_dispatch(sc, dev);

			/* Ack, then reenable, pending interrupts */
			bus_space_write_4(sc->sc_memt, sc->sc_memh,
			    IOC4_SIO_IR, mask);
			bus_space_write_4(sc->sc_memt, sc->sc_memh,
			    IOC4_SIO_IES, mask);
		}
		mask = opending & ioc4_intrbits[dev].other;
		if (mask != 0) {
			(void)iof_intr_dispatch(sc, dev);

			/* Ack, then reenable, pending interrupts */
			bus_space_write_4(sc->sc_memt, sc->sc_memh,
			    IOC4_OTHER_IR, mask);
			bus_space_write_4(sc->sc_memt, sc->sc_memh,
			    IOC4_OTHER_IES, mask);
		}
	}

	return 1;
}

int
iof_intr_dispatch(struct iof_softc *sc, int dev)
{
	struct iof_intr *ii;
	int rc = 0;

	/* Call registered interrupt function. */
	if ((ii = sc->sc_intr[dev]) != NULL && ii->ii_func != NULL) {
		rc = (*ii->ii_func)(ii->ii_arg);
		if (rc != 0)
			ii->ii_count.ec_count++;
	}

	return rc;
}


code:
/*	$OpenBSD: iofreg.h,v 1.5 2013/12/30 05:27:01 miod Exp $	*/

/*
 * Copyright (c) 2009 Miodrag Vallat.
 *
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

/*
 * Register definitions for SGI IOC4 ASIC.
 */

#define IOC4_NDEVS		7

#define IOC4DEV_SERIAL_A	0
#define IOC4DEV_SERIAL_B	1
#define IOC4DEV_SERIAL_C	2
#define IOC4DEV_SERIAL_D	3
#define IOC4DEV_KBC		4
#define IOC4DEV_ATAPI		5
#define IOC4DEV_RTC		6

/* Interrupt control registers */
#define IOC4_SIO_IR		0x00000008
#define	IOC4_OTHER_IR		0x0000000c
#define IOC4_SIO_IES		0x00000010
#define IOC4_OTHER_IES		0x00000014
#define IOC4_SIO_IEC		0x00000018
#define IOC4_OTHER_IEC		0x0000001c
#define IOC4_SIO_CR		0x00000020
#define	IOC4_MCR		0x00000024

/* Keyboard controller registers */
#define	IOC4_KBC_CTRL_STATUS	0x00000200
#define	IOC4_KBC_KBD_RX		0x00000204
#define	IOC4_KBC_AUX_RX		0x00000208
#define	IOC4_KBC_KBD_TX		0x0000020c
#define	IOC4_KBC_AUX_TX		0x00000210

/* bits in the SIO interrupt register */
#define	IOC4_SIRQ_UARTA		0x00000040	/* UART A passthrough */
#define	IOC4_SIRQ_UARTB		0x00004000	/* UART B passthrough */
#define	IOC4_SIRQ_UARTC		0x00400000	/* UART C passthrough */
#define	IOC4_SIRQ_UARTD		0x40000000	/* UART D passthrough */

/* bits in the OTHER interrupt register */
#define	IOC4_OIRQ_ATAPI		0x00000001	/* ATAPI passthrough */
#define	IOC4_OIRQ_KBC		0x00000040	/* keyboard controller */

/* bits in the MCR register */
#define	IOC4_MCR_PCI_66MHZ	0x00000001

#define	IOC4_ATAPI_BASE		0x00000100
#define	IOC4_ATAPI_SIZE		0x00000100

#define	IOC4_KBC_BASE		0x00000200
#define	IOC4_KBC_SIZE		0x00000014

#define	IOC4_UARTA_BASE		0x00000380
#define	IOC4_UARTB_BASE		0x00000388
#define	IOC4_UARTC_BASE		0x00000390
#define	IOC4_UARTD_BASE		0x00000398

#define	IOC4_BYTEBUS_0		0x00080000
#define	IOC4_BYTEBUS_1		0x000a0000
#define	IOC4_BYTEBUS_2		0x000c0000
#define	IOC4_BYTEBUS_3		0x000e0000


code:
/*	$OpenBSD: iofvar.h,v 1.4 2010/04/06 19:12:34 miod Exp $	*/

/*
 * Copyright (c) 2009, 2010 Miodrag Vallat.
 *
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

struct iof_attach_args {
	const char			*iaa_name;

	bus_space_tag_t			 iaa_memt;
	bus_space_handle_t		 iaa_memh;
	bus_dma_tag_t			 iaa_dmat;

	bus_addr_t			 iaa_base;
	uint				 iaa_dev;
	uint				 iaa_clock;

	struct sgi_device_location	 iaa_location;
};

void   *iof_intr_establish(void *, uint, int, int (*)(void *), void *, char *);