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

#ifndef	_SYS_SYSCTRL_H
#define	_SYS_SYSCTRL_H

#pragma ident	"@(#)sysctrl.h	1.57	06/03/10 SMI"

#ifdef	__cplusplus
extern "C" {
#endif

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

/*
 * Debugging macros
 *
 * The DPRINTF macro can be used by setting the sysc_debug_print_level to the
 * appropriate debugging level.  The debug levels are defined in each source
 * file where this header file is included.  The scoping of sysc_debug_info,
 * and sysc_debug_print_level is to the file which included the header file.
 * If multiple levels need to be output, the values can be 'ored'
 * together into sysc_debug_print_level.  If sysc_debug_print_line's bit 1 is
 * set, the line number of the debugging statement is printed out. If it has
 * bit 2 set, the macro will drop into either the debugger or the OBP PROM.
 */

#ifdef  DEBUG

#define	SYSCTRL_ATTACH_DEBUG	0x1
#define	SYSCTRL_INTERRUPT_DEBUG	0x2
#define	SYSCTRL_REGISTERS_DEBUG	0x4
#define	SYSC_DEBUG		SYSCTRL_ATTACH_DEBUG

#include <sys/promif.h>
extern void debug_enter(char *);

extern int sysc_debug_info;
extern int sysc_debug_print_level;

#define	PRINT_LINE_NUMBER	0x1
#define	ENTER_MON		0x2

#define	_PRINTF prom_printf	/* For logging to the console */

#define	DPRINTF(print_flag, args)			\
	if (sysc_debug_print_level & (print_flag) && sysc_debug_info & \
	    PRINT_LINE_NUMBER) \
		_PRINTF("%s line %d:\n", __FILE__, __LINE__);	\
	if (sysc_debug_print_level & (print_flag)) {	\
		_PRINTF args;				\
	if (sysc_debug_info & ENTER_MON)			\
		debug_enter("");				\
	}

#else
#define	DPRINTF(print_flag, args)
#endif /* DEBUG */

/*
 * OBP supplies us with 3 register sets for the clock-board node. The code for
 * the syctrl driver relies on these register sets being presented by the
 * PROM in the order specified below. If this changes, the following comments
 * must be revised and the code in sysctrl_attach() must be changed to reflect
 * these revisions.
 *
 * They are:
 * 	0	Clock frequency registers
 *	1	Misc registers
 *	2       Clock version register
 */

/*
 * The offsets are defined as offsets in bytes from the base of the OBP
 * register to which the register belongs to.
 */

/* Register set 0 */
#define	SYS_OFF_CLK_FREQ2	0x2	/* offset of clock register 2 */

/* Important bits for Clock Frequency register 2 */
#define	RCONS_UART_EN	0x80	/* Remote console reset enabled */
#define	GEN_RESET_EN	0x40	/* Enable reset on freq change */
#define	TOD_RESET_EN	0x20	/* Enable reset from TOD watchdog */
#define	CLOCK_FREQ_8	0x01	/* Frequency bit 8 */
#define	CLOCK_DIV_0	0x02	/* Cpu module divisor bit 0 */
#define	CLOCK_RANGE	0x0c	/* Bits 3:2 control the clock range */
#define	CLOCK_DIV_1	0x10	/* Cpu module divisor bit 1 */

/* Register set 1 */
#define	SYS_OFF_CTRL	0x0	/* Offset of System Control register */
#define	SYS_OFF_STAT1	0x10	/* Offset of System Status1 register */
#define	SYS_OFF_STAT2	0x20	/* Offset of System Status2 register */
#define	SYS_OFF_PSSTAT	0x30	/* Offset of Power Supply Status */
#define	SYS_OFF_PSPRES	0x40	/* Offset of Power Supply Presence */
#define	SYS_OFF_TEMP	0x50	/* Offset of temperature register */
#define	SYS_OFF_DIAG	0x60	/* Offset of interrupt diag register */
#define	SYS_OFF_PPPSR	0x70	/* Offset of second Power Supply Status */
#define	SYS_STATUS1_PADDR	0x1fff8906010 /* physical address for physio */

/* Register set 2 (not present on old vintage clock boards) */
#define	CLK_VERSION_REG	0x0	/* Offset of clock version register */
#define	CLK_VERSION_REG_PADDR 0x1fff890c000 /* physical address for physio */

/* Important bits for the board version register */
#define	OLD_CLK_GEN	0x1
#define	OLD_CLK_DIV	0x2

#define	RMT_CONS_OFFSET	0x4004	/* Offset of Remote Console UART */
#define	RMT_CONS_LEN	0x8	/* Size of Remote Console UART */

/* Bit field defines for System Control register */
#define	SYS_PPS_FAN_FAIL_EN	0x80	/* PPS Fan Fail Interrupt Enable */
#define	SYS_PS_FAIL_EN		0x40	/* PS DC Fail Interrupt Enable */
#define	SYS_AC_PWR_FAIL_EN	0x20	/* AC Power Fail Interrupt Enable */
#define	SYS_SBRD_PRES_EN	0x10	/* Board Insertion Interrupt En */
#define	SYS_PWR_OFF		0x08	/* Bit to turn system power */
#define	SYS_LED_LEFT		0x04	/* System Left LED. Reverse Logic */
#define	SYS_LED_MID		0x02	/* System Middle LED */
#define	SYS_LED_RIGHT		0x01	/* System Right LED */

/* Bit field defines for System Status1 register */
#define	SYS_SLOTS		0xC0	/* system type slot field */
#define	SYS_NOT_SECURE		0x20	/* ==0 Keyswitch in secure pos. */
#define	SYS_NOT_P_FAN_PRES	0x10	/* ==0 PPS cooling tray present */
#define	SYS_NOT_BRD_PRES	0x08	/* ==0 When board inserted */
#define	SYS_NOT_PPS0_PRES	0x04	/* ==0 If PPS0 present */
#define	SYS_TOD_NOT_RST		0x02	/* ==0 if TOD reset occurred */
#define	SYS_GEN_NOT_RST		0x01	/* ==0 if clock freq reset occured */

/* Macros to determine system type from System Status1 register */
#define	SYS_TYPE(x)		((x) & SYS_SLOTS)
#define	SYS_16_SLOT		0x40
#define	SYS_8_SLOT		0xC0
#define	SYS_4_SLOT		0x80
#define	SYS_TESTBED		0x00

/* Bit field defines for Clock Version Register */
#define	SYS_SLOTS2		0x80	/* system type slot2 mask */
#define	SYS_PLUS_SYSTEM		0x00	/* bit 7 is low for plus system */

/* Macros to determine frequency capability from clock version register */
#define	SYS_TYPE2(x)		((x) & SYS_SLOTS2)
#define	ISPLUSSYS(reg)		((reg != 0) && \
					(SYS_TYPE2(*reg) == SYS_PLUS_SYSTEM))

/* Macros to determine system type based on number of physical slots */
#define	IS4SLOT(n)		((n) == 4)
#define	IS5SLOT(n)		((n) == 5)
#define	IS8SLOT(n)		((n) == 8)
#define	IS16SLOT(n)		((n) == 16)
#define	ISTESTBED(n)		((n) == 0)

/* Bit field defines for System Status2 register */
#define	SYS_RMTE_NOT_RST	0x80	/* Remote Console reset occurred */
#define	SYS_PPS0_OK		0x40	/* ==1 PPS0 OK */
#define	SYS_CLK_33_OK		0x20	/* 3.3V OK on clock board */
#define	SYS_CLK_50_OK		0x10	/* 5.0V OK on clock board */
#define	SYS_AC_FAIL		0x08	/* System lost AC Power source */
#define	SYS_RACK_FANFAIL	0x04	/* Peripheral Rack fan status */
#define	SYS_AC_FAN_OK		0x02	/* Status of 4 AC box fans */
#define	SYS_KEYSW_FAN_OK	0x01	/* Status of keyswitch fan */

/* Bit field defines for Power Supply Presence register */
#define	SYS_NOT_PPS1_PRES	0x80	/* ==0 if PPS1 present in 4slot */

/* Bit field defines for Precharge and Peripheral Power Status register */
#define	SYS_NOT_CURRENT_S	0x80	/* Current share backplane */
#define	SYS_PPPSR_BITS		0x7f	/* bulk test bit mask */
#define	SYS_V5_P_OK		0x40	/* ==1 peripheral 5v ok */
#define	SYS_V12_P_OK		0x20	/* ==1 peripheral 12v ok */
#define	SYS_V5_AUX_OK		0x10	/* ==1 auxiliary 5v ok */
#define	SYS_V5_P_PCH_OK		0x08	/* ==1 peripheral 5v precharge ok */
#define	SYS_V12_P_PCH_OK	0x04	/* ==1 peripheral 12v precharge ok */
#define	SYS_V3_PCH_OK		0x02	/* ==1 system 3.3v precharge ok */
#define	SYS_V5_PCH_OK		0x01	/* ==1 system 5.0v precharge ok */

#ifndef _ASM

#define	SYSCTRL_KSTAT_NAME	"sysctrl"
#define	CSR_KSTAT_NAMED		"csr"
#define	STAT1_KSTAT_NAMED	"status1"
#define	STAT2_KSTAT_NAMED	"status2"
#define	CLK_FREQ2_KSTAT_NAMED	"clk_freq2"
#define	FAN_KSTAT_NAMED		"fan_status"
#define	KEY_KSTAT_NAMED		"key_status"
#define	POWER_KSTAT_NAMED	"power_status"
#define	BDLIST_KSTAT_NAME	"bd_list"
#define	CLK_VER_KSTAT_NAME	"clk_ver"

/*
 * The Power Supply shadow kstat is too large to fit in a kstat_named
 * struct, so it has been changed to be a raw kstat.
 */
#define	PSSHAD_KSTAT_NAME	"ps_shadow"

/* States of a power supply DC voltage. */
enum e_state { PS_BOOT = 0, PS_OUT, PS_UNKNOWN, PS_OK, PS_FAIL };
enum e_pres_state { PRES_UNKNOWN = 0, PRES_IN, PRES_OUT };

/*
 * several power supplies are managed -- 8 core power supplies,
 * up to two pps, a couple of clock board powers and a register worth
 * of precharges.
 */
#define	SYS_PS_COUNT 19
/* core PS 0 thru 7 are index 0 thru 7 */
#define	SYS_PPS0_INDEX		8
#define	SYS_CLK_33_INDEX	9
#define	SYS_CLK_50_INDEX	10
#define	SYS_V5_P_INDEX		11
#define	SYS_V12_P_INDEX		12
#define	SYS_V5_AUX_INDEX	13
#define	SYS_V5_P_PCH_INDEX	14
#define	SYS_V12_P_PCH_INDEX	15
#define	SYS_V3_PCH_INDEX	16
#define	SYS_V5_PCH_INDEX	17
#define	SYS_P_FAN_INDEX		18	/* the peripheral fan assy */

/* fan timeout structures */
enum pps_fan_type { RACK = 0, AC = 1, KEYSW = 2 };
#define	SYS_PPS_FAN_COUNT	3

/*
 * States of the secure key switch position.
 */
enum keyswitch_state { KEY_BOOT = 0, KEY_SECURE, KEY_NOT_SECURE };

/* Redundant power states */
enum power_state { BOOT = 0, BELOW_MINIMUM, MINIMUM, REDUNDANT };

/*
 * minor device mask
 * B	- bottom 4 bits (16 slots) are for the slot/receptacle id
 * I	- next 4 bits are for the instance number
 * X	- rest are not used
 *
 * Upper                  Lower
 * XXXXX...............IIIIBBBB
 *
 * Example:
 * device at instance 0 and slot 8, minor device number 0x8 = decimal 8
 * device at instance 1 and slot 10, minor device number 0x1A = decimal 26
 */
#define	SYSC_SLOT_MASK		0x0F
#define	SYSC_INSTANCE_MASK	0xF0
#define	SYSC_INSTANCE_SHIFT	4

/* Macro definitions */
#define	HOTPLUG_DISABLED_PROPERTY "hotplug-disabled"
#define	GETSLOT(unit)		(getminor(unit) & SYSC_SLOT_MASK)
#define	GETINSTANCE(unit) \
	((getminor(unit) & SYSC_INSTANCE_MASK) >> SYSC_INSTANCE_SHIFT)
#define	PUTINSTANCE(inst) \
	(((inst) << SYSC_INSTANCE_SHIFT) & SYSC_INSTANCE_MASK)
#define	GETSOFTC(i) \
	((struct sysctrl_soft_state *)ddi_get_soft_state(sysctrlp, getminor(i)))

/*
 * Definition of sysctrl ioctls.
 */
#define	SYSC_IOC		('H'<<8)

#define	SYSC_CFGA_CMD_GETSTATUS		(SYSC_IOC|68)
#define	SYSC_CFGA_CMD_EJECT		(SYSC_IOC|69)
#define	SYSC_CFGA_CMD_INSERT		(SYSC_IOC|70)
#define	SYSC_CFGA_CMD_CONNECT		(SYSC_IOC|71)
#define	SYSC_CFGA_CMD_DISCONNECT	(SYSC_IOC|72)
#define	SYSC_CFGA_CMD_UNCONFIGURE	(SYSC_IOC|73)
#define	SYSC_CFGA_CMD_CONFIGURE		(SYSC_IOC|74)
#define	SYSC_CFGA_CMD_TEST		(SYSC_IOC|75)
#define	SYSC_CFGA_CMD_TEST_SET_COND	(SYSC_IOC|76)
#define	SYSC_CFGA_CMD_QUIESCE_TEST	(SYSC_IOC|77)

#if defined(_KERNEL)

#define	SPUR_TIMEOUT_USEC			(1 * MICROSEC)
#define	SPUR_LONG_TIMEOUT_USEC			(5 * MICROSEC)
#define	AC_TIMEOUT_USEC				(1 * MICROSEC)
#define	PS_FAIL_TIMEOUT_USEC			(500 * (MICROSEC / MILLISEC))
#define	PPS_FAN_TIMEOUT_USEC			(1 * MICROSEC)

#define	BRD_INSERT_DELAY_USEC			(500 * (MICROSEC / MILLISEC))
#define	BRD_INSERT_RETRY_USEC			(5 * MICROSEC)
#define	BRD_REMOVE_TIMEOUT_USEC			(2 * MICROSEC)
#define	BLINK_LED_TIMEOUT_USEC			(300 * (MICROSEC / MILLISEC))
#define	KEYSWITCH_TIMEOUT_USEC			(1 * MICROSEC)

#define	PS_INSUFFICIENT_COUNTDOWN_SEC		30

/*
 * how many ticks to wait to register the state change
 * NOTE: ticks are measured in PS_FAIL_TIMEOUT_USEC clicks
 */
#define	PS_PRES_CHANGE_TICKS	1
#define	PS_FROM_BOOT_TICKS	1
#define	PS_FROM_UNKNOWN_TICKS	10
#define	PS_POWER_COUNTDOWN_TICKS 60

/* Note: this timeout needs to be longer than FAN_OK_TIMEOUT_USEC */
#define	PS_P_FAN_FROM_UNKNOWN_TICKS 15

#define	PS_FROM_OK_TICKS	1
#define	PS_PCH_FROM_OK_TICKS	3
#define	PS_FROM_FAIL_TICKS	4

/* NOTE: these ticks are measured in PPS_FAN_TIMEOUT_USEC clicks */
#define	PPS_FROM_FAIL_TICKS	7

/*
 * how many spurious interrupts to take during a SPUR_LONG_TIMEOUT_USEC
 * before complaining
 */
#define	MAX_SPUR_COUNT		2

/*
 * Global driver structure which defines the presence and status of
 * all board power supplies.
 */
struct ps_state {
	int pctr;			/* tick counter for presense deglitch */
	int dcctr;			/* tick counter for dc ok deglitch */
	enum e_pres_state pshadow;	/* presense shadow state */
	enum e_state dcshadow;		/* dc ok shadow state */
};

/*
 * for sysctrl_thread_wakeup()
 */
#define	OVERTEMP_POLL	1
#define	KEYSWITCH_POLL	2

/*
 * Structures used in the driver to manage the hardware
 * XXX will need to add a nodeid
 */
struct sysctrl_soft_state {
	dev_info_t *dip;		/* dev info of myself */
	dev_info_t *pdip;		/* dev info of parent */
	struct sysctrl_soft_state *next;
	int mondo;			/* INO for this type of interrupt */
	uchar_t nslots;			/* slots in this system (0-16) */

	pnode_t options_nodeid;		/* for nvram powerfail-time */

	ddi_iblock_cookie_t iblock;	/* High level interrupt cookie */
	ddi_idevice_cookie_t idevice;	/* TODO - Do we need this? */
	ddi_softintr_t spur_id;		/* when we get a spurious int... */
	ddi_iblock_cookie_t spur_int_c;	/* spur int cookie */
	ddi_softintr_t spur_high_id;	/* when we reenable disabled ints */
	ddi_softintr_t spur_long_to_id;	/* long timeout softint */
	ddi_softintr_t ac_fail_id;	/* ac fail softintr id */
	ddi_softintr_t ac_fail_high_id;	/* ac fail re-enable softintr id */
	ddi_softintr_t ps_fail_int_id;	/* ps fail from intr softintr id */
	ddi_iblock_cookie_t ps_fail_c;	/* ps fail softintr cookie */
	ddi_softintr_t ps_fail_poll_id;	/* ps fail from polling softintr */
	ddi_softintr_t pps_fan_id;	/* pps fan fail softintr id */
	ddi_softintr_t pps_fan_high_id;	/* pps fan re-enable softintr id */
	ddi_softintr_t sbrd_pres_id;	/* sbrd softintr id */
	ddi_softintr_t sbrd_gone_id;	/* sbrd removed softintr id */
	ddi_softintr_t blink_led_id;	/* led blinker softint */
	ddi_iblock_cookie_t sys_led_c;	/* mutex cookie for sys LED lock */

	volatile uchar_t *clk_freq1;	/* Clock frequency reg. 1 */
	volatile uchar_t *clk_freq2;	/* Clock frequency reg. 2 */
	volatile uchar_t *status1;	/* System Status1 register */
	volatile uchar_t *status2;	/* System Status2 register */
	volatile uchar_t *ps_stat;	/* Power Supply Status register */
	volatile uchar_t *ps_pres;	/* Power Supply Presence register */
	volatile uchar_t *pppsr;	/* 2nd Power Supply Status register */
	volatile uchar_t *temp_reg;	/* VA of temperature register */
	volatile uchar_t *rcons_ctl;	/* VA of Remote console UART */
	volatile uchar_t *clk_ver;	/* clock version register */

	/* This mutex protects the following data */
	/* NOTE: *csr should only be accessed from interrupt level */
	kmutex_t csr_mutex;		/* locking for csr enable bits */
	volatile uchar_t *csr;		/* System Control Register */
	uchar_t pps_fan_saved;		/* cached pps fanfail state */
	uchar_t saved_en_state;		/* spurious int cache */
	int spur_count;			/* count multiple spurious ints */

	/* This mutex protects the following data */
	kmutex_t spur_int_lock;		/* lock spurious interrupt data */
	timeout_id_t spur_timeout_id;	/* quiet the int timeout id */
	timeout_id_t spur_long_timeout_id; /* spurious long timeout interval */

	/* This mutex protects the following data */
	kmutex_t ps_fail_lock;		/* low level lock */
	struct ps_state ps_stats[SYS_PS_COUNT]; /* state struct for all ps */
	enum power_state power_state;	/* redundant power state */
	int power_countdown;		/* clicks until reboot */

	/* This mutex protects the following data */
	kmutex_t sys_led_lock;		/* low level lock */
	int sys_led;			/* on (TRUE) or off (FALSE) */
	int sys_fault;			/* on (TRUE) or off (FALSE) */

	/* various elements protected by their inherent access patterns */
	int pps_fan_external_state;	/* external state of the pps fans */
	int pps_fan_state_count[SYS_PPS_FAN_COUNT]; /* fan state counter */
	struct temp_stats tempstat;	/* in memory storage of temperature */
	enum keyswitch_state key_shadow; /* external state of the key switch */

	int enable_rcons_atboot;	/* enable remote console at boot */
};

/*
 * Kstat structures used to contain data which is requested by user
 * programs.
 */
struct sysctrl_kstat {
	struct kstat_named	csr;		/* system control register */
	struct kstat_named	status1;	/* system status 1 */
	struct kstat_named	status2;	/* system status 2 */
	struct kstat_named	clk_freq2;	/* Clock register 2 */
	struct kstat_named	fan_status;	/* shadow status 2 for fans */
	struct kstat_named	key_status;	/* shadow status for key */
	struct kstat_named	power_state;	/* redundant power status */
	struct kstat_named	clk_ver;	/* clock version register */
};

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

/*
 * Function prototype
 */
int sysc_policy_disconnect(struct sysctrl_soft_state *,
				sysc_cfga_pkt_t *, sysc_cfga_stat_t *);
int sysc_policy_connect(struct sysctrl_soft_state *,
				sysc_cfga_pkt_t *, sysc_cfga_stat_t *);
int sysc_policy_unconfigure(struct sysctrl_soft_state *,
				sysc_cfga_pkt_t *, sysc_cfga_stat_t *);
int sysc_policy_configure(struct sysctrl_soft_state *,
				sysc_cfga_pkt_t *, sysc_cfga_stat_t *);

void sysc_policy_update(void *softsp, sysc_cfga_stat_t *sc, sysc_evt_t event);

extern void		sysctrl_suspend_prepare(void);
extern int		sysctrl_suspend(sysc_cfga_pkt_t *);
extern void		sysctrl_resume(sysc_cfga_pkt_t *);

#endif /* _KERNEL */
#endif /* _ASM */

#ifdef	__cplusplus
}
#endif

#endif	/* _SYS_SYSCTRL_H */
