code:
/*	$OpenBSD: bonitoreg.h,v 1.7 2010/05/08 21:59:56 miod Exp $	*/
/*	$NetBSD: bonitoreg.h,v 1.6 2005/12/24 20:07:19 perry Exp $	*/

/*
 * The Loongson PCI Northbridge and memory controller is a derivative
 * of the Bonito chip.
 *
 * This file is a stripped-down version of the Bonito layout, containing
 * only definitions applying to the Loongson chip.
 */

/*
 * Bonito Register Map
 * Copyright (c) 1999 Algorithmics Ltd
 *
 * Algorithmics gives permission for anyone to use and modify this file
 * without any obligation or license condition except that you retain
 * this copyright message in any source redistribution in whole or part.
 *
 * Updated copies of this and other files can be found at
 * ftp://ftp.algor.co.uk/pub/bonito/
 *
 * Users of the Bonito controller are warmly recommended to contribute
 * any useful changes back to Algorithmics (mail to
 * bonito@algor.co.uk).
 */

/* Revision 1.48 autogenerated on 08/17/99 15:20:01 */

#ifndef	_BONITOREG_H_
#define	_BONITOREG_H_

#define BONITO(x)	(BONITO_REG_BASE + (x))

#define	REGVAL(x)	*((volatile u_int32_t *)PHYS_TO_XKPHYS(x, CCA_NC))
#define	REGVAL8(x)	*((volatile u_int8_t *)PHYS_TO_XKPHYS(x, CCA_NC))

#define BONITO_FLASH_BASE		0x1c000000UL
#define BONITO_FLASH_SIZE		0x02000000UL
#define BONITO_FLASH_TOP		(BONITO_FLASH_BASE+BONITO_FLASH_SIZE-1)

#define BONITO_BOOT_BASE		0x1fc00000UL
#define BONITO_BOOT_SIZE		0x00100000UL
#define BONITO_BOOT_TOP 		(BONITO_BOOT_BASE+BONITO_BOOT_SIZE-1)
#define BONITO_REG_BASE 		0x1fe00000UL
#define BONITO_REG_SIZE 		0x00040000UL
#define BONITO_REG_TOP			(BONITO_REG_BASE+BONITO_REG_SIZE-1)

#define BONITO_PCILO_BASE		0x10000000UL
#define BONITO_PCILO_SIZE		0x0c000000UL
#define BONITO_PCILO_TOP		(BONITO_PCILO_BASE+BONITO_PCILO_SIZE-1)
#define BONITO_PCILO0_BASE		0x10000000UL
#define BONITO_PCILO1_BASE		0x14000000UL
#define BONITO_PCILO2_BASE		0x18000000UL
#define BONITO_PCIHI_BASE		0x20000000UL
#define BONITO_PCIHI_SIZE		0x60000000UL
#define BONITO_PCIHI_TOP		(BONITO_PCIHI_BASE+BONITO_PCIHI_SIZE-1)
#define LS2F_PCIHI_BASE			0x40000000UL
#define LS2F_PCIHI_SIZE			0x40000000UL
#define LS2F_PCIHI_TOP			(LS2F_PCIHI_BASE+LS2F_PCIHI_SIZE-1)
#define BONITO_PCIIO_BASE		0x1fd00000UL
#define BONITO_PCIIO_LEGACY		0x00004000UL
#define BONITO_PCIIO_SIZE		0x00100000UL
#define BONITO_PCIIO_TOP		(BONITO_PCIIO_BASE+BONITO_PCIIO_SIZE-1)
#define BONITO_PCICFG_BASE		0x1fe80000UL
#define BONITO_PCICFG_SIZE		0x00080000UL
#define BONITO_PCICFG_TOP		(BONITO_PCICFG_BASE+BONITO_PCICFG_SIZE-1)

/* Bonito Register Bases */

#define BONITO_PCICONFIGBASE		0x00
#define BONITO_REGBASE			0x100

/* PCI Configuration  Registers */

#define BONITO_PCI_REG(x)		BONITO(BONITO_PCICONFIGBASE + (x))

#define	BONITO_REV_FPGA(x)		((x) & 0x80)
#define	BONITO_REV_MAJOR(x)		(((x) >> 4) & 0x7)
#define	BONITO_REV_MINOR(x)		((x) & 0xf)

/* Controller configuration */

#define	LOONGSON_PONCFG			BONITO(BONITO_REGBASE + 0x00)
#define LOONGSON_GENCFG			BONITO(BONITO_REGBASE + 0x04)
#define	LOONGSON_LIOCFG			BONITO(BONITO_REGBASE + 0x08)

/* PCI address map control */

#define BONITO_PCIMAP			BONITO(BONITO_REGBASE + 0x10)
#define LOONGSON_PCIX_BRIDGE_CFG	BONITO(BONITO_REGBASE + 0x14)
#define BONITO_PCIMAP_CFG		BONITO(BONITO_REGBASE + 0x18)

/* GPIO Regs - r/w */

#define BONITO_GPIODATA 		BONITO(BONITO_REGBASE + 0x1c)
#define BONITO_GPIOIE			BONITO(BONITO_REGBASE + 0x20)

/* ICU Configuration Regs - r/w */

#define BONITO_INTEDGE			BONITO(BONITO_REGBASE + 0x24)
#define	BONITO_INTSTEER			BONITO(BONITO_REGBASE + 0x28)
#define BONITO_INTPOL			BONITO(BONITO_REGBASE + 0x2c)

/* ICU Enable Regs - INTEN and INTISR are read only */

#define BONITO_INTENSET 		BONITO(BONITO_REGBASE + 0x30)
#define BONITO_INTENCLR 		BONITO(BONITO_REGBASE + 0x34)
#define BONITO_INTEN			BONITO(BONITO_REGBASE + 0x38)
#define BONITO_INTISR			BONITO(BONITO_REGBASE + 0x3c)

/* Memory window */

#define	BONITO_MEM_WIN_BASE_L		BONITO(BONITO_REGBASE + 0x40)
#define	BONITO_MEM_WIN_BASE_H		BONITO(BONITO_REGBASE + 0x44)
#define	BONITO_MEM_WIN_MASK_L		BONITO(BONITO_REGBASE + 0x48)
#define	BONITO_MEM_WIN_MASK_H		BONITO(BONITO_REGBASE + 0x4c)

/* PCI_Hit*_Sel_* */

#define	LOONGSON_PCI_HIT0_SEL_L		BONITO(BONITO_REGBASE + 0x50)
#define	LOONGSON_PCI_HIT0_SEL_H		BONITO(BONITO_REGBASE + 0x54)
#define	LOONGSON_PCI_HIT1_SEL_L		BONITO(BONITO_REGBASE + 0x58)
#define	LOONGSON_PCI_HIT1_SEL_H		BONITO(BONITO_REGBASE + 0x5c)
#define	LOONGSON_PCI_HIT2_SEL_L		BONITO(BONITO_REGBASE + 0x60)
#define	LOONGSON_PCI_HIT2_SEL_H		BONITO(BONITO_REGBASE + 0x64)

/* PCIX Arbitration */

#define	LOONGSON_PXARB_CFG		BONITO(BONITO_REGBASE + 0x68)
#define	LOONGSON_PXARB_STS		BONITO(BONITO_REGBASE + 0x6c)

/* Chip configuration */

#define	LOONGSON_CHIP_CONFIG0		BONITO(BONITO_REGBASE + 0x80)
#define	LOONGSON_PAD1V8_CTRL		BONITO(BONITO_REGBASE + 0x84)
#define	LOONGSON_PAD3V3_CTRL		BONITO(BONITO_REGBASE + 0x88)

/* ###### Bit Definitions for individual Registers #### */

/* gencfg */

#define LOONGSON_GENCFG_OV_EN		0x00000001	/* video accel enable */

#define	BONITO_GENCFG_CPUSELFRESET	0x00000004	/* reset system */

/* pcimap */

#define BONITO_PCIMAP_PCIMAP_LO0	0x0000003f
#define BONITO_PCIMAP_PCIMAP_LO0_SHIFT	0
#define BONITO_PCIMAP_PCIMAP_LO1	0x00000fc0
#define BONITO_PCIMAP_PCIMAP_LO1_SHIFT	6
#define BONITO_PCIMAP_PCIMAP_LO2	0x0003f000
#define BONITO_PCIMAP_PCIMAP_LO2_SHIFT	12
#define	BONITO_PCIMAP_PCIMAP_2		0x00040000	/* real bonito only */
#define BONITO_PCIMAP_WIN(WIN,ADDR)	((((ADDR)>>26) & BONITO_PCIMAP_PCIMAP_LO0) << ((WIN)*6))
#define BONITO_PCIMAP_WINSIZE		(1UL<<26)
#define BONITO_PCIMAP_WINOFFSET(ADDR)	((ADDR) & (BONITO_PCIMAP_WINSIZE - 1))
#define BONITO_PCIMAP_WINBASE(ADDR)	((ADDR) << 26)

/* PCIMAP Cfg */

#define BONITO_PCIMAPCFG_TYPE1		0x00010000

/* PXARB_CFG */

#define	LOONGSON_PXARB_DEVICE_EN	0x00000001
#define	LOONGSON_PXARB_DISABLE_BROKEN	0x00000002
#define	LOONGSON_PXARB_DEFAULT_MAS_EN	0x00000004
#define	LOONGSON_PXARB_DEFAULT_MAS_MSK	0x00000038
#define	LOONGSON_PXARB_DEFAULT_MAS_SHFT		3
#define	LOONGSON_PXARB_PARK_DELAY_MSK	0x000000c0
#define	LOONGSON_PXARB_PARK_DELAY_SHFT		6
#define	LOONGSON_PXARB_PARK_DELAY_0		0
#define	LOONGSON_PXARB_PARK_DELAY_8		1
#define	LOONGSON_PXARB_PARK_DELAY_32		2
#define	LOONGSON_PXARB_PARK_DELAY_128		3
#define	LOONGSON_PXARB_LEVEL_MSK	0x0000ff00
#define	LOONGSON_PXARB_LEVEL_SHFT		8
#define	LOONGSON_PXARB_RUDE_DEV_MSK	0x00ff0000
#define	LOONGSON_PXARB_RUDE_DEV_SHFT		16

#endif	/* _BONITOREG_H_ */


code:
/*	$OpenBSD: bonitovar.h,v 1.6 2014/03/27 22:16:03 miod Exp $	*/
/*	$NetBSD: bonitovar.h,v 1.4 2008/04/28 20:23:28 martin Exp $	*/

/*-
 * Copyright (c) 2001, 2002 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.
 */

#ifndef _LOONGSON_DEV_BONITOVAR_H_
#define	_LOONGSON_DEV_BONITOVAR_H_

struct bonito_cfg_hook;
struct extent;

struct bonito_config {
	int		bc_adbase;	/* AD line base for config access */

	/* Prototype interrupt and GPIO registers. */
	uint32_t	bc_gpioIE;
	uint32_t	bc_intEdge;
	uint32_t	bc_intSteer;
	uint32_t	bc_intPol;

	/* PCI Attach hook for the first bus */
	void		(*bc_attach_hook)(pci_chipset_tag_t);

	/* PCI Interrupt Assignment for the first bus */
	int		(*bc_intr_map)(int, int, int);
};

struct bonito_softc {
	struct device			 sc_dev;
	int				 sc_compatible;	/* real Bonito hw */
	const struct bonito_config	*sc_bonito;
	struct mips_pci_chipset		 sc_pc;

	/* PCI Configuration Space access hooks */
	SLIST_HEAD(, bonito_cfg_hook)	 sc_hook;
};

#ifdef _KERNEL
void	 bonito_intr_disestablish(void *);
void	*bonito_intr_establish(int, int, int, int (*)(void *), void *,
	    const char *);
int	 bonito_pci_hook(pci_chipset_tag_t, void *,
	    int (*)(void *, pci_chipset_tag_t, pcitag_t, int, pcireg_t *),
	    int (*)(void *, pci_chipset_tag_t, pcitag_t, int, pcireg_t));
int	 bonito_print(void *, const char *);
struct extent
	*bonito_get_resource_extent(pci_chipset_tag_t, int);
void	 bonito_setintrmask(int);

void	 bonito_early_setup(void);
#endif /* _KERNEL */

#endif /* _LOONGSON_DEV_BONITOVAR_H_ */

license: unknown


code:
/*	$OpenBSD: gdiumiic.c,v 1.6 2013/04/19 23:44:34 miod Exp $	*/

/*
 * Copyright (c) 2010 Miodrag Vallat.
 * Copyright (c) 2006 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.
 */

/*
 * I2C bus bit-banging through GPIO pins.
 *
 * Derived from /sys/dev/gpio/gpioiic.c, with SDA and SCL pin order
 * exchanged, and knowledge of bus contents added.
 */

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

#include <machine/autoconf.h>

#include <dev/gpio/gpiovar.h>

#include <dev/i2c/i2cvar.h>
#include <dev/i2c/i2c_bitbang.h>

#define GPIOIIC_PIN_SDA		1
#define GPIOIIC_PIN_SCL		0
#define GPIOIIC_NPINS		2

#define GPIOIIC_SDA		0x02
#define GPIOIIC_SCL		0x01

struct gdiumiic_softc {
	struct device		sc_dev;

	void *			sc_gpio;
	struct gpio_pinmap	sc_map;
	int			__map[GPIOIIC_NPINS];

	struct i2c_controller	sc_i2c_tag;
	struct rwlock		sc_i2c_lock;

	int			sc_sda;
	int			sc_scl;
};

int		gdiumiic_match(struct device *, void *, void *);
void		gdiumiic_attach(struct device *, struct device *, void *);
int		gdiumiic_detach(struct device *, int);

int		gdiumiic_i2c_acquire_bus(void *, int);
void		gdiumiic_i2c_release_bus(void *, int);
int		gdiumiic_i2c_send_start(void *, int);
int		gdiumiic_i2c_send_stop(void *, int);
int		gdiumiic_i2c_initiate_xfer(void *, i2c_addr_t, int);
int		gdiumiic_i2c_read_byte(void *, u_int8_t *, int);
int		gdiumiic_i2c_write_byte(void *, u_int8_t, int);

void		gdiumiic_bb_set_bits(void *, u_int32_t);
void		gdiumiic_bb_set_dir(void *, u_int32_t);
u_int32_t	gdiumiic_bb_read_bits(void *);

int		gdiumiic_bustype(struct gpio_attach_args *);
void		gdiumiic_sensors_scan(struct device *,
		    struct i2cbus_attach_args *, void *);

struct cfattach gdiumiic_ca = {
	sizeof(struct gdiumiic_softc),
	gdiumiic_match,
	gdiumiic_attach,
	gdiumiic_detach
};

struct cfdriver gdiumiic_cd = {
	NULL, "gdiumiic", DV_DULL
};

static const struct i2c_bitbang_ops gdiumiic_bbops = {
	gdiumiic_bb_set_bits,
	gdiumiic_bb_set_dir,
	gdiumiic_bb_read_bits,
	{ GPIOIIC_SDA, GPIOIIC_SCL, GPIOIIC_SDA, 0 }
};

#define	GDIUMIIC_BUSTYPE_SENSORS	0
#define	GDIUMIIC_BUSTYPE_VIDCTRL	1

int
gdiumiic_bustype(struct gpio_attach_args *ga)
{
	extern int gdium_revision;

	/*
	 * Hardware pin connections depend upon the motherboard revision.
	 * XXX magic numbers (should match kernel configuration)
	 */
	switch (gdium_revision) {
	case 0:
		if (ga->ga_offset == 46 && ga->ga_mask == 0x03)
			return GDIUMIIC_BUSTYPE_SENSORS;
		break;
	default:
		if (ga->ga_offset == 6 && ga->ga_mask == 0x81)
			return GDIUMIIC_BUSTYPE_SENSORS;
		break;
	}

	if (ga->ga_offset == 41 && ga->ga_mask == 0x03)
		return GDIUMIIC_BUSTYPE_VIDCTRL;

	return -1;

}

int
gdiumiic_match(struct device *parent, void *match, void *aux)
{
	struct cfdata *cf = match;
	struct gpio_attach_args *ga = aux;

	if (ga->ga_offset == -1 || gdiumiic_bustype(ga) < 0)
		return 0;

	return (sys_platform->system_type == LOONGSON_GDIUM &&
	    strcmp(cf->cf_driver->cd_name, "gdiumiic") == 0);
}

void
gdiumiic_attach(struct device *parent, struct device *self, void *aux)
{
	struct gdiumiic_softc *sc = (struct gdiumiic_softc *)self;
	struct gpio_attach_args *ga = aux;
	struct i2cbus_attach_args iba;
	int caps;

	/* Check that we have enough pins */
	if (gpio_npins(ga->ga_mask) != GPIOIIC_NPINS) {
		printf(": invalid pin mask\n");
		return;
	}

	/* Map pins */
	sc->sc_gpio = ga->ga_gpio;
	sc->sc_map.pm_map = sc->__map;
	if (gpio_pin_map(sc->sc_gpio, ga->ga_offset, ga->ga_mask,
	    &sc->sc_map)) {
		printf(": can't map pins\n");
		return;
	}

	/* Configure SDA pin */
	caps = gpio_pin_caps(sc->sc_gpio, &sc->sc_map, GPIOIIC_PIN_SDA);
	if (!(caps & GPIO_PIN_OUTPUT)) {
		printf(": SDA pin is unable to drive output\n");
		goto fail;
	}
	if (!(caps & GPIO_PIN_INPUT)) {
		printf(": SDA pin is unable to read input\n");
		goto fail;
	}
	printf(": SDA[%d]", sc->sc_map.pm_map[GPIOIIC_PIN_SDA]);
	sc->sc_sda = GPIO_PIN_OUTPUT;
#if 0
	if (caps & GPIO_PIN_OPENDRAIN) {
		printf(" open-drain");
		sc->sc_sda |= GPIO_PIN_OPENDRAIN;
	} else if ((caps & GPIO_PIN_PUSHPULL) && (caps & GPIO_PIN_TRISTATE)) {
		printf(" push-pull tri-state");
		sc->sc_sda |= GPIO_PIN_PUSHPULL;
	}
	if (caps & GPIO_PIN_PULLUP) {
		printf(" pull-up");
		sc->sc_sda |= GPIO_PIN_PULLUP;
	}
#endif
	gpio_pin_ctl(sc->sc_gpio, &sc->sc_map, GPIOIIC_PIN_SDA, sc->sc_sda);

	/* Configure SCL pin */
	caps = gpio_pin_caps(sc->sc_gpio, &sc->sc_map, GPIOIIC_PIN_SCL);
	if (!(caps & GPIO_PIN_OUTPUT)) {
		printf(": SCL pin is unable to drive output\n");
		goto fail;
	}
	printf(", SCL[%d]", sc->sc_map.pm_map[GPIOIIC_PIN_SCL]);
	sc->sc_scl = GPIO_PIN_OUTPUT;
#if 0
	if (caps & GPIO_PIN_OPENDRAIN) {
		printf(" open-drain");
		sc->sc_scl |= GPIO_PIN_OPENDRAIN;
		if (caps & GPIO_PIN_PULLUP) {
			printf(" pull-up");
			sc->sc_scl |= GPIO_PIN_PULLUP;
		}
	} else if (caps & GPIO_PIN_PUSHPULL) {
		printf(" push-pull");
		sc->sc_scl |= GPIO_PIN_PUSHPULL;
	}
#endif
	gpio_pin_ctl(sc->sc_gpio, &sc->sc_map, GPIOIIC_PIN_SCL, sc->sc_scl);

	printf("\n");

	/* Attach I2C bus */
	rw_init(&sc->sc_i2c_lock, "iiclk");
	sc->sc_i2c_tag.ic_cookie = sc;
	sc->sc_i2c_tag.ic_acquire_bus = gdiumiic_i2c_acquire_bus;
	sc->sc_i2c_tag.ic_release_bus = gdiumiic_i2c_release_bus;
	sc->sc_i2c_tag.ic_send_start = gdiumiic_i2c_send_start;
	sc->sc_i2c_tag.ic_send_stop = gdiumiic_i2c_send_stop;
	sc->sc_i2c_tag.ic_initiate_xfer = gdiumiic_i2c_initiate_xfer;
	sc->sc_i2c_tag.ic_read_byte = gdiumiic_i2c_read_byte;
	sc->sc_i2c_tag.ic_write_byte = gdiumiic_i2c_write_byte;

	bzero(&iba, sizeof(iba));
	iba.iba_name = "iic";
	iba.iba_tag = &sc->sc_i2c_tag;
	if (gdiumiic_bustype(ga) == GDIUMIIC_BUSTYPE_SENSORS)
		iba.iba_bus_scan = gdiumiic_sensors_scan;
	config_found(self, &iba, iicbus_print);

	return;

fail:
	gpio_pin_unmap(sc->sc_gpio, &sc->sc_map);
}

int
gdiumiic_detach(struct device *self, int flags)
{
	return (0);
}

int
gdiumiic_i2c_acquire_bus(void *cookie, int flags)
{
	struct gdiumiic_softc *sc = cookie;

	if (cold || (flags & I2C_F_POLL))
		return (0);

	return (rw_enter(&sc->sc_i2c_lock, RW_WRITE | RW_INTR));
}

void
gdiumiic_i2c_release_bus(void *cookie, int flags)
{
	struct gdiumiic_softc *sc = cookie;

	if (cold || (flags & I2C_F_POLL))
		return;

	rw_exit(&sc->sc_i2c_lock);
}

int
gdiumiic_i2c_send_start(void *cookie, int flags)
{
	return (i2c_bitbang_send_start(cookie, flags, &gdiumiic_bbops));
}

int
gdiumiic_i2c_send_stop(void *cookie, int flags)
{
	return (i2c_bitbang_send_stop(cookie, flags, &gdiumiic_bbops));
}

int
gdiumiic_i2c_initiate_xfer(void *cookie, i2c_addr_t addr, int flags)
{
	return (i2c_bitbang_initiate_xfer(cookie, addr, flags, &gdiumiic_bbops));
}

int
gdiumiic_i2c_read_byte(void *cookie, u_int8_t *bytep, int flags)
{
	return (i2c_bitbang_read_byte(cookie, bytep, flags, &gdiumiic_bbops));
}

int
gdiumiic_i2c_write_byte(void *cookie, u_int8_t byte, int flags)
{
	return (i2c_bitbang_write_byte(cookie, byte, flags, &gdiumiic_bbops));
}

void
gdiumiic_bb_set_bits(void *cookie, u_int32_t bits)
{
	struct gdiumiic_softc *sc = cookie;

	gpio_pin_write(sc->sc_gpio, &sc->sc_map, GPIOIIC_PIN_SDA,
	    bits & GPIOIIC_SDA ? GPIO_PIN_HIGH : GPIO_PIN_LOW);
	gpio_pin_write(sc->sc_gpio, &sc->sc_map, GPIOIIC_PIN_SCL,
	    bits & GPIOIIC_SCL ? GPIO_PIN_HIGH : GPIO_PIN_LOW);
}

void
gdiumiic_bb_set_dir(void *cookie, u_int32_t bits)
{
	struct gdiumiic_softc *sc = cookie;
	int sda = sc->sc_sda;

	sda &= ~(GPIO_PIN_INPUT | GPIO_PIN_OUTPUT | GPIO_PIN_TRISTATE);
	sda |= (bits & GPIOIIC_SDA ? GPIO_PIN_OUTPUT : GPIO_PIN_INPUT);
#if 0
	if ((sda & GPIO_PIN_PUSHPULL) && !(bits & GPIOIIC_SDA))
		sda |= GPIO_PIN_TRISTATE;
#endif
	if (sc->sc_sda != sda) {
		sc->sc_sda = sda;
		gpio_pin_ctl(sc->sc_gpio, &sc->sc_map, GPIOIIC_PIN_SDA,
		    sc->sc_sda);
	}
}

u_int32_t
gdiumiic_bb_read_bits(void *cookie)
{
	struct gdiumiic_softc *sc = cookie;
	u_int32_t bits = 0;

	if (gpio_pin_read(sc->sc_gpio, &sc->sc_map, GPIOIIC_PIN_SDA) ==
	    GPIO_PIN_HIGH)
		bits |= GPIOIIC_SDA;
	if (gpio_pin_read(sc->sc_gpio, &sc->sc_map, GPIOIIC_PIN_SCL) ==
	    GPIO_PIN_HIGH)
		bits |= GPIOIIC_SCL;

	return bits;
}

/*
 * Attach devices to the first (sensors + RTC) i2c bus.
 *
 * Note that the i2c scan performed by the MI i2c code will fail to
 * identify our lm75 chip correctly.
 */
void
gdiumiic_sensors_scan(struct device *iicdev, struct i2cbus_attach_args *iba,
    void *arg)
{
	struct i2c_attach_args ia;
	/* not worth #define'ing _I2C_PRIVATE for */
	extern int iic_print(void *, const char *);

	bzero(&ia, sizeof ia);
	ia.ia_tag = iba->iba_tag;
	ia.ia_addr = 0x40;
	ia.ia_size = 1;
	ia.ia_name = "stsec";
	config_found(iicdev, &ia, iic_print);

	bzero(&ia, sizeof ia);
	ia.ia_tag = iba->iba_tag;
	ia.ia_addr = 0x48;
	ia.ia_size = 1;
	ia.ia_name = "lm75";
	config_found(iicdev, &ia, iic_print);

	bzero(&ia, sizeof ia);
	ia.ia_tag = iba->iba_tag;
	ia.ia_addr = 0x68;
	ia.ia_size = 1;
	ia.ia_name = "mfokclock";
	config_found(iicdev, &ia, iic_print);
}


code:
/*	$OpenBSD: glx.c,v 1.9 2013/01/15 23:30:36 pirofti Exp $	*/

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

/*
 * AMD CS5536 PCI Mess
 * XXX too many hardcoded numbers... need to expand glxreg.h
 */

#include <sys/param.h>
#include <sys/systm.h>
#include <sys/device.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/pci/pciidereg.h>
#include <dev/pci/pciide_amd_reg.h>
#include <dev/usb/ehcireg.h>
#include <dev/usb/ohcireg.h>

#include <dev/pci/glxreg.h>
#include <dev/pci/glxvar.h>

#include <loongson/dev/bonitovar.h>

/*
 * Since the purpose of this code is to present a different view of the
 * PCI configuration space, it can not attach as a real device.
 * (well it could, and then we'd have to attach a fake pci to it,
 * and fake the configuration space accesses anyways - is it worth doing?)
 *
 * We just keep the `would-be softc' structure as global variables.
 */

static pci_chipset_tag_t	glxbase_pc;
static pcitag_t			glxbase_tag;
static int			glxbase_dev;

/* MSR access through PCI configuration space */
#define	PCI_MSR_CTRL		0x00f0
#define	PCI_MSR_ADDR		0x00f4
#define	PCI_MSR_LO32		0x00f8
#define	PCI_MSR_HI32		0x00fc

int	glx_pci_read_hook(void *, pci_chipset_tag_t, pcitag_t, int, pcireg_t *);
int	glx_pci_write_hook(void *, pci_chipset_tag_t, pcitag_t, int, pcireg_t);

pcireg_t glx_get_status(void);
pcireg_t glx_fn0_read(int);
void	glx_fn0_write(int, pcireg_t);
pcireg_t glx_fn2_read(int);
void	glx_fn2_write(int, pcireg_t);
pcireg_t glx_fn3_read(int);
void	glx_fn3_write(int, pcireg_t);
pcireg_t glx_fn4_read(int);
void	glx_fn4_write(int, pcireg_t);
pcireg_t glx_fn5_read(int);
void	glx_fn5_write(int, pcireg_t);
pcireg_t glx_fn6_read(int);
void	glx_fn6_write(int, pcireg_t);
pcireg_t glx_fn7_read(int);
void	glx_fn7_write(int, pcireg_t);

void
glx_init(pci_chipset_tag_t pc, pcitag_t tag, int dev)
{
	uint64_t msr;

	glxbase_pc = pc;
	glxbase_dev = dev;
	glxbase_tag = tag;

	/*
	 * Register PCI configuration hooks to make the various
	 * embedded devices visible as PCI subfunctions.
	 */

	bonito_pci_hook(pc, NULL, glx_pci_read_hook, glx_pci_write_hook);

	/*
	 * Perform some Geode intialization.
	 */

	msr = rdmsr(DIVIL_BALL_OPTS);	/* 0x71 */
	wrmsr(DIVIL_BALL_OPTS, msr | 0x01);

	/*
	 * Route usb, audio and serial interrupts
	 */

	msr = rdmsr(PIC_YSEL_LOW);
	msr &= ~(0xfUL << 8);
	msr &= ~(0xfUL << 16);
	msr |= 11 << 8;
	msr |= 9 << 16;
	wrmsr(PIC_YSEL_LOW, msr);

	msr = rdmsr(PIC_YSEL_HIGH);
	msr &= ~(0xfUL << 24);
	msr &= ~(0xfUL << 28);
	msr |= 4 << 24;
	msr |= 3 << 28;
	wrmsr(PIC_YSEL_HIGH, msr);

	/*
	 * MFGPT runs on powers of two, adjust the hz value accordingly.
	 */
	stathz = hz = 128;
}

uint64_t
rdmsr(uint msr)
{
	uint64_t lo, hi;
	register_t sr;

#ifdef DIAGNOSTIC
	if (glxbase_tag == 0)
		panic("rdmsr invoked before glx initialization");
#endif

	sr = disableintr();
	pci_conf_write(glxbase_pc, glxbase_tag, PCI_MSR_ADDR, msr);
	lo = (uint32_t)pci_conf_read(glxbase_pc, glxbase_tag, PCI_MSR_LO32);
	hi = (uint32_t)pci_conf_read(glxbase_pc, glxbase_tag, PCI_MSR_HI32);
	setsr(sr);
	return (hi << 32) | lo;
}

void
wrmsr(uint msr, uint64_t value)
{
	register_t sr;

#ifdef DIAGNOSTIC
	if (glxbase_tag == 0)
		panic("wrmsr invoked before glx initialization");
#endif

	sr = disableintr();
	pci_conf_write(glxbase_pc, glxbase_tag, PCI_MSR_ADDR, msr);
	pci_conf_write(glxbase_pc, glxbase_tag, PCI_MSR_LO32, (uint32_t)value);
	pci_conf_write(glxbase_pc, glxbase_tag, PCI_MSR_HI32, value >> 32);
	setsr(sr);
}

int
glx_pci_read_hook(void *v, pci_chipset_tag_t pc, pcitag_t tag,
    int offset, pcireg_t *data)
{
	int bus, dev, fn;

	/*
	 * Do not get in the way of MSR programming
	 */
	if (tag == glxbase_tag && offset >= PCI_MSR_CTRL)
		return 0;

	pci_decompose_tag(pc, tag, &bus, &dev, &fn);
	if (bus != 0 || dev != glxbase_dev)
		return 0;

	*data = 0;

	switch (fn) {
	case 0:	/* PCI-ISA bridge */
		*data = glx_fn0_read(offset);
		break;
	case 1:	/* Flash memory */
		break;
	case 2:	/* IDE controller */
		*data = glx_fn2_read(offset);
		break;
	case 3:	/* AC97 codec */
		*data = glx_fn3_read(offset);
		break;
	case 4:	/* OHCI controller */
		*data = glx_fn4_read(offset);
		break;
	case 5:	/* EHCI controller */
		*data = glx_fn5_read(offset);
		break;
	case 6:	/* UDC */
		break;
	case 7:	/* OTG */
		break;
	}

	return 1;
}

int
glx_pci_write_hook(void *v, pci_chipset_tag_t pc, pcitag_t tag,
    int offset, pcireg_t data)
{
	int bus, dev, fn;

	/*
	 * Do not get in the way of MSR programming
	 */
	if (tag == glxbase_tag && offset >= PCI_MSR_CTRL)
		return 0;

	pci_decompose_tag(pc, tag, &bus, &dev, &fn);
	if (bus != 0 || dev != glxbase_dev)
		return 0;

	switch (fn) {
	case 0:	/* PCI-ISA bridge */
		glx_fn0_write(offset, data);
		break;
	case 1:	/* Flash memory */
		break;
	case 2:	/* IDE controller */
		glx_fn2_write(offset, data);
		break;
	case 3:	/* AC97 codec */
		glx_fn3_write(offset, data);
		break;
	case 4:	/* OHCI controller */
		glx_fn4_write(offset, data);
		break;
	case 5:	/* EHCI controller */
		glx_fn5_write(offset, data);
		break;
	case 6:	/* USB UDC */
		break;
	case 7:	/* USB OTG */
		break;
	}

	return 1;
}

pcireg_t
glx_get_status()
{
	uint64_t msr;
	pcireg_t data;

	data = 0;
	msr = rdmsr(GLPCI_GLD_MSR_ERROR);
	if (msr & (1UL << 5))
		data |= PCI_COMMAND_PARITY_ENABLE;
	data |= PCI_STATUS_66MHZ_SUPPORT |
	    PCI_STATUS_BACKTOBACK_SUPPORT | PCI_STATUS_DEVSEL_MEDIUM;
	if (msr & (1UL << 21))
		data |= PCI_STATUS_PARITY_DETECT;
	if (msr & (1UL << 20))
		data |= PCI_STATUS_TARGET_TARGET_ABORT;
	if (msr & (1UL << 17))
		data |= PCI_STATUS_MASTER_TARGET_ABORT;
	if (msr & (1UL << 16))
		data |= PCI_STATUS_MASTER_ABORT;

	return data;
}

/*
 * Function 0: PCI-ISA bridge
 */

static pcireg_t pcib_bar_sizes[(4 + PCI_MAPREG_END - PCI_MAPREG_START) / 4] = {
	0x008,
	0x100,
	0x040,
	0x020,
	0x080,
	0x020
};

static pcireg_t pcib_bar_values[(4 + PCI_MAPREG_END - PCI_MAPREG_START) / 4];

static uint64_t pcib_bar_msr[(4 + PCI_MAPREG_END - PCI_MAPREG_START) / 4] = {
	DIVIL_LBAR_SMB,
	DIVIL_LBAR_GPIO,
	DIVIL_LBAR_MFGPT,
	DIVIL_LBAR_IRQ,
	DIVIL_LBAR_PMS,
	DIVIL_LBAR_ACPI
};

pcireg_t
glx_fn0_read(int reg)
{
	uint64_t msr;
	pcireg_t data;
	int index;

	switch (reg) {
	case PCI_ID_REG:
	case PCI_SUBSYS_ID_REG:
		data = PCI_ID_CODE(PCI_VENDOR_AMD, PCI_PRODUCT_AMD_CS5536_PCIB);
		break;
	case PCI_COMMAND_STATUS_REG:
		data = glx_get_status();
		data |= PCI_COMMAND_MASTER_ENABLE;
		msr = rdmsr(DIVIL_LBAR_SMB);
		if (msr & (1UL << 32))
			data |= PCI_COMMAND_IO_ENABLE;
		break;
	case PCI_CLASS_REG:
		msr = rdmsr(GLCP_CHIP_REV_ID);
		data = (PCI_CLASS_BRIDGE << PCI_CLASS_SHIFT) |
		    (PCI_SUBCLASS_BRIDGE_ISA << PCI_SUBCLASS_SHIFT) |
		    (msr & PCI_REVISION_MASK);
		break;
	case PCI_BHLC_REG:
		msr = rdmsr(GLPCI_CTRL);
		data = (0x80 << PCI_HDRTYPE_SHIFT) |
		    (((msr & 0xff00000000UL) >> 32) << PCI_LATTIMER_SHIFT) |
		    (0x08 << PCI_CACHELINE_SHIFT);
		break;
	case PCI_MAPREG_START + 0x00:
	case PCI_MAPREG_START + 0x04:
	case PCI_MAPREG_START + 0x08:
	case PCI_MAPREG_START + 0x0c:
	case PCI_MAPREG_START + 0x10:
	case PCI_MAPREG_START + 0x14:
	case PCI_MAPREG_START + 0x18:
		index = (reg - PCI_MAPREG_START) / 4;
		if (pcib_bar_msr[index] == 0)
			data = 0;
		else {
			data = pcib_bar_values[index];
			if (data == 0xffffffff)
				data = PCI_MAPREG_IO_ADDR_MASK;
			else
				data = (pcireg_t)rdmsr(pcib_bar_msr[index]);
			data &= ~(pcib_bar_sizes[index] - 1);
			if (data != 0)
				data |= PCI_MAPREG_TYPE_IO;
		}
		break;
	case PCI_INTERRUPT_REG:
		data = (0x40 << PCI_MAX_LAT_SHIFT) |
		    (PCI_INTERRUPT_PIN_NONE << PCI_INTERRUPT_PIN_SHIFT);
		break;
	default:
		data = 0;
		break;
	}

	return data;
}

void
glx_fn0_write(int reg, pcireg_t data)
{
	uint64_t msr;
	int index;

	switch (reg) {
	case PCI_COMMAND_STATUS_REG:
		for (index = 0; index < nitems(pcib_bar_msr); index++) {
			if (pcib_bar_msr[index] == 0)
				continue;
			msr = rdmsr(pcib_bar_msr[index]);
			if (data & PCI_COMMAND_IO_ENABLE)
				msr |= 1UL << 32;
			else
				msr &= ~(1UL << 32);
			wrmsr(pcib_bar_msr[index], msr);
		}

		msr = rdmsr(GLPCI_GLD_MSR_ERROR);
		if (data & PCI_COMMAND_PARITY_ENABLE)
			msr |= 1UL << 5;
		else
			msr &= ~(1UL << 5);
		wrmsr(GLPCI_GLD_MSR_ERROR, msr);
		break;
	case PCI_BHLC_REG:
		msr = rdmsr(GLPCI_CTRL);
		msr &= 0xff00000000UL;
		msr |= ((uint64_t)PCI_LATTIMER(data)) << 32;
		break;
	case PCI_MAPREG_START + 0x00:
	case PCI_MAPREG_START + 0x04:
	case PCI_MAPREG_START + 0x08:
	case PCI_MAPREG_START + 0x0c:
	case PCI_MAPREG_START + 0x10:
	case PCI_MAPREG_START + 0x14:
	case PCI_MAPREG_START + 0x18:
		index = (reg - PCI_MAPREG_START) / 4;
		if (data == 0xffffffff) {
			pcib_bar_values[index] = data;
		} else if (pcib_bar_msr[index] != 0) {
			if ((data & PCI_MAPREG_TYPE_MASK) ==
			    PCI_MAPREG_TYPE_IO) {
				data &= PCI_MAPREG_IO_ADDR_MASK;
				data &= ~(pcib_bar_sizes[index] - 1);
				wrmsr(pcib_bar_msr[index],
				    (0x0000f000UL << 32) | (1UL << 32) | data);
			} else {
				wrmsr(pcib_bar_msr[index], 0UL);
			}
			pcib_bar_values[index] = 0;
		}
		break;
	}
}

/*
 * Function 2: IDE Controller
 */

static pcireg_t pciide_bar_size = 0x10;
static pcireg_t pciide_bar_value;

pcireg_t
glx_fn2_read(int reg)
{
	uint64_t msr;
	pcireg_t data;

	switch (reg) {
	case PCI_ID_REG:
	case PCI_SUBSYS_ID_REG:
		data = PCI_ID_CODE(PCI_VENDOR_AMD, PCI_PRODUCT_AMD_CS5536_IDE);
		break;
	case PCI_COMMAND_STATUS_REG:
		data = glx_get_status();
		data |= PCI_COMMAND_IO_ENABLE;
		msr = rdmsr(GLIU_PAE);
		if ((msr & (0x3 << 4)) == (0x03 << 4))
			data |= PCI_COMMAND_MASTER_ENABLE;
		break;
	case PCI_CLASS_REG:
		msr = rdmsr(IDE_GLD_MSR_CAP);
		data = (PCI_CLASS_MASS_STORAGE << PCI_CLASS_SHIFT) |
		    (PCI_SUBCLASS_MASS_STORAGE_IDE << PCI_SUBCLASS_SHIFT) |
		    (PCIIDE_INTERFACE_BUS_MASTER_DMA << PCI_INTERFACE_SHIFT) |
		    (msr & PCI_REVISION_MASK);
		break;
	case PCI_BHLC_REG:
		msr = rdmsr(GLPCI_CTRL);
		data = (0x00 << PCI_HDRTYPE_SHIFT) |
		    (((msr & 0xff00000000UL) >> 32) << PCI_LATTIMER_SHIFT) |
		    (0x08 << PCI_CACHELINE_SHIFT);
		break;
	case PCI_MAPREG_START + 0x10:
		data = pciide_bar_value;
		if (data == 0xffffffff)
			data = PCI_MAPREG_IO_ADDR_MASK & ~(pciide_bar_size - 1);
		else {
			msr = rdmsr(IDE_IO_BAR);
			data = msr & 0xfffffff0;
		}
		if (data != 0)
			data |= PCI_MAPREG_TYPE_IO;
		break;
	case PCI_INTERRUPT_REG:
		/* compat mode */
		data = (0x40 << PCI_MAX_LAT_SHIFT) |
		    (PCI_INTERRUPT_PIN_NONE << PCI_INTERRUPT_PIN_SHIFT);
		break;
	/*
	 * The following registers are used by pciide(4)
	 */
	case AMD756_CHANSTATUS_EN:
		data = rdmsr(IDE_CFG);
		break;
	case AMD756_DATATIM:
		data = rdmsr(IDE_DTC);
		break;
	case AMD756_UDMA:
		data = rdmsr(IDE_ETC);
		break;
	default:
		data = 0;
		break;
	}

	return data;
}

void
glx_fn2_write(int reg, pcireg_t data)
{
	uint64_t msr;

	switch (reg) {
	case PCI_COMMAND_STATUS_REG:
		msr = rdmsr(GLIU_PAE);
		if (data & PCI_COMMAND_MASTER_ENABLE)
			msr |= 0x03 << 4;
		else
			msr &= ~(0x03 << 4);
		wrmsr(GLIU_PAE, msr);
		break;
	case PCI_BHLC_REG:
		msr = rdmsr(GLPCI_CTRL);
		msr &= 0xff00000000UL;
		msr |= ((uint64_t)PCI_LATTIMER(data)) << 32;
		break;
	case PCI_MAPREG_START + 0x10:
		if (data == 0xffffffff) {
			pciide_bar_value = data;
		} else {
			if ((data & PCI_MAPREG_TYPE_MASK) ==
			    PCI_MAPREG_TYPE_IO) {
				data &= PCI_MAPREG_IO_ADDR_MASK;
				msr = (uint32_t)data & 0xfffffff0;
				wrmsr(IDE_IO_BAR, msr);
			} else {
				wrmsr(IDE_IO_BAR, 0);
			}
			pciide_bar_value = 0;
		}
		break;
	/*
	 * The following registers are used by pciide(4)
	 */
	case AMD756_CHANSTATUS_EN:
		wrmsr(IDE_CFG, (uint32_t)data);
		break;
	case AMD756_DATATIM:
		wrmsr(IDE_DTC, (uint32_t)data);
		break;
	case AMD756_UDMA:
		wrmsr(IDE_ETC, (uint32_t)data);
		break;
	}
}

/*
 * Function 3: AC97 Codec
 */

static pcireg_t ac97_bar_size = 0x80;
static pcireg_t ac97_bar_value;

pcireg_t
glx_fn3_read(int reg)
{
	uint64_t msr;
	pcireg_t data;

	switch (reg) {
	case PCI_ID_REG:
	case PCI_SUBSYS_ID_REG:
		data = PCI_ID_CODE(PCI_VENDOR_AMD,
		    PCI_PRODUCT_AMD_CS5536_AUDIO);
		break;
	case PCI_COMMAND_STATUS_REG:
		data = glx_get_status();
		data |= PCI_COMMAND_IO_ENABLE;
		msr = rdmsr(GLIU_PAE);
		if ((msr & (0x3 << 8)) == (0x03 << 8))
			data |= PCI_COMMAND_MASTER_ENABLE;
		break;
	case PCI_CLASS_REG:
		msr = rdmsr(ACC_GLD_MSR_CAP);
		data = (PCI_CLASS_MULTIMEDIA << PCI_CLASS_SHIFT) |
		    (PCI_SUBCLASS_MULTIMEDIA_AUDIO << PCI_SUBCLASS_SHIFT) |
		    (msr & PCI_REVISION_MASK);
		break;
	case PCI_BHLC_REG:
		msr = rdmsr(GLPCI_CTRL);
		data = (0x00 << PCI_HDRTYPE_SHIFT) |
		    (((msr & 0xff00000000UL) >> 32) << PCI_LATTIMER_SHIFT) |
		    (0x08 << PCI_CACHELINE_SHIFT);
		break;
	case PCI_MAPREG_START:
		data = ac97_bar_value;
		if (data == 0xffffffff)
			data = PCI_MAPREG_IO_ADDR_MASK & ~(ac97_bar_size - 1);
		else {
			msr = rdmsr(GLIU_IOD_BM1);
			data = (msr >> 20) & 0x000fffff;
			data &= (msr & 0x000fffff);
		}
		if (data != 0)
			data |= PCI_MAPREG_TYPE_IO;
		break;
	case PCI_INTERRUPT_REG:
		data = (0x40 << PCI_MAX_LAT_SHIFT) |
		    (PCI_INTERRUPT_PIN_A << PCI_INTERRUPT_PIN_SHIFT);
		break;
	default:
		data = 0;
		break;
	}

	return data;
}

void
glx_fn3_write(int reg, pcireg_t data)
{
	uint64_t msr;

	switch (reg) {
	case PCI_COMMAND_STATUS_REG:
		msr = rdmsr(GLIU_PAE);
		if (data & PCI_COMMAND_MASTER_ENABLE)
			msr |= 0x03 << 8;
		else
			msr &= ~(0x03 << 8);
		wrmsr(GLIU_PAE, msr);
		break;
	case PCI_BHLC_REG:
		msr = rdmsr(GLPCI_CTRL);
		msr &= 0xff00000000UL;
		msr |= ((uint64_t)PCI_LATTIMER(data)) << 32;
		break;
	case PCI_MAPREG_START:
		if (data == 0xffffffff) {
			ac97_bar_value = data;
		} else {
			if ((data & PCI_MAPREG_TYPE_MASK) ==
			    PCI_MAPREG_TYPE_IO) {
				data &= PCI_MAPREG_IO_ADDR_MASK;
				msr = rdmsr(GLIU_IOD_BM1);
				msr &= 0x0fffff0000000000UL;
				msr |= 5UL << 61;	/* AC97 */
				msr |= ((uint64_t)data & 0xfffff) << 20;
				msr |= 0x000fffff & ~(ac97_bar_size - 1);
				wrmsr(GLIU_IOD_BM1, msr);
			} else {
				wrmsr(GLIU_IOD_BM1, 0);
			}
			ac97_bar_value = 0;
		}
		break;
	}
}

/*
 * Function 4: OHCI Controller
 */

static pcireg_t ohci_bar_size = 0x1000;
static pcireg_t ohci_bar_value;

pcireg_t
glx_fn4_read(int reg)
{
	uint64_t msr;
	pcireg_t data;

	switch (reg) {
	case PCI_ID_REG:
	case PCI_SUBSYS_ID_REG:
		data = PCI_ID_CODE(PCI_VENDOR_AMD, PCI_PRODUCT_AMD_CS5536_OHCI);
		break;
	case PCI_COMMAND_STATUS_REG:
		data = glx_get_status();
		msr = rdmsr(USB_MSR_OHCB);
		if (msr & (1UL << 34))
			data |= PCI_COMMAND_MASTER_ENABLE;
		if (msr & (1UL << 33))
			data |= PCI_COMMAND_MEM_ENABLE;
		break;
	case PCI_CLASS_REG:
		msr = rdmsr(USB_GLD_MSR_CAP);
		data = (PCI_CLASS_SERIALBUS << PCI_CLASS_SHIFT) |
		    (PCI_SUBCLASS_SERIALBUS_USB << PCI_SUBCLASS_SHIFT) |
		    (PCI_INTERFACE_OHCI << PCI_INTERFACE_SHIFT) |
		    (msr & PCI_REVISION_MASK);
		break;
	case PCI_BHLC_REG:
		msr = rdmsr(GLPCI_CTRL);
		data = (0x00 << PCI_HDRTYPE_SHIFT) |
		    (((msr & 0xff00000000UL) >> 32) << PCI_LATTIMER_SHIFT) |
		    (0x08 << PCI_CACHELINE_SHIFT);
		break;
	case PCI_MAPREG_START + 0x00:
		data = ohci_bar_value;
		if (data == 0xffffffff)
			data = PCI_MAPREG_MEM_ADDR_MASK & ~(ohci_bar_size - 1);
		else {
			msr = rdmsr(USB_MSR_OHCB);
			data = msr & 0xffffff00;
		}
		if (data != 0)
			data |= PCI_MAPREG_TYPE_MEM | PCI_MAPREG_MEM_TYPE_32BIT;
		break;
	case PCI_CAPLISTPTR_REG:
		data = 0x40;
		break;
	case PCI_INTERRUPT_REG:
		data = (0x40 << PCI_MAX_LAT_SHIFT) |
		    (PCI_INTERRUPT_PIN_A << PCI_INTERRUPT_PIN_SHIFT);
		break;
	case 0x40:	/* USB capability pointer */
		data = 0;
		break;
	default:
		data = 0;
		break;
	}

	return data;
}

void
glx_fn4_write(int reg, pcireg_t data)
{
	uint64_t msr;

	switch (reg) {
	case PCI_COMMAND_STATUS_REG:
		msr = rdmsr(USB_MSR_OHCB);
		if (data & PCI_COMMAND_MASTER_ENABLE)
			msr |= 1UL << 34;
		else
			msr &= ~(1UL << 34);
		if (data & PCI_COMMAND_MEM_ENABLE)
			msr |= 1UL << 33;
		else
			msr &= ~(1UL << 33);
		wrmsr(USB_MSR_OHCB, msr);
		break;
	case PCI_BHLC_REG:
		msr = rdmsr(GLPCI_CTRL);
		msr &= 0xff00000000UL;
		msr |= ((uint64_t)PCI_LATTIMER(data)) << 32;
		break;
	case PCI_MAPREG_START + 0x00:
		if (data == 0xffffffff) {
			ohci_bar_value = data;
		} else {
			if ((data & PCI_MAPREG_TYPE_MASK) ==
			    PCI_MAPREG_TYPE_MEM) {
				data &= PCI_MAPREG_MEM_ADDR_MASK;
				msr = rdmsr(GLIU_P2D_BM3);
				msr &= 0x0fffff0000000000UL;
				msr |= 2UL << 61;	/* USB */
				msr |= (((uint64_t)data) >> 12) << 20;
				msr |= 0x000fffff;
				wrmsr(GLIU_P2D_BM3, msr);

				msr = rdmsr(USB_MSR_OHCB);
				msr &= ~0xffffff00UL;
				msr |= data;
			} else {
				msr = rdmsr(USB_MSR_OHCB);
				msr &= ~0xffffff00UL;
			}
			wrmsr(USB_MSR_OHCB, msr);
			ohci_bar_value = 0;
		}
		break;
	default:
		break;
	}
}

/*
 * Function 5: EHCI Controller
 */

static pcireg_t ehci_bar_size = 0x1000;
static pcireg_t ehci_bar_value;

pcireg_t
glx_fn5_read(int reg)
{
	uint64_t msr;
	pcireg_t data;

	switch (reg) {
	case PCI_ID_REG:
	case PCI_SUBSYS_ID_REG:
		data = PCI_ID_CODE(PCI_VENDOR_AMD, PCI_PRODUCT_AMD_CS5536_EHCI);
		break;
	case PCI_COMMAND_STATUS_REG:
		data = glx_get_status();
		msr = rdmsr(USB_MSR_EHCB);
		if (msr & (1UL << 34))
			data |= PCI_COMMAND_MASTER_ENABLE;
		if (msr & (1UL << 33))
			data |= PCI_COMMAND_MEM_ENABLE;
		break;
	case PCI_CLASS_REG:
		msr = rdmsr(USB_GLD_MSR_CAP);
		data = (PCI_CLASS_SERIALBUS << PCI_CLASS_SHIFT) |
		    (PCI_SUBCLASS_SERIALBUS_USB << PCI_SUBCLASS_SHIFT) |
		    (PCI_INTERFACE_EHCI << PCI_INTERFACE_SHIFT) |
		    (msr & PCI_REVISION_MASK);
		break;
	case PCI_BHLC_REG:
		msr = rdmsr(GLPCI_CTRL);
		data = (0x00 << PCI_HDRTYPE_SHIFT) |
		    (((msr & 0xff00000000UL) >> 32) << PCI_LATTIMER_SHIFT) |
		    (0x08 << PCI_CACHELINE_SHIFT);
		break;
	case PCI_MAPREG_START + 0x00:
		data = ehci_bar_value;
		if (data == 0xffffffff)
			data = PCI_MAPREG_MEM_ADDR_MASK & ~(ehci_bar_size - 1);
		else {
			msr = rdmsr(USB_MSR_EHCB);
			data = msr & 0xffffff00;
		}
		if (data != 0)
			data |= PCI_MAPREG_TYPE_MEM | PCI_MAPREG_MEM_TYPE_32BIT;
		break;
	case PCI_CAPLISTPTR_REG:
		data = 0x40;
		break;
	case PCI_INTERRUPT_REG:
		data = (0x40 << PCI_MAX_LAT_SHIFT) |
		    (PCI_INTERRUPT_PIN_A << PCI_INTERRUPT_PIN_SHIFT);
		break;
	case 0x40:	/* USB capability pointer */
		data = 0;
		break;
	case PCI_USBREV:
		msr = rdmsr(USB_MSR_EHCB);
		data = PCI_USBREV_2_0;
		data |= ((msr >> 40) & 0x3f) << 8;	/* PCI_EHCI_FLADJ */
		break;
	default:
		data = 0;
		break;
	}

	return data;
}

void
glx_fn5_write(int reg, pcireg_t data)
{
	uint64_t msr;

	switch (reg) {
	case PCI_COMMAND_STATUS_REG:
		msr = rdmsr(USB_MSR_EHCB);
		if (data & PCI_COMMAND_MASTER_ENABLE)
			msr |= 1UL << 34;
		else
			msr &= ~(1UL << 34);
		if (data & PCI_COMMAND_MEM_ENABLE)
			msr |= 1UL << 33;
		else
			msr &= ~(1UL << 33);
		wrmsr(USB_MSR_EHCB, msr);
		break;
	case PCI_BHLC_REG:
		msr = rdmsr(GLPCI_CTRL);
		msr &= 0xff00000000UL;
		msr |= ((uint64_t)PCI_LATTIMER(data)) << 32;
		break;
	case PCI_MAPREG_START + 0x00:
		if (data == 0xffffffff) {
			ehci_bar_value = data;
		} else {
			if ((data & PCI_MAPREG_TYPE_MASK) ==
			    PCI_MAPREG_TYPE_MEM) {
				data &= PCI_MAPREG_MEM_ADDR_MASK;
				msr = rdmsr(GLIU_P2D_BM4);
				msr &= 0x0fffff0000000000UL;
				msr |= 2UL << 61;	/* USB */
				msr |= (((uint64_t)data) >> 12) << 20;
				msr |= 0x000fffff;
				wrmsr(GLIU_P2D_BM4, msr);

				msr = rdmsr(USB_MSR_EHCB);
				msr &= ~0xffffff00UL;
				msr |= data;
			} else {
				msr = rdmsr(USB_MSR_EHCB);
				msr &= ~0xffffff00UL;
			}
			wrmsr(USB_MSR_EHCB, msr);
			ehci_bar_value = 0;
		}
		break;
	default:
		break;
	}
}


code:
/*	$OpenBSD: glxclk.c,v 1.5 2015/07/19 21:11:47 jasper Exp $	*/

/*
 * Copyright (c) 2013 Paul Irofti.
 *
 * 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.
 */

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

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

#include <dev/isa/isavar.h>

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

#include <dev/pci/glxreg.h>
#include <dev/pci/glxvar.h>

struct glxclk_softc {
	struct device		sc_dev;

	bus_space_tag_t		sc_iot;
	bus_space_handle_t	sc_ioh;
};

struct cfdriver glxclk_cd = {
	NULL, "glxclk", DV_DULL
};

int	glxclk_match(struct device *, void *, void *);
void	glxclk_attach(struct device *, struct device *, void *);
int	glxclk_intr(void *);
int	glxclk_stat_intr(void *arg);
void	glxclk_startclock(struct cpu_info *);

struct cfattach glxclk_ca = {
	sizeof(struct glxclk_softc), glxclk_match, glxclk_attach,
};

#define	MSR_LBAR_ENABLE		0x100000000ULL
#define	MSR_LBAR_MFGPT		DIVIL_LBAR_MFGPT
#define	MSR_MFGPT_SIZE		0x40
#define	MSR_MFGPT_ADDR_MASK	0xffc0

#define	AMD5536_MFGPT1_CMP2	0x0000000a	/* Compare value for CMP2 */
#define	AMD5536_MFGPT1_CNT	0x0000000c	/* Up counter */
#define	AMD5536_MFGPT1_SETUP	0x0000000e	/* Setup register */
#define	AMD5536_MFGPT1_SCALE	0x7		/* Set to 128 */
#define	AMD5536_MFGPT1_C2_IRQM	0x00000200

#define	AMD5536_MFGPT2_CMP2	0x00000012	/* Compare value for CMP2 */
#define	AMD5536_MFGPT2_CNT	0x00000014	/* Up counter */
#define	AMD5536_MFGPT2_SETUP	0x00000016	/* Setup register */
#define	AMD5536_MFGPT2_SCALE	0x3		/* Divide by 8 */
#define	AMD5536_MFGPT2_C2_IRQM	0x00000400

#define	AMD5536_MFGPT_CNT_EN	(1 << 15)	/* Enable counting */
#define	AMD5536_MFGPT_CMP2	(1 << 14)	/* Compare 2 output */
#define	AMD5536_MFGPT_CMP1	(1 << 13)	/* Compare 1 output */
#define AMD5536_MFGPT_SETUP	(1 << 12)	/* Set to 1 after 1st write */
#define	AMD5536_MFGPT_STOP_EN	(1 << 11)	/* Stop enable */
#define	AMD5536_MFGPT_CMP2MODE	(1 << 9)|(1 << 8)/* Set to GE + activate IRQ */
#define AMD5536_MFGPT_CLKSEL	(1 << 4)	/* Clock select 14MHz */

struct glxclk_softc *glxclk_sc;

/*
 * Statistics clock interval and variance, in usec.  Variance must be a
 * power of two.  Since this gives us an even number, not an odd number,
 * we discard one case and compensate.  That is, a variance of 1024 would
 * give us offsets in [0..1023].  Instead, we take offsets in [1..1023].
 * This is symmetric about the point 512, or statvar/2, and thus averages
 * to that value (assuming uniform random numbers).
 */
/* XXX fix comment to match value */
int statvar = 8192;
int statmin;			/* statclock interval - 1/2*variance */

int
glxclk_match(struct device *parent, void *match, void *aux)
{
	struct glxpcib_attach_args *gaa = aux;
	struct cfdata *cf = match;

	if (strcmp(gaa->gaa_name, cf->cf_driver->cd_name) != 0)
		return 0;

	return 1;
}

void
glxclk_attach(struct device *parent, struct device *self, void *aux)
{
	glxclk_sc = (struct glxclk_softc *)self;
	struct glxpcib_attach_args *gaa = aux;
	u_int64_t wa;
	int statint, minint;

	glxclk_sc->sc_iot = gaa->gaa_iot;
	glxclk_sc->sc_ioh = gaa->gaa_ioh;

	wa = rdmsr(MSR_LBAR_MFGPT);

	if ((wa & MSR_LBAR_ENABLE) == 0) {
		printf(" not configured\n");
		return;
	}

	if (bus_space_map(glxclk_sc->sc_iot, wa & MSR_MFGPT_ADDR_MASK,
	    MSR_MFGPT_SIZE, 0, &glxclk_sc->sc_ioh)) {
		printf(" not configured\n");
		return;
	}

	printf(": clock");

	/* Set comparator 2 */
	bus_space_write_2(glxclk_sc->sc_iot, glxclk_sc->sc_ioh,
	    AMD5536_MFGPT1_CMP2, 1);

	/* Reset counter to 0 */
	bus_space_write_2(glxclk_sc->sc_iot, glxclk_sc->sc_ioh,
	    AMD5536_MFGPT1_CNT, 0);

	/*
	 * All the bits in the range 11:0 have to be written at once.
	 * After they're set the first time all further writes are
	 * ignored.
	 */
	uint16_t setup = (AMD5536_MFGPT1_SCALE | AMD5536_MFGPT_CMP2MODE |
	    AMD5536_MFGPT_CMP1 | AMD5536_MFGPT_CMP2 | AMD5536_MFGPT_CNT_EN);

	bus_space_write_2(glxclk_sc->sc_iot, glxclk_sc->sc_ioh,
	    AMD5536_MFGPT1_SETUP, setup);

	/* Check to see if the MFGPT_SETUP bit was set */
	setup = bus_space_read_2(glxclk_sc->sc_iot, glxclk_sc->sc_ioh,
	    AMD5536_MFGPT1_SETUP);
	if ((setup & AMD5536_MFGPT_SETUP) == 0) {
		printf(" not configured\n");
		return;
	}

	/* Enable MFGPT1 Comparator 2 Output to the Interrupt Mapper */
	wa = rdmsr(MFGPT_IRQ);
	wa |= AMD5536_MFGPT1_C2_IRQM;
	wrmsr(MFGPT_IRQ, wa);

	/*
	 * Tie PIC input 5 to IG7 for glxclk(4).
	 */
	wa = rdmsr(PIC_ZSEL_LOW);
	wa &= ~(0xfUL << 20);
	wa |= 7 << 20;
	wrmsr(PIC_ZSEL_LOW, wa);

	/* Start the counter */
	setup = (AMD5536_MFGPT_CNT_EN | AMD5536_MFGPT_CMP2);
	bus_space_write_2(glxclk_sc->sc_iot, glxclk_sc->sc_ioh,
	    AMD5536_MFGPT1_SETUP, setup);

	/*
	 * The interrupt argument is NULL in order to notify the dispatcher
	 * to pass the clock frame as argument. This trick also forces keeping
	 * the soft state global because during the interrupt we need to clear
	 * the comp2 event in the MFGPT setup register.
	 */
	isa_intr_establish(sys_platform->isa_chipset, 7, IST_PULSE, IPL_CLOCK,
	    glxclk_intr, NULL, "clock");

	md_startclock = glxclk_startclock;

	printf(", prof");

	/*
	 * Try to be as close as possible, w/o the variance, to the hardclock.
	 * The stat clock has its source set to the 14MHz clock so that the
	 * variance interval can be more generous.
	 *
	 * Experience shows that the clock source goes crazy on scale factors
	 * lower than 8, so keep it at 8 and adjust the counter (statint) so
	 * that it results a 128Hz statclock, just like the hardclock.
	 */
	statint = 16000;
	minint = statint / 2 + 100;
	while (statvar > minint)
		statvar >>= 1;

	/* Set comparator 2 */
	bus_space_write_2(glxclk_sc->sc_iot, glxclk_sc->sc_ioh,
	    AMD5536_MFGPT2_CMP2, statint);
	statmin = statint - (statvar >> 1);

	/* Reset counter to 0 */
	bus_space_write_2(glxclk_sc->sc_iot, glxclk_sc->sc_ioh,
	    AMD5536_MFGPT2_CNT, 0);

	/*
	 * All the bits in the range 11:0 have to be written at once.
	 * After they're set the first time all further writes are
	 * ignored.
	 */
	setup = (AMD5536_MFGPT2_SCALE | AMD5536_MFGPT_CMP2MODE |
	    AMD5536_MFGPT_CLKSEL | AMD5536_MFGPT_CMP1 | AMD5536_MFGPT_CMP2 |
	    AMD5536_MFGPT_CNT_EN);

	bus_space_write_2(glxclk_sc->sc_iot, glxclk_sc->sc_ioh,
	    AMD5536_MFGPT2_SETUP, setup);

	/* Check to see if the MFGPT_SETUP bit was set */
	setup = bus_space_read_2(glxclk_sc->sc_iot, glxclk_sc->sc_ioh,
	    AMD5536_MFGPT2_SETUP);
	if ((setup & AMD5536_MFGPT_SETUP) == 0) {
		printf(" not configured\n");
		return;
	}

	/* Enable MFGPT2 Comparator 2 Output to the Interrupt Mapper */
	wa = rdmsr(MFGPT_IRQ);
	wa |= AMD5536_MFGPT2_C2_IRQM;
	wrmsr(MFGPT_IRQ, wa);

	/*
	 * Tie PIC input 6 to IG8 for glxstat(4).
	 */
	wa = rdmsr(PIC_ZSEL_LOW);
	wa &= ~(0xfUL << 24);
	wa |= 8 << 24;
	wrmsr(PIC_ZSEL_LOW, wa);

	/*
	 * The interrupt argument is NULL in order to notify the dispatcher
	 * to pass the clock frame as argument. This trick also forces keeping
	 * the soft state global because during the interrupt we need to clear
	 * the comp2 event in the MFGPT setup register.
	 */
	isa_intr_establish(sys_platform->isa_chipset, 8, IST_PULSE,
	    IPL_STATCLOCK, glxclk_stat_intr, NULL, "prof");

	printf("\n");
}

void
glxclk_startclock(struct cpu_info *ci)
{
	/* Start the clock. */
	int s = splclock();
	ci->ci_clock_started++;
	splx(s);
}

int
glxclk_intr(void *arg)
{
	struct clockframe *frame = arg;
	uint16_t setup = 0;
	struct cpu_info *ci = curcpu();

	/* Clear the current event */
	setup = bus_space_read_2(glxclk_sc->sc_iot, glxclk_sc->sc_ioh,
	    AMD5536_MFGPT1_SETUP);
	setup |= AMD5536_MFGPT_CMP2;
	bus_space_write_2(glxclk_sc->sc_iot, glxclk_sc->sc_ioh,
	    AMD5536_MFGPT1_SETUP, setup);

	if (ci->ci_clock_started == 0)
		return 1;

	hardclock(frame);

	return 1;
}

int
glxclk_stat_intr(void *arg)
{
	struct clockframe *frame = arg;
	uint16_t setup = 0;
	struct cpu_info *ci = curcpu();
	u_long newint, r, var;

	/* Clear the current event */
	setup = bus_space_read_2(glxclk_sc->sc_iot, glxclk_sc->sc_ioh,
	    AMD5536_MFGPT2_SETUP);
	setup |= AMD5536_MFGPT_CMP2;
	bus_space_write_2(glxclk_sc->sc_iot, glxclk_sc->sc_ioh,
	    AMD5536_MFGPT2_SETUP, setup);

	if (ci->ci_clock_started == 0)
		return 1;

	statclock(frame);

	/*
	 * Compute new randomized interval.  The intervals are uniformly
	 * distributed on [statint - statvar / 2, statint + statvar / 2],
	 * and therefore have mean statint, giving a stathz frequency clock.
	 */
	var = statvar;
	do {
		r = random() & (var - 1);
	} while (r == 0);
	newint = statmin + r;

	bus_space_write_2(glxclk_sc->sc_iot, glxclk_sc->sc_ioh,
	    AMD5536_MFGPT2_CMP2, newint);

	return 1;
}


code:
/*	$OpenBSD: kb3310.c,v 1.19 2013/12/24 01:11:04 dlg Exp $	*/
/*
 * Copyright (c) 2010 Otto Moerbeek <otto@drijf.net>
 *
 * 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.
 */

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

#include <machine/apmvar.h>
#include <machine/autoconf.h>
#include <machine/bus.h>
#include <dev/isa/isavar.h>

#include <dev/pci/glxreg.h>

#include <loongson/dev/bonitoreg.h>
#include <loongson/dev/kb3310var.h>

#include "apm.h"
#include "pckbd.h"
#include "hidkbd.h"

#if NPCKBD > 0 || NHIDKBD > 0
#include <dev/ic/pckbcvar.h>
#include <dev/pckbc/pckbdvar.h>
#include <dev/usb/hidkbdvar.h>
#endif

struct cfdriver ykbec_cd = {
	NULL, "ykbec", DV_DULL,
};

#ifdef KB3310_DEBUG
#define DPRINTF(x)	printf x
#else
#define DPRINTF(x)
#endif

#define IO_YKBEC		0x381
#define IO_YKBECSIZE		0x3

static const struct {
	const char *desc;
	int type;
} ykbec_table[] = {
#define YKBEC_FAN	0
	{ NULL,				SENSOR_FANRPM },
#define YKBEC_ITEMP	1
	{ "Internal temperature",	SENSOR_TEMP },
#define YKBEC_FCAP	2
	{ "Battery full charge capacity", SENSOR_AMPHOUR },
#define YKBEC_BCURRENT	3
	{ "Battery current", 		SENSOR_AMPS },
#define YKBEC_BVOLT	4
	{ "Battery voltage",		SENSOR_VOLTS_DC },
#define YKBEC_BTEMP	5
	{ "Battery temperature",	SENSOR_TEMP },
#define YKBEC_CAP	6
	{ "Battery capacity", 		SENSOR_PERCENT },
#define YKBEC_CHARGING	7
	{ "Battery charging",		SENSOR_INDICATOR },
#define YKBEC_AC	8
	{ "AC-Power",			SENSOR_INDICATOR }
#define YKBEC_NSENSORS	9
};

struct ykbec_softc {
	struct device		sc_dev;
	bus_space_tag_t		sc_iot;
	bus_space_handle_t	sc_ioh;
	struct ksensor		sc_sensor[YKBEC_NSENSORS];
	struct ksensordev	sc_sensordev;
#if NPCKBD > 0 || NHIDKBD > 0
	struct timeout		sc_bell_tmo;
#endif
};

static struct ykbec_softc *ykbec_sc;
static int ykbec_chip_config;

extern void loongson_set_isa_imr(uint);

int	ykbec_match(struct device *, void *, void *);
void	ykbec_attach(struct device *, struct device *, void *);

const struct cfattach ykbec_ca = {
	sizeof(struct ykbec_softc), ykbec_match, ykbec_attach
};

int	ykbec_apminfo(struct apm_power_info *);
void	ykbec_bell(void *, u_int, u_int, u_int, int);
void	ykbec_bell_stop(void *);
void	ykbec_print_bat_info(struct ykbec_softc *);
u_int	ykbec_read(struct ykbec_softc *, u_int);
u_int	ykbec_read16(struct ykbec_softc *, u_int);
void	ykbec_refresh(void *arg);
void	ykbec_write(struct ykbec_softc *, u_int, u_int);

#if NAPM > 0
struct apm_power_info ykbec_apmdata;
const char *ykbec_batstate[] = {
	"high",
	"low",
	"critical",
	"charging",
	"unknown"
};
#define BATTERY_STRING(x) ((x) < nitems(ykbec_batstate) ? \
	ykbec_batstate[x] : ykbec_batstate[4])
#endif

int
ykbec_match(struct device *parent, void *match, void *aux)
{
	struct isa_attach_args *ia = aux;
	bus_space_handle_t ioh;

	/* XXX maybe allow LOONGSON_EBT700 ??? */
	if (sys_platform->system_type != LOONGSON_YEELOONG)
		return (0);

	if ((ia->ia_iobase != IOBASEUNK && ia->ia_iobase != IO_YKBEC) ||
	    /* (ia->ia_iosize != 0 && ia->ia_iosize != IO_YKBECSIZE) || XXX isa.c */
	    ia->ia_maddr != MADDRUNK || ia->ia_msize != 0 ||
	    ia->ia_irq != IRQUNK || ia->ia_drq != DRQUNK)
		return (0);

	if (bus_space_map(ia->ia_iot, IO_YKBEC, IO_YKBECSIZE, 0, &ioh))
		return (0);

	bus_space_unmap(ia->ia_iot, ioh, IO_YKBECSIZE);

	ia->ia_iobase = IO_YKBEC;
	ia->ia_iosize = IO_YKBECSIZE;

	return (1);
}

void
ykbec_attach(struct device *parent, struct device *self, void *aux)
{
	struct isa_attach_args *ia = aux;
	struct ykbec_softc *sc = (struct ykbec_softc *)self;
	int i;

	sc->sc_iot = ia->ia_iot;
	if (bus_space_map(sc->sc_iot, ia->ia_iobase, ia->ia_iosize, 0,
	    &sc->sc_ioh)) {
		printf(": couldn't map I/O space");
		return;
	}

	/* Initialize sensor data. */
	strlcpy(sc->sc_sensordev.xname, sc->sc_dev.dv_xname,
	    sizeof(sc->sc_sensordev.xname));
	if (sensor_task_register(sc, ykbec_refresh, 5) == NULL) {
		printf(", unable to register update task\n");
		return;
	}

#ifdef DEBUG
	ykbec_print_bat_info(sc);
#endif
	printf("\n");

	for (i = 0; i < YKBEC_NSENSORS; i++) {
		sc->sc_sensor[i].type = ykbec_table[i].type;
		if (ykbec_table[i].desc)
			strlcpy(sc->sc_sensor[i].desc, ykbec_table[i].desc,
			    sizeof(sc->sc_sensor[i].desc));
		sensor_attach(&sc->sc_sensordev, &sc->sc_sensor[i]);
	}

	sensordev_install(&sc->sc_sensordev);

#if NAPM > 0
	/* make sure we have the apm state initialized before apm attaches */
	ykbec_refresh(sc);
	apm_setinfohook(ykbec_apminfo);
#endif
#if NPCKBD > 0 || NHIDKBD > 0
	timeout_set(&sc->sc_bell_tmo, ykbec_bell_stop, sc);
#if NPCKBD > 0
	pckbd_hookup_bell(ykbec_bell, sc);
#endif
#if NHIDKBD > 0
	hidkbd_hookup_bell(ykbec_bell, sc);
#endif
#endif
	ykbec_sc = sc;
}

void
ykbec_write(struct ykbec_softc *mcsc, u_int reg, u_int datum)
{
	struct ykbec_softc *sc = (struct ykbec_softc *)mcsc;
	bus_space_tag_t iot = sc->sc_iot;
	bus_space_handle_t ioh = sc->sc_ioh;

	bus_space_write_1(iot, ioh, 0, (reg >> 8) & 0xff);
	bus_space_write_1(iot, ioh, 1, (reg >> 0) & 0xff);
	bus_space_write_1(iot, ioh, 2, datum);
}

u_int
ykbec_read(struct ykbec_softc *mcsc, u_int reg)
{
	struct ykbec_softc *sc = (struct ykbec_softc *)mcsc;
	bus_space_tag_t iot = sc->sc_iot;
	bus_space_handle_t ioh = sc->sc_ioh;

	bus_space_write_1(iot, ioh, 0, (reg >> 8) & 0xff);
	bus_space_write_1(iot, ioh, 1, (reg >> 0) & 0xff);
	return bus_space_read_1(iot, ioh, 2);
}

u_int
ykbec_read16(struct ykbec_softc *mcsc, u_int reg)
{
	u_int val;

	val = ykbec_read(mcsc, reg);
	return (val << 8) | ykbec_read(mcsc, reg + 1);
}

#define KB3310_FAN_SPEED_DIVIDER	480000

#define ECTEMP_CURRENT_REG		0xf458
#define REG_FAN_SPEED_HIGH		0xfe22
#define REG_FAN_SPEED_LOW		0xfe23

#define REG_DESIGN_CAP_HIGH		0xf77d
#define REG_DESIGN_CAP_LOW		0xf77e
#define REG_FULLCHG_CAP_HIGH		0xf780
#define REG_FULLCHG_CAP_LOW		0xf781

#define REG_DESIGN_VOL_HIGH		0xf782
#define REG_DESIGN_VOL_LOW		0xf783
#define REG_CURRENT_HIGH		0xf784
#define REG_CURRENT_LOW			0xf785
#define REG_VOLTAGE_HIGH		0xf786
#define REG_VOLTAGE_LOW			0xf787
#define REG_TEMPERATURE_HIGH		0xf788
#define REG_TEMPERATURE_LOW		0xf789
#define REG_RELATIVE_CAT_HIGH		0xf492
#define REG_RELATIVE_CAT_LOW		0xf493
#define REG_BAT_VENDOR			0xf4c4
#define REG_BAT_CELL_COUNT		0xf4c6

#define REG_BAT_CHARGE			0xf4a2
#define BAT_CHARGE_AC			0x00
#define BAT_CHARGE_DISCHARGE		0x01
#define BAT_CHARGE_CHARGE		0x02

#define REG_POWER_FLAG			0xf440
#define POWER_FLAG_ADAPTER_IN		(1<<0)
#define POWER_FLAG_POWER_ON		(1<<1)
#define POWER_FLAG_ENTER_SUS		(1<<2)

#define REG_BAT_STATUS			0xf4b0
#define BAT_STATUS_BAT_EXISTS		(1<<0)
#define BAT_STATUS_BAT_FULL		(1<<1)
#define BAT_STATUS_BAT_DESTROY		(1<<2)
#define BAT_STATUS_BAT_LOW		(1<<5)

#define REG_CHARGE_STATUS		0xf4b1
#define CHARGE_STATUS_PRECHARGE		(1<<1)
#define CHARGE_STATUS_OVERHEAT		(1<<2)

#define REG_BAT_STATE			0xf482
#define BAT_STATE_DISCHARGING		(1<<0)
#define BAT_STATE_CHARGING		(1<<1)

#define	REG_BEEP_CONTROL		0xf4d0
#define	BEEP_ENABLE			(1<<0)

#define REG_PMUCFG			0xff0c
#define PMUCFG_STOP_MODE		(1<<7)
#define PMUCFG_IDLE_MODE		(1<<6)
#define PMUCFG_LPC_WAKEUP		(1<<5)
#define PMUCFG_RESET_8051		(1<<4)
#define PMUCFG_SCI_WAKEUP		(1<<3)
#define PMUCFG_WDT_WAKEUP		(1<<2)
#define PMUCFG_GPWU_WAKEUP		(1<<1)
#define PMUCFG_IRQ_IDLE			(1<<0)

#define REG_USB0			0xf461
#define REG_USB1			0xf462
#define REG_USB2			0xf463
#define USB_FLAG_ON			1
#define USB_FLAG_OFF			0

#define REG_FAN_CONTROL			0xf4d2
#define	REG_FAN_ON			1
#define REG_FAN_OFF			0

#define YKBEC_SCI_IRQ			0xa

#ifdef DEBUG
void
ykbec_print_bat_info(struct ykbec_softc *sc)
{
	uint bat_status, count, dvolt, dcap;

	printf(": battery ");
	bat_status = ykbec_read(sc, REG_BAT_STATUS);
	if (!ISSET(bat_status, BAT_STATUS_BAT_EXISTS)) {
		printf("absent");
		return;
	}

	count = ykbec_read(sc, REG_BAT_CELL_COUNT);
	dvolt = ykbec_read16(sc, REG_DESIGN_VOL_HIGH);
	dcap = ykbec_read16(sc, REG_DESIGN_CAP_HIGH);
	printf("%d cells, design capacity %dmV %dmAh", count, dvolt, dcap);
}
#endif

void
ykbec_refresh(void *arg)
{
	struct ykbec_softc *sc = (struct ykbec_softc *)arg;
	u_int val, bat_charge, bat_status, charge_status, bat_state, power_flag;
	u_int cap_pct, fullcap;
	int current;
#if NAPM > 0
	struct apm_power_info old;
#endif

	val = ykbec_read16(sc, REG_FAN_SPEED_HIGH) & 0xfffff;
	if (val != 0) {
		val = KB3310_FAN_SPEED_DIVIDER / val;
		sc->sc_sensor[YKBEC_FAN].value = val;
		CLR(sc->sc_sensor[YKBEC_FAN].flags, SENSOR_FINVALID);
	} else
		SET(sc->sc_sensor[YKBEC_FAN].flags, SENSOR_FINVALID);

	val = ykbec_read(sc, ECTEMP_CURRENT_REG);
	sc->sc_sensor[YKBEC_ITEMP].value = val * 1000000 + 273150000;

	fullcap = ykbec_read16(sc, REG_FULLCHG_CAP_HIGH);
	sc->sc_sensor[YKBEC_FCAP].value = fullcap * 1000;

	current = ykbec_read16(sc, REG_CURRENT_HIGH);
	/* sign extend short -> int, int -> int64 will be done next statement */
	current |= -(current & 0x8000);
	sc->sc_sensor[YKBEC_BCURRENT].value = -1000 * current;

	sc->sc_sensor[YKBEC_BVOLT].value = ykbec_read16(sc, REG_VOLTAGE_HIGH) *
	    1000;

	val = ykbec_read16(sc, REG_TEMPERATURE_HIGH);
	sc->sc_sensor[YKBEC_BTEMP].value = val * 1000000 + 273150000;

	cap_pct = ykbec_read16(sc, REG_RELATIVE_CAT_HIGH);
	sc->sc_sensor[YKBEC_CAP].value = cap_pct * 1000;

	bat_charge = ykbec_read(sc, REG_BAT_CHARGE);
	bat_status = ykbec_read(sc, REG_BAT_STATUS);
	charge_status = ykbec_read(sc, REG_CHARGE_STATUS);
	bat_state = ykbec_read(sc, REG_BAT_STATE);
	power_flag = ykbec_read(sc, REG_POWER_FLAG);

	sc->sc_sensor[YKBEC_CHARGING].value = !!ISSET(bat_state,
	    BAT_STATE_CHARGING);
	sc->sc_sensor[YKBEC_AC].value = !!ISSET(power_flag,
	    POWER_FLAG_ADAPTER_IN);

	sc->sc_sensor[YKBEC_CAP].status = ISSET(bat_status, BAT_STATUS_BAT_LOW) ?
		SENSOR_S_CRIT : SENSOR_S_OK;

#if NAPM > 0
	bcopy(&ykbec_apmdata, &old, sizeof(old));
	ykbec_apmdata.battery_life = cap_pct;
	ykbec_apmdata.ac_state = ISSET(power_flag, POWER_FLAG_ADAPTER_IN) ?
	    APM_AC_ON : APM_AC_OFF;
	if (!ISSET(bat_status, BAT_STATUS_BAT_EXISTS)) {
		ykbec_apmdata.battery_state = APM_BATTERY_ABSENT;
		ykbec_apmdata.minutes_left = 0;
		ykbec_apmdata.battery_life = 0;
	} else {
		if (ISSET(bat_state, BAT_STATE_CHARGING))
			ykbec_apmdata.battery_state = APM_BATT_CHARGING;
		else if (ISSET(bat_status, BAT_STATUS_BAT_LOW))
			ykbec_apmdata.battery_state = APM_BATT_CRITICAL;
		/* XXX arbitrary */
		else if (cap_pct > 60)
			ykbec_apmdata.battery_state = APM_BATT_HIGH;
		else
			ykbec_apmdata.battery_state = APM_BATT_LOW;

		/* if charging, current is positive */
		if (ISSET(bat_state, BAT_STATE_CHARGING))
			current = 0;
		else
			current = -current;
		/* XXX Yeeloong draw is about 1A */
		if (current <= 0)
			current = 1000;
		/* XXX at 5?%, the Yeeloong shuts down */
		if (cap_pct <= 5)
			cap_pct = 0;
		else
			cap_pct -= 5;
		fullcap = cap_pct * 60 * fullcap / 100;
		ykbec_apmdata.minutes_left = fullcap / current;

	}
	if (old.ac_state != ykbec_apmdata.ac_state)
		apm_record_event(APM_POWER_CHANGE, "AC power",
			ykbec_apmdata.ac_state ? "restored" : "lost");
	if (old.battery_state != ykbec_apmdata.battery_state)
		apm_record_event(APM_POWER_CHANGE, "battery",
		    BATTERY_STRING(ykbec_apmdata.battery_state));
#endif
}

#if NAPM > 0
int
ykbec_apminfo(struct apm_power_info *info)
{
	 bcopy(&ykbec_apmdata, info, sizeof(struct apm_power_info));
	 return 0;
}

int
ykbec_suspend()
{
	struct ykbec_softc *sc = ykbec_sc;
	int ctrl;

	/*
	 * Set up wakeup sources: currently only the internal keyboard.
	 */
	loongson_set_isa_imr(1 << 1);

	/* USB */
	DPRINTF(("USB\n"));
	ykbec_write(sc, REG_USB0, USB_FLAG_OFF);
	ykbec_write(sc, REG_USB1, USB_FLAG_OFF);
	ykbec_write(sc, REG_USB2, USB_FLAG_OFF);

	/* EC */
	DPRINTF(("REG_PMUCFG\n"));
	ctrl = PMUCFG_SCI_WAKEUP | PMUCFG_WDT_WAKEUP | PMUCFG_GPWU_WAKEUP |
	    PMUCFG_LPC_WAKEUP | PMUCFG_STOP_MODE | PMUCFG_RESET_8051;
	ykbec_write(sc, REG_PMUCFG, ctrl);

	/* FAN */
	DPRINTF(("FAN\n"));
	ykbec_write(sc, REG_FAN_CONTROL, REG_FAN_OFF);

	/* CPU */
	DPRINTF(("CPU\n"));
	ykbec_chip_config = REGVAL(LOONGSON_CHIP_CONFIG0);
	enableintr();
	REGVAL(LOONGSON_CHIP_CONFIG0) = ykbec_chip_config & ~0x7;
	(void)REGVAL(LOONGSON_CHIP_CONFIG0);

	/*
	 * When a resume interrupt fires, we will enter the interrupt
	 * dispatcher, which will do nothing because we are at splhigh,
	 * and execution flow will return here and continue.
	 */
	(void)disableintr();

	return 0;
}

int
ykbec_resume()
{
	struct ykbec_softc *sc = ykbec_sc;

	/* CPU */
	DPRINTF(("CPU\n"));
	REGVAL(LOONGSON_CHIP_CONFIG0) = ykbec_chip_config;
	(void)REGVAL(LOONGSON_CHIP_CONFIG0);

	/* FAN */
	DPRINTF(("FAN\n"));
	ykbec_write(sc, REG_FAN_CONTROL, REG_FAN_ON);

	/* USB */
	DPRINTF(("USB\n"));
	ykbec_write(sc, REG_USB0, USB_FLAG_ON);
	ykbec_write(sc, REG_USB1, USB_FLAG_ON);
	ykbec_write(sc, REG_USB2, USB_FLAG_ON);

	ykbec_refresh(sc);

	return 0;
}
#endif

#if NPCKBD > 0 || NHIDKBD > 0
void
ykbec_bell(void *arg, u_int pitch, u_int period, u_int volume, int poll)
{
	struct ykbec_softc *sc = (struct ykbec_softc *)arg;
	int bctrl;
	int s;

	s = spltty();
	bctrl = ykbec_read(sc, REG_BEEP_CONTROL);
	if (timeout_del(&sc->sc_bell_tmo) || volume == 0) {
		/* inline ykbec_bell_stop(arg); */
		ykbec_write(sc, REG_BEEP_CONTROL, bctrl & ~BEEP_ENABLE);
	}

	if (volume != 0) {
		ykbec_write(sc, REG_BEEP_CONTROL, bctrl | BEEP_ENABLE);
		if (poll) {
			delay(period * 1000);
			ykbec_write(sc, REG_BEEP_CONTROL, bctrl & ~BEEP_ENABLE);
		} else {
			timeout_add_msec(&sc->sc_bell_tmo, period);
		}
	}
	splx(s);
}

void
ykbec_bell_stop(void *arg)
{
	struct ykbec_softc *sc = (struct ykbec_softc *)arg;
	int s;

	s = spltty();
	ykbec_write(sc, REG_BEEP_CONTROL,
	    ykbec_read(sc, REG_BEEP_CONTROL) & ~BEEP_ENABLE);
	splx(s);
}
#endif


code:
/*	$OpenBSD: kb3310var.h,v 1.1 2010/08/31 10:24:46 pirofti Exp $	*/
/*
 * Copyright (c) 2009 Paul Irofti <pirofti@openbsd.org>
 *
 * Permission to use, copy, modify, and/or 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 _LOONGSON_DEV_YKBECVAR_H_
#define _LOONGSON_DEV_YKBECVAR_H_

int ykbec_suspend(void);
int ykbec_resume(void);

#endif


code:
/*	$OpenBSD: m41t8xclock.c,v 1.3 2014/01/06 21:38:46 pirofti Exp $	*/

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

/*
 * M41T8x clock connected to an I2C bus
 */

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

#include <dev/i2c/i2cvar.h>

#include <dev/ic/m41t8xreg.h>

#include <mips64/dev/clockvar.h>

struct m41t8xclock_softc {
	struct device	sc_dev;
	i2c_tag_t	sc_tag;
	i2c_addr_t	sc_addr;
};

int	m41t8xclock_match(struct device *, void *, void *);
void	m41t8xclock_attach(struct device *, struct device *, void *);

const struct cfattach mfokclock_ca = {
	sizeof(struct m41t8xclock_softc),
	m41t8xclock_match, m41t8xclock_attach
};

struct cfdriver mfokclock_cd = {
	NULL, "mfokclock", DV_DULL
};

void	m41t8xclock_get(void *, time_t, struct tod_time *);
void	m41t8xclock_set(void *, struct tod_time *);

int
m41t8xclock_match(struct device *parent, void *vcf, void *aux)
{
	struct i2c_attach_args *ia = (struct i2c_attach_args *)aux;
	struct cfdata *cf = (struct cfdata *)vcf;

	return strcmp(ia->ia_name, cf->cf_driver->cd_name) == 0;
}

void
m41t8xclock_attach(struct device *parent, struct device *self, void *aux)
{
	struct m41t8xclock_softc *sc = (struct m41t8xclock_softc *)self;
	struct i2c_attach_args *ia = (struct i2c_attach_args *)aux;

	sc->sc_tag = ia->ia_tag;
	sc->sc_addr = ia->ia_addr;

	sys_tod.tod_cookie = sc;
	sys_tod.tod_get = m41t8xclock_get;
	sys_tod.tod_set = m41t8xclock_set;

	printf("\n");
}

void
m41t8xclock_get(void *cookie, time_t unused, struct tod_time *tt)
{
	struct m41t8xclock_softc *sc = (struct m41t8xclock_softc *)cookie;
	uint8_t regno, data[M41T8X_TOD_LENGTH];
	int s;

	iic_acquire_bus(sc->sc_tag, 0);
	s = splclock();
	for (regno = M41T8X_TOD_START;
	    regno < M41T8X_TOD_START + M41T8X_TOD_LENGTH; regno++)
		iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP, sc->sc_addr,
		    &regno, sizeof regno, data + regno - M41T8X_TOD_START,
		    sizeof data[0], 0);
	splx(s);
	iic_release_bus(sc->sc_tag, 0);

	tt->sec = FROMBCD(data[M41T8X_SEC] & ~M41T8X_STOP);
	tt->min = FROMBCD(data[M41T8X_MIN]);
	tt->hour = FROMBCD(data[M41T8X_HR] & ~(M41T8X_CEB | M41T8X_CB));
	tt->dow = data[M41T8X_DOW];
	tt->day = FROMBCD(data[M41T8X_DAY]);
	tt->mon = FROMBCD(data[M41T8X_MON]);
	tt->year = FROMBCD(data[M41T8X_YEAR]) + 100;
	if (data[M41T8X_HR] & M41T8X_CB)
		tt->year += 100;
}

void
m41t8xclock_set(void *cookie, struct tod_time *tt)
{
	struct m41t8xclock_softc *sc = (struct m41t8xclock_softc *)cookie;
	uint8_t regno, data[M41T8X_TOD_LENGTH];
	int s;

	iic_acquire_bus(sc->sc_tag, 0);
	s = splclock();
	/* read current state */
	for (regno = M41T8X_TOD_START;
	    regno < M41T8X_TOD_START + M41T8X_TOD_LENGTH; regno++)
		iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP, sc->sc_addr,
		    &regno, sizeof regno, data + regno - M41T8X_TOD_START,
		    sizeof data[0], 0);
	/* compute new state */
	data[M41T8X_HSEC] = 0;
	data[M41T8X_SEC] = TOBCD(tt->sec);
	data[M41T8X_MIN] = TOBCD(tt->min);
	data[M41T8X_HR] &= M41T8X_CEB;
	if (tt->year >= 200)
		data[M41T8X_HR] |= M41T8X_CB;
	data[M41T8X_HR] |= TOBCD(tt->hour);
	data[M41T8X_DAY] = TOBCD(tt->day);
	data[M41T8X_MON] = TOBCD(tt->mon);
	if (tt->year >= 200)
		data[M41T8X_YEAR] = TOBCD(tt->year - 200);
	else
		data[M41T8X_YEAR] = TOBCD(tt->year - 100);
	/* write new state */
	for (regno = M41T8X_TOD_START;
	    regno < M41T8X_TOD_START + M41T8X_TOD_LENGTH; regno++)
		iic_exec(sc->sc_tag, I2C_OP_WRITE_WITH_STOP, sc->sc_addr,
		    &regno, sizeof regno, data + regno, sizeof data[0], 0);
	splx(s);
	iic_release_bus(sc->sc_tag, 0);
}


code:
/*	$OpenBSD: mainbus.c,v 1.8 2013/05/30 16:15:01 deraadt Exp $ */

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

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

#include <machine/autoconf.h>

int	mainbus_match(struct device *, void *, void *);
void	mainbus_attach(struct device *, struct device *, void *);
int	mainbus_print(void *, const char *);

const struct cfattach mainbus_ca = {
	sizeof(struct device), mainbus_match, mainbus_attach
};

struct cfdriver mainbus_cd = {
	NULL, "mainbus", DV_DULL
};

int
mainbus_match(struct device *parent, void *cfdata, void *aux)
{
	static int mainbus_attached = 0;

	if (mainbus_attached != 0)
		return 0;

	return mainbus_attached = 1;
}

void
mainbus_attach(struct device *parent, struct device *self, void *aux)
{
	struct cpu_attach_args caa;

	printf(": %s %s\n", sys_platform->vendor, sys_platform->product);

	bzero(&caa, sizeof caa);
	caa.caa_maa.maa_name = "cpu";
	caa.caa_hw = &bootcpu_hwinfo;
	config_found(self, &caa, mainbus_print);

	caa.caa_maa.maa_name = "bonito";
	config_found(self, &caa.caa_maa, mainbus_print);

	if (md_startclock == NULL) {
		caa.caa_maa.maa_name = "clock";
		config_found(self, &caa.caa_maa, mainbus_print);
	}

	caa.caa_maa.maa_name = "apm";
	config_found(self, &caa.caa_maa, mainbus_print);
}

int
mainbus_print(void *aux, const char *pnp)
{
	return pnp != NULL ? QUIET : UNCONF;
}


code:
/*	$OpenBSD: mcclock.c,v 1.3 2010/01/22 21:44:00 miod Exp $	*/
/*	$NetBSD: mcclock.c,v 1.4 1996/10/13 02:59:41 christos Exp $	*/

/*
 * Copyright (c) 1994, 1995, 1996 Carnegie-Mellon University.
 * All rights reserved.
 *
 * Author: Chris G. Demetriou
 *
 * Permission to use, copy, modify and distribute this software and
 * its documentation is hereby granted, provided that both the copyright
 * notice and this permission notice appear in all copies of the
 * software, derivative works or modified versions, and any portions
 * thereof, and that both notices appear in supporting documentation.
 *
 * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
 * CONDITION.  CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND
 * FOR ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
 *
 * Carnegie Mellon requests users of this software to return to
 *
 *  Software Distribution Coordinator  or  Software.Distribution@CS.CMU.EDU
 *  School of Computer Science
 *  Carnegie Mellon University
 *  Pittsburgh PA 15213-3890
 *
 * any improvements or extensions that they make and grant Carnegie the
 * rights to redistribute these changes.
 */

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

#include <mips64/dev/clockvar.h>
#include <loongson/dev/mcclockvar.h>
#include <dev/ic/mc146818reg.h>

struct cfdriver mcclock_cd = {
	NULL, "mcclock", DV_DULL,
};

void	mcclock_get(void *, time_t, struct tod_time *);
void	mcclock_set(void *, struct tod_time *);

struct tod_desc mcclock_clockfns = {
	NULL, mcclock_get, mcclock_set,
};

#define	mc146818_write(dev, reg, datum)					\
	    (*(dev)->sc_busfns->mc_bf_write)(dev, reg, datum)
#define	mc146818_read(dev, reg)						\
	    (*(dev)->sc_busfns->mc_bf_read)(dev, reg)

void
mcclock_attach(sc, busfns)
	struct mcclock_softc *sc;
	const struct mcclock_busfns *busfns;
{

	printf(": mc146818 or compatible\n");

	sc->sc_busfns = busfns;

	/* Turn interrupts off, just in case. */
	mc146818_write(sc, MC_REGB, MC_REGB_BINARY | MC_REGB_24HR);
	mc146818_write(sc, MC_REGA, MC_BASE_32_KHz | MC_RATE_NONE);

	sys_tod.tod_cookie = sc;
	sys_tod.tod_get = mcclock_get;
	sys_tod.tod_set = mcclock_set;
}

/*
 * Get the time of day, based on the clock's value and/or the base value.
 */
void
mcclock_get(dev, base, ct)
	void *dev;
	time_t base;
	struct tod_time *ct;
{
	struct mcclock_softc *sc = (struct mcclock_softc *)dev;
	mc_todregs regs;
	int s;

	s = splclock();
	MC146818_GETTOD(sc, &regs)
	splx(s);

	ct->sec = regs[MC_SEC];
	ct->min = regs[MC_MIN];
	ct->hour = regs[MC_HOUR];
	ct->dow = regs[MC_DOW];
	ct->day = regs[MC_DOM];
	ct->mon = regs[MC_MONTH];
	ct->year = regs[MC_YEAR] + 100;
}

/*
 * Reset the TODR based on the time value.
 */
void
mcclock_set(dev, ct)
	void *dev;
	struct tod_time *ct;
{
	struct mcclock_softc *sc = (struct mcclock_softc *)dev;
	mc_todregs regs;
	int s;

	s = splclock();
	MC146818_GETTOD(sc, &regs);
	splx(s);

	regs[MC_SEC] = ct->sec;
	regs[MC_MIN] = ct->min;
	regs[MC_HOUR] = ct->hour;
	regs[MC_DOW] = ct->dow;
	regs[MC_DOM] = ct->day;
	regs[MC_MONTH] = ct->mon;
	regs[MC_YEAR] = ct->year - 100;

	s = splclock();
	MC146818_PUTTOD(sc, &regs);
	splx(s);
}


code:
/*	$OpenBSD: mcclock_isa.c,v 1.1.1.1 2009/12/25 21:04:32 miod Exp $	*/
/*	$NetBSD: mcclock_isa.c,v 1.5 1996/12/05 01:39:29 cgd Exp $	*/

/*
 * Copyright (c) 1995, 1996 Carnegie-Mellon University.
 * All rights reserved.
 *
 * Author: Chris G. Demetriou
 *
 * Permission to use, copy, modify and distribute this software and
 * its documentation is hereby granted, provided that both the copyright
 * notice and this permission notice appear in all copies of the
 * software, derivative works or modified versions, and any portions
 * thereof, and that both notices appear in supporting documentation.
 *
 * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
 * CONDITION.  CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND
 * FOR ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
 *
 * Carnegie Mellon requests users of this software to return to
 *
 *  Software Distribution Coordinator  or  Software.Distribution@CS.CMU.EDU
 *  School of Computer Science
 *  Carnegie Mellon University
 *  Pittsburgh PA 15213-3890
 *
 * any improvements or extensions that they make and grant Carnegie the
 * rights to redistribute these changes.
 */

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

#include <machine/bus.h>

#include <mips64/dev/clockvar.h>
#include <loongson/dev/mcclockvar.h>
#include <dev/ic/mc146818reg.h>
#include <dev/isa/isavar.h>

struct mcclock_isa_softc {
	struct mcclock_softc	sc_mcclock;

	bus_space_tag_t		sc_iot;
	bus_space_handle_t	sc_ioh;
};

int	mcclock_isa_match(struct device *, void *, void *);
void	mcclock_isa_attach(struct device *, struct device *, void *);

struct cfattach mcclock_isa_ca = {
	sizeof (struct mcclock_isa_softc), mcclock_isa_match,
	    mcclock_isa_attach,
};

void	mcclock_isa_write(struct mcclock_softc *, u_int, u_int);
u_int	mcclock_isa_read(struct mcclock_softc *, u_int);

const struct mcclock_busfns mcclock_isa_busfns = {
	mcclock_isa_write, mcclock_isa_read,
};

int
mcclock_isa_match(parent, match, aux)
	struct device *parent;
	void *match;
	void *aux;
{
	struct isa_attach_args *ia = aux;
	bus_space_handle_t ioh;

        if ((ia->ia_iobase != IOBASEUNK && ia->ia_iobase != 0x70) ||
            /* (ia->ia_iosize != 0 && ia->ia_iosize != 0x2) || XXX isa.c */
            ia->ia_maddr != MADDRUNK || ia->ia_msize != 0 ||
            ia->ia_irq != IRQUNK || ia->ia_drq != DRQUNK)
                return (0);

	if (bus_space_map(ia->ia_iot, 0x70, 0x2, 0, &ioh))
		return (0);

	bus_space_unmap(ia->ia_iot, ioh, 0x2);

	ia->ia_iobase = 0x70;
	ia->ia_iosize = 0x2;

	return (1);
}

void
mcclock_isa_attach(parent, self, aux)
	struct device *parent, *self;
	void *aux;
{
	struct isa_attach_args *ia = aux;
	struct mcclock_isa_softc *sc = (struct mcclock_isa_softc *)self;

	sc->sc_iot = ia->ia_iot;
	if (bus_space_map(sc->sc_iot, ia->ia_iobase, ia->ia_iosize, 0,
	    &sc->sc_ioh))
		panic("mcclock_isa_attach: couldn't map clock I/O space");

	mcclock_attach(&sc->sc_mcclock, &mcclock_isa_busfns);
}

void
mcclock_isa_write(mcsc, reg, datum)
	struct mcclock_softc *mcsc;
	u_int reg, datum;
{
	struct mcclock_isa_softc *sc = (struct mcclock_isa_softc *)mcsc;
	bus_space_tag_t iot = sc->sc_iot;
	bus_space_handle_t ioh = sc->sc_ioh;

	bus_space_write_1(iot, ioh, 0, reg);
	bus_space_write_1(iot, ioh, 1, datum);
}

u_int
mcclock_isa_read(mcsc, reg)
	struct mcclock_softc *mcsc;
	u_int reg;
{
	struct mcclock_isa_softc *sc = (struct mcclock_isa_softc *)mcsc;
	bus_space_tag_t iot = sc->sc_iot;
	bus_space_handle_t ioh = sc->sc_ioh;

	bus_space_write_1(iot, ioh, 0, reg);
	return bus_space_read_1(iot, ioh, 1);
}


code:
/*	$OpenBSD: mcclockvar.h,v 1.1.1.1 2009/11/26 12:14:01 miod Exp $	*/
/*	$NetBSD: mcclockvar.h,v 1.2 1996/04/17 22:22:38 cgd Exp $	*/

/*
 * Copyright (c) 1996 Carnegie-Mellon University.
 * All rights reserved.
 *
 * Author: Chris G. Demetriou
 *
 * Permission to use, copy, modify and distribute this software and
 * its documentation is hereby granted, provided that both the copyright
 * notice and this permission notice appear in all copies of the
 * software, derivative works or modified versions, and any portions
 * thereof, and that both notices appear in supporting documentation.
 *
 * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
 * CONDITION.  CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND
 * FOR ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
 *
 * Carnegie Mellon requests users of this software to return to
 *
 *  Software Distribution Coordinator  or  Software.Distribution@CS.CMU.EDU
 *  School of Computer Science
 *  Carnegie Mellon University
 *  Pittsburgh PA 15213-3890
 *
 * any improvements or extensions that they make and grant Carnegie the
 * rights to redistribute these changes.
 */

struct mcclock_softc {
	struct device sc_dev;
	const struct mcclock_busfns *sc_busfns;
};

struct mcclock_busfns {
	void    (*mc_bf_write)(struct mcclock_softc *, u_int, u_int);
	u_int   (*mc_bf_read)(struct mcclock_softc *, u_int);
};

void	mcclock_attach(struct mcclock_softc *,
	    const struct mcclock_busfns *);


code:
/*	$OpenBSD: ohci_voyager.c,v 1.6 2014/05/19 13:11:31 mpi Exp $	*/
/*	OpenBSD: ohci_pci.c,v 1.33 2008/06/26 05:42:17 ray Exp 	*/
/*	$NetBSD: ohci_pci.c,v 1.23 2002/10/02 16:51:47 thorpej Exp $	*/

/*
 * Copyright (c) 1998 The NetBSD Foundation, Inc.
 * All rights reserved.
 *
 * This code is derived from software contributed to The NetBSD Foundation
 * by Lennart Augustsson (lennart@augustsson.net) at
 * Carlstedt Research & Technology.
 *
 * 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.
 */

/*
 * USB Open Host Controller driver.
 *
 * OHCI spec: http://www.intel.com/design/usb/ohci11d.pdf
 * USB spec: http://www.teleport.com/cgi-bin/mailmerge.cgi/~usb/cgiform.tpl
 */

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

#include <machine/bus.h>

#include <dev/pci/pcivar.h>

#include <loongson/dev/voyagerreg.h>
#include <loongson/dev/voyagervar.h>

#include <dev/usb/usb.h>
#include <dev/usb/usbdi.h>
#include <dev/usb/usbdivar.h>
#include <dev/usb/usb_mem.h>

#include <dev/usb/ohcireg.h>
#include <dev/usb/ohcivar.h>

extern int gdium_revision;

int	ohci_voyager_match(struct device *, void *, void *);
void	ohci_voyager_attach(struct device *, struct device *, void *);
void	ohci_voyager_attach_deferred(struct device *);

struct ohci_voyager_softc {
	struct ohci_softc	sc;
	void 			*sc_ih;
};

struct cfattach ohci_voyager_ca = {
	sizeof(struct ohci_voyager_softc),
	ohci_voyager_match, ohci_voyager_attach, NULL, ohci_activate
};

int
ohci_voyager_match(struct device *parent, void *vcf, void *aux)
{
	struct voyager_attach_args *vaa = (struct voyager_attach_args *)aux;
	struct cfdata *cf = (struct cfdata *)vcf;

	return gdium_revision == 0 &&
	    strcmp(vaa->vaa_name, cf->cf_driver->cd_name) == 0;
}

void
ohci_voyager_attach(struct device *parent, struct device *self, void *aux)
{
	struct ohci_voyager_softc *sc = (struct ohci_voyager_softc *)self;
	struct voyager_attach_args *vaa = (struct voyager_attach_args *)aux;
	struct pci_attach_args *pa = vaa->vaa_pa;
	int s;
	const char *vendor;
	char *devname = sc->sc.sc_bus.bdev.dv_xname;

	/* Map I/O registers */
	sc->sc.sc_size = VOYAGER_OHCI_SIZE;
	sc->sc.iot = vaa->vaa_mmiot;
	if (bus_space_subregion(vaa->vaa_mmiot, vaa->vaa_mmioh,
	    VOYAGER_OHCI_BASE, VOYAGER_OHCI_SIZE, &sc->sc.ioh) != 0) {
		printf(": can't map mem space\n");
		return;
	}

	/* Record what interrupts were enabled by SMM/BIOS. */
	sc->sc.sc_intre = bus_space_read_4(sc->sc.iot, sc->sc.ioh,
	    OHCI_INTERRUPT_ENABLE);

	/* Disable interrupts, so we don't get any spurious ones. */
	bus_space_write_4(sc->sc.iot, sc->sc.ioh, OHCI_INTERRUPT_DISABLE,
	    OHCI_MIE);

	sc->sc.sc_bus.dmatag = pa->pa_dmat;

	bus_space_barrier(sc->sc.iot, sc->sc.ioh, 0, sc->sc.sc_size,
	    BUS_SPACE_BARRIER_READ|BUS_SPACE_BARRIER_WRITE);
	bus_space_write_4(sc->sc.iot, sc->sc.ioh,
	    OHCI_INTERRUPT_DISABLE, OHCI_MIE);

	s = splusb();
	/* establish the interrupt. */
	sc->sc_ih = voyager_intr_establish(parent, VOYAGER_INTR_USB_HOST,
	    IPL_USB, ohci_intr, sc, devname);
	if (sc->sc_ih == NULL) {
		printf(": couldn't establish interrupt\n");
		splx(s);
		return;
	}
	printf(": %s", voyager_intr_string(sc->sc_ih));

	/* Figure out vendor for root hub descriptor. */
	vendor = pci_findvendor(pa->pa_id);
	sc->sc.sc_id_vendor = PCI_VENDOR(pa->pa_id);
	if (vendor)
		strlcpy(sc->sc.sc_vendor, vendor, sizeof (sc->sc.sc_vendor));
	else
		snprintf(sc->sc.sc_vendor, sizeof (sc->sc.sc_vendor),
		    "vendor 0x%04x", PCI_VENDOR(pa->pa_id));

	/* Display revision and perform legacy emulation handover. */
	if (ohci_checkrev(&sc->sc) != USBD_NORMAL_COMPLETION ||
	    ohci_handover(&sc->sc) != USBD_NORMAL_COMPLETION) {
		splx(s);
		return;
	}

	/* Ignore interrupts for now */
	sc->sc.sc_bus.dying = 1;

	config_defer(self, ohci_voyager_attach_deferred);

	splx(s);
}

void
ohci_voyager_attach_deferred(struct device *self)
{
	struct ohci_voyager_softc *sc = (struct ohci_voyager_softc *)self;
	usbd_status r;
	int s;

	s = splusb();

	sc->sc.sc_bus.dying = 0;

	r = ohci_init(&sc->sc);
	if (r != USBD_NORMAL_COMPLETION) {
		printf("%s: init failed, error=%d\n",
		    sc->sc.sc_bus.bdev.dv_xname, r);
		bus_space_unmap(sc->sc.iot, sc->sc.ioh, sc->sc.sc_size);
		splx(s);
		return;
	}

	splx(s);

	/* Attach usb device. */
	config_found(self, &sc->sc.sc_bus, usbctlprint);
}


code:
/*	$OpenBSD: pcib.c,v 1.4 2012/08/18 16:00:20 miod Exp $	*/

/*-
 * Copyright (c) 1996 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 <machine/autoconf.h>
#include <machine/bus.h>

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

#include <dev/isa/isavar.h>

#include <dev/pci/glxreg.h>
#include <dev/pci/glxvar.h>

#include "isa.h"

int	pcibmatch(struct device *, void *, void *);
void	pcibattach(struct device *, struct device *, void *);

struct pcib_softc {
	struct device	sc_dev;
	bus_space_tag_t	sc_iot;
	bus_space_tag_t	sc_memt;
	bus_dma_tag_t	sc_dmat;
};

const struct cfattach pcib_ca = {
	sizeof(struct pcib_softc), pcibmatch, pcibattach
};

void	pcib_callback(struct device *);
void	pcib_isa_attach(struct pcib_softc *, bus_space_tag_t, bus_space_tag_t,
	    bus_dma_tag_t);
int	pcib_print(void *, const char *);

struct cfdriver pcib_cd = {
	NULL, "pcib", DV_DULL
};

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

	switch (PCI_VENDOR(pa->pa_id)) {
	case PCI_VENDOR_INTEL:
		switch (PCI_PRODUCT(pa->pa_id)) {
		case PCI_PRODUCT_INTEL_SIO:
		case PCI_PRODUCT_INTEL_82371MX:
		case PCI_PRODUCT_INTEL_82371AB_ISA:
		case PCI_PRODUCT_INTEL_82440MX_ISA:
			/* The above bridges mis-identify themselves */
			return (1);
		}
		break;
	case PCI_VENDOR_SIS:
		switch (PCI_PRODUCT(pa->pa_id)) {
		case PCI_PRODUCT_SIS_85C503:
			/* mis-identifies itself as a miscellaneous prehistoric */
			return (1);
		}
		break;
	case PCI_VENDOR_VIATECH:
		switch (PCI_PRODUCT(pa->pa_id)) {
		case PCI_PRODUCT_VIATECH_VT82C686A_SMB:
			/* mis-identifies itself as a ISA bridge */
			return (0);
		}
		break;
	}

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

	return (0);
}

void
pcibattach(struct device *parent, struct device *self, void *aux)
{
	struct pcib_softc *sc = (struct pcib_softc *)self;
	struct pci_attach_args *pa = (struct pci_attach_args *)aux;

	printf("\n");

	/*
	 * If we are actually a glxpcib, we can't use softc fields
	 * beyond the base struct device, for this would corrupt
	 * the glxpcib softc. Decide to attach isa immediately in this
	 * case - glxpcib-based designs are not expected to have ISA
	 * slots and attaching isa early should not cause problems.
	 */
	if (strncmp(self->dv_xname, "glxpcib", 7) == 0) {
		pcib_isa_attach(sc, pa->pa_iot, pa->pa_memt, pa->pa_dmat);
	} else {
		/*
		 * Wait until all PCI devices are attached before attaching isa;
		 * existing pcib code on other platforms mentions that not
		 * doing this ``might mess the interrupt setup on some
		 * systems'', although this is very unlikely to be the case
		 * on loongson.
		 */
		sc->sc_iot = pa->pa_iot;
		sc->sc_memt = pa->pa_memt;
		sc->sc_dmat = pa->pa_dmat;
		config_defer(self, pcib_callback);
	}
}

void
pcib_callback(struct device *self)
{
	struct pcib_softc *sc = (struct pcib_softc *)self;

	pcib_isa_attach(sc, sc->sc_iot, sc->sc_memt, sc->sc_dmat);
}

void
pcib_isa_attach(struct pcib_softc *sc, bus_space_tag_t iot,
    bus_space_tag_t memt, bus_dma_tag_t dmat)
{
	struct isabus_attach_args iba;

	/*
	 * Attach the ISA bus behind this bridge.
	 */
	memset(&iba, 0, sizeof(iba));
	iba.iba_busname = "isa";
	iba.iba_iot = iot;
	iba.iba_memt = memt;
#if NISADMA > 0
	iba.iba_dmat = dmat;
#endif
	iba.iba_ic = sys_platform->isa_chipset;
	if (iba.iba_ic != NULL)
		config_found(&sc->sc_dev, &iba, pcib_print);
}

int
pcib_print(void *aux, const char *pnp)
{
	/* Only ISAs can attach to pcib's; easy. */
	if (pnp)
		printf("isa at %s", pnp);
	return (UNCONF);
}