/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License (the "License").
 * You may not use this file except in compliance with the License.
 *
 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 * or http://www.opensolaris.org/os/licensing.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information: Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 */

/*
 * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */

#ifndef	_SYS_AC_H
#define	_SYS_AC_H

#pragma ident	"%Z%%M%	%I%	%E% SMI"

#ifdef	__cplusplus
extern "C" {
#endif

/* useful debugging stuff */
#define	AC_ATTACH_DEBUG		0x1
#define	AC_REGISTERS_DEBUG	0x2

/*
 * OBP supplies us with two register sets for the AC nodes. They are:
 *
 *	0		miscellaneous regs
 *	1		Cache tags
 *
 * We do not use the cache tags for anything in the kernel, so we
 * do not map them in.
 */

/* Macros for physical acccess, fhc.h has to be present */
#define	AC_OFFSET		0x00001000000ull
#define	AC_CENTRAL		0x80000000
#define	AC_ARB_FAST		0x00002000
#define	AC_BCSR(board)		(FHC_BOARD_BASE(2*(board)) + FHC_OFFSET + \
				AC_OFFSET + AC_OFF_BCSR)

/* Register set 0 Offsets */
#define	AC_OFF_BRCS		0x10
#define	AC_OFF_BCSR		0x20
#define	AC_OFF_ESR		0x30
#define	AC_OFF_EMR		0x40
#define	AC_OFF_MEMCTL		0x60
#define	AC_OFF_MEMDEC0		0x70
#define	AC_OFF_MEMDEC1		0x80
#define	AC_OFF_UPA0		0x2000
#define	AC_OFF_UPA1		0x4000
#define	AC_OFF_CNTR		0x6000
#define	AC_OFF_MCCR		0x6020

/* Use predefined strings to name the kstats from this driver. */
#define	AC_KSTAT_NAME		"address_controller"
#define	MEMCTL_KSTAT_NAMED	"acmemctl"
#define	MEMDECODE0_KSTAT_NAMED	"acmemdecode0"
#define	MEMDECODE1_KSTAT_NAMED	"acmemdecode1"
#define	CNTR_KSTAT_NAMED	"accounter"
#define	MCCR_KSTAT_NAMED	"acmccr"
#define	BANK_0_KSTAT_NAMED	"acbank0"
#define	BANK_1_KSTAT_NAMED	"acbank1"

/* used for the picN kstats */
#define	AC_NUM_PICS	2
#define	AC_COUNTER_TO_PIC0(CNTR)	((CNTR) & 0xFFFFFFFFULL)
#define	AC_COUNTER_TO_PIC1(CNTR)	((CNTR) >> 32)

/* used to clear/set the pcr */
#define	AC_CLEAR_PCR(PCR)		((PCR) & ~(0x3F3F))
#define	AC_SET_HOT_PLUG(PCR)		((PCR) | (0x3F3F))

/* used for programming the pic */
#define	AC_SET_PIC_BUS_PAUSE(BRD)	(0x80000000LL - 0x9ac4 - ((BRD) << 3))

/* defines for AC Board Configuration and Status Register */
#define	NO_CACHE	0
#define	CACHE_512K	2
#define	CACHE_1M	3
#define	CACHE_2M	4
#define	CACHE_4M	5
#define	CACHE_8M	6
#define	CACHE_16M	7

#define	ARB_MASTER	0x8000
#define	ARB_INIT	0x4000
#define	ARB_FAST	0x2000
#define	FTC_CPAR	0x0200

#define	AC_CSR_REFEN		(1ULL << 27)

/* defines for Memory decode registers */
#define	AC_MEM_VALID		0x8000000000000000ULL

/* size of a memory SIMM group */
#define	RASIZE0(memctl)		(8 << ((((memctl) >> 8) & 0x7) << 1))
#define	RASIZE1(memctl)		(8 << ((((memctl) >> 11) & 0x7) << 1))
#define	RATBL0(memctl)		(((memctl) >> 8) & 0x7)
#define	RATBL1(memctl)		(((memctl) >> 11) & 0x7)

/*
 * Interleave factor of a memory SIMM group.
 * Possible values are 1, 2, 4, 8, and 16. 1 means not interleaved.
 * Larger groups can be interleaved with smaller groups. Groups
 * on the same board can be interleaved as well.
 */
#define	INTLV0(memctl)		(1 << ((memctl) & 0x7))
#define	INTLV1(memctl)		(1 << (((memctl) >> 3) & 0x7))
#define	INTVAL0(memctl)		((memctl) & 0x7)
#define	INTVAL1(memctl)		(((memctl) >> 3) & 0x7)

/*
 * Physical base mask of a memory SIMM group. Note that this is
 * not the real physical base, and is just used to match up the
 * interleaving of groups. The mask bits (UK) are used to mask
 * out the match (UM) field so that the bases can be compared.
 */
#define	GRP_UK(memdec)	(((memdec) >> 39) & 0xFFF)
#define	GRP_UM(memdec)	(((memdec) >> 12) & 0x7FFF)
#define	GRP_BASE(memdec) (GRP_UM(memdec) & ~(GRP_UK(memdec)))
#define	GRP_LK(memdec)	(((memdec) >> 6) & 0xf)
#define	GRP_LM(memdec)	((memdec) & 0xf)
#define	GRP_LBASE(memdec) (GRP_LM(memdec) & ~(GRP_LK(memdec)))
#define	GRP_REALBASE(m) ((GRP_BASE(m) << 26) | (GRP_LBASE(m) << 6))
#define	GRP_UK2SPAN(memdec) ((GRP_UK(memdec) + 1) << 26)
#define	GRP_SPANMB(memdec) (GRP_UK2SPAN(memdec) >> 20)

/*
 * memory states and conditions for sunfire memory system
 */
enum ac_bank_id { Bank0 = 0, Bank1 = 1 };
enum ac_bank_status { StUnknown = 0, StNoMem, StBad, StActive, StSpare };
enum ac_bank_condition { ConUnknown = 0, ConOK, ConFailing, ConFailed,
			ConTest, ConBad };

/*
 * AC memory bank ioctl interface.
 */

/* 'G' (for gigabytes!) does not appear to be used elsewhere in the kernel */
#define	AC_IOC		('G'<<8)

/*
 * For all AC_MEM_ ioctls the arg pointer points to a sysc_cfga_cmd_t
 * except for AC_MEM_ADMIN_VER. The private pointer then points to a
 * structure of the appropriate type, if required.
 */
#define	AC_MEM_ADMIN_VER	(AC_IOC|0)	/* arg is &ac_mem_version_t */
#define	AC_MEM_CONFIGURE	(AC_IOC|1)	/* private == NULL */
#define	AC_MEM_UNCONFIGURE	(AC_IOC|2)	/* private == NULL */
#define	AC_MEM_STAT		(AC_IOC|3)	/* ac_stat_t */
#define	AC_MEM_TEST_START	(AC_IOC|4)	/* ac_mem_test_start_t */
#define	AC_MEM_TEST_STOP	(AC_IOC|5)	/* ac_mem_test_stop_t */
#define	AC_MEM_TEST_READ	(AC_IOC|6)	/* ac_mem_test_read_t */
#define	AC_MEM_TEST_WRITE	(AC_IOC|7)	/* ac_mem_test_write_t */
#define	AC_MEM_EXERCISE		(AC_IOC|128)	/* various */

#define	AC_OUTPUT_LEN		MAXPATHLEN		/* output str len */

typedef enum {
	AC_ERR_DEFAULT = 0,	/* generic errors */
	AC_ERR_INTRANS,		/* hardware in transition */
	AC_ERR_UTHREAD,		/* can't stop user thread */
	AC_ERR_KTHREAD,		/* can't stop kernel thread */
	AC_ERR_SUSPEND,		/* can't suspend a device */
	AC_ERR_RESUME,		/* can't resume a device */
	AC_ERR_POWER,		/* not enough power for slot */
	AC_ERR_COOLING,		/* not enough cooling for slot */
	AC_ERR_PRECHARGE,	/* not enough precharge for slot */
	AC_ERR_HOTPLUG,		/* Hot Plug Unavailable */
	AC_ERR_HW_COMPAT,	/* incompatible hardware found during dr */
	AC_ERR_NON_DR_PROM,	/* prom not support Dynamic Reconfiguration */
	AC_ERR_CORE_RESOURCE,	/* core resource cannot be removed */
	AC_ERR_PROM,		/* error encountered in OBP/POST */
	AC_ERR_DR_INIT,		/* error encountered in sysc_dr_init op */
	AC_ERR_NDI_ATTACH,	/* error encountered in NDI attach operations */
	AC_ERR_NDI_DETACH,	/* error encountered in NDI detach operations */
	AC_ERR_RSTATE,		/* wrong receptacle state */
	AC_ERR_OSTATE,		/* wrong occupant state */
	AC_ERR_COND,		/* invalid condition */
	AC_ERR_BD,		/* invalid board id */
	AC_ERR_BD_TYPE,		/* invalid board type */
	AC_ERR_BD_STATE,	/* invalid board state */
	AC_ERR_MEM_PERM,	/* no write permission */
	AC_ERR_MEM_BK,		/* invalid memory bank */
	AC_ERR_MEM_TEST,	/* invalid memory test id */
	AC_ERR_MEM_TEST_PAR,	/* invalid memory test parameter(s) */
	AC_ERR_KPM_CANCELLED,	/* kphysm_del_cancel (for complete) */
	AC_ERR_KPM_REFUSED,	/* kphysm_pre_del failed (for complete) */
	AC_ERR_KPM_SPAN,	/* memory already in use (add) */
	AC_ERR_KPM_DUP,		/* memory span duplicate (delete) */
	AC_ERR_KPM_FAULT,	/* memory access test failed (add) */
	AC_ERR_KPM_RESOURCE,	/* some resource was not available */
	AC_ERR_KPM_NOTSUP,	/* operation not supported */
	AC_ERR_KPM_NOHANDLES,	/* cannot allocate any more handles */
	AC_ERR_KPM_NONRELOC,	/* non-relocatable pages in span */
	AC_ERR_KPM_HANDLE,	/* bad handle supplied */
	AC_ERR_KPM_BUSY,	/* memory in span is being deleted */
	AC_ERR_KPM_NOTVIABLE,	/* vM viability test failed */
	AC_ERR_KPM_SEQUENCE,	/* function called out of sequence */
	AC_ERR_KPM_NOWORK,	/* no pages to delete */
	AC_ERR_KPM_NOTFINISHED,	/* thread not finished */
	AC_ERR_KPM_NOTRUNNING,	/* thread not running */
	AC_ERR_VMEM,		/* insufficient virtual memory */
	AC_ERR_INTR,		/* delete interrupt by user */
	AC_ERR_TIMEOUT,		/* delete timed out */
	AC_ERR_MEM_DEINTLV	/* could not de-interleave memory */
} ac_err_t;

/*
 * Config admin command structure for AC_MEM ioctls.
 */
typedef struct ac_cfga_cmd {
	uint_t		force:1;	/* force this state transition */
	uint_t		test:1;		/* Need to test hardware */
	int		arg;		/* generic data for test */
	ac_err_t	errtype;	/* error code returned */
	char		*outputstr;	/* output returned from ioctl */
	void		*private;	/* command private data */
} ac_cfga_cmd_t;

typedef struct ac_cfga_cmd32 {
	uint_t		force:1;	/* force this state transition */
	uint_t		test:1;		/* Need to test hardware */
	int		arg;		/* generic data for test */
	ac_err_t	errtype;	/* error code returned */
	caddr32_t	outputstr;	/* output returned from ioctl */
	caddr32_t	private;	/* command private data */
} ac_cfga_cmd32_t;

typedef uint_t ac_mem_version_t;		/* platform interface rev */
#define	AC_MEM_ADMIN_VERSION	1

typedef uint_t mem_test_handle_t;

typedef struct {
	uint64_t		module_id;
	uint64_t		afsr;
	uint64_t		afar;
	uint64_t		udbh_error_reg;
	uint64_t		udbl_error_reg;
} sunfire_processor_error_regs_t;

/*
 * page_size gives the requires size for the read or write buffer.
 * A read can be restricted to one or more line_size units starting
 * at a multiple of line_size units from the start of the page.
 * afar_base is the physical base of the bank being tested so
 * that the afar value can be translated to an offset into the bank.
 */
typedef struct {
	mem_test_handle_t	handle;
	pid_t			tester_pid;	/* PID of test starter */
	sysc_cfga_cond_t	prev_condition;
	u_longlong_t		bank_size;	/* bytes */
	uint_t			page_size;	/* bytes */
	uint_t			line_size;	/* bytes */
	u_longlong_t		afar_base;
} ac_mem_test_start_t;

typedef struct {
	mem_test_handle_t	handle;
	sysc_cfga_cond_t	condition;
} ac_mem_test_stop_t;

/*
 * line_offset is in the range 0 - (page_size/line_size)-1
 * line_count is in the range 1 - (page_size/line_size)
 */
typedef struct {
	u_longlong_t		page_num;
	uint_t			line_offset;
	uint_t			line_count;
} ac_test_addr_t;

/*
 * Data will be transferred in/out of the buffer at:
 * 		(page_buf + (line_offset*line_size))
 */
typedef struct {
	mem_test_handle_t	handle;
	void			*page_buf;
	ac_test_addr_t		address;
	sunfire_processor_error_regs_t	*error_buf;
} ac_mem_test_read_t;

typedef struct {
	mem_test_handle_t	handle;
	void			*page_buf;
	ac_test_addr_t		address;
} ac_mem_test_write_t;

#ifdef _SYSCALL32

/* Kernel's view of ILP32 structure version. */

typedef struct {
	mem_test_handle_t	handle;
	caddr32_t		page_buf;		/* void * */
	ac_test_addr_t		address;
	caddr32_t		error_buf; /* sunfire_processor_error_regs_t */
} ac_mem_test_read32_t;

typedef struct {
	mem_test_handle_t	handle;
	caddr32_t		page_buf;		/* void * */
	ac_test_addr_t		address;
} ac_mem_test_write32_t;

#endif /* _SYSCALL32 */

/* structure returned from AC_MEM_STAT ioctl */
typedef struct {
	sysc_cfga_rstate_t	rstate;
	sysc_cfga_ostate_t	ostate;
	sysc_cfga_cond_t	condition;
	time_t			status_time;
	uint_t			board;
	uint_t			real_size;
	uint_t			use_size;
	uint_t			busy;		/* add/delete in progress */
	uint_t			page_size;	/* bytes */
	uint64_t		phys_pages;
	uint64_t		managed;
	uint64_t		nonrelocatable;
	/* to supply address, group, info */
	uint64_t		ac_memctl;
	uint64_t		ac_decode0;
	uint64_t		ac_decode1;
} ac_stat_t;

#ifdef _SYSCALL32

/* Kernel's view of ILP32 structure version. */

typedef struct {
	sysc_cfga_rstate_t	rstate;
	sysc_cfga_ostate_t	ostate;
	sysc_cfga_cond_t	condition;
	time32_t		status_time;
	uint_t			board;
	uint_t			real_size;
	uint_t			use_size;
	uint_t			busy;		/* add/delete in progress */
	uint_t			page_size;	/* bytes */
	uint64_t		phys_pages;
	uint64_t		managed;
	uint64_t		nonrelocatable;
	/* to supply address, group, info */
	uint64_t		ac_memctl;
	uint64_t		ac_decode0;
	uint64_t		ac_decode1;
} ac_stat32_t;

#endif /* _SYSCALL32 */

/* Command values in cmd_cfga.arg for the AC_MEM_EXERCISE ioctl. */
#define	AC_MEMX_RELOCATE_ALL	0

/* Stats structure for AC_MEMX_RELOCATE_ALL (cmd_cfga.private != NULL). */
struct ac_memx_relocate_stats {
	uint_t		base;
	uint_t		npgs;
	uint_t		nopaget;
	uint_t		nolock;
	uint_t		isfree;
	uint_t		reloc;
	uint_t		noreloc;
};

/* End of ioctl interface. */

#if defined(_KERNEL)

typedef struct {
	ac_cfga_cmd_t	cmd_cfga;
	char		*errbuf;	/* internal error buffer */
	struct ac_soft_state *softsp;
	uint_t		bank;		/* Decoded bank number. */
} ac_cfga_pkt_t;

#define	AC_ERR_SET(pkt, err)	(pkt)->cmd_cfga.errtype = (err)

#define	MEM_BOARD_VISIBLE(BD) \
		((BD)->sc.rstate == SYSC_CFGA_RSTATE_CONNECTED && \
		(BD)->sc.ostate == SYSC_CFGA_OSTATE_CONFIGURED)

#ifndef	TRUE
#define	TRUE (1)
#endif
#ifndef	FALSE
#define	FALSE (0)
#endif

#define	AC_BANK0_STATUS		"bank-0-status"
#define	AC_BANK1_STATUS		"bank-1-status"
#define	AC_BANK_NOMEM		"nomem"
#define	AC_BANK_OK		"ok"
#define	AC_BANK_SPARE		"spare"
#define	AC_BANK_FAILED		"failed"

/*
 * Test for a valid size setting. The size must be set as
 * a contiguous number of bits starting at the least significant bit.
 * Adding one to such a number causes a carry to be propagated to
 * the first zero bit, eg 00111 -> 01000. Thus for a correctly
 * formed value, the AND of the two numbers is 0.
 */
#define	GRP_SIZE_IS_SET(memdec)	((GRP_UK(memdec) & (GRP_UK(memdec) + 1)) == 0)

/* set the decode register bits according to the desired bank layout */
#define	SETUP_DECODE(addr, mb, intlv, group) \
	((((addr) >> 26) & 0x7fffULL) << 12) |			/* UM */ \
	((((mb) >> 6) - 1ULL) << 39) |				/* UK */ \
	((group) & 0xfULL) |					/* LM */ \
	((0xfULL << (intlv) & 0xfULL) << 6)			/* LK */

/*
 * Driver minor number macros.
 */
#define	AC_GETINSTANCE(M)	((M) >> 1)
#define	AC_GETBANK(M)		((M) & 1)
#define	AC_PUTINSTANCE(I)	((I) << 1)

/*
 * Attachment point names.
 */
#define	NAME_BANK0	"bank0"
#define	NAME_BANK1	"bank1"

/*
 * Memory Database
 * This information is generally accessed through the bd_list so we will
 * just protect it by that for now.
 */
struct ac_mem_info {
	int busy;				/* A bank is in transition */
	time_t status_change;			/* Time of last change */

	sysc_cfga_rstate_t rstate;
	sysc_cfga_ostate_t ostate;
	sysc_cfga_cond_t condition;
	uint_t real_size;			/* Real size in MB of bank */
	uint_t use_size;			/* In use size in MB */
};

/* Structures used in the driver to manage the hardware */
struct ac_soft_state {
	dev_info_t *dip;	/* dev info of myself */
	dev_info_t *pdip;	/* dev info of my parent */
	int board;		/* Board number for this AC */

	/* fields protected by bd_list lock */
	struct ac_mem_info bank[2];	/* memory bank information */

	/* Mapped addresses of registers */
	void *ac_base;		/* Base address of Address Controller */
	volatile uint32_t *ac_id;		/* ID register */
	volatile uint64_t *ac_memctl;		/* Memory Control */
	volatile uint64_t *ac_memdecode0;	/* Memory Decode 0 */
	volatile uint64_t *ac_memdecode1;	/* Memory Decode 1 */
	volatile uint64_t *ac_counter;		/* AC counter register */
	volatile uint32_t *ac_mccr;		/* AC Counter control */
	kstat_t *ac_ksp;
	kstat_t	*ac_counters_ksp;		/* performance counter kstat */
};

extern void	ac_blkcopy(caddr_t, caddr_t, uint_t, uint_t);
extern void	ac_mapin(uint64_t, caddr_t);
extern void	ac_unmap(caddr_t);

/* kstat structure used by ac to pass data to user programs. */
struct ac_kstat {
	struct kstat_named ac_memctl;		/* AC Memory control */
	struct kstat_named ac_memdecode0;	/* AC Memory Decode Bank 0 */
	struct kstat_named ac_memdecode1;	/* AC Memory Decode Bank 1 */
	struct kstat_named ac_mccr;		/* AC Mem Counter Control */
	struct kstat_named ac_counter;		/* AC Counter */
	struct kstat_named ac_bank0_status;
	struct kstat_named ac_bank1_status;
};

#endif	/* _KERNEL */

#ifdef	__cplusplus
}
#endif

#endif	/* _SYS_AC_H */