code:
/*	$OpenBSD: glxsb.c,v 1.29 2014/12/09 06:58:28 doug Exp $	*/

/*
 * Copyright (c) 2006 Tom Cosgrove <tom@openbsd.org>
 * Copyright (c) 2003, 2004 Theo de Raadt
 * Copyright (c) 2003 Jason Wright
 *
 * 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.
 */

/*
 * Driver for the security block on the AMD Geode LX processors
 * http://www.amd.com/files/connectivitysolutions/geode/geode_lx/33234d_lx_ds.pdf
 */

#include <sys/param.h>
#include <sys/systm.h>
#include <sys/device.h>
#include <sys/malloc.h>
#include <sys/mbuf.h>
#include <sys/types.h>
#include <sys/timeout.h>

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

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

#ifdef CRYPTO
#include <crypto/cryptodev.h>
#include <crypto/rijndael.h>
#include <crypto/xform.h>
#include <crypto/cryptosoft.h>
#endif

#define SB_GLD_MSR_CAP		0x58002000	/* RO - Capabilities */
#define SB_GLD_MSR_CONFIG	0x58002001	/* RW - Master Config */
#define SB_GLD_MSR_SMI		0x58002002	/* RW - SMI */
#define SB_GLD_MSR_ERROR	0x58002003	/* RW - Error */
#define SB_GLD_MSR_PM		0x58002004	/* RW - Power Mgmt */
#define SB_GLD_MSR_DIAG		0x58002005	/* RW - Diagnostic */
#define SB_GLD_MSR_CTRL		0x58002006	/* RW - Security Block Cntrl */

						/* For GLD_MSR_CTRL: */
#define SB_GMC_DIV0		0x0000		/* AES update divisor values */
#define SB_GMC_DIV1		0x0001
#define SB_GMC_DIV2		0x0002
#define SB_GMC_DIV3		0x0003
#define SB_GMC_DIV_MASK		0x0003
#define SB_GMC_SBI		0x0004		/* AES swap bits */
#define SB_GMC_SBY		0x0008		/* AES swap bytes */
#define SB_GMC_TW		0x0010		/* Time write (EEPROM) */
#define SB_GMC_T_SEL0		0x0000		/* RNG post-proc: none */
#define SB_GMC_T_SEL1		0x0100		/* RNG post-proc: LFSR */
#define SB_GMC_T_SEL2		0x0200		/* RNG post-proc: whitener */
#define SB_GMC_T_SEL3		0x0300		/* RNG LFSR+whitener */
#define SB_GMC_T_SEL_MASK	0x0300
#define SB_GMC_T_NE		0x0400		/* Noise (generator) Enable */
#define SB_GMC_T_TM		0x0800		/* RNG test mode */
						/*     (deterministic) */

/* Security Block configuration/control registers (offsets from base) */

#define SB_CTL_A		0x0000		/* RW - SB Control A */
#define SB_CTL_B		0x0004		/* RW - SB Control B */
#define SB_AES_INT		0x0008		/* RW - SB AES Interrupt */
#define SB_SOURCE_A		0x0010		/* RW - Source A */
#define SB_DEST_A		0x0014		/* RW - Destination A */
#define SB_LENGTH_A		0x0018		/* RW - Length A */
#define SB_SOURCE_B		0x0020		/* RW - Source B */
#define SB_DEST_B		0x0024		/* RW - Destination B */
#define SB_LENGTH_B		0x0028		/* RW - Length B */
#define SB_WKEY			0x0030		/* WO - Writable Key 0-3 */
#define SB_WKEY_0		0x0030		/* WO - Writable Key 0 */
#define SB_WKEY_1		0x0034		/* WO - Writable Key 1 */
#define SB_WKEY_2		0x0038		/* WO - Writable Key 2 */
#define SB_WKEY_3		0x003C		/* WO - Writable Key 3 */
#define SB_CBC_IV		0x0040		/* RW - CBC IV 0-3 */
#define SB_CBC_IV_0		0x0040		/* RW - CBC IV 0 */
#define SB_CBC_IV_1		0x0044		/* RW - CBC IV 1 */
#define SB_CBC_IV_2		0x0048		/* RW - CBC IV 2 */
#define SB_CBC_IV_3		0x004C		/* RW - CBC IV 3 */
#define SB_RANDOM_NUM		0x0050		/* RW - Random Number */
#define SB_RANDOM_NUM_STATUS	0x0054		/* RW - Random Number Status */
#define SB_EEPROM_COMM		0x0800		/* RW - EEPROM Command */
#define SB_EEPROM_ADDR		0x0804		/* RW - EEPROM Address */
#define SB_EEPROM_DATA		0x0808		/* RW - EEPROM Data */
#define SB_EEPROM_SEC_STATE	0x080C		/* RW - EEPROM Security State */

						/* For SB_CTL_A and _B */
#define SB_CTL_ST		0x0001		/* Start operation (enc/dec) */
#define SB_CTL_ENC		0x0002		/* Encrypt (0 is decrypt) */
#define SB_CTL_DEC		0x0000		/* Decrypt */
#define SB_CTL_WK		0x0004		/* Use writable key (we set) */
#define SB_CTL_DC		0x0008		/* Destination coherent */
#define SB_CTL_SC		0x0010		/* Source coherent */
#define SB_CTL_CBC		0x0020		/* CBC (0 is ECB) */

						/* For SB_AES_INT */
#define SB_AI_DISABLE_AES_A	0x00001		/* Disable AES A compl int */
#define SB_AI_ENABLE_AES_A	0x00000		/* Enable AES A compl int */
#define SB_AI_DISABLE_AES_B	0x00002		/* Disable AES B compl int */
#define SB_AI_ENABLE_AES_B	0x00000		/* Enable AES B compl int */
#define SB_AI_DISABLE_EEPROM	0x00004		/* Disable EEPROM op comp int */
#define SB_AI_ENABLE_EEPROM	0x00000		/* Enable EEPROM op compl int */
#define SB_AI_AES_A_COMPLETE	0x10000		/* AES A operation complete */
#define SB_AI_AES_B_COMPLETE	0x20000		/* AES B operation complete */
#define SB_AI_EEPROM_COMPLETE	0x40000		/* EEPROM operation complete */

#define SB_RNS_TRNG_VALID	0x0001		/* in SB_RANDOM_NUM_STATUS */

#define SB_MEM_SIZE		0x0810		/* Size of memory block */

#define SB_AES_ALIGN		0x0010		/* Source and dest buffers */
						/* must be 16-byte aligned */
#define SB_AES_BLOCK_SIZE	0x0010

/*
 * The Geode LX security block AES acceleration doesn't perform scatter-
 * gather: it just takes source and destination addresses.  Therefore the
 * plain- and ciphertexts need to be contiguous.  To this end, we allocate
 * a buffer for both, and accept the overhead of copying in and out.  If
 * the number of bytes in one operation is bigger than allowed for by the
 * buffer (buffer is twice the size of the max length, as it has both input
 * and output) then we have to perform multiple encryptions/decryptions.
 */
#define GLXSB_MAX_AES_LEN	16384

#ifdef CRYPTO
struct glxsb_dma_map {
	bus_dmamap_t		dma_map;
	bus_dma_segment_t	dma_seg;
	int			dma_nsegs;
	int			dma_size;
	caddr_t			dma_vaddr;
	uint32_t		dma_paddr;
};
struct glxsb_session {
	uint32_t	ses_key[4];
	int		ses_klen;
	int		ses_used;
	struct swcr_data *ses_swd_auth;
	struct swcr_data *ses_swd_enc;
};
#endif /* CRYPTO */

struct glxsb_softc {
	struct device		sc_dev;
	bus_space_tag_t		sc_iot;
	bus_space_handle_t	sc_ioh;
	struct timeout		sc_to;

#ifdef CRYPTO
	bus_dma_tag_t		sc_dmat;
	struct glxsb_dma_map	sc_dma;
	int32_t			sc_cid;
	int			sc_nsessions;
	struct glxsb_session	*sc_sessions;
#endif /* CRYPTO */

	uint64_t		save_gld_msr;
};

int	glxsb_match(struct device *, void *, void *);
void	glxsb_attach(struct device *, struct device *, void *);
int	glxsb_activate(struct device *, int);
void	glxsb_rnd(void *);

struct cfattach glxsb_ca = {
	sizeof(struct glxsb_softc), glxsb_match, glxsb_attach, NULL,
	glxsb_activate
};

struct cfdriver glxsb_cd = {
	NULL, "glxsb", DV_DULL
};

#ifdef CRYPTO

#define GLXSB_SESSION(sid)		((sid) & 0x0fffffff)
#define	GLXSB_SID(crd,ses)		(((crd) << 28) | ((ses) & 0x0fffffff))

static struct glxsb_softc *glxsb_sc;

int glxsb_crypto_setup(struct glxsb_softc *);
int glxsb_crypto_newsession(uint32_t *, struct cryptoini *);
int glxsb_crypto_process(struct cryptop *);
int glxsb_crypto_freesession(uint64_t);
static __inline void glxsb_aes(struct glxsb_softc *, uint32_t, uint32_t,
    uint32_t, void *, int, void *);

int glxsb_dma_alloc(struct glxsb_softc *, int, struct glxsb_dma_map *);
void glxsb_dma_pre_op(struct glxsb_softc *, struct glxsb_dma_map *);
void glxsb_dma_post_op(struct glxsb_softc *, struct glxsb_dma_map *);
void glxsb_dma_free(struct glxsb_softc *, struct glxsb_dma_map *);

#endif /* CRYPTO */

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

	if (PCI_VENDOR(pa->pa_id) == PCI_VENDOR_AMD &&
	    PCI_PRODUCT(pa->pa_id) == PCI_PRODUCT_AMD_GEODE_LX_CRYPTO)
		return (1);

	return (0);
}

void
glxsb_attach(struct device *parent, struct device *self, void *aux)
{
	struct glxsb_softc *sc = (void *) self;
	struct pci_attach_args *pa = aux;
	bus_addr_t membase;
	bus_size_t memsize;
	uint64_t msr;
#ifdef CRYPTO
	uint32_t intr;
#endif

	msr = rdmsr(SB_GLD_MSR_CAP);
	if ((msr & 0xFFFF00) != 0x130400) {
		printf(": unknown ID 0x%x\n", (int) ((msr & 0xFFFF00) >> 16));
		return;
	}

	/* printf(": revision %d", (int) (msr & 0xFF)); */

	/* Map in the security block configuration/control registers */
	if (pci_mapreg_map(pa, PCI_MAPREG_START,
	    PCI_MAPREG_TYPE_MEM | PCI_MAPREG_MEM_TYPE_32BIT, 0, &sc->sc_iot,
	    &sc->sc_ioh, &membase, &memsize, SB_MEM_SIZE)) {
		printf(": can't find mem space\n");
		return;
	}

	/*
	 * Configure the Security Block.
	 *
	 * We want to enable the noise generator (T_NE), and enable the
	 * linear feedback shift register and whitener post-processing
	 * (T_SEL = 3).  Also ensure that test mode (deterministic values)
	 * is disabled.
	 */
	msr = rdmsr(SB_GLD_MSR_CTRL);
	msr &= ~(SB_GMC_T_TM | SB_GMC_T_SEL_MASK);
	msr |= SB_GMC_T_NE | SB_GMC_T_SEL3;
#if 0
	msr |= SB_GMC_SBI | SB_GMC_SBY;		/* for AES, if necessary */
#endif
	wrmsr(SB_GLD_MSR_CTRL, msr);

	/* Install a periodic collector for the "true" (AMD's word) RNG */
	timeout_set(&sc->sc_to, glxsb_rnd, sc);
	glxsb_rnd(sc);
	printf(": RNG");

#ifdef CRYPTO
	/* We don't have an interrupt handler, so disable completion INTs */
	intr = SB_AI_DISABLE_AES_A | SB_AI_DISABLE_AES_B |
	    SB_AI_DISABLE_EEPROM | SB_AI_AES_A_COMPLETE |
	    SB_AI_AES_B_COMPLETE | SB_AI_EEPROM_COMPLETE;
	bus_space_write_4(sc->sc_iot, sc->sc_ioh, SB_AES_INT, intr);

	sc->sc_dmat = pa->pa_dmat;

	if (glxsb_crypto_setup(sc))
		printf(" AES");
#endif

	printf("\n");
}

int
glxsb_activate(struct device *self, int act)
{
	struct glxsb_softc *sc = (struct glxsb_softc *)self;

	switch (act) {
	case DVACT_QUIESCE:
		/* XXX should wait for current crypto op to finish */
		break;
	case DVACT_SUSPEND:
		sc->save_gld_msr = rdmsr(SB_GLD_MSR_CTRL);
		break;
	case DVACT_RESUME:
		wrmsr(SB_GLD_MSR_CTRL, sc->save_gld_msr);
		break;
	}
	return (0);
}

void
glxsb_rnd(void *v)
{
	struct glxsb_softc *sc = v;
	uint32_t status, value;

	status = bus_space_read_4(sc->sc_iot, sc->sc_ioh, SB_RANDOM_NUM_STATUS);
	if (status & SB_RNS_TRNG_VALID) {
		value = bus_space_read_4(sc->sc_iot, sc->sc_ioh, SB_RANDOM_NUM);
		add_true_randomness(value);
	}

	timeout_add_msec(&sc->sc_to, 10);
}

#ifdef CRYPTO
int
glxsb_crypto_setup(struct glxsb_softc *sc)
{
	int algs[CRYPTO_ALGORITHM_MAX + 1];

	/* Allocate a contiguous DMA-able buffer to work in */
	if (glxsb_dma_alloc(sc, GLXSB_MAX_AES_LEN * 2, &sc->sc_dma) != 0)
		return 0;

	bzero(algs, sizeof(algs));
	algs[CRYPTO_AES_CBC] = CRYPTO_ALG_FLAG_SUPPORTED;
	algs[CRYPTO_MD5_HMAC] = CRYPTO_ALG_FLAG_SUPPORTED;
	algs[CRYPTO_SHA1_HMAC] = CRYPTO_ALG_FLAG_SUPPORTED;
	algs[CRYPTO_RIPEMD160_HMAC] = CRYPTO_ALG_FLAG_SUPPORTED;
	algs[CRYPTO_SHA2_256_HMAC] = CRYPTO_ALG_FLAG_SUPPORTED;
	algs[CRYPTO_SHA2_384_HMAC] = CRYPTO_ALG_FLAG_SUPPORTED;
	algs[CRYPTO_SHA2_512_HMAC] = CRYPTO_ALG_FLAG_SUPPORTED;

	sc->sc_cid = crypto_get_driverid(0);
	if (sc->sc_cid < 0)
		return 0;

	crypto_register(sc->sc_cid, algs, glxsb_crypto_newsession,
	    glxsb_crypto_freesession, glxsb_crypto_process);

	sc->sc_nsessions = 0;

	glxsb_sc = sc;

	return 1;
}

int
glxsb_crypto_newsession(uint32_t *sidp, struct cryptoini *cri)
{
	struct glxsb_softc *sc = glxsb_sc;
	struct glxsb_session *ses = NULL;
	struct auth_hash	*axf;
	struct enc_xform	*txf;
	struct cryptoini	*c;
	struct swcr_data	*swd;
	int sesn, i;

	if (sc == NULL || sidp == NULL || cri == NULL)
		return (EINVAL);

	for (sesn = 0; sesn < sc->sc_nsessions; sesn++) {
		if (sc->sc_sessions[sesn].ses_used == 0) {
			ses = &sc->sc_sessions[sesn];
			break;
		}
	}

	if (ses == NULL) {
		sesn = sc->sc_nsessions;
		ses = mallocarray(sesn + 1, sizeof(*ses), M_DEVBUF,
		    M_NOWAIT);
		if (ses == NULL)
			return (ENOMEM);
		if (sesn != 0) {
			bcopy(sc->sc_sessions, ses, sesn * sizeof(*ses));
			explicit_bzero(sc->sc_sessions, sesn * sizeof(*ses));
			free(sc->sc_sessions, M_DEVBUF, 0);
		}
		sc->sc_sessions = ses;
		ses = &sc->sc_sessions[sesn];
		sc->sc_nsessions++;
	}

	bzero(ses, sizeof(*ses));
	ses->ses_used = 1;

	for (c = cri; c != NULL; c = c->cri_next) {
		switch (c->cri_alg) {
		case CRYPTO_AES_CBC:

			if (c->cri_klen != 128) {
				swd = malloc(sizeof(struct swcr_data),
				    M_CRYPTO_DATA, M_NOWAIT|M_ZERO);
				if (swd == NULL) {
					glxsb_crypto_freesession(sesn);
					return (ENOMEM);
				}
				ses->ses_swd_enc = swd;
				txf = &enc_xform_rijndael128;
				if (txf->ctxsize > 0) {
					swd->sw_kschedule =
					    malloc(txf->ctxsize,
						M_CRYPTO_DATA,
						M_NOWAIT|M_ZERO);
					if (swd->sw_kschedule == NULL) {
						glxsb_crypto_freesession(sesn);
						return (EINVAL);
					}
				}
				if (txf->setkey(swd->sw_kschedule, c->cri_key,
				    c->cri_klen / 8) < 0) {
					glxsb_crypto_freesession(sesn);
					return (EINVAL);
				}
				swd->sw_exf = txf;
				break;
			}

			ses->ses_klen = c->cri_klen;

			/* Copy the key (Geode LX wants the primary key only) */
			bcopy(c->cri_key, ses->ses_key, sizeof(ses->ses_key));
			break;

		case CRYPTO_MD5_HMAC:
			axf = &auth_hash_hmac_md5_96;
			goto authcommon;
		case CRYPTO_SHA1_HMAC:
			axf = &auth_hash_hmac_sha1_96;
			goto authcommon;
		case CRYPTO_RIPEMD160_HMAC:
			axf = &auth_hash_hmac_ripemd_160_96;
			goto authcommon;
		case CRYPTO_SHA2_256_HMAC:
			axf = &auth_hash_hmac_sha2_256_128;
			goto authcommon;
		case CRYPTO_SHA2_384_HMAC:
			axf = &auth_hash_hmac_sha2_384_192;
			goto authcommon;
		case CRYPTO_SHA2_512_HMAC:
			axf = &auth_hash_hmac_sha2_512_256;
		authcommon:
			swd = malloc(sizeof(struct swcr_data), M_CRYPTO_DATA,
			    M_NOWAIT|M_ZERO);
			if (swd == NULL) {
				glxsb_crypto_freesession(sesn);
				return (ENOMEM);
			}
			ses->ses_swd_auth = swd;

			swd->sw_ictx = malloc(axf->ctxsize, M_CRYPTO_DATA,
			    M_NOWAIT);
			if (swd->sw_ictx == NULL) {
				glxsb_crypto_freesession(sesn);
				return (ENOMEM);
			}

			swd->sw_octx = malloc(axf->ctxsize, M_CRYPTO_DATA,
			    M_NOWAIT);
			if (swd->sw_octx == NULL) {
				glxsb_crypto_freesession(sesn);
				return (ENOMEM);
			}

			for (i = 0; i < c->cri_klen / 8; i++)
				c->cri_key[i] ^= HMAC_IPAD_VAL;

			axf->Init(swd->sw_ictx);
			axf->Update(swd->sw_ictx, c->cri_key, c->cri_klen / 8);
			axf->Update(swd->sw_ictx, hmac_ipad_buffer,
			    axf->blocksize - (c->cri_klen / 8));

			for (i = 0; i < c->cri_klen / 8; i++)
				c->cri_key[i] ^= (HMAC_IPAD_VAL ^
				    HMAC_OPAD_VAL);

			axf->Init(swd->sw_octx);
			axf->Update(swd->sw_octx, c->cri_key, c->cri_klen / 8);
			axf->Update(swd->sw_octx, hmac_opad_buffer,
			    axf->blocksize - (c->cri_klen / 8));

			for (i = 0; i < c->cri_klen / 8; i++)
				c->cri_key[i] ^= HMAC_OPAD_VAL;

			swd->sw_axf = axf;
			swd->sw_alg = c->cri_alg;

			break;
		default:
			glxsb_crypto_freesession(sesn);
			return (EINVAL);
		}
	}

	*sidp = GLXSB_SID(0, sesn);
	return (0);
}

int
glxsb_crypto_freesession(uint64_t tid)
{
	struct glxsb_softc *sc = glxsb_sc;
	struct swcr_data *swd;
	struct auth_hash *axf;
	struct enc_xform *txf;
	int sesn;
	uint32_t sid = ((uint32_t)tid) & 0xffffffff;

	if (sc == NULL)
		return (EINVAL);
	sesn = GLXSB_SESSION(sid);
	if (sesn >= sc->sc_nsessions)
		return (EINVAL);
	if ((swd = sc->sc_sessions[sesn].ses_swd_enc)) {
		txf = swd->sw_exf;

		if (swd->sw_kschedule) {
			explicit_bzero(swd->sw_kschedule, txf->ctxsize);
			free(swd->sw_kschedule, M_CRYPTO_DATA, 0);
		}
		free(swd, M_CRYPTO_DATA, 0);
	}
	if ((swd = sc->sc_sessions[sesn].ses_swd_auth)) {
		axf = swd->sw_axf;

		if (swd->sw_ictx) {
			explicit_bzero(swd->sw_ictx, axf->ctxsize);
			free(swd->sw_ictx, M_CRYPTO_DATA, 0);
		}
		if (swd->sw_octx) {
			explicit_bzero(swd->sw_octx, axf->ctxsize);
			free(swd->sw_octx, M_CRYPTO_DATA, 0);
		}
		free(swd, M_CRYPTO_DATA, 0);
	}
	explicit_bzero(&sc->sc_sessions[sesn], sizeof(sc->sc_sessions[sesn]));
	return (0);
}

/*
 * Must be called at splnet() or higher
 */
static __inline void
glxsb_aes(struct glxsb_softc *sc, uint32_t control, uint32_t psrc,
    uint32_t pdst, void *key, int len, void *iv)
{
	uint32_t status;
	int i;

	if (len & 0xF) {
		printf("%s: len must be a multiple of 16 (not %d)\n",
		    sc->sc_dev.dv_xname, len);
		return;
	}

	/* Set the source */
	bus_space_write_4(sc->sc_iot, sc->sc_ioh, SB_SOURCE_A, psrc);

	/* Set the destination address */
	bus_space_write_4(sc->sc_iot, sc->sc_ioh, SB_DEST_A, pdst);

	/* Set the data length */
	bus_space_write_4(sc->sc_iot, sc->sc_ioh, SB_LENGTH_A, len);

	/* Set the IV */
	if (iv != NULL) {
		bus_space_write_region_4(sc->sc_iot, sc->sc_ioh,
		    SB_CBC_IV, iv, 4);
		control |= SB_CTL_CBC;
	}

	/* Set the key */
	bus_space_write_region_4(sc->sc_iot, sc->sc_ioh, SB_WKEY, key, 4);

	/* Ask the security block to do it */
	bus_space_write_4(sc->sc_iot, sc->sc_ioh, SB_CTL_A,
	    control | SB_CTL_WK | SB_CTL_DC | SB_CTL_SC | SB_CTL_ST);

	/*
	 * Now wait until it is done.
	 *
	 * We do a busy wait.  Obviously the number of iterations of
	 * the loop required to perform the AES operation depends upon
	 * the number of bytes to process.
	 *
	 * On a 500 MHz Geode LX we see
	 *
	 *	length (bytes)	typical max iterations
	 *	    16		   12
	 *	    64		   22
	 *	   256		   59
	 *	  1024		  212
	 *	  8192		1,537
	 *
	 * Since we have a maximum size of operation defined in
	 * GLXSB_MAX_AES_LEN, we use this constant to decide how long
	 * to wait.  Allow an order of magnitude longer than it should
	 * really take, just in case.
	 */
	for (i = 0; i < GLXSB_MAX_AES_LEN * 10; i++) {
		status = bus_space_read_4(sc->sc_iot, sc->sc_ioh, SB_CTL_A);

		if ((status & SB_CTL_ST) == 0)		/* Done */
			return;
	}

	printf("%s: operation failed to complete\n", sc->sc_dev.dv_xname);
}

static int
glxsb_crypto_swauth(struct cryptop *crp, struct cryptodesc *crd,
    struct swcr_data *sw, caddr_t buf)
{
	int	type;

	if (crp->crp_flags & CRYPTO_F_IMBUF)
		type = CRYPTO_BUF_MBUF;
	else
		type = CRYPTO_BUF_IOV;

	return (swcr_authcompute(crp, crd, sw, buf, type));
}

static int
glxsb_crypto_swenc(struct cryptop *crp, struct cryptodesc *crd,
    struct swcr_data *sw, caddr_t buf)
{
	int	type;

	if (crp->crp_flags & CRYPTO_F_IMBUF)
		type = CRYPTO_BUF_MBUF;
	else
		type = CRYPTO_BUF_IOV;

	return (swcr_encdec(crd, sw, buf, type));
}

static int
glxsb_crypto_encdec(struct cryptop *crp, struct cryptodesc *crd,
    struct glxsb_session *ses, struct glxsb_softc *sc, caddr_t buf)
{
	char *op_src, *op_dst;
	uint32_t op_psrc, op_pdst;
	uint8_t op_iv[SB_AES_BLOCK_SIZE];
	int err = 0;
	int len, tlen, xlen;
	int offset;
	uint32_t control;

	if (crd == NULL || (crd->crd_len % SB_AES_BLOCK_SIZE) != 0) {
		err = EINVAL;
		goto out;
	}

	/* How much of our buffer will we need to use? */
	xlen = crd->crd_len > GLXSB_MAX_AES_LEN ?
	    GLXSB_MAX_AES_LEN : crd->crd_len;

	/*
	 * XXX Check if we can have input == output on Geode LX.
	 * XXX In the meantime, use two separate (adjacent) buffers.
	 */
	op_src = sc->sc_dma.dma_vaddr;
	op_dst = sc->sc_dma.dma_vaddr + xlen;

	op_psrc = sc->sc_dma.dma_paddr;
	op_pdst = sc->sc_dma.dma_paddr + xlen;

	if (crd->crd_flags & CRD_F_ENCRYPT) {
		control = SB_CTL_ENC;
		if (crd->crd_flags & CRD_F_IV_EXPLICIT)
			bcopy(crd->crd_iv, op_iv, sizeof(op_iv));
		else
			arc4random_buf(op_iv, sizeof(op_iv));

		if ((crd->crd_flags & CRD_F_IV_PRESENT) == 0) {
			if (crp->crp_flags & CRYPTO_F_IMBUF)
				err = m_copyback((struct mbuf *)crp->crp_buf,
				    crd->crd_inject, sizeof(op_iv), op_iv,
				    M_NOWAIT);
			else if (crp->crp_flags & CRYPTO_F_IOV)
				cuio_copyback((struct uio *)crp->crp_buf,
				    crd->crd_inject, sizeof(op_iv), op_iv);
			else
				bcopy(op_iv,
				    crp->crp_buf + crd->crd_inject, sizeof(op_iv));
			if (err)
				goto out;
		}
	} else {
		control = SB_CTL_DEC;
		if (crd->crd_flags & CRD_F_IV_EXPLICIT)
			bcopy(crd->crd_iv, op_iv, sizeof(op_iv));
		else {
			if (crp->crp_flags & CRYPTO_F_IMBUF)
				m_copydata((struct mbuf *)crp->crp_buf,
				    crd->crd_inject, sizeof(op_iv), op_iv);
			else if (crp->crp_flags & CRYPTO_F_IOV)
				cuio_copydata((struct uio *)crp->crp_buf,
				    crd->crd_inject, sizeof(op_iv), op_iv);
			else
				bcopy(crp->crp_buf + crd->crd_inject,
				    op_iv, sizeof(op_iv));
		}
	}

	offset = 0;
	tlen = crd->crd_len;

	/* Process the data in GLXSB_MAX_AES_LEN chunks */
	while (tlen > 0) {
		len = (tlen > GLXSB_MAX_AES_LEN) ? GLXSB_MAX_AES_LEN : tlen;

		if (crp->crp_flags & CRYPTO_F_IMBUF)
			m_copydata((struct mbuf *)crp->crp_buf,
			    crd->crd_skip + offset, len, op_src);
		else if (crp->crp_flags & CRYPTO_F_IOV)
			cuio_copydata((struct uio *)crp->crp_buf,
			    crd->crd_skip + offset, len, op_src);
		else
			bcopy(crp->crp_buf + crd->crd_skip + offset, op_src,
			    len);

		glxsb_dma_pre_op(sc, &sc->sc_dma);

		glxsb_aes(sc, control, op_psrc, op_pdst, ses->ses_key,
		    len, op_iv);

		glxsb_dma_post_op(sc, &sc->sc_dma);

		if (crp->crp_flags & CRYPTO_F_IMBUF)
			err = m_copyback((struct mbuf *)crp->crp_buf,
			    crd->crd_skip + offset, len, op_dst, M_NOWAIT);
		else if (crp->crp_flags & CRYPTO_F_IOV)
			cuio_copyback((struct uio *)crp->crp_buf,
			    crd->crd_skip + offset, len, op_dst);
		else
			bcopy(op_dst, crp->crp_buf + crd->crd_skip + offset,
			    len);
		if (err)
			break;

		offset += len;
		tlen -= len;

		if (tlen > 0) {
			/* Copy out last block for use as next iteration */
			if (crd->crd_flags & CRD_F_ENCRYPT)
				bcopy(op_dst + len - sizeof(op_iv), op_iv,
				    sizeof(op_iv));
			else
				bcopy(op_src + len - sizeof(op_iv), op_iv,
				    sizeof(op_iv));
		}
	}

	/* All AES processing has now been done. */
	explicit_bzero(sc->sc_dma.dma_vaddr, xlen * 2);

out:
	return (err);
}

int
glxsb_crypto_process(struct cryptop *crp)
{
	struct glxsb_softc *sc = glxsb_sc;
	struct glxsb_session *ses;
	struct cryptodesc *crd;
	int sesn,err = 0;
	int s;

	s = splnet();

	if (crp == NULL || crp->crp_callback == NULL) {
		err = EINVAL;
		goto out;
	}
	crd = crp->crp_desc;
	if (crd == NULL) {
		err = EINVAL;
		goto out;
	}

	sesn = GLXSB_SESSION(crp->crp_sid);
	if (sesn >= sc->sc_nsessions) {
		err = EINVAL;
		goto out;
	}
	ses = &sc->sc_sessions[sesn];
	if (ses->ses_used == 0) {
		err = EINVAL;
		goto out;
	}

	for (crd = crp->crp_desc; crd; crd = crd->crd_next) {
		switch (crd->crd_alg) {
		case CRYPTO_AES_CBC:
			if (ses->ses_swd_enc) {
				if ((err = glxsb_crypto_swenc(crp, crd, ses->ses_swd_enc,
				    crp->crp_buf)) != 0)
					goto out;
			} else if ((err = glxsb_crypto_encdec(crp, crd, ses, sc,
			    crp->crp_buf)) != 0)
				goto out;
			break;

		case CRYPTO_MD5_HMAC:
		case CRYPTO_SHA1_HMAC:
		case CRYPTO_RIPEMD160_HMAC:
		case CRYPTO_SHA2_256_HMAC:
		case CRYPTO_SHA2_384_HMAC:
		case CRYPTO_SHA2_512_HMAC:
			if ((err = glxsb_crypto_swauth(crp, crd, ses->ses_swd_auth,
			    crp->crp_buf)) != 0)
				goto out;
			break;

		default:
			err = EINVAL;
			goto out;
		}
	}

out:
	crp->crp_etype = err;
	crypto_done(crp);
	splx(s);
	return (err);
}

int
glxsb_dma_alloc(struct glxsb_softc *sc, int size, struct glxsb_dma_map *dma)
{
	int rc;

	dma->dma_nsegs = 1;
	dma->dma_size = size;

	rc = bus_dmamap_create(sc->sc_dmat, size, dma->dma_nsegs, size,
	    0, BUS_DMA_NOWAIT, &dma->dma_map);
	if (rc != 0) {
		printf("%s: couldn't create DMA map for %d bytes (%d)\n",
		    sc->sc_dev.dv_xname, size, rc);

		goto fail0;
	}

	rc = bus_dmamem_alloc(sc->sc_dmat, size, SB_AES_ALIGN, 0,
	    &dma->dma_seg, dma->dma_nsegs, &dma->dma_nsegs, BUS_DMA_NOWAIT);
	if (rc != 0) {
		printf("%s: couldn't allocate DMA memory of %d bytes (%d)\n",
		    sc->sc_dev.dv_xname, size, rc);

		goto fail1;
	}

	rc = bus_dmamem_map(sc->sc_dmat, &dma->dma_seg, 1, size,
	    &dma->dma_vaddr, BUS_DMA_NOWAIT);
	if (rc != 0) {
		printf("%s: couldn't map DMA memory for %d bytes (%d)\n",
		    sc->sc_dev.dv_xname, size, rc);

		goto fail2;
	}

	rc = bus_dmamap_load(sc->sc_dmat, dma->dma_map, dma->dma_vaddr,
	    size, NULL, BUS_DMA_NOWAIT);
	if (rc != 0) {
		printf("%s: couldn't load DMA memory for %d bytes (%d)\n",
		    sc->sc_dev.dv_xname, size, rc);

		goto fail3;
	}

	dma->dma_paddr = dma->dma_map->dm_segs[0].ds_addr;

	return 0;

fail3:
	bus_dmamem_unmap(sc->sc_dmat, dma->dma_vaddr, size);
fail2:
	bus_dmamem_free(sc->sc_dmat, &dma->dma_seg, dma->dma_nsegs);
fail1:
	bus_dmamap_destroy(sc->sc_dmat, dma->dma_map);
fail0:
	return rc;
}

void
glxsb_dma_pre_op(struct glxsb_softc *sc, struct glxsb_dma_map *dma)
{
	bus_dmamap_sync(sc->sc_dmat, dma->dma_map, 0, dma->dma_size,
	    BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE);
}

void
glxsb_dma_post_op(struct glxsb_softc *sc, struct glxsb_dma_map *dma)
{
	bus_dmamap_sync(sc->sc_dmat, dma->dma_map, 0, dma->dma_size,
	    BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE);
}

void
glxsb_dma_free(struct glxsb_softc *sc, struct glxsb_dma_map *dma)
{
	bus_dmamap_unload(sc->sc_dmat, dma->dma_map);
	bus_dmamem_unmap(sc->sc_dmat, dma->dma_vaddr, dma->dma_size);
	bus_dmamem_free(sc->sc_dmat, &dma->dma_seg, dma->dma_nsegs);
	bus_dmamap_destroy(sc->sc_dmat, dma->dma_map);
}

#endif /* CRYPTO */


code:
/*	$OpenBSD: gscpcib.c,v 1.6 2009/03/29 21:53:52 sthen Exp $	*/
/*
 * Copyright (c) 2004 Alexander Yurchenko <grange@openbsd.org>
 *
 * 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.
 */

/*
 * Special driver for the National Semiconductor Geode SC1100 PCI-ISA bridge
 * that attaches instead of pcib(4). In addition to the core pcib(4)
 * functionality this driver provides support for the GPIO interface.
 */

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

#include <machine/bus.h>

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

#include <dev/gpio/gpiovar.h>

#include <i386/pci/gscpcibreg.h>

struct gscpcib_softc {
	struct device sc_dev;

	/* GPIO interface */
	bus_space_tag_t sc_gpio_iot;
	bus_space_handle_t sc_gpio_ioh;
	struct gpio_chipset_tag sc_gpio_gc;
	gpio_pin_t sc_gpio_pins[GSCGPIO_NPINS];
};

int	gscpcib_match(struct device *, void *, void *);
void	gscpcib_attach(struct device *, struct device *, void *);

int	gscpcib_gpio_pin_read(void *, int);
void	gscpcib_gpio_pin_write(void *, int, int);
void	gscpcib_gpio_pin_ctl(void *, int, int);

/* arch/i386/pci/pcib.c */
void    pcibattach(struct device *, struct device *, void *);

struct cfattach gscpcib_ca = {
	sizeof (struct gscpcib_softc),
	gscpcib_match,
	gscpcib_attach
};

struct cfdriver gscpcib_cd = {
	NULL, "gscpcib", DV_DULL
};

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

	if (PCI_CLASS(pa->pa_class) != PCI_CLASS_BRIDGE ||
	    PCI_SUBCLASS(pa->pa_class) != PCI_SUBCLASS_BRIDGE_ISA)
		return (0);

	if (PCI_VENDOR(pa->pa_id) == PCI_VENDOR_NS &&
	    PCI_PRODUCT(pa->pa_id) == PCI_PRODUCT_NS_SC1100_ISA)
		return (2);	/* supersede pcib(4) */

	return (0);
}

void
gscpcib_attach(struct device *parent, struct device *self, void *aux)
{
#ifndef SMALL_KERNEL
	struct gscpcib_softc *sc = (struct gscpcib_softc *)self;
	struct pci_attach_args *pa = aux;
	struct gpiobus_attach_args gba;
	pcireg_t gpiobase;
	int i;
	int gpio_present = 0;

	/* Map GPIO I/O space */
	gpiobase = pci_conf_read(pa->pa_pc, pa->pa_tag, GSCGPIO_BASE);
	sc->sc_gpio_iot = pa->pa_iot;
	if (PCI_MAPREG_IO_ADDR(gpiobase) == 0 ||
	    bus_space_map(sc->sc_gpio_iot, PCI_MAPREG_IO_ADDR(gpiobase),
	    GSCGPIO_SIZE, 0, &sc->sc_gpio_ioh)) {
		printf(": can't map GPIO i/o space");
		goto corepcib;
	}

	/* Initialize pins array */
	for (i = 0; i < GSCGPIO_NPINS; i++) {
		sc->sc_gpio_pins[i].pin_num = i;
		sc->sc_gpio_pins[i].pin_caps = GPIO_PIN_INPUT |
		    GPIO_PIN_OUTPUT | GPIO_PIN_OPENDRAIN |
		    GPIO_PIN_PUSHPULL | GPIO_PIN_TRISTATE |
		    GPIO_PIN_PULLUP;

		/* Read initial state */
		sc->sc_gpio_pins[i].pin_state = gscpcib_gpio_pin_read(sc, i) ?
		    GPIO_PIN_HIGH : GPIO_PIN_LOW;
	}

	/* Create controller tag */
	sc->sc_gpio_gc.gp_cookie = sc;
	sc->sc_gpio_gc.gp_pin_read = gscpcib_gpio_pin_read;
	sc->sc_gpio_gc.gp_pin_write = gscpcib_gpio_pin_write;
	sc->sc_gpio_gc.gp_pin_ctl = gscpcib_gpio_pin_ctl;

	gba.gba_name = "gpio";
	gba.gba_gc = &sc->sc_gpio_gc;
	gba.gba_pins = sc->sc_gpio_pins;
	gba.gba_npins = GSCGPIO_NPINS;

	gpio_present = 1;

corepcib:
#endif	/* !SMALL_KERNEL */
	/* Provide core pcib(4) functionality */
	pcibattach(parent, self, aux);

#ifndef SMALL_KERNEL
	/* Attach GPIO framework */
	if (gpio_present)
		config_found(&sc->sc_dev, &gba, gpiobus_print);
#endif	/* !SMALL_KERNEL */
}

#ifndef SMALL_KERNEL
static __inline void
gscpcib_gpio_pin_select(struct gscpcib_softc *sc, int pin)
{
	bus_space_write_4(sc->sc_gpio_iot, sc->sc_gpio_ioh, GSCGPIO_SEL, pin);
}

int
gscpcib_gpio_pin_read(void *arg, int pin)
{
	struct gscpcib_softc *sc = arg;
	int reg, shift;
	u_int32_t data;

	reg = (pin < 32 ? GSCGPIO_GPDI0 : GSCGPIO_GPDI1);
	shift = pin % 32;
	data = bus_space_read_4(sc->sc_gpio_iot, sc->sc_gpio_ioh, reg);

	return ((data >> shift) & 0x1);
}

void
gscpcib_gpio_pin_write(void *arg, int pin, int value)
{
	struct gscpcib_softc *sc = arg;
	int reg, shift;
	u_int32_t data;

	reg = (pin < 32 ? GSCGPIO_GPDO0 : GSCGPIO_GPDO1);
	shift = pin % 32;
	data = bus_space_read_4(sc->sc_gpio_iot, sc->sc_gpio_ioh, reg);
	if (value == 0)
		data &= ~(1 << shift);
	else if (value == 1)
		data |= (1 << shift);

	bus_space_write_4(sc->sc_gpio_iot, sc->sc_gpio_ioh, reg, data);
}

void
gscpcib_gpio_pin_ctl(void *arg, int pin, int flags)
{
	struct gscpcib_softc *sc = arg;
	u_int32_t conf;

	gscpcib_gpio_pin_select(sc, pin);
	conf = bus_space_read_4(sc->sc_gpio_iot, sc->sc_gpio_ioh,
	    GSCGPIO_CONF);

	conf &= ~(GSCGPIO_CONF_OUTPUTEN | GSCGPIO_CONF_PUSHPULL |
	    GSCGPIO_CONF_PULLUP);
	if ((flags & GPIO_PIN_TRISTATE) == 0)
		conf |= GSCGPIO_CONF_OUTPUTEN;
	if (flags & GPIO_PIN_PUSHPULL)
		conf |= GSCGPIO_CONF_PUSHPULL;
	if (flags & GPIO_PIN_PULLUP)
		conf |= GSCGPIO_CONF_PULLUP;
	bus_space_write_4(sc->sc_gpio_iot, sc->sc_gpio_ioh,
	    GSCGPIO_CONF, conf);
}
#endif	/* !SMALL_KERNEL */

license: unknown


code:
/*	$OpenBSD: gscpcibreg.h,v 1.1 2004/06/03 18:22:21 grange Exp $	*/
/*
 * Copyright (c) 2004 Alexander Yurchenko <grange@openbsd.org>
 *
 * 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.
 */

#ifndef _I386_PCI_GSCGPIOREG_H_
#define _I386_PCI_GSCGPIOREG_H_

#define GSCGPIO_NPINS	64

#define GSCGPIO_BASE	0x10
#define GSCGPIO_SIZE	64

#define GSCGPIO_GPDO0	0x00
#define GSCGPIO_GPDI0	0x04
#define GSCGPIO_GPIEN0	0x08
#define GSCGPIO_GPST0	0x0c
#define GSCGPIO_GPDO1	0x10
#define GSCGPIO_GPDI1	0x14
#define GSCGPIO_GPIEN1	0x18
#define GSCGPIO_GPST1	0x1c
#define GSCGPIO_SEL	0x20
#define GSCGPIO_CONF	0x24
#define GSCGPIO_RESET	0x28

#define GSCGPIO_CONF_OUTPUTEN	(1 << 0)
#define GSCGPIO_CONF_PUSHPULL	(1 << 1)
#define GSCGPIO_CONF_PULLUP	(1 << 2)

#endif	/* !_I386_PCI_GSCGPIOREG_H_ */


code:
/*	$OpenBSD: gscpm.c,v 1.9 2012/08/16 18:42:04 tedu Exp $	*/
/*
 * Copyright (c) 2004 Alexander Yurchenko <grange@openbsd.org>
 *
 * 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.
 */

/*
 * National Semiconductor Geode SC1100 SMI/ACPI module.
 */

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

#include <machine/bus.h>

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

#include <i386/pci/gscpmreg.h>

struct gscpm_softc {
	struct device sc_dev;

	pci_chipset_tag_t sc_pc;
	pcitag_t sc_tag;
	bus_space_tag_t sc_iot;
	bus_space_handle_t sc_acpi_ioh;
};

int	gscpm_match(struct device *, void *, void *);
void	gscpm_attach(struct device *, struct device *, void *);

void	gscpm_setperf(int);

u_int	gscpm_get_timecount(struct timecounter *tc);

struct timecounter gscpm_timecounter = {
	gscpm_get_timecount,	/* get_timecount */
	0,			/* no poll_pps */
	0xffffff,		/* counter_mask */
	3579545,		/* frequency */
	"GSCPM",		/* name */
	1000			/* quality */
};

struct cfattach gscpm_ca = {
	sizeof (struct gscpm_softc),
	gscpm_match,
	gscpm_attach
};

struct cfdriver gscpm_cd = {
	NULL, "gscpm", DV_DULL
};

#if 0
static void *gscpm_cookie;	/* XXX */
#endif

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

	if (PCI_VENDOR(pa->pa_id) == PCI_VENDOR_NS &&
	    PCI_PRODUCT(pa->pa_id) == PCI_PRODUCT_NS_SC1100_SMI)
		return (1);

	return (0);
}

void
gscpm_attach(struct device *parent, struct device *self, void *aux)
{
	struct gscpm_softc *sc = (struct gscpm_softc *)self;
	struct pci_attach_args *pa = aux;
	pcireg_t csr, acpibase;

	sc->sc_pc = pa->pa_pc;
	sc->sc_tag = pa->pa_tag;
	sc->sc_iot = pa->pa_iot;

	/* Enable I/O space */
	csr = pci_conf_read(sc->sc_pc, sc->sc_tag, PCI_COMMAND_STATUS_REG);
	pci_conf_write(sc->sc_pc, sc->sc_tag, PCI_COMMAND_STATUS_REG,
	    csr | PCI_COMMAND_IO_ENABLE);

	/* Map ACPI registers */
	acpibase = pci_conf_read(sc->sc_pc, sc->sc_tag, GSCPM_ACPIBASE);
	if (PCI_MAPREG_IO_ADDR(acpibase) == 0 ||
	    bus_space_map(sc->sc_iot, PCI_MAPREG_IO_ADDR(acpibase),
	    GSCPM_ACPISIZE, 0, &sc->sc_acpi_ioh)) {
		printf(": failed to map ACPI registers\n");
		return;
	}

	printf("\n");

	/* Hook into the kern_tc */
	gscpm_timecounter.tc_priv = sc;
	tc_init(&gscpm_timecounter);

/* XXX: disabled due to unresolved yet hardware errata */
#if 0
	/* Hook into the hw.setperf sysctl */
	gscpm_cookie = sc;
	cpu_setperf = gscpm_setperf;
#endif

}

u_int
gscpm_get_timecount(struct timecounter *tc)
{
	struct gscpm_softc *sc = tc->tc_priv;

	return (bus_space_read_4(sc->sc_iot, sc->sc_acpi_ioh, GSCPM_PM_TMR));
}

#if 0
void
gscpm_setperf(int level)
{
	struct gscpm_softc *sc = gscpm_cookie;
	int i;
	u_int32_t pctl;

	pctl = bus_space_read_4(sc->sc_iot, sc->sc_acpi_ioh, GSCPM_P_CNT);

	if (level == 100) {
		/* 100 is a maximum perfomance, disable throttling */
		pctl &= ~GSCPM_P_CNT_THTEN;
	} else {
		for (i = 0; i < GSCPM_THT_LEVELS; i++)
			if (level >= gscpm_tht[i].level)
				break;
		pctl = (0xf0 | GSCPM_P_CNT_THTEN |
		    GSCPM_P_CNT_CLK(gscpm_tht[i].value));
	}

	/* Update processor control register */
	bus_space_write_4(sc->sc_iot, sc->sc_acpi_ioh, GSCPM_P_CNT, pctl);
}
#endif


code:
/*	$OpenBSD: gscpmreg.h,v 1.1 2004/09/15 20:28:53 grange Exp $	*/
/*
 * Copyright (c) 2004 Alexander Yurchenko <grange@openbsd.org>
 *
 * 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.
 */

#ifndef _DEV_PCI_GSCPMREG_H_
#define _DEV_PCI_GSCPMREG_H_

/*
 * National Semiconductor Geode SC1100 SMI/ACPI module register definitions.
 */

/* PCI configuration space */
#define GSCPM_ACPIBASE		0x40	/* ACPI I/O space base address */
#define GSCPM_ACPISIZE		256	/* ACPI I/O space size */

/* ACPI I/O space */
#define GSCPM_P_CNT		0x00	/* processor control */
#define		GSCPM_P_CNT_CLK(val)	((val) & 0x7)
#define		GSCPM_P_CNT_THTEN	(1 << 4)  /* throttle enable */
#define GSCPM_PM_TMR		0x1c	/* PM timer */

/* CPU throttling values */
static const struct {
	int level;
	int value;
} gscpm_tht[] = {
	{ 88, 1 },
	{ 75, 2 },
	{ 63, 3 },
	{ 50, 4 },
	{ 38, 5 },
	{ 25, 6 },
	{ 13, 7 }
};

#define GSCPM_THT_LEVELS (sizeof(gscpm_tht) / sizeof(gscpm_tht[0]))

#endif	/* !_DEV_PCI_GSCPMREG_H_ */


code:
/*	$OpenBSD: ichpcib.c,v 1.28 2014/09/14 14:17:23 jsg Exp $	*/
/*
 * Copyright (c) 2004 Alexander Yurchenko <grange@openbsd.org>
 *
 * 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.
 */

/*
 * Special driver for the Intel ICHx/ICHx-M LPC bridges that attaches
 * instead of pcib(4). In addition to the core pcib(4) functionality this
 * driver provides support for the Intel SpeedStep technology and
 * power management timer.
 */

#include <sys/param.h>
#include <sys/systm.h>
#include <sys/device.h>
#include <sys/sysctl.h>
#include <sys/timetc.h>

#include <machine/bus.h>

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

#include <dev/pci/ichreg.h>

#include <machine/cpu.h>
#include <machine/cpufunc.h>

struct ichpcib_softc {
	struct device sc_dev;

	bus_space_tag_t sc_pm_iot;
	bus_space_handle_t sc_pm_ioh;
};

int	ichpcib_match(struct device *, void *, void *);
void	ichpcib_attach(struct device *, struct device *, void *);

int	ichss_present(struct pci_attach_args *);
void	ichss_setperf(int);

/* arch/i386/pci/pcib.c */
void    pcibattach(struct device *, struct device *, void *);

u_int	ichpcib_get_timecount(struct timecounter *tc);

struct timecounter ichpcib_timecounter = {
	ichpcib_get_timecount,	/* get_timecount */
	0,			/* no poll_pps */
	0xffffff,		/* counter_mask */
	3579545,		/* frequency */
	"ICHPM",		/* name */
	1000			/* quality */
};

struct cfattach ichpcib_ca = {
	sizeof(struct ichpcib_softc), ichpcib_match, ichpcib_attach
};

struct cfdriver ichpcib_cd = {
	NULL, "ichpcib", DV_DULL
};

#ifndef SMALL_KERNEL
static const char p4hint[] = "Mobile Intel(R) Pentium(R) 4";
struct ichpcib_softc *ichss_sc;
extern int setperf_prio;
#endif	/* !SMALL_KERNEL */

const struct pci_matchid ichpcib_devices[] = {
	{ PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_6300ESB_LPC },
	{ PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_6321ESB_LPC },
	{ PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82801AA_LPC },
	{ PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82801AB_LPC },
	{ PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82801BA_LPC },
	{ PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82801BAM_LPC },
	{ PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82801CA_LPC },
	{ PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82801CAM_LPC },
	{ PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82801DB_LPC },
	{ PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82801DBM_LPC },
	{ PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82801E_LPC },
	{ PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82801EB_LPC },
	{ PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82801FB_LPC },
	{ PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82801FBM_LPC },
	{ PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82801GB_LPC },
	{ PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82801GBM_LPC },
	{ PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82801GH_LPC },
	{ PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82801GHM_LPC },
	{ PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82801H_LPC },
	{ PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82801HBM_LPC },
	{ PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82801IB_LPC },
	{ PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82801IH_LPC },
	{ PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82801IO_LPC },
	{ PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82801IR_LPC }
};

int
ichpcib_match(struct device *parent, void *match, void *aux)
{
	if (pci_matchbyid((struct pci_attach_args *)aux, ichpcib_devices,
	    sizeof(ichpcib_devices) / sizeof(ichpcib_devices[0])))
		return (2);	/* supersede pcib(4) */
	return (0);
}

void
ichpcib_attach(struct device *parent, struct device *self, void *aux)
{
	struct ichpcib_softc *sc = (struct ichpcib_softc *)self;
	struct pci_attach_args *pa = aux;
	pcireg_t cntl, pmbase;

	/* Check if power management I/O space is enabled */
	cntl = pci_conf_read(pa->pa_pc, pa->pa_tag, ICH_ACPI_CNTL);
	if ((cntl & ICH_ACPI_CNTL_ACPI_EN) == 0) {
		printf(": PM disabled");
		goto corepcib;
	}

	/* Map power management I/O space */
	sc->sc_pm_iot = pa->pa_iot;
	pmbase = pci_conf_read(pa->pa_pc, pa->pa_tag, ICH_PMBASE);
	if (bus_space_map(sc->sc_pm_iot, PCI_MAPREG_IO_ADDR(pmbase),
	    ICH_PMSIZE, 0, &sc->sc_pm_ioh) != 0)
		goto corepcib;

	/* Register new timecounter */
	ichpcib_timecounter.tc_priv = sc;
	tc_init(&ichpcib_timecounter);

	printf(": %s-bit timer at %lluHz",
	    (ichpcib_timecounter.tc_counter_mask == 0xffffffff ? "32" : "24"),
	    (unsigned long long)ichpcib_timecounter.tc_frequency);

#ifndef SMALL_KERNEL
	/* Check for SpeedStep */
	if (ichss_present(pa)) {
		printf(": SpeedStep");

		/* Enable SpeedStep */
		pci_conf_write(pa->pa_pc, pa->pa_tag, ICH_GEN_PMCON1,
		    pci_conf_read(pa->pa_pc, pa->pa_tag, ICH_GEN_PMCON1) |
			ICH_GEN_PMCON1_SS_EN);

		/* Hook into hw.setperf sysctl */
		ichss_sc = sc;
		cpu_setperf = ichss_setperf;
		setperf_prio = 2;
	}
#endif /* !SMALL_KERNEL */

corepcib:
	/* Provide core pcib(4) functionality */
	pcibattach(parent, self, aux);
}

#ifndef SMALL_KERNEL
int
ichss_present(struct pci_attach_args *pa)
{
	pcitag_t br_tag;
	pcireg_t br_id, br_class;
	struct cpu_info *ci;
	int family, model, stepping, brandid, ret;

	ret = 0;
	if (setperf_prio > 2)
		return (ret);

	ci = curcpu();
	family = (ci->ci_signature >> 8) & 15;
	model = (ci->ci_signature >> 4) & 15;
	stepping = ci->ci_signature & 15;
	brandid = cpu_miscinfo & 0xff; /* XXX should put this in ci */

	/*
	 * This form of SpeedStep works only with certain Intel processors.
	 * However, other processors can be coupled with these ICH southbridges
	 * causing false positives. This heuristic comes partly from the
	 * Linux speedstep-ich driver.
	 */
	if (PCI_PRODUCT(pa->pa_id) == PCI_PRODUCT_INTEL_82801DBM_LPC ||
	    PCI_PRODUCT(pa->pa_id) == PCI_PRODUCT_INTEL_82801CAM_LPC ||
	    PCI_PRODUCT(pa->pa_id) == PCI_PRODUCT_INTEL_82801BAM_LPC) {
		if (family == 15 && model == 2) {
			switch(stepping) {
			case 4:
				if (brandid == 14 || brandid == 15)
					ret = 1;
				break;
			case 7:
				if (brandid == 14)
					ret = 1;
				break;
			case 9:
				if (brandid == 14 && strncasecmp(cpu_model,
				    p4hint, sizeof(p4hint)-1) == 0) {
					ret = 1;
				}
				break;
			}
		} else if (family == 6 && model == 11) {
			if (stepping == 1)
				ret = 1;
		}

		/*
		 * Old revisions of the 82815 hostbridge found on
		 * Dell Inspirons 8000 and 8100 don't support
		 * SpeedStep.
		 */
		if (PCI_PRODUCT(pa->pa_id) == PCI_PRODUCT_INTEL_82801BAM_LPC) {
			/*
			 * XXX: dev 0 func 0 is not always a hostbridge,
			 * should be converted to use pchb(4) hook.
			 */
			br_tag = pci_make_tag(pa->pa_pc, pa->pa_bus, 0, 0);
			br_id = pci_conf_read(pa->pa_pc, br_tag, PCI_ID_REG);
			br_class = pci_conf_read(pa->pa_pc, br_tag, PCI_CLASS_REG);

			if (PCI_PRODUCT(br_id) == PCI_PRODUCT_INTEL_82815_HB &&
			    PCI_REVISION(br_class) < 5) {
				ret = 0;
			}
		}
	}

	return (ret);
}

void
ichss_setperf(int level)
{
	struct ichpcib_softc *sc = ichss_sc;
	u_int8_t state, ostate, cntl;
	int s;

#ifdef DIAGNOSTIC
	if (sc == NULL) {
		printf("%s: no ichss_sc", __func__);
		return;
	}
#endif

	s = splhigh();
	state = bus_space_read_1(sc->sc_pm_iot, sc->sc_pm_ioh, ICH_PM_SS_CNTL);
	ostate = state;

	/* Only two states are available */
	if (level <= 50)
		state |= ICH_PM_SS_STATE_LOW;
	else
		state &= ~ICH_PM_SS_STATE_LOW;

	/*
	 * An Intel SpeedStep technology transition _always_ occur on
	 * writes to the ICH_PM_SS_CNTL register, even if the value
	 * written is the same as the previous value. So do the write
	 * only if the state has changed.
	 */
	if (state != ostate) {
		/* Disable bus mastering arbitration */
		cntl = bus_space_read_1(sc->sc_pm_iot, sc->sc_pm_ioh,
		    ICH_PM_CNTL);
		bus_space_write_1(sc->sc_pm_iot, sc->sc_pm_ioh, ICH_PM_CNTL,
		    cntl | ICH_PM_ARB_DIS);

		/* Do the transition */
		bus_space_write_1(sc->sc_pm_iot, sc->sc_pm_ioh, ICH_PM_SS_CNTL,
		    state);

		/* Restore bus mastering arbitration state */
		bus_space_write_1(sc->sc_pm_iot, sc->sc_pm_ioh, ICH_PM_CNTL,
		    cntl);

		if (update_cpuspeed != NULL)
			update_cpuspeed();
	}
	splx(s);
}
#endif	/* !SMALL_KERNEL */

u_int
ichpcib_get_timecount(struct timecounter *tc)
{
	struct ichpcib_softc *sc = tc->tc_priv;
	u_int u1, u2, u3;

	u2 = bus_space_read_4(sc->sc_pm_iot, sc->sc_pm_ioh, ICH_PM_TMR);
	u3 = bus_space_read_4(sc->sc_pm_iot, sc->sc_pm_ioh, ICH_PM_TMR);
	do {
		u1 = u2;
		u2 = u3;
		u3 = bus_space_read_4(sc->sc_pm_iot, sc->sc_pm_ioh,
		    ICH_PM_TMR);
	} while (u1 > u2 || u2 > u3);

	return (u2);
}


code:
/*	$OpenBSD: opti82c558.c,v 1.8 2008/06/26 05:42:11 ray Exp $	*/
/*	$NetBSD: opti82c558.c,v 1.2 2000/07/18 11:24:09 soda Exp $	*/

/*-
 * Copyright (c) 1999 The NetBSD Foundation, Inc.
 * All rights reserved.
 *
 * This code is derived from software contributed to The NetBSD Foundation
 * by Jason R. Thorpe of the Numerical Aerospace Simulation Facility,
 * NASA Ames Research Center.
 *
 * 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 NETBSD FOUNDATION, INC. AND CONTRIBUTORS
 * ``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 FOUNDATION OR CONTRIBUTORS
 * 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.
 */

/*
 * Copyright (c) 1999, by UCHIYAMA Yasushi
 * All rights reserved.
 *
 * 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. The name of the developer may NOT be used to endorse or promote products
 *    derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``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 OR CONTRIBUTORS 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.
 */

/*
 * Support for the Opti 82c558 PCI-ISA bridge interrupt controller.
 */

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

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

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

#include <i386/pci/pcibiosvar.h>
#include <i386/pci/opti82c558reg.h>

int	opti82c558_getclink(pciintr_icu_handle_t, int, int *);
int	opti82c558_get_intr(pciintr_icu_handle_t, int, int *);
int	opti82c558_set_intr(pciintr_icu_handle_t, int, int);
int	opti82c558_get_trigger(pciintr_icu_handle_t, int, int *);
int	opti82c558_set_trigger(pciintr_icu_handle_t, int, int);

const struct pciintr_icu opti82c558_pci_icu = {
	opti82c558_getclink,
	opti82c558_get_intr,
	opti82c558_set_intr,
	opti82c558_get_trigger,
	opti82c558_set_trigger,
};

struct opti82c558_handle {
	pci_chipset_tag_t ph_pc;
	pcitag_t ph_tag;
};

static const int viper_pirq_decode[] = {
	-1, 5, 9, 10, 11, 12, 14, 15
};

static const int viper_pirq_encode[] = {
	-1,		/* 0 */
	-1,		/* 1 */
	-1,		/* 2 */
	-1,		/* 3 */
	-1,		/* 4 */
	VIPER_PIRQ_5,	/* 5 */
	-1,		/* 6 */
	-1,		/* 7 */
	-1,		/* 8 */
	VIPER_PIRQ_9,	/* 9 */
	VIPER_PIRQ_10,	/* 10 */
	VIPER_PIRQ_11,	/* 11 */
	VIPER_PIRQ_12,	/* 12 */
	-1,		/* 13 */
	VIPER_PIRQ_14,	/* 14 */
	VIPER_PIRQ_15,	/* 15 */
};

int
opti82c558_init(pci_chipset_tag_t pc, bus_space_tag_t iot, pcitag_t tag,
    pciintr_icu_tag_t *ptagp, pciintr_icu_handle_t *phandp)
{
	struct opti82c558_handle *ph;

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

	ph->ph_pc = pc;
	ph->ph_tag = tag;

	*ptagp = &opti82c558_pci_icu;
	*phandp = ph;
	return (0);
}

int
opti82c558_getclink(pciintr_icu_handle_t v, int link, int *clinkp)
{

	if (VIPER_LEGAL_LINK(link - 1)) {
		*clinkp = link - 1;
		return (0);
	}

	return (1);
}

int
opti82c558_get_intr(pciintr_icu_handle_t v, int clink, int *irqp)
{
	struct opti82c558_handle *ph = v;
	pcireg_t reg;
	int val;

	if (VIPER_LEGAL_LINK(clink) == 0)
		return (1);

	reg = pci_conf_read(ph->ph_pc, ph->ph_tag, VIPER_CFG_PIRQ);
	val = VIPER_PIRQ(reg, clink);
	*irqp = (val == VIPER_PIRQ_NONE) ? 0xff : viper_pirq_decode[val];

	return (0);
}

int
opti82c558_set_intr(pciintr_icu_handle_t v, int clink, int irq)
{
	struct opti82c558_handle *ph = v;
	int shift;
	pcireg_t reg;

	if (VIPER_LEGAL_LINK(clink) == 0 || VIPER_LEGAL_IRQ(irq) == 0)
		return (1);

	reg = pci_conf_read(ph->ph_pc, ph->ph_tag, VIPER_CFG_PIRQ);
	shift = VIPER_PIRQ_SELECT_SHIFT * clink;
	reg &= ~(VIPER_PIRQ_SELECT_MASK << shift);
	reg |= (viper_pirq_encode[irq] << shift);
	pci_conf_write(ph->ph_pc, ph->ph_tag, VIPER_CFG_PIRQ, reg);

	return (0);
}

int
opti82c558_get_trigger(pciintr_icu_handle_t v, int irq, int *triggerp)
{
	struct opti82c558_handle *ph = v;
	pcireg_t reg;

	if (VIPER_LEGAL_IRQ(irq) == 0) {
		/* ISA IRQ? */
		*triggerp = IST_EDGE;
		return (0);
	}

	reg = pci_conf_read(ph->ph_pc, ph->ph_tag, VIPER_CFG_PIRQ);
	if ((reg >> (VIPER_CFG_TRIGGER_SHIFT + viper_pirq_encode[irq])) & 1)
		*triggerp = IST_LEVEL;
	else
		*triggerp = IST_EDGE;

	return (0);
}

int
opti82c558_set_trigger(pciintr_icu_handle_t v, int irq, int trigger)
{
	struct opti82c558_handle *ph = v;
	int shift;
	pcireg_t reg;

	if (VIPER_LEGAL_IRQ(irq) == 0) {
		/* ISA IRQ? */
		return ((trigger != IST_LEVEL) ? 0 : 1);
	}

	reg = pci_conf_read(ph->ph_pc, ph->ph_tag, VIPER_CFG_PIRQ);
	shift = (VIPER_CFG_TRIGGER_SHIFT + viper_pirq_encode[irq]);
	if (trigger == IST_LEVEL)
		reg |= (1 << shift);
	else
		reg &= ~(1 << shift);
	pci_conf_write(ph->ph_pc, ph->ph_tag, VIPER_CFG_PIRQ, reg);

	return (0);
}


code:
/*	$OpenBSD: opti82c558reg.h,v 1.3 2000/03/28 03:37:59 mickey Exp $	*/
/*	$NetBSD: opti82c558reg.h,v 1.1 1999/11/17 01:21:20 thorpej Exp $  */

/*
 * Copyright (c) 1999, by UCHIYAMA Yasushi
 * All rights reserved.
 *
 * 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. The name of the developer may NOT be used to endorse or promote products
 *    derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``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 OR CONTRIBUTORS 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.
 */

/*
 * Register definitions for the Opti 82c558 PCI-ISA bridge interrupt
 * controller.
 */

/*
 * PCI IRQ Select Register
 */

#define	VIPER_CFG_PIRQ		0x40	/* PCI configuration space */

/*
 * Trigger setting:
 *
 *	[1:7]=>5,9,10,11,12,14,15 Edge = 0 Level = 1
 */
#define	VIPER_CFG_TRIGGER_SHIFT	16

#define	VIPER_LEGAL_LINK(link)	((link) >= 0 && (link) <= 3)

#define	VIPER_PIRQ_MASK		0xde20
#define	VIPER_LEGAL_IRQ(irq)	((irq) >= 0 && (irq) <= 15 &&		\
				 ((1 << (irq)) & VIPER_PIRQ_MASK) != 0)

#define	VIPER_PIRQ_NONE		0
#define	VIPER_PIRQ_5		1
#define	VIPER_PIRQ_9		2
#define	VIPER_PIRQ_10		3
#define	VIPER_PIRQ_11		4
#define	VIPER_PIRQ_12		5
#define	VIPER_PIRQ_14		6
#define	VIPER_PIRQ_15		7

#define	VIPER_PIRQ_SELECT_MASK	0x07
#define	VIPER_PIRQ_SELECT_SHIFT	3

#define	VIPER_PIRQ(reg, x)	(((reg) >> ((x) * VIPER_PIRQ_SELECT_SHIFT)) \
				 & VIPER_PIRQ_SELECT_MASK)


code:
/*	$OpenBSD: opti82c700.c,v 1.8 2008/06/26 05:42:11 ray Exp $	*/
/*	$NetBSD: opti82c700.c,v 1.2 2000/07/18 11:07:20 soda Exp $	*/

/*-
 * Copyright (c) 1999 The NetBSD Foundation, Inc.
 * All rights reserved.
 *
 * This code is derived from software contributed to The NetBSD Foundation
 * by Jason R. Thorpe of the Numerical Aerospace Simulation Facility,
 * NASA Ames Research Center.
 *
 * 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 NETBSD FOUNDATION, INC. AND CONTRIBUTORS
 * ``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 FOUNDATION OR CONTRIBUTORS
 * 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.
 */

/*
 * Copyright (c) 1999, by UCHIYAMA Yasushi
 * All rights reserved.
 *
 * 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. The name of the developer may NOT be used to endorse or promote products
 *    derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``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 OR CONTRIBUTORS 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.
 */

/*
 * Support for the Opti 82c700 FireStar PCI-ISA bridge interrupt controller.
 */

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

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

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

#include <i386/pci/pcibiosvar.h>
#include <i386/pci/opti82c700reg.h>

#ifdef FIRESTARDEBUG
#define	DPRINTF(arg)	printf arg
#else
#define	DPRINTF(arg)
#endif

int	opti82c700_getclink(pciintr_icu_handle_t, int, int *);
int	opti82c700_get_intr(pciintr_icu_handle_t, int, int *);
int	opti82c700_set_intr(pciintr_icu_handle_t, int, int);
int	opti82c700_get_trigger(pciintr_icu_handle_t, int, int *);
int	opti82c700_set_trigger(pciintr_icu_handle_t, int, int);

const struct pciintr_icu opti82c700_pci_icu = {
	opti82c700_getclink,
	opti82c700_get_intr,
	opti82c700_set_intr,
	opti82c700_get_trigger,
	opti82c700_set_trigger,
};

struct opti82c700_handle {
	pci_chipset_tag_t ph_pc;
	pcitag_t ph_tag;
};

int	opti82c700_addr(int, int *, int *);
#ifdef FIRESTARDEBUG
void	opti82c700_pir_dump(struct opti82c700_handle *);
#endif

int
opti82c700_init(pci_chipset_tag_t pc, bus_space_tag_t iot, pcitag_t tag,
    pciintr_icu_tag_t *ptagp, pciintr_icu_handle_t *phandp)
{
	struct opti82c700_handle *ph;

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

	ph->ph_pc = pc;
	ph->ph_tag = tag;
#ifdef FIRESTARDEBUG
	opti82c700_pir_dump(ph);
#endif
	*ptagp = &opti82c700_pci_icu;
	*phandp = ph;
	return (0);
}

int
opti82c700_addr(int link, int *addrofs, int *ofs)
{
	int regofs, src;

	regofs = FIRESTAR_PIR_REGOFS(link);
	src = FIRESTAR_PIR_SELECTSRC(link);

	switch (src) {
	case FIRESTAR_PIR_SELECT_NONE:
		return (1);

	case FIRESTAR_PIR_SELECT_IRQ:
		if (regofs < 0 || regofs > 7)
			return (1);
		*addrofs = FIRESTAR_CFG_INTR_IRQ + (regofs >> 2);
		*ofs = (regofs & 3) << 3;
		break;

	case FIRESTAR_PIR_SELECT_PIRQ:
		/* FALLTHROUGH */
	case FIRESTAR_PIR_SELECT_BRIDGE:
		if (regofs < 0 || regofs > 3)
			return (1);
		*addrofs = FIRESTAR_CFG_INTR_PIRQ;
		*ofs = regofs << 2;
		break;

	default:
		return (1);
	}

	return (0);
}

int
opti82c700_getclink(pciintr_icu_handle_t v, int link, int *clinkp)
{
	DPRINTF(("FireStar link value 0x%x: ", link));

	switch (FIRESTAR_PIR_SELECTSRC(link)) {
	default:
		DPRINTF(("bogus IRQ selection source\n"));
		return (1);
	case FIRESTAR_PIR_SELECT_NONE:
		DPRINTF(("No interrupt connection\n"));
		return (1);
	case FIRESTAR_PIR_SELECT_IRQ:
		DPRINTF(("FireStar IRQ pin"));
		break;
	case FIRESTAR_PIR_SELECT_PIRQ:
		DPRINTF(("FireStar PIO pin or Serial IRQ PIRQ#"));
		break;
	case FIRESTAR_PIR_SELECT_BRIDGE:
		DPRINTF(("FireBridge 1 INTx# pin"));
		break;
	}

	DPRINTF((" REGOFST:%#x\n", FIRESTAR_PIR_REGOFS(link)));
	*clinkp = link;

	return (0);
}

int
opti82c700_get_intr(pciintr_icu_handle_t v, int clink, int *irqp)
{
	struct opti82c700_handle *ph = v;
	pcireg_t reg;
	int val, addrofs, ofs;

	if (opti82c700_addr(clink, &addrofs, &ofs))
		return (1);

	reg = pci_conf_read(ph->ph_pc, ph->ph_tag, addrofs);
	val = (reg >> ofs) & FIRESTAR_CFG_PIRQ_MASK;

	*irqp = (val == FIRESTAR_PIRQ_NONE) ?
	    I386_PCI_INTERRUPT_LINE_NO_CONNECTION : val;

	return (0);
}

int
opti82c700_set_intr(pciintr_icu_handle_t v, int clink, int irq)
{
	struct opti82c700_handle *ph = v;
	int addrofs, ofs;
	pcireg_t reg;

	if (FIRESTAR_LEGAL_IRQ(irq) == 0)
		return (1);

	if (opti82c700_addr(clink, &addrofs, &ofs))
		return (1);

	reg = pci_conf_read(ph->ph_pc, ph->ph_tag, addrofs);
	reg &= ~(FIRESTAR_CFG_PIRQ_MASK << ofs);
	reg |= (irq << ofs);
	pci_conf_write(ph->ph_pc, ph->ph_tag, addrofs, reg);

	return (0);
}

int
opti82c700_get_trigger(pciintr_icu_handle_t v, int irq, int *triggerp)
{
	struct opti82c700_handle *ph = v;
	int i, val, addrofs, ofs;
	pcireg_t reg;

	if (FIRESTAR_LEGAL_IRQ(irq) == 0) {
		/* ISA IRQ? */
		*triggerp = IST_EDGE;
		return (0);
	}

	/*
	 * Search PCIDV1 registers.
	 */
	for (i = 0; i < 8; i++) {
		opti82c700_addr(FIRESTAR_PIR_MAKELINK(FIRESTAR_PIR_SELECT_IRQ,
		    i), &addrofs, &ofs);
		reg = pci_conf_read(ph->ph_pc, ph->ph_tag, addrofs);
		val = (reg >> ofs) & FIRESTAR_CFG_PIRQ_MASK;
		if (val != irq)
			continue;
		val = ((reg >> ofs) >> FIRESTAR_TRIGGER_SHIFT) &
		    FIRESTAR_TRIGGER_MASK;
		*triggerp = val ? IST_LEVEL : IST_EDGE;
		return (0);
	}

	/*
	 * Search PIO PCIIRQ.
	 */
	for (i = 0; i < 4; i++) {
		opti82c700_addr(FIRESTAR_PIR_MAKELINK(FIRESTAR_PIR_SELECT_PIRQ,
		    i), &addrofs, &ofs);
		reg = pci_conf_read(ph->ph_pc, ph->ph_tag, addrofs);
		val = (reg >> ofs) & FIRESTAR_CFG_PIRQ_MASK;
		if (val != irq)
			continue;
		*triggerp = IST_LEVEL;
		return (0);
	}

	return (1);
}

int
opti82c700_set_trigger(pciintr_icu_handle_t v, int irq, int trigger)
{
	struct opti82c700_handle *ph = v;
	int i, val, addrofs, ofs;
	pcireg_t reg;

	if (FIRESTAR_LEGAL_IRQ(irq) == 0) {
		/* ISA IRQ? */
		return ((trigger != IST_LEVEL) ? 0 : 1);
	}

	/*
	 * Search PCIDV1 registers.
	 */
	for (i = 0; i < 8; i++) {
		opti82c700_addr(FIRESTAR_PIR_MAKELINK(FIRESTAR_PIR_SELECT_IRQ,
		    i), &addrofs, &ofs);
		reg = pci_conf_read(ph->ph_pc, ph->ph_tag, addrofs);
		val = (reg >> ofs) & FIRESTAR_CFG_PIRQ_MASK;
		if (val != irq)
			continue;
		if (trigger == IST_LEVEL)
			reg |= (FIRESTAR_TRIGGER_MASK <<
			    (FIRESTAR_TRIGGER_SHIFT + ofs));
		else
			reg &= ~(FIRESTAR_TRIGGER_MASK <<
			    (FIRESTAR_TRIGGER_SHIFT + ofs));
		pci_conf_write(ph->ph_pc, ph->ph_tag, addrofs, reg);
		return (0);
	}

	/*
	 * Search PIO PCIIRQ.
	 */
	for (i = 0; i < 4; i++) {
		opti82c700_addr(FIRESTAR_PIR_MAKELINK(FIRESTAR_PIR_SELECT_PIRQ,
		    i), &addrofs, &ofs);
		reg = pci_conf_read(ph->ph_pc, ph->ph_tag, addrofs);
		val = (reg >> ofs) & FIRESTAR_CFG_PIRQ_MASK;
		if (val != irq)
			continue;
		return (trigger == IST_LEVEL ? 0 : 1);
	}

	return (1);
}


code:
/*	$OpenBSD: opti82c700reg.h,v 1.4 2000/08/08 19:12:47 mickey Exp $	*/
/*	$NetBSD: opti82c700reg.h,v 1.1 1999/11/17 01:21:20 thorpej Exp $	*/

/*
 * Copyright (c) 1999, by UCHIYAMA Yasushi
 * All rights reserved.
 *
 * 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. The name of the developer may NOT be used to endorse or promote products
 *    derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``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 OR CONTRIBUTORS 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.
 */

/*
 * Register definitions for the Opti 82c700 PCI-ISA bridge interrupt
 * controller.
 */

#define	FIRESTAR_CFG_INTR_IRQ	0xb0	/* PCI configuration space */
#define	FIRESTAR_CFG_INTR_PIRQ	0xb8	/* PCI configuration space */

#define	FIRESTAR_PIRQ_NONE	0
#define	FIRESTAR_PIRQ_MASK	0xdffa
#define	FIRESTAR_LEGAL_IRQ(irq)	((irq) >= 0 && (irq) <= 15 &&		\
				 ((1 << (irq)) & FIRESTAR_PIRQ_MASK) != 0)

#define	FIRESTAR_CFG_PIRQ_MASK	0x0f

#define	FIRESTAR_TRIGGER_MASK	0x01
#define	FIRESTAR_TRIGGER_SHIFT	4

/*
 * Opti's suggested Link values.
 */
#define	FIRESTAR_PIR_REGOFS_MASK	0x07
#define	FIRESTAR_PIR_REGOFS_SHIFT	4
#define	FIRESTAR_PIR_REGOFS(link)					\
	(((link) >> FIRESTAR_PIR_REGOFS_SHIFT) & FIRESTAR_PIR_REGOFS_MASK)

#define	FIRESTAR_PIR_SELECTSRC_MASK	0x07
#define	FIRESTAR_PIR_SELECTSRC_SHIFT	0
#define	FIRESTAR_PIR_SELECTSRC(link)					\
	(((link) >> FIRESTAR_PIR_SELECTSRC_SHIFT) & FIRESTAR_PIR_SELECTSRC_MASK)

#define	FIRESTAR_PIR_SELECT_NONE	0
#define	FIRESTAR_PIR_SELECT_IRQ		1
#define	FIRESTAR_PIR_SELECT_PIRQ	2
#define	FIRESTAR_PIR_SELECT_BRIDGE	3

#define	FIRESTAR_PIR_MAKELINK(src, ofs)					\
	(((src) << FIRESTAR_PIR_SELECTSRC_SHIFT) |			\
	 ((ofs) << FIRESTAR_PIR_REGOFS_SHIFT))


code:
/*	$OpenBSD: pchb.c,v 1.89 2014/03/26 14:41:41 mpi Exp $ */
/*	$NetBSD: pchb.c,v 1.65 2007/08/15 02:26:13 markd Exp $	*/

/*
 * Copyright (c) 2000 Michael Shalayeff
 * All rights reserved.
 *
 * 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 OR HIS RELATIVES 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 MIND, 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.
 */
/*-
 * Copyright (c) 1996, 1998, 2000 The NetBSD Foundation, Inc.
 * All rights reserved.
 *
 * This code is derived from software contributed to The NetBSD Foundation
 * by Jason R. Thorpe.
 *
 * 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 NETBSD FOUNDATION, INC. AND CONTRIBUTORS
 * ``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 FOUNDATION OR CONTRIBUTORS
 * 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.
 */

#include <sys/param.h>
#include <sys/systm.h>
#include <sys/device.h>
#include <sys/timeout.h>
#include <sys/rwlock.h>

#include <machine/bus.h>

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

#include <dev/pci/agpvar.h>
#include <dev/pci/ppbreg.h>

#include <dev/rndvar.h>

#include <dev/ic/i82802reg.h>

#include "agp.h"

#define PCISET_INTEL_BRIDGETYPE_MASK	0x3
#define PCISET_INTEL_TYPE_COMPAT	0x1
#define PCISET_INTEL_TYPE_AUX		0x2

#define PCISET_INTEL_BUSCONFIG_REG	0x48
#define PCISET_INTEL_BRIDGE_NUMBER(reg)	(((reg) >> 8) & 0xff)
#define PCISET_INTEL_PCI_BUS_NUMBER(reg)	(((reg) >> 16) & 0xff)

#define PCISET_INTEL_SDRAMC_REG	0x74
#define PCISET_INTEL_SDRAMC_IPDLT	(1 << 24)

/* XXX should be in dev/ic/i82424{reg.var}.h */
#define I82424_CPU_BCTL_REG		0x53
#define I82424_PCI_BCTL_REG		0x54

#define I82424_BCTL_CPUMEM_POSTEN	0x01
#define I82424_BCTL_CPUPCI_POSTEN	0x02
#define I82424_BCTL_PCIMEM_BURSTEN	0x01
#define I82424_BCTL_PCI_BURSTEN		0x02

/* XXX should be in dev/ic/amd64htreg.h */
#define AMD64HT_LDT0_BUS	0x94
#define AMD64HT_LDT0_TYPE	0x98
#define AMD64HT_LDT1_BUS	0xb4
#define AMD64HT_LDT1_TYPE	0xb8
#define AMD64HT_LDT2_BUS	0xd4
#define AMD64HT_LDT2_TYPE	0xd8
#define AMD64HT_LDT3_BUS	0xf4
#define AMD64HT_LDT3_TYPE	0xf8

#define AMD64HT_NUM_LDT		4

#define AMD64HT_LDT_TYPE_MASK		0x0000001f
#define  AMD64HT_LDT_INIT_COMPLETE	0x00000002
#define  AMD64HT_LDT_NC			0x00000004

#define AMD64HT_LDT_SEC_BUS_NUM(reg)	(((reg) >> 8) & 0xff)

struct pchb_softc {
	struct device sc_dev;

	bus_space_tag_t sc_bt;
	bus_space_handle_t sc_bh;

	/* rng stuff */
	int sc_rng_active;
	int sc_rng_ax;
	int sc_rng_i;
	struct timeout sc_rng_to;
};

int	pchbmatch(struct device *, void *, void *);
void	pchbattach(struct device *, struct device *, void *);
int	pchbactivate(struct device *, int);

struct cfattach pchb_ca = {
	sizeof(struct pchb_softc), pchbmatch, pchbattach, NULL,
	pchbactivate
};

struct cfdriver pchb_cd = {
	NULL, "pchb", DV_DULL
};

int	pchb_print(void *, const char *);
void	pchb_rnd(void *);
void	pchb_amd64ht_attach(struct device *, struct pci_attach_args *, int);

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

#ifdef __i386__
	/* XXX work around broken via82x866 chipsets */
	const struct pci_matchid via_devices[] = {
		{ PCI_VENDOR_VIATECH, PCI_PRODUCT_VIATECH_VT82C586_PWR },
		{ PCI_VENDOR_VIATECH, PCI_PRODUCT_VIATECH_VT82C596 },
		{ PCI_VENDOR_VIATECH, PCI_PRODUCT_VIATECH_VT82C596B_PM },
		{ PCI_VENDOR_VIATECH, PCI_PRODUCT_VIATECH_VT82C686A_SMB }
	};
	if (pci_matchbyid(pa, via_devices,
	    sizeof(via_devices) / sizeof(via_devices[0])))
		return (0);
#endif /* __i386__ */

	if (PCI_CLASS(pa->pa_class) == PCI_CLASS_BRIDGE &&
	    PCI_SUBCLASS(pa->pa_class) == PCI_SUBCLASS_BRIDGE_HOST)
		return (1);

	return (0);
}

void
pchbattach(struct device *parent, struct device *self, void *aux)
{
	struct pchb_softc *sc = (struct pchb_softc *)self;
	struct pci_attach_args *pa = aux;
	struct pcibus_attach_args pba;
	pcireg_t bcreg, bir;
	u_char bdnum, pbnum;
	pcitag_t tag;
	int i, r;
	int doattach = 0;

	switch (PCI_VENDOR(pa->pa_id)) {
	case PCI_VENDOR_AMD:
		printf("\n");
		switch (PCI_PRODUCT(pa->pa_id)) {
		case PCI_PRODUCT_AMD_AMD64_0F_HT:
		case PCI_PRODUCT_AMD_AMD64_10_HT:
			for (i = 0; i < AMD64HT_NUM_LDT; i++)
				pchb_amd64ht_attach(self, pa, i);
			break;
		}
		break;
#ifdef __i386__
	case PCI_VENDOR_RCC:
		{
			/*
			 * The variable below is a bit vector representing the
			 * Serverworks busses that have already been attached.
			 * Bit 0 represents bus 0 and so forth.  The initial
			 * value is 1 because we never actually want to
			 * attach bus 0 since bus 0 is the mainbus.
			 */
			static u_int32_t rcc_bus_visited = 1;

			printf("\n");
			bdnum = pci_conf_read(pa->pa_pc, pa->pa_tag, 0x44);
			if (bdnum >= (sizeof(rcc_bus_visited) * 8) ||
			    (rcc_bus_visited & (1 << bdnum)))
				break;

			rcc_bus_visited |= 1 << bdnum;

			/*
			 * This host bridge has a second PCI bus.
			 * Configure it.
			 */
			pbnum = bdnum;
			doattach = 1;
			break;
		}
#endif
	case PCI_VENDOR_INTEL:
		switch (PCI_PRODUCT(pa->pa_id)) {
#ifdef __i386__
		case PCI_PRODUCT_INTEL_82452_HB:
			bcreg = pci_conf_read(pa->pa_pc, pa->pa_tag, 0x40);
			pbnum = PCISET_INTEL_BRIDGE_NUMBER(bcreg);
			if (pbnum != 0xff) {
				pbnum++;
				doattach = 1;
			}
			break;
		case PCI_PRODUCT_INTEL_82443BX_AGP:     /* 82443BX AGP (PAC) */
		case PCI_PRODUCT_INTEL_82443BX_NOAGP:   /* 82443BX Host-PCI (no AGP) */
			/*
			 * An incorrect address may be driven on the
			 * DRAM bus, resulting in memory data being
			 * fetched from the wrong location.  This is
			 * the workaround.
			 */
			if (PCI_REVISION(pa->pa_class) < 0x3) {
				bcreg = pci_conf_read(pa->pa_pc, pa->pa_tag,
				    PCISET_INTEL_SDRAMC_REG);
				bcreg |= PCISET_INTEL_SDRAMC_IPDLT;
				pci_conf_write(pa->pa_pc, pa->pa_tag,
				    PCISET_INTEL_SDRAMC_REG, bcreg);
			}
			break;
		case PCI_PRODUCT_INTEL_PCI450_PB:
			bcreg = pci_conf_read(pa->pa_pc, pa->pa_tag,
			    PCISET_INTEL_BUSCONFIG_REG);
			bdnum = PCISET_INTEL_BRIDGE_NUMBER(bcreg);
			pbnum = PCISET_INTEL_PCI_BUS_NUMBER(bcreg);
			switch (bdnum & PCISET_INTEL_BRIDGETYPE_MASK) {
			default:
				printf(": bdnum=%x (reserved)", bdnum);
				break;
			case PCISET_INTEL_TYPE_COMPAT:
				printf(": Compatibility PB (bus %d)", pbnum);
				break;
			case PCISET_INTEL_TYPE_AUX:
				printf(": Auxiliary PB (bus %d)", pbnum);
				doattach = 1;
			}
			break;
		case PCI_PRODUCT_INTEL_CDC:
			bcreg = pci_conf_read(pa->pa_pc, pa->pa_tag,
			    I82424_CPU_BCTL_REG);
			if (bcreg & I82424_BCTL_CPUPCI_POSTEN) {
				bcreg &= ~I82424_BCTL_CPUPCI_POSTEN;
				pci_conf_write(pa->pa_pc, pa->pa_tag,
				    I82424_CPU_BCTL_REG, bcreg);
				printf(": disabled CPU-PCI write posting");
			}
			break;
		case PCI_PRODUCT_INTEL_82454NX:
			pbnum = 0;
			switch (pa->pa_device) {
			case 18: /* PXB 0 bus A - primary bus */
				break;
			case 19: /* PXB 0 bus B */
				/* read SUBA0 from MIOC */
				tag = pci_make_tag(pa->pa_pc, 0, 16, 0);
				bcreg = pci_conf_read(pa->pa_pc, tag, 0xd0);
				pbnum = ((bcreg & 0x0000ff00) >> 8) + 1;
				break;
			case 20: /* PXB 1 bus A */
				/* read BUSNO1 from MIOC */
				tag = pci_make_tag(pa->pa_pc, 0, 16, 0);
				bcreg = pci_conf_read(pa->pa_pc, tag, 0xd0);
				pbnum = (bcreg & 0xff000000) >> 24;
				break;
			case 21: /* PXB 1 bus B */
				/* read SUBA1 from MIOC */
				tag = pci_make_tag(pa->pa_pc, 0, 16, 0);
				bcreg = pci_conf_read(pa->pa_pc, tag, 0xd4);
				pbnum = (bcreg & 0x000000ff) + 1;
				break;
			}
			if (pbnum != 0)
				doattach = 1;
			break;
		/* RNG */
		case PCI_PRODUCT_INTEL_82810_HB:
		case PCI_PRODUCT_INTEL_82810_DC100_HB:
		case PCI_PRODUCT_INTEL_82810E_HB:
		case PCI_PRODUCT_INTEL_82815_HB:
		case PCI_PRODUCT_INTEL_82820_HB:
		case PCI_PRODUCT_INTEL_82840_HB:
		case PCI_PRODUCT_INTEL_82850_HB:
		case PCI_PRODUCT_INTEL_82860_HB:
#endif /* __i386__ */
		case PCI_PRODUCT_INTEL_82915G_HB:
		case PCI_PRODUCT_INTEL_82945G_HB:
		case PCI_PRODUCT_INTEL_82925X_HB:
		case PCI_PRODUCT_INTEL_82955X_HB:
			sc->sc_bt = pa->pa_memt;
			if (bus_space_map(sc->sc_bt, I82802_IOBASE,
			    I82802_IOSIZE, 0, &sc->sc_bh))
				break;

			/* probe and init rng */
			if (!(bus_space_read_1(sc->sc_bt, sc->sc_bh,
			    I82802_RNG_HWST) & I82802_RNG_HWST_PRESENT))
				break;

			/* enable RNG */
			bus_space_write_1(sc->sc_bt, sc->sc_bh,
			    I82802_RNG_HWST,
			    bus_space_read_1(sc->sc_bt, sc->sc_bh,
			    I82802_RNG_HWST) | I82802_RNG_HWST_ENABLE);

			/* see if we can read anything */
			for (i = 1000; i-- &&
			    !(bus_space_read_1(sc->sc_bt, sc->sc_bh,
			    I82802_RNG_RNGST) & I82802_RNG_RNGST_DATAV); )
				DELAY(10);

			if (!(bus_space_read_1(sc->sc_bt, sc->sc_bh,
			    I82802_RNG_RNGST) & I82802_RNG_RNGST_DATAV))
				break;

			r = bus_space_read_1(sc->sc_bt, sc->sc_bh,
			    I82802_RNG_DATA);

			timeout_set(&sc->sc_rng_to, pchb_rnd, sc);
			sc->sc_rng_i = 4;
			pchb_rnd(sc);
			sc->sc_rng_active = 1;
			break;
		}
		printf("\n");
		break;
	case PCI_VENDOR_VIATECH:
		switch (PCI_PRODUCT(pa->pa_id)) {
		case PCI_PRODUCT_VIATECH_VT8251_PCIE_0:
			/*
			 * Bump the host bridge into PCI-PCI bridge
			 * mode by clearing magic bit on the VLINK
			 * device.  This allows us to read the bus
			 * number for the PCI bus attached to this
			 * host bridge.
			 */
			tag = pci_make_tag(pa->pa_pc, 0, 17, 7);
			bcreg = pci_conf_read(pa->pa_pc, tag, 0xfc);
			bcreg &= ~0x00000004; /* XXX Magic */
			pci_conf_write(pa->pa_pc, tag, 0xfc, bcreg);

			bir = pci_conf_read(pa->pa_pc,
			    pa->pa_tag, PPB_REG_BUSINFO);
			pbnum = PPB_BUSINFO_PRIMARY(bir);
			if (pbnum > 0)
				doattach = 1;

			/* Switch back to host bridge mode. */
			bcreg |= 0x00000004; /* XXX Magic */
			pci_conf_write(pa->pa_pc, tag, 0xfc, bcreg);
			break;
		}
		printf("\n");
		break;
	default:
		printf("\n");
		break;
	}

#if NAGP > 0
	/*
	 * Intel IGD have an odd interface and attach at vga, however,
	 * in that mode they don't have the AGP cap bit, so this
	 * test should be sufficient
	 */
	if (pci_get_capability(pa->pa_pc, pa->pa_tag, PCI_CAP_AGP,
	    NULL, NULL) != 0) {
		struct agp_attach_args	aa;
		aa.aa_busname = "agp";
		aa.aa_pa = pa;

		config_found(self, &aa, agpdev_print);
	}
#endif /* NAGP > 0 */

	if (doattach == 0)
		return;

	bzero(&pba, sizeof(pba));
	pba.pba_busname = "pci";
	pba.pba_iot = pa->pa_iot;
	pba.pba_memt = pa->pa_memt;
	pba.pba_dmat = pa->pa_dmat;
	pba.pba_busex = pa->pa_busex;
	pba.pba_domain = pa->pa_domain;
	pba.pba_bus = pbnum;
	pba.pba_pc = pa->pa_pc;
	config_found(self, &pba, pchb_print);
}

int
pchbactivate(struct device *self, int act)
{
	struct pchb_softc *sc = (struct pchb_softc *)self;
	int rv = 0;

	switch (act) {
	case DVACT_RESUME:
		/* re-enable RNG, if we have it */
		if (sc->sc_rng_active)
			bus_space_write_1(sc->sc_bt, sc->sc_bh,
			    I82802_RNG_HWST,
			    bus_space_read_1(sc->sc_bt, sc->sc_bh,
			    I82802_RNG_HWST) | I82802_RNG_HWST_ENABLE);
		rv = config_activate_children(self, act);
		break;
	default:
		rv = config_activate_children(self, act);
		break;
	}
	return (rv);
}

int
pchb_print(void *aux, const char *pnp)
{
	struct pcibus_attach_args *pba = aux;

	if (pnp)
		printf("%s at %s", pba->pba_busname, pnp);
	printf(" bus %d", pba->pba_bus);
	return (UNCONF);
}

/*
 * Should do FIPS testing as per:
 *	http://csrc.nist.gov/publications/fips/fips140-1/fips1401.pdf
 */
void
pchb_rnd(void *v)
{
	struct pchb_softc *sc = v;

	/*
	 * Don't wait for data to be ready. If it's not there, we'll check
	 * next time.
	 */
	if ((bus_space_read_1(sc->sc_bt, sc->sc_bh, I82802_RNG_RNGST) &
	    I82802_RNG_RNGST_DATAV)) {

		sc->sc_rng_ax = (sc->sc_rng_ax << 8) |
		    bus_space_read_1(sc->sc_bt, sc->sc_bh, I82802_RNG_DATA);

		if (!sc->sc_rng_i--) {
			sc->sc_rng_i = 4;
			add_true_randomness(sc->sc_rng_ax);
		}
	}

	timeout_add(&sc->sc_rng_to, 1);
}

void
pchb_amd64ht_attach(struct device *self, struct pci_attach_args *pa, int i)
{
	struct pcibus_attach_args pba;
	pcireg_t type, bus;
	int reg;

	reg = AMD64HT_LDT0_TYPE + i * 0x20;
	type = pci_conf_read(pa->pa_pc, pa->pa_tag, reg);
	if ((type & AMD64HT_LDT_INIT_COMPLETE) == 0 ||
	    (type & AMD64HT_LDT_NC) == 0)
		return;

	reg = AMD64HT_LDT0_BUS + i * 0x20;
	bus = pci_conf_read(pa->pa_pc, pa->pa_tag, reg);
	if (AMD64HT_LDT_SEC_BUS_NUM(bus) > 0) {
		bzero(&pba, sizeof(pba));
		pba.pba_busname = "pci";
		pba.pba_iot = pa->pa_iot;
		pba.pba_memt = pa->pa_memt;
		pba.pba_dmat = pa->pa_dmat;
		pba.pba_busex = pa->pa_busex;
		pba.pba_domain = pa->pa_domain;
		pba.pba_bus = AMD64HT_LDT_SEC_BUS_NUM(bus);
		pba.pba_pc = pa->pa_pc;
		config_found(self, &pba, pchb_print);
	}
}


code:
/*	$OpenBSD: pci_addr_fixup.c,v 1.25 2014/05/04 20:09:15 sf Exp $	*/
/*	$NetBSD: pci_addr_fixup.c,v 1.7 2000/08/03 20:10:45 nathanw Exp $	*/

/*-
 * Copyright (c) 2000 UCHIYAMA Yasushi.  All rights reserved.
 *
 * 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.
 * 3. The name of the author may not be used to endorse or promote products
 *    derived from this software without specific prior written permission.
 *
 * 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.
 */

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

#include <uvm/uvm_extern.h>

#include <machine/bus.h>

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

#include <i386/pci/pcibiosvar.h>

typedef int (*pciaddr_resource_manage_func_t)(struct pcibios_softc *, pci_chipset_tag_t, pcitag_t, int,
	struct extent *, int, bus_addr_t *, bus_size_t);
void	pciaddr_resource_manage(struct pcibios_softc *,
    pci_chipset_tag_t, pcitag_t, pciaddr_resource_manage_func_t);
void	pciaddr_resource_reserve(struct pcibios_softc *,
    pci_chipset_tag_t, pcitag_t);
void	pciaddr_resource_reserve_disabled(struct pcibios_softc *,
    pci_chipset_tag_t, pcitag_t);
int	pciaddr_do_resource_reserve(struct pcibios_softc *,
    pci_chipset_tag_t, pcitag_t, int, struct extent *, int,
    bus_addr_t *, bus_size_t);
int	pciaddr_do_resource_reserve_disabled(struct pcibios_softc *,
    pci_chipset_tag_t, pcitag_t, int, struct extent *, int, u_long *,
    bus_size_t);
void	pciaddr_resource_allocate(struct pcibios_softc *,
    pci_chipset_tag_t, pcitag_t);
int	pciaddr_do_resource_allocate(struct pcibios_softc *,
    pci_chipset_tag_t, pcitag_t, int, struct extent *, int, bus_addr_t *,
    bus_size_t);
bus_addr_t pciaddr_ioaddr(u_int32_t);
void	pciaddr_print_devid(pci_chipset_tag_t, pcitag_t);

int	pciaddr_device_is_agp(pci_chipset_tag_t, pcitag_t);

#define PCIADDR_MEM_START	0x0
#define PCIADDR_MEM_END		0xffffffff
#define PCIADDR_PORT_START	0x0
#define PCIADDR_PORT_END	0xffff

/* for ISA devices */
#define PCIADDR_ISAPORT_RESERVE	0x5800 /* empirical value */
#define PCIADDR_ISAMEM_RESERVE	(16 * 1024 * 1024)

void
pci_addr_fixup(struct pcibios_softc *sc, pci_chipset_tag_t pc, int maxbus)
{
	extern paddr_t avail_end;
	const char *verbose_header =
		"[%s]-----------------------\n"
		"  device vendor product\n"
		"  register space address    size\n"
		"--------------------------------------------\n";
	const char *verbose_footer =
		"--------------------------[%3d devices bogus]\n";

	const struct {
		bus_addr_t start;
		bus_size_t size;
		char *name;
	} system_reserve [] = {
		{ 0xfec00000, 0x100000, "I/O APIC" },
		{ 0xfee00000, 0x100000, "Local APIC" },
		{ 0xfffe0000, 0x20000, "BIOS PROM" },
		{ 0, 0, 0 }, /* terminator */
	}, *srp;
	paddr_t start;
	int error;

	sc->extent_mem = extent_create("PCI I/O memory space",
	    PCIADDR_MEM_START, PCIADDR_MEM_END, M_DEVBUF, 0, 0, EX_NOWAIT);
	KASSERT(sc->extent_mem);
	sc->extent_port = extent_create("PCI I/O port space",
	    PCIADDR_PORT_START, PCIADDR_PORT_END, M_DEVBUF, 0, 0, EX_NOWAIT);
	KASSERT(sc->extent_port);

	/*
	 * 1. check & reserve system BIOS setting.
	 */
	PCIBIOS_PRINTV((verbose_header, "System BIOS Setting"));
	pci_device_foreach(sc, pc, maxbus, pciaddr_resource_reserve);
	pci_device_foreach(sc, pc, maxbus, pciaddr_resource_reserve_disabled);
	PCIBIOS_PRINTV((verbose_footer, sc->nbogus));

	/*
	 * 2. reserve non-PCI area.
	 */
	for (srp = system_reserve; srp->size; srp++) {
		error = extent_alloc_region(sc->extent_mem, srp->start,
		    srp->size, EX_NOWAIT| EX_MALLOCOK);
		if (error != 0)
			printf("WARNING: can't reserve area for %s.\n",
			       srp->name);
	}

	/*
	 * 3. determine allocation space
	 */
	start = round_page(avail_end + 1);
	if (start < PCIADDR_ISAMEM_RESERVE)
		start = PCIADDR_ISAMEM_RESERVE;
	sc->mem_alloc_start = (start + 0x100000 + 1) & ~(0x100000 - 1);
	sc->port_alloc_start = PCIADDR_ISAPORT_RESERVE;
	PCIBIOS_PRINTV((" Physical memory end: 0x%08lx\n PCI memory mapped I/O "
	    "space start: 0x%08lx\n", avail_end, sc->mem_alloc_start));

	/*
	 * 4. do fixup
	 */
	PCIBIOS_PRINTV((verbose_header, "PCIBIOS fixup stage"));
	sc->nbogus = 0;
	pci_device_foreach(sc, pc, maxbus, pciaddr_resource_allocate);
	PCIBIOS_PRINTV((verbose_footer, sc->nbogus));

}

void
pciaddr_resource_reserve(struct pcibios_softc *sc, pci_chipset_tag_t pc,
    pcitag_t tag)
{
	if (pcibios_flags & PCIBIOS_VERBOSE)
		pciaddr_print_devid(pc, tag);
	pciaddr_resource_manage(sc, pc, tag, pciaddr_do_resource_reserve);
}

void
pciaddr_resource_reserve_disabled(struct pcibios_softc *sc,
    pci_chipset_tag_t pc, pcitag_t tag)
{
	if (pcibios_flags & PCIBIOS_VERBOSE)
		pciaddr_print_devid(pc, tag);
	pciaddr_resource_manage(sc, pc, tag,
	    pciaddr_do_resource_reserve_disabled);
}

void
pciaddr_resource_allocate(struct pcibios_softc *sc, pci_chipset_tag_t pc,
    pcitag_t tag)
{
	if (pcibios_flags & PCIBIOS_VERBOSE)
		pciaddr_print_devid(pc, tag);
	pciaddr_resource_manage(sc, pc, tag, pciaddr_do_resource_allocate);
}

void
pciaddr_resource_manage(struct pcibios_softc *sc, pci_chipset_tag_t pc,
    pcitag_t tag, pciaddr_resource_manage_func_t func)
{
	struct extent *ex;
	pcireg_t val, mask;
	bus_addr_t addr;
	bus_size_t size;
	int error, mapreg, type, reg_start, reg_end, width;

	val = pci_conf_read(pc, tag, PCI_BHLC_REG);
	switch (PCI_HDRTYPE_TYPE(val)) {
	default:
		printf("WARNING: unknown PCI device header 0x%x.\n",
		    PCI_HDRTYPE_TYPE(val));
		sc->nbogus++;
		return;
	case 0:
		reg_start = PCI_MAPREG_START;
		reg_end   = PCI_MAPREG_END;
		break;
	case 1: /* PCI-PCI bridge */
		reg_start = PCI_MAPREG_START;
		reg_end   = PCI_MAPREG_PPB_END;
		break;
	case 2: /* PCI-CardBus bridge */
		reg_start = PCI_MAPREG_START;
		reg_end   = PCI_MAPREG_PCB_END;
		break;
	}
	error = 0;

	for (mapreg = reg_start; mapreg < reg_end; mapreg += width) {
		/* inquire PCI device bus space requirement */
		val = pci_conf_read(pc, tag, mapreg);
		pci_conf_write(pc, tag, mapreg, ~0);

		mask = pci_conf_read(pc, tag, mapreg);
		pci_conf_write(pc, tag, mapreg, val);

		type = PCI_MAPREG_TYPE(val);
		width = 4;
		if (type == PCI_MAPREG_TYPE_MEM) {
			if (PCI_MAPREG_MEM_TYPE(val) ==
			    PCI_MAPREG_MEM_TYPE_64BIT) {
				/* XXX We could examine the upper 32 bits
				 * XXX of the BAR here, but we are totally
				 * XXX unprepared to handle a non-zero value,
				 * XXX either here or anywhere else in
				 * XXX i386-land.
				 * XXX So just arrange to not look at the
				 * XXX upper 32 bits, lest we misinterpret
				 * XXX it as a 32-bit BAR set to zero.
				 */
			    width = 8;
			}
			addr = PCI_MAPREG_MEM_ADDR(val);
			size = PCI_MAPREG_MEM_SIZE(mask);
			ex = sc->extent_mem;
		} else {
			/* XXX some devices give 32bit value */
			addr = PCI_MAPREG_IO_ADDR(val) & PCIADDR_PORT_END;
			size = PCI_MAPREG_IO_SIZE(mask);
			ex = sc->extent_port;
		}

		if (!size) /* unused register */
			continue;

		/* reservation/allocation phase */
		error += (*func) (sc, pc, tag, mapreg, ex, type, &addr, size);

		PCIBIOS_PRINTV(("\t%02xh %s 0x%08x 0x%08x\n",
				mapreg, type ? "port" : "mem ",
				(unsigned int)addr, (unsigned int)size));
	}

	if (error)
		sc->nbogus++;

	PCIBIOS_PRINTV(("\t\t[%s]\n", error ? "NG" : "OK"));
}

int
pciaddr_do_resource_allocate(struct pcibios_softc *sc, pci_chipset_tag_t pc,
    pcitag_t tag, int mapreg, struct extent *ex, int type, bus_addr_t *addr,
    bus_size_t size)
{
	bus_addr_t start;
	int error;

	if (*addr) /* no need to allocate */
		return (0);

	/* XXX Don't allocate if device is AGP device to avoid conflict. */
	if (pciaddr_device_is_agp(pc, tag))
		return (0);

	start = (type == PCI_MAPREG_TYPE_MEM ? sc->mem_alloc_start
		: sc->port_alloc_start);
	if (start < ex->ex_start || start + size - 1 >= ex->ex_end) {
		PCIBIOS_PRINTV(("No available resources. fixup failed\n"));
		return (1);
	}
	error = extent_alloc_subregion(ex, start, ex->ex_end, size, size, 0, 0,
	    EX_FAST|EX_NOWAIT|EX_MALLOCOK, addr);
	if (error) {
		PCIBIOS_PRINTV(("No available resources. fixup failed\n"));
		return (1);
	}

	/* write new address to PCI device configuration header */
	pci_conf_write(pc, tag, mapreg, *addr);
	/* check */
	if (pcibios_flags & PCIBIOS_VERBOSE) {
		printf("pci_addr_fixup: ");
		pciaddr_print_devid(pc, tag);
	}

	if (pciaddr_ioaddr(pci_conf_read(pc, tag, mapreg)) != *addr) {
		pci_conf_write(pc, tag, mapreg, 0); /* clear */
		PCIBIOS_PRINTV(("fixup failed. (new address=%#lx)\n", *addr));
		return (1);
	}
	PCIBIOS_PRINTV(("new address 0x%08lx\n", *addr));

	return (0);
}

int
pciaddr_do_resource_reserve(struct pcibios_softc *sc, pci_chipset_tag_t pc,
    pcitag_t tag, int mapreg, struct extent *ex, int type, bus_addr_t *addr,
    bus_size_t size)
{
	pcireg_t val;
	int error;

	if (*addr == 0)
		return (0);

	val = pci_conf_read(pc, tag, PCI_COMMAND_STATUS_REG);
	if (type == PCI_MAPREG_TYPE_MEM &&
	    (val & PCI_COMMAND_MEM_ENABLE) != PCI_COMMAND_MEM_ENABLE)
		return (0);
	if (type == PCI_MAPREG_TYPE_IO &&
	    (val & PCI_COMMAND_IO_ENABLE) != PCI_COMMAND_IO_ENABLE)
		return (0);

	error = extent_alloc_region(ex, *addr, size, EX_NOWAIT | EX_MALLOCOK);
	if (error) {
		PCIBIOS_PRINTV(("Resource conflict.\n"));
		pci_conf_write(pc, tag, mapreg, 0); /* clear */
		return (1);
	}

	return (0);
}

int
pciaddr_do_resource_reserve_disabled(struct pcibios_softc *sc,
    pci_chipset_tag_t pc, pcitag_t tag, int mapreg, struct extent *ex, int type,
    u_long *addr, bus_size_t size)
{
	pcireg_t val;
	int error;

	if (*addr == 0)
		return (0);

	val = pci_conf_read(pc, tag, PCI_COMMAND_STATUS_REG);
	if (type == PCI_MAPREG_TYPE_MEM &&
	    (val & PCI_COMMAND_MEM_ENABLE) == PCI_COMMAND_MEM_ENABLE)
		return (0);
	if (type == PCI_MAPREG_TYPE_IO &&
	    (val & PCI_COMMAND_IO_ENABLE) == PCI_COMMAND_IO_ENABLE)
		return (0);

	PCIBIOS_PRINTV(("disabled %s space at addr 0x%lx size 0x%lx\n",
	    type == PCI_MAPREG_TYPE_MEM ? "mem" : "io", *addr, size));

	error = extent_alloc_region(ex, *addr, size, EX_NOWAIT | EX_MALLOCOK);
	if (error) {
		PCIBIOS_PRINTV(("Resource conflict.\n"));
		pci_conf_write(pc, tag, mapreg, 0); /* clear */
		return (1);
	}

	return (0);
}

bus_addr_t
pciaddr_ioaddr(u_int32_t val)
{
	return ((PCI_MAPREG_TYPE(val) == PCI_MAPREG_TYPE_MEM)
		? PCI_MAPREG_MEM_ADDR(val)
		: (PCI_MAPREG_IO_ADDR(val) & PCIADDR_PORT_END));
}

void
pciaddr_print_devid(pci_chipset_tag_t pc, pcitag_t tag)
{
	int bus, device, function;
	pcireg_t id;

	id = pci_conf_read(pc, tag, PCI_ID_REG);
	pci_decompose_tag(pc, tag, &bus, &device, &function);
	printf("%03d:%02d:%d %04x:%04x\n", bus, device, function,
	       PCI_VENDOR(id), PCI_PRODUCT(id));
}

int
pciaddr_device_is_agp(pci_chipset_tag_t pc, pcitag_t tag)
{
	pcireg_t class, status, rval;
	int off;

	/* Check AGP device. */
	class = pci_conf_read(pc, tag, PCI_CLASS_REG);
	if (PCI_CLASS(class) == PCI_CLASS_DISPLAY) {
		status = pci_conf_read(pc, tag, PCI_COMMAND_STATUS_REG);
		if (status & PCI_STATUS_CAPLIST_SUPPORT) {
			rval = pci_conf_read(pc, tag, PCI_CAPLISTPTR_REG);
			for (off = PCI_CAPLIST_PTR(rval);
			    off != 0;
			    off = PCI_CAPLIST_NEXT(rval) ) {
				rval = pci_conf_read(pc, tag, off);
				if (PCI_CAPLIST_CAP(rval) == PCI_CAP_AGP)
					return (1);
			}
		}
	}
	return (0);
}