code:
/*	$OpenBSD: psychoreg.h,v 1.13 2008/07/20 10:37:43 kettenis Exp $	*/
/*	$NetBSD: psychoreg.h,v 1.6.4.2 2001/09/13 01:14:40 thorpej Exp $ */

/*
 * Copyright (c) 1998, 1999 Eduardo E. Horvath
 * Copyright (c) 1999 Matthew R. Green
 * 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.
 */
#ifndef _SPARC64_DEV_PSYCHOREG_H_
#define _SPARC64_DEV_PSYCHOREG_H_

/*
 * Sun4u PCI definitions.  Here's where we deal w/the machine
 * dependencies of psycho and the PCI controller on the UltraIIi.
 *
 * All PCI registers are bit-swapped, however they are not byte-swapped.
 * This means that they must be accessed using little-endian access modes,
 * either map the pages little-endian or use little-endian ASIs.
 *
 * PSYCHO implements two PCI buses, A and B.
 */

struct psychoreg {
	struct upareg {
		u_int64_t	upa_portid;	/* UPA port ID register */		/* 1fe.0000.0000 */
		u_int64_t	upa_config;	/* UPA config register */		/* 1fe.0000.0008 */
	} sys_upa;

	u_int64_t	psy_csr;		/* PSYCHO control/status register */	/* 1fe.0000.0010 */
	/*
	 * 63     59     55     50     45     4        3       2     1      0
	 * +------+------+------+------+--//---+--------+-------+-----+------+
	 * | IMPL | VERS | MID  | IGN  |  xxx  | APCKEN | APERR | IAP | MODE |
	 * +------+------+------+------+--//---+--------+-------+-----+------+
	 *
	 */
#define PSYCHO_GCSR_IMPL(csr)	((u_int)(((csr) >> 60) & 0xf))
#define PSYCHO_GCSR_VERS(csr)	((u_int)(((csr) >> 56) & 0xf))
#define PSYCHO_GCSR_MID(csr)	((u_int)(((csr) >> 51) & 0x1f))
#define PSYCHO_GCSR_IGN(csr)	((u_int)(((csr) >> 46) & 0x1f))
#define PSYCHO_CSR_APCKEN	8	/* UPA addr parity check enable */
#define PSYCHO_CSR_APERR	4	/* UPA addr parity error */
#define PSYCHO_CSR_IAP		2	/* invert UPA address parity */
#define PSYCHO_CSR_MODE		1	/* UPA/PCI handshake */

	u_int64_t	pad0;
	u_int64_t	psy_ecccr;		/* ECC control register */		/* 1fe.0000.0020 */
	u_int64_t	reserved;							/* 1fe.0000.0028 */
	u_int64_t	psy_ue_afsr;		/* Uncorrectable Error AFSR */		/* 1fe.0000.0030 */
	u_int64_t	psy_ue_afar;		/* Uncorrectable Error AFAR */		/* 1fe.0000.0038 */
	u_int64_t	psy_ce_afsr;		/* Correctable Error AFSR */		/* 1fe.0000.0040 */
	u_int64_t	psy_ce_afar;		/* Correctable Error AFAR */		/* 1fe.0000.0048 */

	u_int64_t	pad1[22];

	struct perfmon {
		u_int64_t	pm_cr;		/* Performance monitor control reg */	/* 1fe.0000.0100 */
		u_int64_t	pm_count;	/* Performance monitor counter reg */	/* 1fe.0000.0108 */
	} psy_pm;

	u_int64_t	pad2[30];

	struct iommureg psy_iommu;							/* 1fe.0000.0200,0210 */

	u_int64_t	pad3[256];

	u_int64_t	pcia_slot0_int;		/* PCI bus a slot 0 irq map reg */	/* 1fe.0000.0c00 */
	u_int64_t	pcia_slot1_int;		/* PCI bus a slot 1 irq map reg */	/* 1fe.0000.0c08 */
	u_int64_t	pcia_slot2_int;		/* PCI bus a slot 2 irq map reg (IIi)*/	/* 1fe.0000.0c10 */
	u_int64_t	pcia_slot3_int;		/* PCI bus a slot 3 irq map reg (IIi)*/	/* 1fe.0000.0c18 */
	u_int64_t	pcib_slot0_int;		/* PCI bus b slot 0 irq map reg */	/* 1fe.0000.0c20 */
	u_int64_t	pcib_slot1_int;		/* PCI bus b slot 1 irq map reg */	/* 1fe.0000.0c28 */
	u_int64_t	pcib_slot2_int;		/* PCI bus b slot 2 irq map reg */	/* 1fe.0000.0c30 */
	u_int64_t	pcib_slot3_int;		/* PCI bus b slot 3 irq map reg */	/* 1fe.0000.0c38 */

	u_int64_t	pad4[120];

	u_int64_t	scsi_int_map;		/* SCSI interrupt map reg */		/* 1fe.0000.1000 */
	u_int64_t	ether_int_map;		/* ethernet interrupt map reg */	/* 1fe.0000.1008 */
	u_int64_t	bpp_int_map;		/* parallel interrupt map reg */	/* 1fe.0000.1010 */
	u_int64_t	audior_int_map;		/* audio record interrupt map reg */	/* 1fe.0000.1018 */
	u_int64_t	audiop_int_map;		/* audio playback interrupt map reg */	/* 1fe.0000.1020 */
	u_int64_t	power_int_map;		/* power fail interrupt map reg */	/* 1fe.0000.1028 */
	u_int64_t	ser_kbd_ms_int_map;	/* serial/kbd/mouse interrupt map reg *//* 1fe.0000.1030 */
	u_int64_t	fd_int_map;		/* floppy interrupt map reg */		/* 1fe.0000.1038 */
	u_int64_t	spare_int_map;		/* spare interrupt map reg */		/* 1fe.0000.1040 */
	u_int64_t	kbd_int_map;		/* kbd [unused] interrupt map reg */	/* 1fe.0000.1048 */
	u_int64_t	mouse_int_map;		/* mouse [unused] interrupt map reg */	/* 1fe.0000.1050 */
	u_int64_t	serial_int_map;		/* second serial interrupt map reg */	/* 1fe.0000.1058 */
	u_int64_t	timer0_int_map;		/* timer 0 interrupt map reg */		/* 1fe.0000.1060 */
	u_int64_t	timer1_int_map;		/* timer 1 interrupt map reg */		/* 1fe.0000.1068 */
	u_int64_t	ue_int_map;		/* UE interrupt map reg */		/* 1fe.0000.1070 */
	u_int64_t	ce_int_map;		/* CE interrupt map reg */		/* 1fe.0000.1078 */
	u_int64_t	pciaerr_int_map;	/* PCI bus a error interrupt map reg */	/* 1fe.0000.1080 */
	u_int64_t	pciberr_int_map;	/* PCI bus b error interrupt map reg */	/* 1fe.0000.1088 */
	u_int64_t	pwrmgt_int_map;		/* power mgmt wake interrupt map reg */	/* 1fe.0000.1090 */
	u_int64_t	ffb0_int_map;		/* FFB0 graphics interrupt map reg */	/* 1fe.0000.1098 */
	u_int64_t	ffb1_int_map;		/* FFB1 graphics interrupt map reg */	/* 1fe.0000.10a0 */

	u_int64_t	pad5[107];

	/* Note: clear interrupt 0 registers are not really used */
	u_int64_t	pcia0_clr_int[4];	/* PCI a slot 0 clear int regs 0..7 */	/* 1fe.0000.1400-1418 */
	u_int64_t	pcia1_clr_int[4];	/* PCI a slot 1 clear int regs 0..7 */	/* 1fe.0000.1420-1438 */
	u_int64_t	pcia2_clr_int[4];	/* PCI a slot 2 clear int regs 0..7 */	/* 1fe.0000.1440-1458 */
	u_int64_t	pcia3_clr_int[4];	/* PCI a slot 3 clear int regs 0..7 */	/* 1fe.0000.1480-1478 */
	u_int64_t	pcib0_clr_int[4];	/* PCI b slot 0 clear int regs 0..7 */	/* 1fe.0000.1480-1498 */
	u_int64_t	pcib1_clr_int[4];	/* PCI b slot 1 clear int regs 0..7 */	/* 1fe.0000.14a0-14b8 */
	u_int64_t	pcib2_clr_int[4];	/* PCI b slot 2 clear int regs 0..7 */	/* 1fe.0000.14c0-14d8 */
	u_int64_t	pcib3_clr_int[4];	/* PCI b slot 3 clear int regs 0..7 */	/* 1fe.0000.14d0-14f8 */

	u_int64_t	pad6[96];

	u_int64_t	scsi_clr_int;		/* SCSI clear int reg */		/* 1fe.0000.1800 */
	u_int64_t	ether_clr_int;		/* ethernet clear int reg */		/* 1fe.0000.1808 */
	u_int64_t	bpp_clr_int;		/* parallel clear int reg */		/* 1fe.0000.1810 */
	u_int64_t	audior_clr_int;		/* audio record clear int reg */	/* 1fe.0000.1818 */
	u_int64_t	audiop_clr_int;		/* audio playback clear int reg */	/* 1fe.0000.1820 */
	u_int64_t	power_clr_int;		/* power fail clear int reg */		/* 1fe.0000.1828 */
	u_int64_t	ser_kb_ms_clr_int;	/* serial/kbd/mouse clear int reg */	/* 1fe.0000.1830 */
	u_int64_t	fd_clr_int;		/* floppy clear int reg */		/* 1fe.0000.1838 */
	u_int64_t	spare_clr_int;		/* spare clear int reg */		/* 1fe.0000.1840 */
	u_int64_t	kbd_clr_int;		/* kbd [unused] clear int reg */	/* 1fe.0000.1848 */
	u_int64_t	mouse_clr_int;		/* mouse [unused] clear int reg */	/* 1fe.0000.1850 */
	u_int64_t	serial_clr_int;		/* second serial clear int reg */	/* 1fe.0000.1858 */
	u_int64_t	timer0_clr_int;		/* timer 0 clear int reg */		/* 1fe.0000.1860 */
	u_int64_t	timer1_clr_int;		/* timer 1 clear int reg */		/* 1fe.0000.1868 */
	u_int64_t	ue_clr_int;		/* UE clear int reg */			/* 1fe.0000.1870 */
	u_int64_t	ce_clr_int;		/* CE clear int reg */			/* 1fe.0000.1878 */
	u_int64_t	pciaerr_clr_int;	/* PCI bus a error clear int reg */	/* 1fe.0000.1880 */
	u_int64_t	pciberr_clr_int;	/* PCI bus b error clear int reg */	/* 1fe.0000.1888 */
	u_int64_t	pwrmgt_clr_int;		/* power mgmt wake clr interrupt reg */	/* 1fe.0000.1890 */

	u_int64_t	pad7[45];

	u_int64_t	intr_retry_timer;	/* interrupt retry timer */		/* 1fe.0000.1a00 */

	u_int64_t	pad8[63];

	struct timer_counter {
		u_int64_t	tc_count;	/* timer/counter 0/1 count register */	/* 1fe.0000.1c00,1c10 */
		u_int64_t	tc_limit;	/* timer/counter 0/1 limit register */	/* 1fe.0000.1c08,1c18 */
	} tc[2];

	u_int64_t	pci_dma_write_sync;	/* PCI DMA write sync register (IIi) */	/* 1fe.0000.1c20 */

	u_int64_t	pad9[123];

	struct pci_ctl {
		u_int64_t	pci_csr;	/* PCI a/b control/status register */	/* 1fe.0000.2000,4000 */
		u_int64_t	pad10;
		u_int64_t	pci_afsr;	/* PCI a/b AFSR register */		/* 1fe.0000.2010,4010 */
		u_int64_t	pci_afar;	/* PCI a/b AFAR register */		/* 1fe.0000.2018,4018 */
		u_int64_t	pci_diag;	/* PCI a/b diagnostic register */	/* 1fe.0000.2020,4020 */
		u_int64_t	pci_tasr;	/* PCI target address space reg (IIi)*/	/* 1fe.0000.2028,4028 */

		u_int64_t	pad11[250];

		/* This is really the IOMMU's, not the PCI bus's */
		struct iommu_strbuf pci_strbuf;						/* 1fe.0000.2800-210 */
#define psy_iommu_strbuf psy_pcictl[0].pci_strbuf

		u_int64_t	pad12[765];
	} psy_pcictl[2];			/* For PCI a and b */

	/* NB: FFB0 and FFB1 intr map regs also appear at 1fe.0000.6000 and 1fe.0000.8000 respectively */
	u_int64_t	pad13[2048];

	u_int64_t	dma_scb_diag0;		/* DMA scoreboard diag reg 0 */		/* 1fe.0000.a000 */
	u_int64_t	dma_scb_diag1;		/* DMA scoreboard diag reg 1 */		/* 1fe.0000.a008 */

	u_int64_t	pad14[126];

	u_int64_t	iommu_svadiag;		/* IOMMU virtual addr diag reg */	/* 1fe.0000.a400 */
	u_int64_t	iommu_tlb_comp_diag;	/* IOMMU TLB tag compare diag reg */	/* 1fe.0000.a408 */

	u_int64_t	pad15[30];

	u_int64_t	iommu_queue_diag[16];	/* IOMMU LRU queue diag */		/* 1fe.0000.a500-a578 */
	u_int64_t	tlb_tag_diag[16];	/* TLB tag diag */			/* 1fe.0000.a580-a5f8 */
	u_int64_t	tlb_data_diag[16];	/* TLB data RAM diag */			/* 1fe.0000.a600-a678 */

	u_int64_t	pad16[48];

	u_int64_t	pci_int_diag;		/* PCI int state diag reg */		/* 1fe.0000.a800 */
	u_int64_t	obio_int_diag;		/* OBIO and misc int state diag reg */	/* 1fe.0000.a808 */

	u_int64_t	pad17[254];

	struct strbuf_diag {
		u_int64_t	strbuf_data_diag[128];	/* streaming buffer data RAM diag */	/* 1fe.0000.b000-b3f8 */
		u_int64_t	strbuf_error_diag[128];	/* streaming buffer error status diag *//* 1fe.0000.b400-b7f8 */
		u_int64_t	strbuf_pg_tag_diag[16];	/* streaming buffer page tag diag */	/* 1fe.0000.b800-b878 */
		u_int64_t	pad18[16];
		u_int64_t	strbuf_ln_tag_diag[16];	/* streaming buffer line tag diag */	/* 1fe.0000.b900-b978 */
		u_int64_t	pad19[208];
	} psy_strbufdiag[2];					/* For PCI a and b */

	u_int64_t	pad18[1036];

	u_int64_t	stick_cmp_low;		/* STICK comparison low reg */			/* 1fe.0000.f060 */
	u_int64_t	stick_cmp_high;		/* STICK comparison high reg */			/* 1fe.0000.f068 */
	u_int64_t	stick_reg_low;		/* STICK counter low reg */			/* 1fe.0000.f070 */
	u_int64_t	stick_reg_high;		/* STICK counter high reg */			/* 1fe.0000.f078 */

	/*
	 * Here is the rest of the map, which we're not specifying:
	 *
	 * 1fe.0100.0000 - 1fe.01ff.ffff	PCI configuration space
	 * 1fe.0100.0000 - 1fe.0100.00ff	PCI B configuration header
	 * 1fe.0101.0000 - 1fe.0101.00ff	PCI A configuration header
	 * 1fe.0200.0000 - 1fe.0200.ffff	PCI A I/O space
	 * 1fe.0201.0000 - 1fe.0201.ffff	PCI B I/O space
	 * 1ff.0000.0000 - 1ff.7fff.ffff	PCI A memory space
	 * 1ff.8000.0000 - 1ff.ffff.ffff	PCI B memory space
	 *
	 * NB: config and I/O space can use 1-4 byte accesses, not 8 byte
	 * accesses.  Memory space can use any sized accesses.
	 *
	 * Note that the SUNW,sabre/SUNW,simba combinations found on the
	 * Ultra5 and Ultra10 machines uses slightly differrent addresses
	 * than the above.  This is mostly due to the fact that the APB is
	 * a multi-function PCI device with two PCI bridges, and the U2P is
	 * two separate PCI bridges.  It uses the same PCI configuration
	 * space, though the configuration header for each PCI bus is
	 * located differently due to the SUNW,simba PCI busses being
	 * function 0 and function 1 of the APB, whereas the psycho's are
	 * each their own PCI device.  The I/O and memory spaces are each
	 * split into 8 equally sized areas (8x2MB blocks for I/O space,
	 * and 8x512MB blocks for memory space).  These are allocated in to
	 * either PCI A or PCI B, or neither in the APB's `I/O Address Map
	 * Register A/B' (0xde) and `Memory Address Map Register A/B' (0xdf)
	 * registers of each simba.  We must ensure that both of the
	 * following are correct (the prom should do this for us):
	 *
	 *    (PCI A Memory Address Map) & (PCI B Memory Address Map) == 0
	 *
	 *    (PCI A I/O Address Map) & (PCI B I/O Address Map) == 0
	 *
	 * 1fe.0100.0000 - 1fe.01ff.ffff	PCI configuration space
	 * 1fe.0100.0800 - 1fe.0100.08ff	PCI B configuration header
	 * 1fe.0100.0900 - 1fe.0100.09ff	PCI A configuration header
	 * 1fe.0200.0000 - 1fe.02ff.ffff	PCI I/O space (divided)
	 * 1ff.0000.0000 - 1ff.ffff.ffff	PCI memory space (divided)
	 */
};

/* what the bits mean! */

/* uncorrectable error fault status */
#define	PSY_UEAFSR_PDRD	0x4000000000000000	/* primary pci dma read */
#define	PSY_UEAFSR_PDWR	0x2000000000000000	/* primary pci dma write */
#define	PSY_UEAFSR_SDRD	0x0800000000000000	/* secondary pci dma read */
#define	PSY_UEAFSR_SDWR	0x0400000000000000	/* secondary pci dma write */
#define	PSY_UEAFSR_SDTE	0x0200000000000000	/* secondary dma translation error */
#define	PSY_UEAFSR_PDTE	0x0100000000000000	/* primary dma translation error */
#define	PSY_UEAFSR_MASK	0x0000ffff00000000	/* byte mask */
#define	PSY_UEAFSR_OFF	0x00000000e0000000	/* offset (afar [5:3]) */
#define	PSY_UEAFSR_BLK	0x0000000000800000	/* block operation */

/* correctable error fault status */
#define	PSY_CEAFSR_PDRD	0x4000000000000000	/* primary pci dma read */
#define	PSY_CEAFSR_PDWR	0x2000000000000000	/* primary pci dma write */
#define	PSY_CEAFSR_SDRD	0x0800000000000000	/* secondary pci dma read */
#define	PSY_CEAFSR_SDWR	0x0400000000000000	/* secondary pci dma write */
#define	PSY_CEAFSR_SYND	0x00ff000000000000	/* ecc syndrome */
#define	PSY_CEAFSR_MASK	0x0000ffff00000000	/* byte mask */
#define	PSY_CEAFSR_OFF	0x00000000e0000000	/* offset (afar [5:3]) */
#define	PSY_CEAFSR_BLK	0x0000000000800000	/* block operation */

/* PCI [a|b] control/status register */
/* note that the sabre only has one set of PCI control/status registers */
#define	PCICTL_MRLM	0x0000001000000000	/* Memory Read Line/Multiple */
#define	PCICTL_SERR	0x0000000400000000	/* SERR asserted; W1C */
#define	PCICTL_ARB_PARK	0x0000000000200000	/* PCI arbitration parking */
#define	PCICTL_CPU_PRIO	0x0000000000100000	/* PCI arbitration parking */
#define	PCICTL_ARB_PRIO	0x00000000000f0000	/* PCI arbitration parking */
#define	PCICTL_ERRINTEN	0x0000000000000100	/* PCI error interrupt enable */
#define	PCICTL_RTRYWAIT 0x0000000000000080	/* PCI error interrupt enable */
#define	PCICTL_4ENABLE	0x000000000000000f	/* enable 4 PCI slots */
#define	PCICTL_6ENABLE	0x000000000000003f	/* enable 6 PCI slots */

/* PCI [a|b] afsr */
#define	PSY_PCIAFSR_PMA		0x8000000000000000	/* primary master abort */
#define	PSY_PCIAFSR_PTA		0x4000000000000000	/* primary target abort */
#define	PSY_PCIAFSR_PTRY	0x2000000000000000	/* primary excessive retry */
#define	PSY_PCIAFSR_PPERR	0x1000000000000000	/* primary parity error */
#define	PSY_PCIAFSR_SMA		0x0800000000000000	/* secondary master abort */
#define	PSY_PCIAFSR_STA		0x0400000000000000	/* secondary target abort */
#define	PSY_PCIAFSR_STRY	0x0200000000000000	/* secondary excessive retry */
#define	PSY_PCIAFSR_SPERR	0x0100000000000000	/* secondary parity error */
#define	PSY_PCIAFSR_RESV1	0x00ff000000000000	/* reserved */
#define	PSY_PCIAFSR_BMSK	0x0000ffff00000000	/* byte mask of transfer */
#define	PSY_PCIAFSR_BLK		0x0000000080000000	/* block operation */
#define	PSY_PCIAFSR_RESV2	0x0000000040000000	/* reserved */
#define	PSY_PCIAFSR_MID		0x000000003e000000	/* mid causing error */
#define	PSY_PCIAFSR_RESV3	0x0000000001ffffff	/* reserved */

/* performance counter control */
#define	PSY_PMCR_CLR1		0x0000000000008000	/* clear cnt 1 */
#define	PSY_PMCR_SEL1		0x0000000000001f00	/* set cnt 1 src */
#define	PSY_PMCR_CLR0		0x0000000000000080	/* clear cnt 0 */
#define	PSY_PMCR_SEL0		0x000000000000001f	/* set cnt 0 src */

#define	PSY_PMCRSEL_SDVRA	0x0000000000000000	/* stream dvma read, A */
#define	PSY_PMCRSEL_SDVWA	0x0000000000000001	/* stream dvma write, A */
#define	PSY_PMCRSEL_CDVRA	0x0000000000000002	/* consist dvma read, A */
#define	PSY_PMCRSEL_CDVWA	0x0000000000000003	/* consist dvma write, A */
#define	PSY_PMCRSEL_SBMA	0x0000000000000004	/* stream buf miss, A */
#define	PSY_PMCRSEL_DVA		0x0000000000000005	/* dvma cycles, A */
#define	PSY_PMCRSEL_DVWA	0x0000000000000006	/* dvma words, A */
#define	PSY_PMCRSEL_PIOA	0x0000000000000007	/* pio cycles, A */
#define	PSY_PMCRSEL_SDVRB	0x0000000000000008	/* stream dvma read, B */
#define	PSY_PMCRSEL_SDVWB	0x0000000000000009	/* stream dvma write, B */
#define	PSY_PMCRSEL_CDVRB	0x000000000000000a	/* consist dvma read, B */
#define	PSY_PMCRSEL_CDVWB	0x000000000000000b	/* consist dvma write, B */
#define	PSY_PMCRSEL_SBMB	0x000000000000000c	/* stream buf miss, B */
#define	PSY_PMCRSEL_DVB		0x000000000000000d	/* dvma cycles, B */
#define	PSY_PMCRSEL_DVWB	0x000000000000000e	/* dvma words, B */
#define	PSY_PMCRSEL_PIOB	0x000000000000000f	/* pio cycles, B */
#define	PSY_PMCRSEL_TLBMISS	0x0000000000000010	/* tlb misses */
#define	PSY_PMCRSEL_NINTRS	0x0000000000000011	/* interrupts */
#define	PSY_PMCRSEL_INACK	0x0000000000000012	/* intr nacks */
#define	PSY_PMCRSEL_PIOR	0x0000000000000013	/* pio read xfers */
#define	PSY_PMCRSEL_PIOW	0x0000000000000014	/* pio write xfers */
#define	PSY_PMCRSEL_MERGE	0x0000000000000015	/* merge buffer xacts */
#define	PSY_PMCRSEL_TBLA	0x0000000000000016	/* tbl walk retries, A */
#define	PSY_PMCRSEL_STCA	0x0000000000000017	/* stc retries, A */
#define	PSY_PMCRSEL_TBLB	0x0000000000000018	/* tbl walk retries, B */
#define	PSY_PMCRSEL_STCB	0x0000000000000019	/* stc retries, B */

/*
 * these are the PROM structures we grovel
 */

/*
 * For the physical addresses split into 3 32 bit values, we deocde
 * them like the following (IEEE1275 PCI Bus binding 2.0, 2.2.1.1
 * Numerical Representation):
 *
 * 	phys.hi cell:	npt000ss bbbbbbbb dddddfff rrrrrrrr
 * 	phys.mid cell:	hhhhhhhh hhhhhhhh hhhhhhhh hhhhhhhh
 * 	phys.lo cell:	llllllll llllllll llllllll llllllll
 *
 * where these bits affect the address' properties:
 *	n	not-relocatable
 *	p	prefetchable
 *	t	aliased (non-relocatable IO), below 1MB (memory) or
 *		below 64KB (reloc. IO)
 *	ss	address space code:
 *		00 - configuration space
 *		01 - I/O space
 *		10 - 32 bit memory space
 *		11 - 64 bit memory space
 *	bb..bb	8 bit bus number
 *	ddddd	5 bit device number
 *	fff	3 bit function number
 *	rr..rr	8 bit register number
 *	hh..hh	32 bit unsigned value
 *	ll..ll	32 bit unsigned value
 * the values of hh..hh and ll..ll are combined to form a larger number.
 *
 * For config space, we don't have to do much special.  For I/O space,
 * hh..hh must be zero, and if n == 0 ll..ll is the offset from the
 * start of I/O space, otherwise ll..ll is the I/O space.  For memory
 * space, hh..hh must be zero for the 32 bit space, and is the high 32
 * bits in 64 bit space, with ll..ll being the low 32 bits in both cases,
 * with offset handling being driver via `n == 0' as for I/O space.
 */

/* commonly used */
#define TAG2BUS(tag)	((tag) >> 16) & 0xff;
#define TAG2DEV(tag)	((tag) >> 11) & 0x1f;
#define TAG2FN(tag)	((tag) >> 8) & 0x7;

struct psycho_registers {
	u_int32_t	phys_hi;
	u_int32_t	phys_mid;
	u_int32_t	phys_lo;
	u_int32_t	size_hi;
	u_int32_t	size_lo;
};

struct psycho_ranges {
	u_int32_t	cspace;
	u_int32_t	child_hi;
	u_int32_t	child_lo;
	u_int32_t	phys_hi;
	u_int32_t	phys_lo;
	u_int32_t	size_hi;
	u_int32_t	size_lo;
};

struct psycho_interrupt_map {
	u_int32_t	phys_hi;
	u_int32_t	phys_mid;
	u_int32_t	phys_lo;
	u_int32_t	intr;
	int32_t		child_node;
	u_int32_t	child_intr;
};

struct psycho_interrupt_map_mask {
	u_int32_t	phys_hi;
	u_int32_t	phys_mid;
	u_int32_t	phys_lo;
	u_int32_t	intr;
};

#endif /* _SPARC64_DEV_PSYCHOREG_H_ */


code:
/*	$OpenBSD: psychovar.h,v 1.7 2008/05/24 23:31:37 kettenis Exp $	*/
/*	$NetBSD: psychovar.h,v 1.6 2001/07/20 00:07:13 eeh Exp $	*/

/*
 * Copyright (c) 1999, 2000 Matthew R. Green
 * 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.
 */

#ifndef _SPARC64_DEV_PSYCHOVAR_H_
#define _SPARC64_DEV_PSYCHOVAR_H_

/* per real PCI bus info */
struct psycho_softc;

struct psycho_pbm {
	/* link to mum */
	struct psycho_softc		*pp_sc;

	/*
	 * note that the sabre really only has one ranges property,
	 * used for both simba a and simba b (but the ranges for
	 * real psychos are the same for PCI A and PCI B anyway).
	 */
	struct psycho_registers		*pp_regs;
	struct psycho_ranges		*pp_range;

	/* counts of above */
	int				pp_nregs;
	int				pp_nrange;
	int				pp_nintmap;

	/* PCI Bus Module A or PCI Bus Module B */
	int				pp_id;
#define PSYCHO_PBM_UNKNOWN	0
#define PSYCHO_PBM_A		1
#define PSYCHO_PBM_B		2

	/* chipset tag for this instance */
	pci_chipset_tag_t		pp_pc;

	/* our tags */
	bus_space_tag_t			pp_memt;
	bus_space_tag_t			pp_iot;
	bus_dma_tag_t			pp_dmat;
	int				pp_bus;
	int				pp_flags;

	/* and pointers into the psycho regs for our bits */
	bus_space_handle_t		pp_pcictl;
	struct strbuf_ctl		pp_sb;
	/* area we can use for flushing our streaming buffer */
	char				pp_flush[0x80];
};

/*
 * per-PCI bus on mainbus softc structure; one for sabre, or two
 * per pair of psycho's.
 */
struct psycho_softc {
	struct	device			sc_dev;

	/*
	 * one sabre has two simba's.  psycho's are separately attached,
	 * with the `other' psycho_pbm allocated at the first's attach.
	 */
	struct psycho_pbm		*__sc_psycho_this;
	struct psycho_pbm		*__sc_psycho_other;
#define	sc_psycho_this	__sc_psycho_this
#define	sc_psycho_other	__sc_psycho_other

	/*
	 * PSYCHO register.  we record the base physical address of these
	 * also as it is the base of the entire PSYCHO
	 */
	bus_space_handle_t		sc_regsh;
	bus_space_handle_t		sc_pcictl;
	paddr_t				sc_basepaddr;

	/* Interrupt Group Number for this device */
	int				sc_ign;

	/* our tags (from parent) */
	bus_space_tag_t			sc_bustag;
	bus_dma_tag_t			sc_dmatag;

	/* config space */
	bus_space_tag_t			sc_configtag;
	bus_space_handle_t		sc_configaddr;

	int				sc_clockfreq;
	int				sc_node;	/* prom node */
	int				sc_mode;	/* (whatareya?) */
#define	PSYCHO_MODE_SABRE	1	/* i'm a sabre (yob) */
#define	PSYCHO_MODE_PSYCHO	2	/* i'm a psycho (w*nker) */
#define	PSYCHO_MODE_CMU_CH	3	/* i'm a CMU-CH (castrate) */

	struct iommu_state		*sc_is;
};

/* config space is per-psycho.  mem/io/dma are per-pci bus */
bus_dma_tag_t psycho_alloc_dma_tag(struct psycho_pbm *);

bus_space_tag_t psycho_alloc_mem_tag(struct psycho_pbm *);
bus_space_tag_t psycho_alloc_io_tag(struct psycho_pbm *);
bus_space_tag_t psycho_alloc_config_tag(struct psycho_pbm *);

/* uperf attachment to psycho's */
struct uperf_psycho_attach_args {
	char *upaa_name;
	struct perfmon *upaa_regs;
};

#define psycho_psychoreg_read(sc, reg)				\
	bus_space_read_8((sc)->sc_bustag, (sc)->sc_regsh,	\
	    offsetof(struct psychoreg, reg))

#define psycho_psychoreg_write(sc, reg, v)			\
	bus_space_write_8((sc)->sc_bustag, (sc)->sc_regsh,	\
	    offsetof(struct psychoreg, reg), (v))

#define psycho_psychoreg_vaddr(sc, reg)				\
        (bus_space_vaddr((sc)->sc_bustag, (sc)->sc_regsh) +	\
                offsetof(struct psychoreg, reg))

#define psycho_pcictl_read(sc, reg)				\
	bus_space_read_8((sc)->sc_bustag, (sc)->sc_pcictl,	\
	     offsetof(struct pci_ctl, reg))

#define psycho_pcictl_write(sc, reg, v)	\
	bus_space_write_8((sc)->sc_bustag, (sc)->sc_pcictl,	\
	    offsetof(struct pci_ctl, reg), (v))

#endif /* _SPARC64_DEV_PSYCHOVAR_H_ */

license: unknown


code:
/*	$OpenBSD: pyro.c,v 1.28 2014/12/09 06:58:29 doug Exp $	*/

/*
 * Copyright (c) 2002 Jason L. Wright (jason@thought.net)
 * Copyright (c) 2003 Henric Jungheim
 * Copyright (c) 2007 Mark Kettenis
 * 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 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/device.h>
#include <sys/errno.h>
#include <sys/malloc.h>
#include <sys/systm.h>

#define _SPARC_BUS_DMA_PRIVATE
#include <machine/bus.h>
#include <machine/autoconf.h>
#include <machine/openfirm.h>

#ifdef DDB
#include <machine/db_machdep.h>
#endif

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

#include <sparc64/dev/iommureg.h>
#include <sparc64/dev/iommuvar.h>
#include <sparc64/dev/msivar.h>
#include <sparc64/dev/pyrovar.h>

#ifdef DEBUG
#define PDB_PROM        0x01
#define PDB_BUSMAP      0x02
#define PDB_INTR        0x04
#define PDB_CONF        0x08
int pyro_debug = ~0;
#define DPRINTF(l, s)   do { if (pyro_debug & l) printf s; } while (0)
#else
#define DPRINTF(l, s)
#endif

#define FIRE_EQ_BASE_ADDR		0x10000
#define FIRE_EQ_CNTRL_SET		0x11000
#define  FIRE_EQ_CTRL_SET_EN		0x0000100000000000UL
#define FIRE_EQ_CNTRL_CLEAR		0x11200
#define FIRE_EQ_STATE			0x11400
#define FIRE_EQ_TAIL			0x11600
#define FIRE_EQ_HEAD			0x11800
#define FIRE_MSI_MAP			0x20000
#define  FIRE_MSI_MAP_V			0x8000000000000000UL
#define  FIRE_MSI_MAP_EQWR_N		0x4000000000000000UL
#define  FIRE_MSI_MAP_EQNUM		0x000000000000003fUL
#define FIRE_MSI_CLEAR			0x28000
#define  FIRE_MSI_CLEAR_EQWR_N		0x4000000000000000UL
#define FIRE_INTRMONDO_DATA0		0x2c000
#define FIRE_INTRMONDO_DATA1		0x2c008
#define FIRE_MSI32_ADDR			0x34000
#define FIRE_MSI64_ADDR			0x34008

#define FIRE_RESET_GEN			0x7010

#define FIRE_RESET_GEN_XIR		0x0000000000000002UL

#define FIRE_INTRMAP_INT_CNTRL_NUM_MASK	0x000003c0
#define FIRE_INTRMAP_INT_CNTRL_NUM0	0x00000040
#define FIRE_INTRMAP_INT_CNTRL_NUM1	0x00000080
#define FIRE_INTRMAP_INT_CNTRL_NUM2	0x00000100
#define FIRE_INTRMAP_INT_CNTRL_NUM3	0x00000200
#define FIRE_INTRMAP_T_JPID_SHIFT	26
#define FIRE_INTRMAP_T_JPID_MASK	0x7c000000

#define OBERON_INTRMAP_T_DESTID_SHIFT	21
#define OBERON_INTRMAP_T_DESTID_MASK	0x7fe00000

extern struct sparc_pci_chipset _sparc_pci_chipset;

int pyro_match(struct device *, void *, void *);
void pyro_attach(struct device *, struct device *, void *);
void pyro_init(struct pyro_softc *, int);
void pyro_init_iommu(struct pyro_softc *, struct pyro_pbm *);
void pyro_init_msi(struct pyro_softc *, struct pyro_pbm *);
int pyro_print(void *, const char *);

pci_chipset_tag_t pyro_alloc_chipset(struct pyro_pbm *, int,
    pci_chipset_tag_t);
bus_space_tag_t pyro_alloc_mem_tag(struct pyro_pbm *);
bus_space_tag_t pyro_alloc_io_tag(struct pyro_pbm *);
bus_space_tag_t pyro_alloc_config_tag(struct pyro_pbm *);
bus_space_tag_t pyro_alloc_bus_tag(struct pyro_pbm *, const char *,
    int, int, int);
bus_dma_tag_t pyro_alloc_dma_tag(struct pyro_pbm *);

int pyro_conf_size(pci_chipset_tag_t, pcitag_t);
pcireg_t pyro_conf_read(pci_chipset_tag_t, pcitag_t, int);
void pyro_conf_write(pci_chipset_tag_t, pcitag_t, int, pcireg_t);

int pyro_intr_map(struct pci_attach_args *, pci_intr_handle_t *);
int pyro_bus_map(bus_space_tag_t, bus_space_tag_t, bus_addr_t,
    bus_size_t, int, bus_space_handle_t *);
paddr_t pyro_bus_mmap(bus_space_tag_t, bus_space_tag_t, bus_addr_t, off_t,
    int, int);
void *pyro_intr_establish(bus_space_tag_t, bus_space_tag_t, int, int, int,
    int (*)(void *), void *, const char *);
void pyro_msi_ack(struct intrhand *);

int pyro_msi_eq_intr(void *);

int pyro_dmamap_create(bus_dma_tag_t, bus_dma_tag_t, bus_size_t, int,
    bus_size_t, bus_size_t, int, bus_dmamap_t *);

#ifdef DDB
void pyro_xir(void *, int);
#endif

int
pyro_match(struct device *parent, void *match, void *aux)
{
	struct mainbus_attach_args *ma = aux;
	char *str;

	if (strcmp(ma->ma_name, "pci") != 0)
		return (0);

	str = getpropstring(ma->ma_node, "compatible");
	if (strcmp(str, "pciex108e,80f0") == 0 ||
	    strcmp(str, "pciex108e,80f8") == 0)
		return (1);

	return (0);
}

void
pyro_attach(struct device *parent, struct device *self, void *aux)
{
	struct pyro_softc *sc = (struct pyro_softc *)self;
	struct mainbus_attach_args *ma = aux;
	char *str;
	int busa;

	sc->sc_node = ma->ma_node;
	sc->sc_dmat = ma->ma_dmatag;
	sc->sc_bust = ma->ma_bustag;
	sc->sc_csr = ma->ma_reg[0].ur_paddr;
	sc->sc_xbc = ma->ma_reg[1].ur_paddr;
	sc->sc_ign = INTIGN(ma->ma_upaid << INTMAP_IGN_SHIFT);

	if ((ma->ma_reg[0].ur_paddr & 0x00700000) == 0x00600000)
		busa = 1;
	else
		busa = 0;

	if (bus_space_map(sc->sc_bust, sc->sc_csr,
	    ma->ma_reg[0].ur_len, 0, &sc->sc_csrh)) {
		printf(": failed to map csr registers\n");
		return;
	}

	if (bus_space_map(sc->sc_bust, sc->sc_xbc,
	    ma->ma_reg[1].ur_len, 0, &sc->sc_xbch)) {
		printf(": failed to map xbc registers\n");
		return;
	}

	str = getpropstring(ma->ma_node, "compatible");
	if (strcmp(str, "pciex108e,80f8") == 0)
		sc->sc_oberon = 1;

	pyro_init(sc, busa);
}

void
pyro_init(struct pyro_softc *sc, int busa)
{
	struct pyro_pbm *pbm;
	struct pcibus_attach_args pba;
	int *busranges = NULL, nranges;

	pbm = malloc(sizeof(*pbm), M_DEVBUF, M_NOWAIT | M_ZERO);
	if (pbm == NULL)
		panic("pyro: can't alloc pyro pbm");

	pbm->pp_sc = sc;
	pbm->pp_bus_a = busa;

	if (getprop(sc->sc_node, "ranges", sizeof(struct pyro_range),
	    &pbm->pp_nrange, (void **)&pbm->pp_range))
		panic("pyro: can't get ranges");

	if (getprop(sc->sc_node, "bus-range", sizeof(int), &nranges,
	    (void **)&busranges))
		panic("pyro: can't get bus-range");

	printf(": \"%s\", rev %d, ign %x, bus %c %d to %d\n",
	    sc->sc_oberon ? "Oberon" : "Fire",
	    getpropint(sc->sc_node, "module-revision#", 0), sc->sc_ign,
	    busa ? 'A' : 'B', busranges[0], busranges[1]);

	printf("%s: ", sc->sc_dv.dv_xname);
	pyro_init_iommu(sc, pbm);

	pbm->pp_memt = pyro_alloc_mem_tag(pbm);
	pbm->pp_iot = pyro_alloc_io_tag(pbm);
	pbm->pp_cfgt = pyro_alloc_config_tag(pbm);
	pbm->pp_dmat = pyro_alloc_dma_tag(pbm);

	pyro_init_msi(sc, pbm);

	if (bus_space_map(pbm->pp_cfgt, 0, 0x10000000, 0, &pbm->pp_cfgh))
		panic("pyro: can't map config space");

	pbm->pp_pc = pyro_alloc_chipset(pbm, sc->sc_node, &_sparc_pci_chipset);

	pbm->pp_pc->bustag = pbm->pp_cfgt;
	pbm->pp_pc->bushandle = pbm->pp_cfgh;

	bzero(&pba, sizeof(pba));
	pba.pba_busname = "pci";
	pba.pba_domain = pci_ndomains++;
	pba.pba_bus = busranges[0];
	pba.pba_pc = pbm->pp_pc;
	pba.pba_flags = pbm->pp_flags;
	pba.pba_dmat = pbm->pp_dmat;
	pba.pba_memt = pbm->pp_memt;
	pba.pba_iot = pbm->pp_iot;
	pba.pba_pc->conf_size = pyro_conf_size;
	pba.pba_pc->conf_read = pyro_conf_read;
	pba.pba_pc->conf_write = pyro_conf_write;
	pba.pba_pc->intr_map = pyro_intr_map;

	free(busranges, M_DEVBUF, 0);

#ifdef DDB
	db_register_xir(pyro_xir, sc);
#endif

	config_found(&sc->sc_dv, &pba, pyro_print);
}

void
pyro_init_iommu(struct pyro_softc *sc, struct pyro_pbm *pbm)
{
	struct iommu_state *is = &pbm->pp_is;
	int tsbsize = 7;
	u_int32_t iobase = -1;
	char *name;

	is->is_bustag = sc->sc_bust;

	if (bus_space_subregion(is->is_bustag, sc->sc_csrh,
	    0x40000, 0x100, &is->is_iommu)) {
		panic("pyro: unable to create iommu handle");
	}

	is->is_sb[0] = &pbm->pp_sb;
	is->is_sb[0]->sb_bustag = is->is_bustag;

	name = (char *)malloc(32, M_DEVBUF, M_NOWAIT);
	if (name == NULL)
		panic("couldn't malloc iommu name");
	snprintf(name, 32, "%s dvma", sc->sc_dv.dv_xname);

	/* On Oberon, we need to flush the cache. */
	if (sc->sc_oberon)
		is->is_flags |= IOMMU_FLUSH_CACHE;

	iommu_init(name, is, tsbsize, iobase);
}

void
pyro_init_msi(struct pyro_softc *sc, struct pyro_pbm *pbm)
{
	u_int32_t msi_addr_range[3];
	u_int32_t msi_eq_devino[3] = { 0, 36, 24 };
	int ihandle;
	int msis, msi_eq_size;

	if (OF_getprop(sc->sc_node, "msi-address-ranges",
	    msi_addr_range, sizeof(msi_addr_range)) <= 0)
		return;
	pbm->pp_msiaddr = msi_addr_range[1];
	pbm->pp_msiaddr |= ((bus_addr_t)msi_addr_range[0]) << 32;

	msis = getpropint(sc->sc_node, "#msi", 256);
	pbm->pp_msi = mallocarray(msis, sizeof(*pbm->pp_msi), M_DEVBUF,
	    M_NOWAIT | M_ZERO);
	if (pbm->pp_msi == NULL)
		return;

	msi_eq_size = getpropint(sc->sc_node, "msi-eq-size", 256);
	pbm->pp_meq = msi_eq_alloc(pbm->pp_dmat, msi_eq_size);
	if (pbm->pp_meq == NULL)
		goto free_table;

	bzero(pbm->pp_meq->meq_va,
	    pbm->pp_meq->meq_nentries * sizeof(struct pyro_msi_msg));

	bus_space_write_8(sc->sc_bust, sc->sc_csrh, FIRE_EQ_BASE_ADDR,
	    pbm->pp_meq->meq_map->dm_segs[0].ds_addr);

	bus_space_write_8(sc->sc_bust, sc->sc_csrh,
	    FIRE_INTRMONDO_DATA0, sc->sc_ign);
	bus_space_write_8(sc->sc_bust, sc->sc_csrh,
	    FIRE_INTRMONDO_DATA1, 0);

	bus_space_write_8(sc->sc_bust, sc->sc_csrh, FIRE_MSI32_ADDR,
	    pbm->pp_msiaddr);

	bus_space_write_8(sc->sc_bust, sc->sc_csrh, FIRE_EQ_HEAD, 0);
	bus_space_write_8(sc->sc_bust, sc->sc_csrh, FIRE_EQ_TAIL, 0);

	OF_getprop(sc->sc_node, "msi-eq-to-devino",
	    msi_eq_devino, sizeof(msi_eq_devino));

	ihandle = msi_eq_devino[2] | sc->sc_ign;
	if (pyro_intr_establish(pbm->pp_memt, sc->sc_bust, ihandle,
	    IPL_HIGH, 0, pyro_msi_eq_intr, pbm, sc->sc_dv.dv_xname) == NULL)
		goto free_table;

	/* Enable EQ. */
	bus_space_write_8(sc->sc_bust, sc->sc_csrh, FIRE_EQ_CNTRL_SET,
	    FIRE_EQ_CTRL_SET_EN);

	pbm->pp_flags |= PCI_FLAGS_MSI_ENABLED;
	return;

free_table:
	free(pbm->pp_msi, M_DEVBUF, 0);
}

int
pyro_print(void *aux, const char *p)
{
	if (p == NULL)
		return (UNCONF);
	return (QUIET);
}

int
pyro_conf_size(pci_chipset_tag_t pc, pcitag_t tag)
{
	return PCIE_CONFIG_SPACE_SIZE;
}

pcireg_t
pyro_conf_read(pci_chipset_tag_t pc, pcitag_t tag, int reg)
{
	struct cpu_info *ci = curcpu();
	pcireg_t val;
	int s;

	s = splhigh();
	membar(Sync);
	ci->ci_pci_probe = 1;
	val = bus_space_read_4(pc->bustag, pc->bushandle,
	    (PCITAG_OFFSET(tag) << 4) + reg);
	membar(Sync);
	if (ci->ci_pci_fault)
		val = 0xffffffff;
	ci->ci_pci_probe = ci->ci_pci_fault = 0;
	splx(s);

	return (val);
}

void
pyro_conf_write(pci_chipset_tag_t pc, pcitag_t tag, int reg, pcireg_t data)
{
        bus_space_write_4(pc->bustag, pc->bushandle,
	    (PCITAG_OFFSET(tag) << 4) + reg, data);
}

/*
 * Bus-specific interrupt mapping
 */
int
pyro_intr_map(struct pci_attach_args *pa, pci_intr_handle_t *ihp)
{
	struct pyro_pbm *pp = pa->pa_pc->cookie;
	struct pyro_softc *sc = pp->pp_sc;
	u_int dev;

	if (*ihp != (pci_intr_handle_t)-1) {
		*ihp |= sc->sc_ign;
		return (0);
	}

	/*
	 * We didn't find a PROM mapping for this interrupt.  Try to
	 * construct one ourselves based on the swizzled interrupt pin
	 * and the interrupt mapping for PCI slots documented in the
	 * UltraSPARC-IIi User's Manual.
	 */

	if (pa->pa_intrpin == 0)
		return (-1);

	/*
	 * This deserves some documentation.  Should anyone
	 * have anything official looking, please speak up.
	 */
	dev = pa->pa_device - 1;

	*ihp = (pa->pa_intrpin - 1) & INTMAP_PCIINT;
	*ihp |= (dev << 2) & INTMAP_PCISLOT;
	*ihp |= sc->sc_ign;

	return (0);
}

bus_space_tag_t
pyro_alloc_mem_tag(struct pyro_pbm *pp)
{
	return (pyro_alloc_bus_tag(pp, "mem",
	    0x02,       /* 32-bit mem space (where's the #define???) */
	    ASI_PRIMARY, ASI_PRIMARY_LITTLE));
}

bus_space_tag_t
pyro_alloc_io_tag(struct pyro_pbm *pp)
{
	return (pyro_alloc_bus_tag(pp, "io",
	    0x01,       /* IO space (where's the #define???) */
	    ASI_PHYS_NON_CACHED_LITTLE, ASI_PHYS_NON_CACHED));
}

bus_space_tag_t
pyro_alloc_config_tag(struct pyro_pbm *pp)
{
	return (pyro_alloc_bus_tag(pp, "cfg",
	    0x00,       /* Config space (where's the #define???) */
	    ASI_PHYS_NON_CACHED_LITTLE, ASI_PHYS_NON_CACHED));
}

bus_space_tag_t
pyro_alloc_bus_tag(struct pyro_pbm *pbm, const char *name, int ss,
    int asi, int sasi)
{
	struct pyro_softc *sc = pbm->pp_sc;
	struct sparc_bus_space_tag *bt;

	bt = malloc(sizeof(*bt), M_DEVBUF, M_NOWAIT | M_ZERO);
	if (bt == NULL)
		panic("pyro: could not allocate bus tag");

	snprintf(bt->name, sizeof(bt->name), "%s-pbm_%s(%d/%2.2x)",
	    sc->sc_dv.dv_xname, name, ss, asi);

	bt->cookie = pbm;
	bt->parent = sc->sc_bust;
	bt->default_type = ss;
	bt->asi = asi;
	bt->sasi = sasi;
	bt->sparc_bus_map = pyro_bus_map;
	bt->sparc_bus_mmap = pyro_bus_mmap;
	bt->sparc_intr_establish = pyro_intr_establish;
	return (bt);
}

bus_dma_tag_t
pyro_alloc_dma_tag(struct pyro_pbm *pbm)
{
	struct pyro_softc *sc = pbm->pp_sc;
	bus_dma_tag_t dt, pdt = sc->sc_dmat;

	dt = malloc(sizeof(*dt), M_DEVBUF, M_NOWAIT | M_ZERO);
	if (dt == NULL)
		panic("pyro: could not alloc dma tag");

	dt->_cookie = pbm;
	dt->_parent = pdt;
	dt->_dmamap_create	= pyro_dmamap_create;
	dt->_dmamap_destroy	= iommu_dvmamap_destroy;
	dt->_dmamap_load	= iommu_dvmamap_load;
	dt->_dmamap_load_raw	= iommu_dvmamap_load_raw;
	dt->_dmamap_unload	= iommu_dvmamap_unload;
	dt->_dmamap_sync	= iommu_dvmamap_sync;
	dt->_dmamem_alloc	= iommu_dvmamem_alloc;
	dt->_dmamem_free	= iommu_dvmamem_free;
	return (dt);
}

pci_chipset_tag_t
pyro_alloc_chipset(struct pyro_pbm *pbm, int node, pci_chipset_tag_t pc)
{
	pci_chipset_tag_t npc;

	npc = malloc(sizeof *npc, M_DEVBUF, M_NOWAIT);
	if (npc == NULL)
		panic("pyro: could not allocate pci_chipset_tag_t");
	memcpy(npc, pc, sizeof *pc);
	npc->cookie = pbm;
	npc->rootnode = node;
	return (npc);
}

int
pyro_dmamap_create(bus_dma_tag_t t, bus_dma_tag_t t0, bus_size_t size,
    int nsegments, bus_size_t maxsegsz, bus_size_t boundary, int flags,
    bus_dmamap_t *dmamp)
{
	struct pyro_pbm *pp = t->_cookie;

	return (iommu_dvmamap_create(t, t0, &pp->pp_sb, size, nsegments,
	    maxsegsz, boundary, flags, dmamp));
}

int
pyro_bus_map(bus_space_tag_t t, bus_space_tag_t t0, bus_addr_t offset,
    bus_size_t size, int flags, bus_space_handle_t *hp)
{
	struct pyro_pbm *pbm = t->cookie;
	int i, ss;

	DPRINTF(PDB_BUSMAP, ("pyro_bus_map: type %d off %qx sz %qx flags %d",
	    t->default_type,
	    (unsigned long long)offset,
	    (unsigned long long)size,
	    flags));

	ss = t->default_type;
	DPRINTF(PDB_BUSMAP, (" cspace %d", ss));

	if (t->parent == 0 || t->parent->sparc_bus_map == 0) {
		printf("\npyro_bus_map: invalid parent");
		return (EINVAL);
	}

	if (flags & BUS_SPACE_MAP_PROMADDRESS) {
		return ((*t->parent->sparc_bus_map)
		    (t, t0, offset, size, flags, hp));
	}

	for (i = 0; i < pbm->pp_nrange; i++) {
		bus_addr_t paddr;

		if (((pbm->pp_range[i].cspace >> 24) & 0x03) != ss)
			continue;

		paddr = pbm->pp_range[i].phys_lo + offset;
		paddr |= ((bus_addr_t)pbm->pp_range[i].phys_hi) << 32;
		return ((*t->parent->sparc_bus_map)
		    (t, t0, paddr, size, flags, hp));
	}

	return (EINVAL);
}

paddr_t
pyro_bus_mmap(bus_space_tag_t t, bus_space_tag_t t0, bus_addr_t paddr,
    off_t off, int prot, int flags)
{
	bus_addr_t offset = paddr;
	struct pyro_pbm *pbm = t->cookie;
	int i, ss;

	ss = t->default_type;

	DPRINTF(PDB_BUSMAP, ("pyro_bus_mmap: prot %d flags %d pa %qx\n",
	    prot, flags, (unsigned long long)paddr));

	if (t->parent == 0 || t->parent->sparc_bus_mmap == 0) {
		printf("\npyro_bus_mmap: invalid parent");
		return (-1);
	}

	for (i = 0; i < pbm->pp_nrange; i++) {
		bus_addr_t paddr;

		if (((pbm->pp_range[i].cspace >> 24) & 0x03) != ss)
			continue;

		paddr = pbm->pp_range[i].phys_lo + offset;
		paddr |= ((bus_addr_t)pbm->pp_range[i].phys_hi) << 32;
		return ((*t->parent->sparc_bus_mmap)
		    (t, t0, paddr, off, prot, flags));
	}

	return (-1);
}

void *
pyro_intr_establish(bus_space_tag_t t, bus_space_tag_t t0, int ihandle,
    int level, int flags, int (*handler)(void *), void *arg, const char *what)
{
	struct pyro_pbm *pbm = t->cookie;
	struct pyro_softc *sc = pbm->pp_sc;
	struct intrhand *ih = NULL;
	volatile u_int64_t *intrmapptr = NULL, *intrclrptr = NULL;
	int ino;

	if (ihandle & PCI_INTR_MSI) {
		pci_chipset_tag_t pc = pbm->pp_pc;
		pcitag_t tag = ihandle & ~PCI_INTR_MSI;
		int msinum = pbm->pp_msinum++;
		u_int64_t reg;

		ih = bus_intr_allocate(t0, handler, arg, ihandle, level,
		     NULL, NULL, what);
		if (ih == NULL)
			return (NULL);

		if (ih->ih_name)
			evcount_attach(&ih->ih_count, ih->ih_name, NULL);
		else
			evcount_attach(&ih->ih_count, "unknown", NULL);

		ih->ih_ack = pyro_msi_ack;

		pbm->pp_msi[msinum] = ih;
		ih->ih_number = msinum;

		if (flags & BUS_INTR_ESTABLISH_MPSAFE)
			ih->ih_mpsafe = 1;

		pci_msi_enable(pc, tag, pbm->pp_msiaddr, msinum);

		/* Map MSI to the right EQ and mark it as valid. */
		reg = bus_space_read_8(sc->sc_bust, sc->sc_csrh,
		    FIRE_MSI_MAP + msinum * 8);
		reg &= ~FIRE_MSI_MAP_EQNUM;
		bus_space_write_8(sc->sc_bust, sc->sc_csrh,
		    FIRE_MSI_MAP + msinum * 8, reg);

		bus_space_write_8(sc->sc_bust, sc->sc_csrh,
		    FIRE_MSI_CLEAR + msinum * 8, FIRE_MSI_CLEAR_EQWR_N);

		reg = bus_space_read_8(sc->sc_bust, sc->sc_csrh,
		    FIRE_MSI_MAP + msinum * 8);
		reg |= FIRE_MSI_MAP_V;
		bus_space_write_8(sc->sc_bust, sc->sc_csrh,
		    FIRE_MSI_MAP + msinum * 8, reg);

		return (ih);
	}

	ino = INTINO(ihandle);

	if (level == IPL_NONE)
		level = INTLEV(ihandle);
	if (level == IPL_NONE) {
		printf(": no IPL, setting IPL 2.\n");
		level = 2;
	}

	if ((flags & BUS_INTR_ESTABLISH_SOFTINTR) == 0) {
		u_int64_t *imap, *iclr;

		imap = bus_space_vaddr(sc->sc_bust, sc->sc_csrh) + 0x1000;
		iclr = bus_space_vaddr(sc->sc_bust, sc->sc_csrh) + 0x1400;
		intrmapptr = &imap[ino];
		intrclrptr = &iclr[ino];
		ino |= INTVEC(ihandle);
	}

	ih = bus_intr_allocate(t0, handler, arg, ino, level, intrmapptr,
	    intrclrptr, what);
	if (ih == NULL)
		return (NULL);

	if (flags & BUS_INTR_ESTABLISH_MPSAFE)
		ih->ih_mpsafe = 1;

	intr_establish(ih->ih_pil, ih);

	if (intrmapptr != NULL) {
		u_int64_t intrmap;

		intrmap = *intrmapptr;
		intrmap &= ~FIRE_INTRMAP_INT_CNTRL_NUM_MASK;
		intrmap |= FIRE_INTRMAP_INT_CNTRL_NUM0;
		if (sc->sc_oberon) {
			intrmap &= ~OBERON_INTRMAP_T_DESTID_MASK;
			intrmap |= CPU_JUPITERID <<
			    OBERON_INTRMAP_T_DESTID_SHIFT;
		} else {
			intrmap &= ~FIRE_INTRMAP_T_JPID_MASK;
			intrmap |= CPU_UPAID << FIRE_INTRMAP_T_JPID_SHIFT;
		}
		intrmap |= INTMAP_V;
		*intrmapptr = intrmap;
		intrmap = *intrmapptr;
		ih->ih_number |= intrmap & INTMAP_INR;
	}

	return (ih);
}

void
pyro_msi_ack(struct intrhand *ih)
{
}

int
pyro_msi_eq_intr(void *arg)
{
	struct pyro_pbm *pbm = arg;
	struct pyro_softc *sc = pbm->pp_sc;
	struct msi_eq *meq = pbm->pp_meq;
	struct pyro_msi_msg *msg;
	uint64_t head, tail;
	struct intrhand *ih;
	int msinum;

	head = bus_space_read_8(sc->sc_bust, sc->sc_csrh, FIRE_EQ_HEAD);
	tail = bus_space_read_8(sc->sc_bust, sc->sc_csrh, FIRE_EQ_TAIL);

	if (head == tail)
		return (0);

	while (head != tail) {
		msg = (struct pyro_msi_msg *)meq->meq_va;

		if (msg[head].mm_type == 0)
			break;
		msg[head].mm_type = 0;

		msinum = msg[head].mm_data;
		ih = pbm->pp_msi[msinum];
		bus_space_write_8(sc->sc_bust, sc->sc_csrh,
		    FIRE_MSI_CLEAR + msinum * 8, FIRE_MSI_CLEAR_EQWR_N);

		send_softint(-1, ih->ih_pil, ih);

		head += 1;
		head &= (meq->meq_nentries - 1);
	}

	bus_space_write_8(sc->sc_bust, sc->sc_csrh, FIRE_EQ_HEAD, head);

	return (1);
}

#ifdef DDB
void
pyro_xir(void *arg, int cpu)
{
	struct pyro_softc *sc = arg;

	bus_space_write_8(sc->sc_bust, sc->sc_xbch, FIRE_RESET_GEN,
	    FIRE_RESET_GEN_XIR);
}
#endif

const struct cfattach pyro_ca = {
	sizeof(struct pyro_softc), pyro_match, pyro_attach
};

struct cfdriver pyro_cd = {
	NULL, "pyro", DV_DULL
};


code:
/*	$OpenBSD: pyrovar.h,v 1.4 2011/07/06 05:48:57 kettenis Exp $	*/

/*
 * Copyright (c) 2007 Mark Kettenis
 * 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 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.
 */

struct pyro_msi_msg {
	uint8_t		mm_type;
	uint8_t		mm_reserved[3];
	uint16_t	mm_reqid;
	uint16_t	mm_data;
	uint64_t	mm_reserved2[7];
};

struct pyro_range {
	u_int32_t	cspace;
	u_int32_t	child_hi;
	u_int32_t	child_lo;
	u_int32_t	phys_hi;
	u_int32_t	phys_lo;
	u_int32_t	size_hi;
	u_int32_t	size_lo;
};

struct pyro_pbm {
	struct pyro_softc *pp_sc;

	struct pyro_range *pp_range;
	pci_chipset_tag_t pp_pc;
	int pp_nrange;

	bus_space_tag_t		pp_memt;
	bus_space_tag_t		pp_iot;
	bus_space_tag_t		pp_cfgt;
	bus_space_handle_t	pp_cfgh;
	bus_dma_tag_t		pp_dmat;
	int			pp_bus_a;
	struct iommu_state	pp_is;
	struct strbuf_ctl	pp_sb;

	struct msi_eq *pp_meq;
	bus_addr_t pp_msiaddr;
	int pp_msinum;
	struct intrhand **pp_msi;

	int pp_flags;
};

struct pyro_softc {
	struct device sc_dv;
	int sc_node;
	int sc_ign;
	bus_dma_tag_t sc_dmat;
	bus_space_tag_t sc_bust;
	bus_addr_t sc_csr, sc_xbc;
	bus_space_handle_t sc_csrh, sc_xbch;

	int sc_oberon;
};


code:
/*	$OpenBSD: radeonfb.c,v 1.5 2013/10/20 20:07:27 miod Exp $	*/

/*
 * Copyright (c) 2009 Mark Kettenis.
 *
 * 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/device.h>
#include <sys/pciio.h>
#include <sys/systm.h>

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

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

#include <dev/wscons/wsconsio.h>
#include <dev/wscons/wsdisplayvar.h>

#include <dev/rasops/rasops.h>

#include <machine/fbvar.h>

#define RADEON_PCI_MEM		0x10
#define RADEON_PCI_MMIO		0x18

#define RADEON_PALETTE_INDEX		0x00b0
#define RADEON_PALETTE_DATA		0x00b4

#define RADEON_CRTC_OFFSET		0x0224

#define RADEON_SURFACE_CNTL		0x0b00
#define  RADEON_NONSURF_AP0_SWP_16BPP	0x00100000
#define  RADEON_NONSURF_AP0_SWP_32BPP	0x00200000
#define  RADEON_NONSURF_AP1_SWP_16BPP	0x00400000
#define  RADEON_NONSURF_AP1_SWP_32BPP	0x00800000

#define RADEON_RBBM_STATUS		0x0e40
#define  RADEON_RBBM_FIFOCNT_MASK	0x0000007f
#define  RADEON_RBBM_ACTIVE		0x80000000

#define RADEON_SRC_Y_X			0x1434
#define RADEON_DST_Y_X			0x1438
#define RADEON_DST_HEIGHT_WIDTH		0x143c

#define RADEON_DP_GUI_MASTER_CNTL	0x146c
#define  RADEON_GMC_DST_8BPP		0x00000200
#define  RADEON_GMC_DST_32BPP		0x00000600
#define  RADEON_GMC_BRUSH_NONE		0x000000e0
#define  RADEON_GMC_BRUSH_SOLID_COLOR	0x000000d0
#define  RADEON_GMC_SRC_DATATYPE_COLOR	0x00003000
#define  RADEON_GMC_SRC_SOURCE_MEMORY	0x02000000
#define  RADEON_ROP3_S			0x00cc0000
#define  RADEON_ROP3_P			0x00f00000
#define  RADEON_GMC_CLR_CMP_CNTL_DIS    0x10000000

#define RADEON_DP_BRUSH_BKGD_CLR	0x1478
#define RADEON_DP_BRUSH_FRGD_CLR	0x147c

#define RADEON_DP_CNTL			0x16c0
#define  RADEON_DST_X_LEFT_TO_RIGHT	0x00000001
#define  RADEON_DST_Y_TOP_TO_BOTTOM	0x00000002
#define RADEON_DP_WRITE_MASK		0x16cc

#define RADEON_DEFAULT_PITCH_OFFSET	0x16e0
#define RADEON_DEFAULT_SC_BOTTOM_RIGHT	0x16e8

#define RADEON_WAIT_UNTIL		0x1720
#define  RADEON_WAIT_2D_IDLECLEAN	0x00010000
#define  RADEON_WAIT_3D_IDLECLEAN	0x00020000
#define  RADEON_WAIT_HOST_IDLECLEAN	0x00040000

#define RADEON_RB3D_DSTCACHE_CTLSTAT	0x325c
#define  RADEON_RB3D_DC_FLUSH_ALL	0x0000000f
#define  RADEON_RB3D_DC_BUSY		0x80000000

#define RADEON_COORDS(x, y)	((y << 16) | (x))

#ifdef APERTURE
extern int allowaperture;
#endif

struct radeonfb_softc {
	struct sunfb	sc_sunfb;

	bus_space_tag_t sc_memt;
	bus_space_handle_t sc_memh;
	bus_addr_t	sc_membase;
	bus_size_t	sc_memsize;
	bus_size_t	sc_memoff;

	bus_space_tag_t	sc_mmiot;
	bus_space_handle_t sc_mmioh;
	bus_addr_t	sc_mmiobase;
	bus_size_t	sc_mmiosize;

	pcitag_t	sc_pcitag;

	int		sc_mode;
	u_int8_t	sc_cmap_red[256];
	u_int8_t	sc_cmap_green[256];
	u_int8_t	sc_cmap_blue[256];
};

int	radeonfb_ioctl(void *, u_long, caddr_t, int, struct proc *);
paddr_t	radeonfb_mmap(void *, off_t, int);

struct wsdisplay_accessops radeonfb_accessops = {
	.ioctl = radeonfb_ioctl,
	.mmap = radeonfb_mmap
};

int	radeonfb_match(struct device *, void *, void *);
void	radeonfb_attach(struct device *, struct device *, void *);

struct cfattach radeonfb_ca = {
	sizeof(struct radeonfb_softc), radeonfb_match, radeonfb_attach
};

struct cfdriver radeonfb_cd = {
	NULL, "radeonfb", DV_DULL
};

int	radeonfb_is_console(int);
int	radeonfb_getcmap(struct radeonfb_softc *, struct wsdisplay_cmap *);
int	radeonfb_putcmap(struct radeonfb_softc *, struct wsdisplay_cmap *);
void	radeonfb_setcolor(void *, u_int, u_int8_t, u_int8_t, u_int8_t);

int	radeonfb_copycols(void *, int, int, int, int);
int	radeonfb_erasecols(void *, int, int, int, long);
int	radeonfb_copyrows(void *, int, int, int);
int	radeonfb_eraserows(void *, int, int, long);

void	radeonfb_init(struct radeonfb_softc *);
void	radeonfb_wait_fifo(struct radeonfb_softc *, int);
void	radeonfb_wait(struct radeonfb_softc *);
void	radeonfb_copyrect(struct radeonfb_softc *, int, int, int, int, int, int);
void	radeonfb_fillrect(struct radeonfb_softc *, int, int, int, int, int);

int
radeonfb_match(struct device *parent, void *cf, void *aux)
{
	struct pci_attach_args *pa = aux;
	char buf[32];
	int node;

	node = PCITAG_NODE(pa->pa_tag);
	OF_getprop(node, "name", buf, sizeof(buf));
	if (strcmp(buf, "SUNW,XVR-100") == 0 ||
	    strcmp(buf, "SUNW,XVR-300") == 0)
		return (10);

	return (0);
}

void
radeonfb_attach(struct device *parent, struct device *self, void *aux)
{
	struct radeonfb_softc *sc = (struct radeonfb_softc *)self;
	struct pci_attach_args *pa = aux;
	struct rasops_info *ri;
	int node, console, flags;
	char *model;

	sc->sc_pcitag = pa->pa_tag;

	node = PCITAG_NODE(pa->pa_tag);
	console = radeonfb_is_console(node);

	printf("\n");

	model = getpropstring(node, "model");
	printf("%s: %s", self->dv_xname, model);

	if (pci_mapreg_map(pa, RADEON_PCI_MEM, PCI_MAPREG_TYPE_MEM,
	    BUS_SPACE_MAP_LINEAR, &sc->sc_memt, &sc->sc_memh,
	    &sc->sc_membase, &sc->sc_memsize, 0)) {
		printf("\n%s: can't map video memory\n", self->dv_xname);
		return;
	}

	if (pci_mapreg_map(pa, RADEON_PCI_MMIO, PCI_MAPREG_TYPE_MEM, 0,
	    &sc->sc_mmiot, &sc->sc_mmioh, &sc->sc_mmiobase,
	    &sc->sc_mmiosize, 0)) {
		printf("\n%s: can't map registers\n", self->dv_xname);
		return;
	}

	fb_setsize(&sc->sc_sunfb, 8, 1152, 900, node, 0);

	printf(", %dx%d\n", sc->sc_sunfb.sf_width, sc->sc_sunfb.sf_height);

	/*
	 * The firmware sets up the framebuffer such that at starts at
	 * an offset from the start of video memory.
	 */
	sc->sc_memoff =
	    bus_space_read_4(sc->sc_mmiot, sc->sc_mmioh, RADEON_CRTC_OFFSET);

	ri = &sc->sc_sunfb.sf_ro;
	ri->ri_bits = bus_space_vaddr(sc->sc_memt, sc->sc_memh);
	ri->ri_bits += sc->sc_memoff;
	ri->ri_hw = sc;

	if (sc->sc_sunfb.sf_depth == 32)
		flags = 0;
	else
		flags = RI_BSWAP;

	fbwscons_init(&sc->sc_sunfb, flags, console);
	fbwscons_setcolormap(&sc->sc_sunfb, radeonfb_setcolor);
	sc->sc_mode = WSDISPLAYIO_MODE_EMUL;

	radeonfb_init(sc);
	ri->ri_ops.copyrows = radeonfb_copyrows;
	ri->ri_ops.copycols = radeonfb_copycols;
	ri->ri_ops.eraserows = radeonfb_eraserows;
	ri->ri_ops.erasecols = radeonfb_erasecols;

	if (console)
		fbwscons_console_init(&sc->sc_sunfb, -1);
	fbwscons_attach(&sc->sc_sunfb, &radeonfb_accessops, console);
}

int
radeonfb_ioctl(void *v, u_long cmd, caddr_t data, int flags, struct proc *p)
{
	struct radeonfb_softc *sc = v;
	struct wsdisplay_fbinfo *wdf;
	struct pcisel *sel;

	switch (cmd) {
	case WSDISPLAYIO_GTYPE:
		*(u_int *)data = WSDISPLAY_TYPE_RADEONFB;
		break;
	case WSDISPLAYIO_SMODE:
		sc->sc_mode = *(u_int *)data;
		if (sc->sc_mode == WSDISPLAYIO_MODE_EMUL) {
			struct rasops_info *ri = &sc->sc_sunfb.sf_ro;

			/* Restore colormap. */
			fbwscons_setcolormap(&sc->sc_sunfb, radeonfb_setcolor);

			/* Clear screen. */
			radeonfb_init(sc);
			radeonfb_fillrect(sc, 0, 0, ri->ri_width,
			    ri->ri_height, ri->ri_devcmap[WSCOL_WHITE]);
		}
		break;

	case WSDISPLAYIO_GINFO:
		wdf = (void *)data;
		wdf->height = sc->sc_sunfb.sf_height;
		wdf->width  = sc->sc_sunfb.sf_width;
		wdf->depth  = sc->sc_sunfb.sf_depth;
		if (sc->sc_sunfb.sf_depth == 32)
			wdf->cmsize = 0;
		else
			wdf->cmsize = 256;
		break;
	case WSDISPLAYIO_GETSUPPORTEDDEPTH:
		if (sc->sc_sunfb.sf_depth == 32)
			*(u_int *)data = WSDISPLAYIO_DEPTH_24_32;
		else
			return (-1);
		break;
	case WSDISPLAYIO_LINEBYTES:
		*(u_int *)data = sc->sc_sunfb.sf_linebytes;
		break;

	case WSDISPLAYIO_GETCMAP:
		return radeonfb_getcmap(sc, (struct wsdisplay_cmap *)data);
	case WSDISPLAYIO_PUTCMAP:
		return radeonfb_putcmap(sc, (struct wsdisplay_cmap *)data);

	case WSDISPLAYIO_GPCIID:
		sel = (struct pcisel *)data;
		sel->pc_bus = PCITAG_BUS(sc->sc_pcitag);
		sel->pc_dev = PCITAG_DEV(sc->sc_pcitag);
		sel->pc_func = PCITAG_FUN(sc->sc_pcitag);
		break;

	case WSDISPLAYIO_SVIDEO:
	case WSDISPLAYIO_GVIDEO:
		break;

	case WSDISPLAYIO_GCURPOS:
	case WSDISPLAYIO_SCURPOS:
	case WSDISPLAYIO_GCURMAX:
	case WSDISPLAYIO_GCURSOR:
	case WSDISPLAYIO_SCURSOR:
	default:
		return -1; /* not supported yet */
        }

	return (0);
}

paddr_t
radeonfb_mmap(void *v, off_t off, int prot)
{
	struct radeonfb_softc *sc = v;

	if (off & PGOFSET)
		return (-1);

	switch (sc->sc_mode) {
	case WSDISPLAYIO_MODE_MAPPED:
#ifdef APERTURE
		if (allowaperture == 0)
			return (-1);
#endif

		if (sc->sc_mmiosize == 0)
			return (-1);

		if (off >= sc->sc_membase &&
		    off < (sc->sc_membase + sc->sc_memsize))
			return (bus_space_mmap(sc->sc_memt,
			    sc->sc_membase, off - sc->sc_membase,
			    prot, BUS_SPACE_MAP_LINEAR));

		if (off >= sc->sc_mmiobase &&
		    off < (sc->sc_mmiobase + sc->sc_mmiosize))
			return (bus_space_mmap(sc->sc_mmiot,
			    sc->sc_mmiobase, off - sc->sc_mmiobase,
			    prot, BUS_SPACE_MAP_LINEAR));
		break;

	case WSDISPLAYIO_MODE_DUMBFB:
		if ((sc->sc_memoff % PAGE_SIZE) != 0)
			return (-1);

		if (off >= 0 && off < sc->sc_memsize / 2) {
			bus_addr_t base = sc->sc_membase + sc->sc_memoff;

			/*
			 * In 32bpp mode, use the second aperture,
			 * which has been set up by the firmware to do
			 * proper byte swapping.
			 */
			if (sc->sc_sunfb.sf_depth == 32)
				base += sc->sc_memsize / 2;

			return (bus_space_mmap(sc->sc_memt, base, off,
			    prot, BUS_SPACE_MAP_LINEAR));
		}
		break;
	}

	return (-1);
}

int
radeonfb_is_console(int node)
{
	extern int fbnode;

	return (fbnode == node);
}

int
radeonfb_getcmap(struct radeonfb_softc *sc, struct wsdisplay_cmap *cm)
{
	u_int index = cm->index;
	u_int count = cm->count;
	int error;

	if (index >= 256 || count > 256 - index)
		return (EINVAL);

	error = copyout(&sc->sc_cmap_red[index], cm->red, count);
	if (error)
		return (error);
	error = copyout(&sc->sc_cmap_green[index], cm->green, count);
	if (error)
		return (error);
	error = copyout(&sc->sc_cmap_blue[index], cm->blue, count);
	if (error)
		return (error);
	return (0);
}

int
radeonfb_putcmap(struct radeonfb_softc *sc, struct wsdisplay_cmap *cm)
{
	u_int index = cm->index;
	u_int count = cm->count;
	u_int i;
	int error;
	u_char *r, *g, *b;

	if (index >= 256 || count > 256 - index)
		return (EINVAL);

	if ((error = copyin(cm->red, &sc->sc_cmap_red[index], count)) != 0)
		return (error);
	if ((error = copyin(cm->green, &sc->sc_cmap_green[index], count)) != 0)
		return (error);
	if ((error = copyin(cm->blue, &sc->sc_cmap_blue[index], count)) != 0)
		return (error);

	r = &sc->sc_cmap_red[index];
	g = &sc->sc_cmap_green[index];
	b = &sc->sc_cmap_blue[index];

	bus_space_write_4(sc->sc_mmiot, sc->sc_mmioh,
	    RADEON_PALETTE_INDEX, index);
	for (i = 0; i < count; i++) {
		bus_space_write_4(sc->sc_mmiot, sc->sc_mmioh,
		    RADEON_PALETTE_DATA, (*r << 16) | (*g << 8) | *b);
		r++, g++, b++;
	}
	return (0);
}

void
radeonfb_setcolor(void *v, u_int index, u_int8_t r, u_int8_t g, u_int8_t b)
{
	struct radeonfb_softc *sc = v;

	sc->sc_cmap_red[index] = r;
	sc->sc_cmap_green[index] = g;
	sc->sc_cmap_blue[index] = b;

	bus_space_write_4(sc->sc_mmiot, sc->sc_mmioh,
	    RADEON_PALETTE_INDEX, index);
	bus_space_write_4(sc->sc_mmiot, sc->sc_mmioh,
	    RADEON_PALETTE_DATA, (r << 16) | (g << 8) | b);
}

/*
 * Accelerated routines.
 */

int
radeonfb_copycols(void *cookie, int row, int src, int dst, int num)
{
	struct rasops_info *ri = cookie;
	struct radeonfb_softc *sc = ri->ri_hw;

	num *= ri->ri_font->fontwidth;
	src *= ri->ri_font->fontwidth;
	dst *= ri->ri_font->fontwidth;
	row *= ri->ri_font->fontheight;

	radeonfb_copyrect(sc, ri->ri_xorigin + src, ri->ri_yorigin + row,
	    ri->ri_xorigin + dst, ri->ri_yorigin + row,
	    num, ri->ri_font->fontheight);

	return 0;
}

int
radeonfb_erasecols(void *cookie, int row, int col, int num, long attr)
{
	struct rasops_info *ri = cookie;
	struct radeonfb_softc *sc = ri->ri_hw;
	int bg, fg;

	ri->ri_ops.unpack_attr(cookie, attr, &fg, &bg, NULL);

	row *= ri->ri_font->fontheight;
	col *= ri->ri_font->fontwidth;
	num *= ri->ri_font->fontwidth;

	radeonfb_fillrect(sc, ri->ri_xorigin + col, ri->ri_yorigin + row,
	    num, ri->ri_font->fontheight, ri->ri_devcmap[bg]);

	return 0;
}

int
radeonfb_copyrows(void *cookie, int src, int dst, int num)
{
	struct rasops_info *ri = cookie;
	struct radeonfb_softc *sc = ri->ri_hw;

	num *= ri->ri_font->fontheight;
	src *= ri->ri_font->fontheight;
	dst *= ri->ri_font->fontheight;

	radeonfb_copyrect(sc, ri->ri_xorigin, ri->ri_yorigin + src,
	    ri->ri_xorigin, ri->ri_yorigin + dst, ri->ri_emuwidth, num);

	return 0;
}

int
radeonfb_eraserows(void *cookie, int row, int num, long attr)
{
	struct rasops_info *ri = cookie;
	struct radeonfb_softc *sc = ri->ri_hw;
	int bg, fg;
	int x, y, w;

	ri->ri_ops.unpack_attr(cookie, attr, &fg, &bg, NULL);

	if ((num == ri->ri_rows) && ISSET(ri->ri_flg, RI_FULLCLEAR)) {
		num = ri->ri_height;
		x = y = 0;
		w = ri->ri_width;
	} else {
		num *= ri->ri_font->fontheight;
		x = ri->ri_xorigin;
		y = ri->ri_yorigin + row * ri->ri_font->fontheight;
		w = ri->ri_emuwidth;
	}
	radeonfb_fillrect(sc, x, y, w, num, ri->ri_devcmap[bg]);

	return 0;
}

void
radeonfb_init(struct radeonfb_softc *sc)
{
	radeonfb_wait_fifo(sc, 2);
	bus_space_write_4(sc->sc_mmiot, sc->sc_mmioh,
	    RADEON_DEFAULT_PITCH_OFFSET,
	    ((sc->sc_sunfb.sf_linebytes >> 6) << 22) | (sc->sc_memoff >> 10));
	bus_space_write_4(sc->sc_mmiot, sc->sc_mmioh,
	    RADEON_DEFAULT_SC_BOTTOM_RIGHT, 0x1fff1fff);
}

void
radeonfb_wait_fifo(struct radeonfb_softc *sc, int n)
{
	int i;

	for (i = 1000000; i != 0; i--) {
		if ((bus_space_read_4(sc->sc_mmiot, sc->sc_mmioh,
		    RADEON_RBBM_STATUS) & RADEON_RBBM_FIFOCNT_MASK) >= n)
			break;
		DELAY(1);
	}
}

void
radeonfb_wait(struct radeonfb_softc *sc)
{
	int i;

	radeonfb_wait_fifo(sc, 64);

	for (i = 1000000; i != 0; i--) {
		if ((bus_space_read_4(sc->sc_mmiot, sc->sc_mmioh,
		    RADEON_RBBM_STATUS) & RADEON_RBBM_ACTIVE) == 0)
			break;
		DELAY(1);
	}

	bus_space_write_4(sc->sc_mmiot, sc->sc_mmioh,
	    RADEON_RB3D_DSTCACHE_CTLSTAT, RADEON_RB3D_DC_FLUSH_ALL);

	for (i = 1000000; i != 0; i--) {
		if ((bus_space_read_4(sc->sc_mmiot, sc->sc_mmioh,
		    RADEON_RB3D_DSTCACHE_CTLSTAT) & RADEON_RB3D_DC_BUSY) == 0)
			break;
		DELAY(1);
	}
}

void
radeonfb_copyrect(struct radeonfb_softc *sc, int sx, int sy, int dx, int dy,
    int w, int h)
{
	uint32_t gmc;
	uint32_t dir;

	radeonfb_wait_fifo(sc, 1);
	bus_space_write_4(sc->sc_mmiot, sc->sc_mmioh, RADEON_WAIT_UNTIL,
	    RADEON_WAIT_HOST_IDLECLEAN | RADEON_WAIT_2D_IDLECLEAN);

	if (dy < sy) {
		dir = RADEON_DST_Y_TOP_TO_BOTTOM;
	} else {
		sy += h - 1;
		dy += h - 1;
		dir = 0;
	}
	if (dx < sx) {
		dir |= RADEON_DST_X_LEFT_TO_RIGHT;
	} else {
		sx += w - 1;
		dx += w - 1;
	}

	radeonfb_wait_fifo(sc, 6);

	if (sc->sc_sunfb.sf_depth == 32)
		gmc = RADEON_GMC_DST_32BPP;
	else
		gmc = RADEON_GMC_DST_8BPP;
	gmc |= RADEON_GMC_BRUSH_NONE;
	gmc |= RADEON_GMC_SRC_DATATYPE_COLOR;
	gmc |= RADEON_GMC_SRC_SOURCE_MEMORY;
	gmc |= RADEON_ROP3_S;
	gmc |= RADEON_GMC_CLR_CMP_CNTL_DIS;
	bus_space_write_4(sc->sc_mmiot, sc->sc_mmioh,
	    RADEON_DP_GUI_MASTER_CNTL, gmc);
	bus_space_write_4(sc->sc_mmiot, sc->sc_mmioh,
	    RADEON_DP_WRITE_MASK, 0xffffffff);
	bus_space_write_4(sc->sc_mmiot, sc->sc_mmioh,
	    RADEON_DP_CNTL, dir);

	bus_space_write_4(sc->sc_mmiot, sc->sc_mmioh,
	    RADEON_SRC_Y_X, RADEON_COORDS(sx, sy));
	bus_space_write_4(sc->sc_mmiot, sc->sc_mmioh,
	    RADEON_DST_Y_X, RADEON_COORDS(dx, dy));
	bus_space_write_4(sc->sc_mmiot, sc->sc_mmioh,
	    RADEON_DST_HEIGHT_WIDTH, RADEON_COORDS(w, h));

	radeonfb_wait(sc);
}

void
radeonfb_fillrect(struct radeonfb_softc *sc, int x, int y, int w, int h,
    int color)
{
	uint32_t gmc;

	radeonfb_wait_fifo(sc, 1);
	bus_space_write_4(sc->sc_mmiot, sc->sc_mmioh, RADEON_WAIT_UNTIL,
	    RADEON_WAIT_HOST_IDLECLEAN | RADEON_WAIT_2D_IDLECLEAN);

	radeonfb_wait_fifo(sc, 6);

	if (sc->sc_sunfb.sf_depth == 32)
		gmc = RADEON_GMC_DST_32BPP;
	else
		gmc = RADEON_GMC_DST_8BPP;
	gmc |= RADEON_GMC_BRUSH_SOLID_COLOR;
	gmc |= RADEON_GMC_SRC_DATATYPE_COLOR;
	gmc |= RADEON_ROP3_P;
	gmc |= RADEON_GMC_CLR_CMP_CNTL_DIS;
	bus_space_write_4(sc->sc_mmiot, sc->sc_mmioh,
	    RADEON_DP_GUI_MASTER_CNTL, gmc);
	bus_space_write_4(sc->sc_mmiot, sc->sc_mmioh,
	    RADEON_DP_BRUSH_FRGD_CLR, color);
	bus_space_write_4(sc->sc_mmiot, sc->sc_mmioh,
	    RADEON_DP_WRITE_MASK, 0xffffffff);
	bus_space_write_4(sc->sc_mmiot, sc->sc_mmioh, RADEON_DP_CNTL,
	    RADEON_DST_Y_TOP_TO_BOTTOM | RADEON_DST_X_LEFT_TO_RIGHT);

	bus_space_write_4(sc->sc_mmiot, sc->sc_mmioh,
	    RADEON_DST_Y_X, RADEON_COORDS(x, y));
	bus_space_write_4(sc->sc_mmiot, sc->sc_mmioh,
	    RADEON_DST_HEIGHT_WIDTH, RADEON_COORDS(w, h));

        radeonfb_wait(sc);
}


code:
/*	$OpenBSD: raptor.c,v 1.8 2013/10/20 20:07:27 miod Exp $	*/

/*
 * Copyright (c) 2009 Mark Kettenis.
 *
 * 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/device.h>
#include <sys/pciio.h>
#include <sys/systm.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/wscons/wsconsio.h>
#include <dev/wscons/wsdisplayvar.h>

#include <dev/rasops/rasops.h>

#include <machine/fbvar.h>

/*
 * Tech Source uses the Raptor name for most of its graphics cards.
 * This driver supports the origional Raptor GFX cards built around
 * the Number 9 Imagine-128 chips.
 *
 * Official documentation for the Imagine-128 isn't available.  The
 * information used for writing this driver comes mostly from the Xorg
 * i128 driver.
 */

#define I128_PCI_MW0		0x10
#define I128_PCI_MW1		0x14
#define I128_PCI_RBASE		0x20

#define I128_WR_ADR		0x0000
#define I128_PAL_DAT		0x0004
#define I128_PEL_MASK		0x0008

#define I128_INTM		0x4004
#define I128_FLOW		0x4008
#define  I128_FLOW_DEB		0x00000001
#define  I128_FLOW_MCB		0x00000002
#define  I128_FLOW_CLP		0x00000004
#define  I128_FLOW_PRV		0x00000008
#define  I128_FLOW_ACTIVE	0x0000000f
#define I128_BUSY		0x400c
#define  I128_BUSY_BUSY		0x00000001
#define I128_BUF_CTRL		0x4020
#define  I128_BC_PSIZ_8B	0x00000000
#define  I128_BC_PSIZ_16B	0x01000000
#define  I128_BC_PSIZ_32B	0x02000000
#define I128_DE_PGE		0x4024
#define I128_DE_SORG		0x4028
#define I128_DE_DORG		0x402c
#define I128_DE_MSRC		0x4030
#define I128_DE_WKEY		0x4038
#define I128_DE_ZPTCH		0x403c
#define I128_DE_SPTCH		0x4040
#define I128_DE_DPTCH		0x4044
#define I128_CMD		0x4048
#define I128_CMD_OPC		0x4050
#define  I128_CO_BITBLT		0x00000001
#define I128_CMD_ROP		0x4054
#define  I128_CR_COPY		0x0000000c
#define I128_CMD_STYLE		0x4058
#define  I128_CS_SOLID		0x00000001
#define I128_CMD_PATRN		0x405c
#define I128_CMD_CLP		0x4060
#define I128_CMD_HDF		0x4064
#define I128_FORE		0x4068
#define I128_MASK		0x4070
#define I128_RMSK		0x4074
#define I128_LPAT		0x4078
#define I128_PCTRL		0x407c
#define I128_CLPTL		0x4080
#define I128_CLPBR		0x4084
#define I128_XY0_SRC		0x4088
#define I128_XY1_DST		0x408c
#define I128_XY2_WH		0x4090
#define I128_XY3_DIR		0x4094
#define  I128_DIR_BT		0x00000001
#define  I128_DIR_RL		0x00000002
#define I128_XY4_ZM		0x4098
#define  I128_ZOOM_NONE		0x00000000
#define I128_ACNTRL		0x416c

#define I128_COORDS(x, y)	((x << 16) | (y))

#ifdef APERTURE
extern int allowaperture;
#endif

struct raptor_softc {
	struct sunfb	sc_sunfb;

	bus_space_tag_t sc_memt;
	bus_space_handle_t sc_memh;
	bus_addr_t	sc_membase;
	bus_size_t	sc_memsize;

	bus_space_tag_t	sc_mmiot;
	bus_space_handle_t sc_mmioh;
	bus_addr_t	sc_mmiobase;
	bus_size_t	sc_mmiosize;

	pcitag_t	sc_pcitag;

	int		sc_mode;
	u_int8_t	sc_cmap_red[256];
	u_int8_t	sc_cmap_green[256];
	u_int8_t	sc_cmap_blue[256];
};

int	raptor_ioctl(void *, u_long, caddr_t, int, struct proc *);
paddr_t	raptor_mmap(void *, off_t, int);

struct wsdisplay_accessops raptor_accessops = {
	.ioctl = raptor_ioctl,
	.mmap = raptor_mmap
};

int	raptor_match(struct device *, void *, void *);
void	raptor_attach(struct device *, struct device *, void *);

struct cfattach raptor_ca = {
	sizeof(struct raptor_softc), raptor_match, raptor_attach
};

struct cfdriver raptor_cd = {
	NULL, "raptor", DV_DULL
};

int	raptor_is_console(int);
int	raptor_getcmap(struct raptor_softc *, struct wsdisplay_cmap *);
int	raptor_putcmap(struct raptor_softc *, struct wsdisplay_cmap *);
void	raptor_setcolor(void *, u_int, u_int8_t, u_int8_t, u_int8_t);

int	raptor_copycols(void *, int, int, int, int);
int	raptor_erasecols(void *, int, int, int, long);
int	raptor_copyrows(void *, int, int, int);
int	raptor_eraserows(void *, int, int, long);

void	raptor_init(struct raptor_softc *);
int	raptor_wait(struct raptor_softc *);
void	raptor_copyrect(struct raptor_softc *, int, int, int, int, int, int);
void	raptor_fillrect(struct raptor_softc *, int, int, int, int, int);

int
raptor_match(struct device *parent, void *cf, void *aux)
{
	struct pci_attach_args *pa = aux;
	int node;
	char *name;

	node = PCITAG_NODE(pa->pa_tag);
	name = getpropstring(node, "name");
	if (strcmp(name, "TECH-SOURCE,raptor") == 0 ||
	    strcmp(name, "TSI,raptor") == 0)
		return (10);

	return (0);
}

void
raptor_attach(struct device *parent, struct device *self, void *aux)
{
	struct raptor_softc *sc = (struct raptor_softc *)self;
	struct pci_attach_args *pa = aux;
	struct rasops_info *ri;
	int node, console;
	char *model;

	sc->sc_pcitag = pa->pa_tag;

	node = PCITAG_NODE(pa->pa_tag);
	console = raptor_is_console(node);

	printf("\n");

	model = getpropstring(node, "model");
	printf("%s: %s", self->dv_xname, model);

	if (pci_mapreg_map(pa, I128_PCI_MW0, PCI_MAPREG_TYPE_MEM,
	    BUS_SPACE_MAP_LINEAR, &sc->sc_memt, &sc->sc_memh,
	    &sc->sc_membase, &sc->sc_memsize, 0)) {
		printf("\n%s: can't map video memory\n", self->dv_xname);
		return;
	}

	if (pci_mapreg_map(pa, I128_PCI_RBASE, PCI_MAPREG_TYPE_MEM, 0,
	    &sc->sc_mmiot, &sc->sc_mmioh, &sc->sc_mmiobase,
	    &sc->sc_mmiosize, 0)) {
		bus_space_unmap(sc->sc_memt, sc->sc_memh, sc->sc_memsize);
		printf("\n%s: can't map mmio\n", self->dv_xname);
		return;
	}

	fb_setsize(&sc->sc_sunfb, 8, 1152, 900, node, 0);

	printf(", %dx%d\n", sc->sc_sunfb.sf_width, sc->sc_sunfb.sf_height);

	ri = &sc->sc_sunfb.sf_ro;
	ri->ri_bits = bus_space_vaddr(sc->sc_memt, sc->sc_memh);
	ri->ri_hw = sc;

	fbwscons_init(&sc->sc_sunfb, RI_BSWAP, console);
	fbwscons_setcolormap(&sc->sc_sunfb, raptor_setcolor);
	sc->sc_mode = WSDISPLAYIO_MODE_EMUL;

	raptor_init(sc);
	ri->ri_ops.copyrows = raptor_copyrows;
	ri->ri_ops.copycols = raptor_copycols;
	ri->ri_ops.eraserows = raptor_eraserows;
	ri->ri_ops.erasecols = raptor_erasecols;

	if (console)
		fbwscons_console_init(&sc->sc_sunfb, -1);
	fbwscons_attach(&sc->sc_sunfb, &raptor_accessops, console);
}

int
raptor_ioctl(void *v, u_long cmd, caddr_t data, int flags, struct proc *p)
{
	struct raptor_softc *sc = v;
	struct wsdisplay_fbinfo *wdf;
	struct pcisel *sel;

	switch (cmd) {
	case WSDISPLAYIO_GTYPE:
		*(u_int *)data = WSDISPLAY_TYPE_RAPTOR;
		break;
	case WSDISPLAYIO_SMODE:
		sc->sc_mode = *(u_int *)data;
		if (sc->sc_mode == WSDISPLAYIO_MODE_EMUL)
			fbwscons_setcolormap(&sc->sc_sunfb, raptor_setcolor);
		break;
	case WSDISPLAYIO_GINFO:
		wdf = (void *)data;
		wdf->height = sc->sc_sunfb.sf_height;
		wdf->width  = sc->sc_sunfb.sf_width;
		wdf->depth  = sc->sc_sunfb.sf_depth;
		wdf->cmsize = 256;
		break;
	case WSDISPLAYIO_LINEBYTES:
		*(u_int *)data = sc->sc_sunfb.sf_linebytes;
		break;

	case WSDISPLAYIO_GETCMAP:
		return raptor_getcmap(sc, (struct wsdisplay_cmap *)data);
	case WSDISPLAYIO_PUTCMAP:
		return raptor_putcmap(sc, (struct wsdisplay_cmap *)data);

	case WSDISPLAYIO_GPCIID:
		sel = (struct pcisel *)data;
		sel->pc_bus = PCITAG_BUS(sc->sc_pcitag);
		sel->pc_dev = PCITAG_DEV(sc->sc_pcitag);
		sel->pc_func = PCITAG_FUN(sc->sc_pcitag);
		break;

	case WSDISPLAYIO_SVIDEO:
	case WSDISPLAYIO_GVIDEO:
		break;

	case WSDISPLAYIO_GCURPOS:
	case WSDISPLAYIO_SCURPOS:
	case WSDISPLAYIO_GCURMAX:
	case WSDISPLAYIO_GCURSOR:
	case WSDISPLAYIO_SCURSOR:
	default:
		return -1; /* not supported yet */
        }

	return (0);
}

paddr_t
raptor_mmap(void *v, off_t off, int prot)
{
	struct raptor_softc *sc = v;

	if (off & PGOFSET)
		return (-1);

	switch (sc->sc_mode) {
	case WSDISPLAYIO_MODE_MAPPED:
#ifdef APERTURE
		if (allowaperture == 0)
			return (-1);
#endif

		if (sc->sc_mmiosize == 0)
			return (-1);

		if (off >= sc->sc_membase &&
		    off < (sc->sc_membase + sc->sc_memsize))
			return (bus_space_mmap(sc->sc_memt,
			    sc->sc_membase, off - sc->sc_membase,
			    prot, BUS_SPACE_MAP_LINEAR));

		if (off >= sc->sc_mmiobase &&
		    off < (sc->sc_mmiobase + sc->sc_mmiosize))
			return (bus_space_mmap(sc->sc_mmiot,
			    sc->sc_mmiobase, off - sc->sc_mmiobase,
			    prot, BUS_SPACE_MAP_LINEAR));
		break;

	case WSDISPLAYIO_MODE_DUMBFB:
		if (off >= 0 && off < sc->sc_memsize)
			return (bus_space_mmap(sc->sc_memt, sc->sc_membase,
			    off, prot, BUS_SPACE_MAP_LINEAR));
		break;
	}

	return (-1);
}

int
raptor_is_console(int node)
{
	extern int fbnode;

	return (fbnode == node);
}

int
raptor_getcmap(struct raptor_softc *sc, struct wsdisplay_cmap *cm)
{
	u_int index = cm->index;
	u_int count = cm->count;
	int error;

	if (index >= 256 || count > 256 - index)
		return (EINVAL);

	error = copyout(&sc->sc_cmap_red[index], cm->red, count);
	if (error)
		return (error);
	error = copyout(&sc->sc_cmap_green[index], cm->green, count);
	if (error)
		return (error);
	error = copyout(&sc->sc_cmap_blue[index], cm->blue, count);
	if (error)
		return (error);
	return (0);
}

int
raptor_putcmap(struct raptor_softc *sc, struct wsdisplay_cmap *cm)
{
	u_int index = cm->index;
	u_int count = cm->count;
	u_int i;
	int error;
	u_char *r, *g, *b;

	if (index >= 256 || count > 256 - index)
		return (EINVAL);

	if ((error = copyin(cm->red, &sc->sc_cmap_red[index], count)) != 0)
		return (error);
	if ((error = copyin(cm->green, &sc->sc_cmap_green[index], count)) != 0)
		return (error);
	if ((error = copyin(cm->blue, &sc->sc_cmap_blue[index], count)) != 0)
		return (error);

	r = &sc->sc_cmap_red[index];
	g = &sc->sc_cmap_green[index];
	b = &sc->sc_cmap_blue[index];

	bus_space_write_4(sc->sc_mmiot, sc->sc_mmioh, I128_PEL_MASK, 0xff);
	for (i = 0; i < count; i++) {
		bus_space_write_4(sc->sc_mmiot, sc->sc_mmioh,
		    I128_WR_ADR, index);
		bus_space_write_4(sc->sc_mmiot, sc->sc_mmioh,
		    I128_PAL_DAT, *r);
		bus_space_write_4(sc->sc_mmiot, sc->sc_mmioh,
		    I128_PAL_DAT, *g);
		bus_space_write_4(sc->sc_mmiot, sc->sc_mmioh,
		    I128_PAL_DAT, *b);
		r++, g++, b++, index++;
	}
	return (0);
}

void
raptor_setcolor(void *v, u_int index, u_int8_t r, u_int8_t g, u_int8_t b)
{
	struct raptor_softc *sc = v;

	sc->sc_cmap_red[index] = r;
	sc->sc_cmap_green[index] = g;
	sc->sc_cmap_blue[index] = b;

	bus_space_write_4(sc->sc_mmiot, sc->sc_mmioh, I128_PEL_MASK, 0xff);
	bus_space_write_4(sc->sc_mmiot, sc->sc_mmioh, I128_WR_ADR, index);
	bus_space_write_4(sc->sc_mmiot, sc->sc_mmioh, I128_PAL_DAT, r);
	bus_space_write_4(sc->sc_mmiot, sc->sc_mmioh, I128_PAL_DAT, g);
	bus_space_write_4(sc->sc_mmiot, sc->sc_mmioh, I128_PAL_DAT, b);
}

/*
 * Accelerated routines.
 */

int
raptor_copycols(void *cookie, int row, int src, int dst, int num)
{
	struct rasops_info *ri = cookie;
	struct raptor_softc *sc = ri->ri_hw;

	num *= ri->ri_font->fontwidth;
	src *= ri->ri_font->fontwidth;
	dst *= ri->ri_font->fontwidth;
	row *= ri->ri_font->fontheight;

	raptor_copyrect(sc, ri->ri_xorigin + src, ri->ri_yorigin + row,
	    ri->ri_xorigin + dst, ri->ri_yorigin + row,
	    num, ri->ri_font->fontheight);

	return 0;
}

int
raptor_erasecols(void *cookie, int row, int col, int num, long attr)
{
	struct rasops_info *ri = cookie;
	struct raptor_softc *sc = ri->ri_hw;
	int bg, fg;

	ri->ri_ops.unpack_attr(cookie, attr, &fg, &bg, NULL);

	row *= ri->ri_font->fontheight;
	col *= ri->ri_font->fontwidth;
	num *= ri->ri_font->fontwidth;

	raptor_fillrect(sc, ri->ri_xorigin + col, ri->ri_yorigin + row,
	    num, ri->ri_font->fontheight, ri->ri_devcmap[bg]);

	return 0;
}

int
raptor_copyrows(void *cookie, int src, int dst, int num)
{
	struct rasops_info *ri = cookie;
	struct raptor_softc *sc = ri->ri_hw;

	num *= ri->ri_font->fontheight;
	src *= ri->ri_font->fontheight;
	dst *= ri->ri_font->fontheight;

	raptor_copyrect(sc, ri->ri_xorigin, ri->ri_yorigin + src,
	    ri->ri_xorigin, ri->ri_yorigin + dst, ri->ri_emuwidth, num);

	return 0;
}

int
raptor_eraserows(void *cookie, int row, int num, long attr)
{
	struct rasops_info *ri = cookie;
	struct raptor_softc *sc = ri->ri_hw;
	int bg, fg;
	int x, y, w;

	ri->ri_ops.unpack_attr(cookie, attr, &fg, &bg, NULL);

	if ((num == ri->ri_rows) && ISSET(ri->ri_flg, RI_FULLCLEAR)) {
		num = ri->ri_height;
		x = y = 0;
		w = ri->ri_width;
	} else {
		num *= ri->ri_font->fontheight;
		x = ri->ri_xorigin;
		y = ri->ri_yorigin + row * ri->ri_font->fontheight;
		w = ri->ri_emuwidth;
	}
	raptor_fillrect(sc, x, y, w, num, ri->ri_devcmap[bg]);

	return 0;
}

void
raptor_init(struct raptor_softc *sc)
{
	/* Configure pixel format based on depth. */
	switch(sc->sc_sunfb.sf_depth) {
	case 8:
		bus_space_write_4(sc->sc_mmiot, sc->sc_mmioh,
		    I128_BUF_CTRL, I128_BC_PSIZ_8B);
		break;
	case 16:
		bus_space_write_4(sc->sc_mmiot, sc->sc_mmioh,
		    I128_BUF_CTRL, I128_BC_PSIZ_16B);
		break;
	case 24:
	case 32:
		bus_space_write_4(sc->sc_mmiot, sc->sc_mmioh,
		    I128_BUF_CTRL, I128_BC_PSIZ_32B);
		break;
	default:
		panic("unsupported depth");
		break;
	}

	/* Mostly magic. */
	bus_space_write_4(sc->sc_mmiot, sc->sc_mmioh, I128_DE_PGE, 0);
	bus_space_write_4(sc->sc_mmiot, sc->sc_mmioh, I128_DE_SORG, 0);
	bus_space_write_4(sc->sc_mmiot, sc->sc_mmioh, I128_DE_DORG, 0);
	bus_space_write_4(sc->sc_mmiot, sc->sc_mmioh, I128_DE_MSRC, 0);
	bus_space_write_4(sc->sc_mmiot, sc->sc_mmioh, I128_DE_WKEY, 0);
	bus_space_write_4(sc->sc_mmiot, sc->sc_mmioh, I128_DE_SPTCH,
	    sc->sc_sunfb.sf_linebytes);
	bus_space_write_4(sc->sc_mmiot, sc->sc_mmioh, I128_DE_DPTCH,
	    sc->sc_sunfb.sf_linebytes);
	bus_space_write_4(sc->sc_mmiot, sc->sc_mmioh, I128_DE_ZPTCH,
	    sc->sc_sunfb.sf_linebytes);
	bus_space_write_4(sc->sc_mmiot, sc->sc_mmioh, I128_RMSK, 0);
	bus_space_write_4(sc->sc_mmiot, sc->sc_mmioh, I128_XY4_ZM,
	    I128_ZOOM_NONE);
	bus_space_write_4(sc->sc_mmiot, sc->sc_mmioh, I128_LPAT,
	    0xffffffff);
	bus_space_write_4(sc->sc_mmiot, sc->sc_mmioh, I128_PCTRL, 0);
	bus_space_write_4(sc->sc_mmiot, sc->sc_mmioh, I128_CLPTL, 0);
	bus_space_write_4(sc->sc_mmiot, sc->sc_mmioh, I128_CLPBR,
	    I128_COORDS(4095, 2047));
#if 0
	/* XXX For some reason this makes schizo(4) freak out. */
	bus_space_write_4(sc->sc_mmiot, sc->sc_mmioh, I128_ACNTRL, 0);
#endif
	bus_space_write_4(sc->sc_mmiot, sc->sc_mmioh, I128_INTM, 3);
}

int
raptor_wait(struct raptor_softc *sc)
{
	int i;

	for (i = 1000000; i != 0; i--) {
		if ((bus_space_read_4(sc->sc_mmiot, sc->sc_mmioh,
		    I128_FLOW) & I128_FLOW_ACTIVE) == 0)
			break;
		DELAY(1);
	}

	return i;
}

void
raptor_copyrect(struct raptor_softc *sc, int sx, int sy, int dx, int dy,
    int w, int h)
{
	int dir = 0;

	while (bus_space_read_4(sc->sc_mmiot, sc->sc_mmioh,
	    I128_BUSY) & I128_BUSY_BUSY)
		DELAY(1);

	if (sx < dx) {
		sx += w - 1;
		dx += w - 1;
		dir |= I128_DIR_RL;
	}
	if (sy < dy) {
		sy += h - 1;
		dy += h - 1;
		dir |= I128_DIR_BT;
	}

	bus_space_write_4(sc->sc_mmiot, sc->sc_mmioh, I128_CMD,
	    I128_CR_COPY << 8 | I128_CO_BITBLT);
	bus_space_write_4(sc->sc_mmiot, sc->sc_mmioh, I128_XY3_DIR, dir);
	bus_space_write_4(sc->sc_mmiot, sc->sc_mmioh, I128_XY2_WH,
	    I128_COORDS(w , h));
	bus_space_write_4(sc->sc_mmiot, sc->sc_mmioh, I128_XY0_SRC,
	    I128_COORDS(sx, sy));
	/* Must be last; triggers operation. */
	bus_space_write_4(sc->sc_mmiot, sc->sc_mmioh, I128_XY1_DST,
	    I128_COORDS(dx, dy));

	raptor_wait(sc);
}

void
raptor_fillrect(struct raptor_softc *sc, int x, int y, int w, int h, int color)
{
	while (bus_space_read_4(sc->sc_mmiot, sc->sc_mmioh,
	    I128_BUSY) & I128_BUSY_BUSY)
		DELAY(1);

	bus_space_write_4(sc->sc_mmiot, sc->sc_mmioh, I128_CMD,
	    I128_CS_SOLID << 16 | I128_CR_COPY << 8 | I128_CO_BITBLT);
	bus_space_write_4(sc->sc_mmiot, sc->sc_mmioh, I128_FORE, color);
	bus_space_write_4(sc->sc_mmiot, sc->sc_mmioh, I128_XY3_DIR, 0);
	bus_space_write_4(sc->sc_mmiot, sc->sc_mmioh, I128_XY2_WH,
	    I128_COORDS(w, h));
	bus_space_write_4(sc->sc_mmiot, sc->sc_mmioh, I128_XY0_SRC, 0);
	/* Must be last; triggers operation. */
	bus_space_write_4(sc->sc_mmiot, sc->sc_mmioh, I128_XY1_DST,
	    I128_COORDS(x, y));

	raptor_wait(sc);
}


code:
/*	$OpenBSD: rtc.c,v 1.10 2014/07/11 08:18:31 guenther Exp $	*/

/*
 * Copyright (c) 1992, 1993
 *	The Regents of the University of California.  All rights reserved.
 * Copyright (c) 1994 Gordon W. Ross
 * Copyright (c) 1993 Adam Glass
 * Copyright (c) 1996 Paul Kranenburg
 * Copyright (c) 1996
 * 	The President and Fellows of Harvard College. All rights reserved.
 *
 * This software was developed by the Computer Systems Engineering group
 * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and
 * contributed to Berkeley.
 *
 * All advertising materials mentioning features or use of this software
 * must display the following acknowledgement:
 *	This product includes software developed by Harvard University.
 *	This product includes software developed by the University of
 *	California, Lawrence Berkeley Laboratory.
 *
 * 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. All advertising materials mentioning features or use of this software
 *    must display the following acknowledgement:
 *	This product includes software developed by the University of
 *	California, Berkeley and its contributors.
 *	This product includes software developed by Paul Kranenburg.
 *	This product includes software developed by Harvard University.
 * 4. Neither the name of the University nor the names of its contributors
 *    may be used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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.
 *
 */

/*
 * Driver for rtc device on Blade 1000, Fire V210, etc.
 */

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

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

#include <dev/clock_subr.h>
#include <dev/ic/mc146818reg.h>

#include <sparc64/dev/ebusreg.h>
#include <sparc64/dev/ebusvar.h>

/*
 * Register definitions for the Texas Instruments bq4802.
 */

#define BQ4802_SEC		0x00	/* Seconds. */
#define BQ4802_MIN		0x02	/* Minutes. */
#define BQ4802_HOUR		0x04	/* Hours. */
#define BQ4802_DAY		0x06	/* Day (01-31). */
#define BQ4802_DOW		0x08	/* Day of week (01-07). */
#define BQ4802_MONTH		0x09	/* Month (01-12). */
#define BQ4802_YEAR		0x0a	/* Year (00-99). */
#define BQ4802_CENTURY		0x0f	/* Century (00-99). */

#define BQ4802_CTRL		0x0e	/* Control. */
#define   BQ4802_24HR		0x02	/* 24-hour mode. */
#define   BQ4802_UTI		0x08	/* Update transfer inhibit. */

extern todr_chip_handle_t todr_handle;

struct rtc_softc {
	struct device		sc_dv;
	bus_space_tag_t		sc_iot;
	bus_space_handle_t	sc_ioh;
	struct intrhand		*sc_ih;
};

int	rtc_match(struct device *, void *, void *);
void	rtc_attach(struct device *, struct device *, void *);

struct cfattach rtc_ca = {
	sizeof(struct rtc_softc), rtc_match, rtc_attach
};

struct cfdriver rtc_cd = {
	NULL, "rtc", DV_DULL
};

int rtc_intr(void *arg);

u_int8_t rtc_read_reg(struct rtc_softc *, bus_size_t);
void rtc_write_reg(struct rtc_softc *sc, bus_size_t, u_int8_t);

int rtc_gettime(todr_chip_handle_t, struct timeval *);
int rtc_settime(todr_chip_handle_t, struct timeval *);
int rtc_bq4802_gettime(todr_chip_handle_t, struct timeval *);
int rtc_bq4802_settime(todr_chip_handle_t, struct timeval *);
int rtc_getcal(todr_chip_handle_t, int *);
int rtc_setcal(todr_chip_handle_t, int);

int
rtc_match(struct device *parent, void *cf, void *aux)
{
	struct ebus_attach_args *ea = aux;

	if (strcmp("rtc", ea->ea_name) == 0)
		return (1);
	return (0);
}

void
rtc_attach(struct device *parent, struct device *self, void *aux)
{
	struct rtc_softc *sc = (void *)self;
	struct ebus_attach_args *ea = aux;
	todr_chip_handle_t handle;
	char *model;
	u_int8_t csr;

	if (ebus_bus_map(ea->ea_iotag, 0,
	    EBUS_PADDR_FROM_REG(&ea->ea_regs[0]),
	    ea->ea_regs[0].size, 0, 0, &sc->sc_ioh) == 0) {
		sc->sc_iot = ea->ea_iotag;
	} else if (ebus_bus_map(ea->ea_memtag, 0,
	    EBUS_PADDR_FROM_REG(&ea->ea_regs[0]),
	    ea->ea_regs[0].size, 0, 0, &sc->sc_ioh) == 0) {
		sc->sc_iot = ea->ea_memtag;
	} else {
		printf("%s: can't map register\n", self->dv_xname);
		return;
	}

	model = getpropstring(ea->ea_node, "model");
	if (*model == '\0')
		model = getpropstring(ea->ea_node, "compatible");
	printf(": %s\n", *model != '\0' ? model : "unknown");

	/* Setup our todr_handle */
	handle = malloc(sizeof(struct todr_chip_handle), M_DEVBUF, M_NOWAIT);
	if (handle == NULL)
		panic("couldn't allocate todr_handle");
	handle->cookie = sc;
	handle->todr_gettime = rtc_gettime;
	handle->todr_settime = rtc_settime;
	handle->todr_getcal = rtc_getcal;
	handle->todr_setcal = rtc_setcal;

	handle->bus_cookie = NULL;
	handle->todr_setwen = NULL;
	todr_handle = handle;

	/* The bq4802 is not compatible with the mc146818. */
	if (strcmp(model, "bq4802") == 0) {
		handle->todr_gettime = rtc_bq4802_gettime;
		handle->todr_settime = rtc_bq4802_settime;

		/* Turn on 24-hour mode. */
		csr = bus_space_read_1(sc->sc_iot, sc->sc_ioh, BQ4802_CTRL);
		csr |= BQ4802_24HR;
		bus_space_write_1(sc->sc_iot, sc->sc_ioh, BQ4802_CTRL, csr);
		return;
	}

	/*
	 * Turn interrupts off, just in case. (Although they shouldn't
	 * be wired to an interrupt controller on sparcs).
	 */
	rtc_write_reg(sc, MC_REGB, MC_REGB_BINARY | MC_REGB_24HR);

	/*
	 * On ds1287 models (which really are ns87317 chips), the
	 * interrupt is wired to the powerbutton.
	 */
	if (strcmp(model, "ds1287") == 0 && ea->ea_nintrs > 0) {
		sc->sc_ih = bus_intr_establish(sc->sc_iot, ea->ea_intrs[0],
		    IPL_BIO, 0, rtc_intr, sc, self->dv_xname);
		if (sc->sc_ih == NULL) {
			printf("%s: can't establish interrupt\n",
			    self->dv_xname);
		}
	}
}

int
rtc_intr(void *arg)
{
	extern int allowpowerdown;

	if (allowpowerdown == 1) {
		allowpowerdown = 0;
		prsignal(initprocess, SIGUSR2);
	}
	return (1);
}

/*
 * Register access is indirect, through an address and data port.
 */

#define	RTC_ADDR	0
#define	RTC_DATA	1

u_int8_t
rtc_read_reg(struct rtc_softc *sc, bus_size_t reg)
{
	bus_space_write_1(sc->sc_iot, sc->sc_ioh, RTC_ADDR, reg);
	return (bus_space_read_1(sc->sc_iot, sc->sc_ioh, RTC_DATA));
}

void
rtc_write_reg(struct rtc_softc *sc, bus_size_t reg, u_int8_t val)
{
	bus_space_write_1(sc->sc_iot, sc->sc_ioh, RTC_ADDR, reg);
	bus_space_write_1(sc->sc_iot, sc->sc_ioh, RTC_DATA, val);
}

/*
 * RTC todr routines.
 */

/*
 * Get time-of-day and convert to a `struct timeval'
 * Return 0 on success; an error number otherwise.
 */
int
rtc_gettime(todr_chip_handle_t handle, struct timeval *tv)
{
	struct rtc_softc *sc = handle->cookie;
	struct clock_ymdhms dt;
	int year;
	u_int8_t csr;

	/* Stop updates. */
	csr = rtc_read_reg(sc, MC_REGB);
	csr |= MC_REGB_SET;
	rtc_write_reg(sc, MC_REGB, csr);

	/* Read time */
	dt.dt_sec = rtc_read_reg(sc, MC_SEC);
	dt.dt_min = rtc_read_reg(sc, MC_MIN);
	dt.dt_hour = rtc_read_reg(sc, MC_HOUR);
	dt.dt_day = rtc_read_reg(sc, MC_DOM);
	dt.dt_wday = rtc_read_reg(sc, MC_DOW);
	dt.dt_mon = rtc_read_reg(sc, MC_MONTH);
	year = rtc_read_reg(sc, MC_YEAR);

	if ((year += 1900) < POSIX_BASE_YEAR)
		year += 100;

	dt.dt_year = year;

	/* time wears on */
	csr = rtc_read_reg(sc, MC_REGB);
	csr &= ~MC_REGB_SET;
	rtc_write_reg(sc, MC_REGB, csr);

	/* simple sanity checks */
	if (dt.dt_mon > 12 || dt.dt_day > 31 ||
	    dt.dt_hour >= 24 || dt.dt_min >= 60 || dt.dt_sec >= 60)
		return (1);

	tv->tv_sec = clock_ymdhms_to_secs(&dt);
	tv->tv_usec = 0;
	return (0);
}

/*
 * Set the time-of-day clock based on the value of the `struct timeval' arg.
 * Return 0 on success; an error number otherwise.
 */
int
rtc_settime(todr_chip_handle_t handle, struct timeval *tv)
{
	struct rtc_softc *sc = handle->cookie;
	struct clock_ymdhms dt;
	u_int8_t csr;
	int year;

	/* Note: we ignore `tv_usec' */
	clock_secs_to_ymdhms(tv->tv_sec, &dt);

	year = dt.dt_year % 100;

	/* enable write */
	csr = rtc_read_reg(sc, MC_REGB);
	csr |= MC_REGB_SET;
	rtc_write_reg(sc, MC_REGB, csr);

	rtc_write_reg(sc, MC_SEC, dt.dt_sec);
	rtc_write_reg(sc, MC_MIN, dt.dt_min);
	rtc_write_reg(sc, MC_HOUR, dt.dt_hour);
	rtc_write_reg(sc, MC_DOW, dt.dt_wday);
	rtc_write_reg(sc, MC_DOM, dt.dt_day);
	rtc_write_reg(sc, MC_MONTH, dt.dt_mon);
	rtc_write_reg(sc, MC_YEAR, year);

	/* load them up */
	csr = rtc_read_reg(sc, MC_REGB);
	csr &= ~MC_REGB_SET;
	rtc_write_reg(sc, MC_REGB, csr);
	return (0);
}

/*
 * Get time-of-day and convert to a `struct timeval'
 * Return 0 on success; an error number otherwise.
 */
int
rtc_bq4802_gettime(todr_chip_handle_t handle, struct timeval *tv)
{
	struct rtc_softc *sc = handle->cookie;
	struct clock_ymdhms dt;
	bus_space_tag_t iot = sc->sc_iot;
	bus_space_handle_t ioh = sc->sc_ioh;
	u_int8_t csr;

	/* Stop updates. */
	csr = bus_space_read_1(iot, ioh, BQ4802_CTRL);
	csr |= BQ4802_UTI;
	bus_space_write_1(iot, ioh, BQ4802_CTRL, csr);

	/* Read time */
	dt.dt_sec = FROMBCD(bus_space_read_1(iot, ioh, BQ4802_SEC));
	dt.dt_min = FROMBCD(bus_space_read_1(iot, ioh, BQ4802_MIN));
	dt.dt_hour = FROMBCD(bus_space_read_1(iot, ioh, BQ4802_HOUR));
	dt.dt_day = FROMBCD(bus_space_read_1(iot, ioh, BQ4802_DAY));
	dt.dt_wday = FROMBCD(bus_space_read_1(iot, ioh, BQ4802_DOW));
	dt.dt_mon = FROMBCD(bus_space_read_1(iot, ioh, BQ4802_MONTH));
	dt.dt_year = FROMBCD(bus_space_read_1(iot, ioh, BQ4802_YEAR)) +
	    FROMBCD(bus_space_read_1(iot, ioh, BQ4802_CENTURY)) * 100;

	/* time wears on */
	csr = bus_space_read_1(iot, ioh, BQ4802_CTRL);
	csr &= ~BQ4802_UTI;
	bus_space_write_1(iot, ioh, BQ4802_CTRL, csr);

	/* simple sanity checks */
	if (dt.dt_mon > 12 || dt.dt_day > 31 ||
	    dt.dt_hour >= 24 || dt.dt_min >= 60 || dt.dt_sec >= 60)
		return (1);

	tv->tv_sec = clock_ymdhms_to_secs(&dt);
	tv->tv_usec = 0;
	return (0);
}

/*
 * Set the time-of-day clock based on the value of the `struct timeval' arg.
 * Return 0 on success; an error number otherwise.
 */
int
rtc_bq4802_settime(todr_chip_handle_t handle, struct timeval *tv)
{
	struct rtc_softc *sc = handle->cookie;
	struct clock_ymdhms dt;
	bus_space_tag_t iot = sc->sc_iot;
	bus_space_handle_t ioh = sc->sc_ioh;
	u_int8_t csr;

	/* Note: we ignore `tv_usec' */
	clock_secs_to_ymdhms(tv->tv_sec, &dt);

	/* enable write */
	csr = bus_space_read_1(iot, ioh, BQ4802_CTRL);
	csr |= BQ4802_UTI;
	bus_space_write_1(iot, ioh, BQ4802_CTRL, csr);

	bus_space_write_1(iot, ioh, BQ4802_SEC, TOBCD(dt.dt_sec));
	bus_space_write_1(iot, ioh, BQ4802_MIN, TOBCD(dt.dt_min));
	bus_space_write_1(iot, ioh, BQ4802_HOUR, TOBCD(dt.dt_hour));
	bus_space_write_1(iot, ioh, BQ4802_DOW, TOBCD(dt.dt_wday));
	bus_space_write_1(iot, ioh, BQ4802_DAY, TOBCD(dt.dt_day));
	bus_space_write_1(iot, ioh, BQ4802_MONTH, TOBCD(dt.dt_mon));
	bus_space_write_1(iot, ioh, BQ4802_YEAR, TOBCD(dt.dt_year % 100));
	bus_space_write_1(iot, ioh, BQ4802_CENTURY, TOBCD(dt.dt_year / 100));

	/* load them up */
	csr = bus_space_read_1(iot, ioh, BQ4802_CTRL);
	csr &= ~BQ4802_UTI;
	bus_space_write_1(iot, ioh, BQ4802_CTRL, csr);
	return (0);
}

int
rtc_getcal(todr_chip_handle_t handle, int *vp)
{
	return (EOPNOTSUPP);
}

int
rtc_setcal(todr_chip_handle_t handle, int v)
{
	return (EOPNOTSUPP);
}