xref: /onnv-gate/usr/src/uts/sun4u/montecarlo/io/scsb.c (revision 11311:639e7bc0b42f)
11708Sstevel /*
21708Sstevel  * CDDL HEADER START
31708Sstevel  *
41708Sstevel  * The contents of this file are subject to the terms of the
51708Sstevel  * Common Development and Distribution License (the "License").
61708Sstevel  * You may not use this file except in compliance with the License.
71708Sstevel  *
81708Sstevel  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
91708Sstevel  * or http://www.opensolaris.org/os/licensing.
101708Sstevel  * See the License for the specific language governing permissions
111708Sstevel  * and limitations under the License.
121708Sstevel  *
131708Sstevel  * When distributing Covered Code, include this CDDL HEADER in each
141708Sstevel  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
151708Sstevel  * If applicable, add the following below this CDDL HEADER, with the
161708Sstevel  * fields enclosed by brackets "[]" replaced with your own identifying
171708Sstevel  * information: Portions Copyright [yyyy] [name of copyright owner]
181708Sstevel  *
191708Sstevel  * CDDL HEADER END
201708Sstevel  */
211708Sstevel 
221708Sstevel /*
23*11311SSurya.Prakki@Sun.COM  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
241708Sstevel  * Use is subject to license terms.
251708Sstevel  */
261708Sstevel 
271708Sstevel /*
281708Sstevel  * Netra ct800 and Netra ct400 (MonteCarlo/Tonga)
291708Sstevel  * System Controller and Status Boards STREAMS driver.
301708Sstevel  *
311708Sstevel  * This driver handles all communications with the Netra ct400 and ct800
321708Sstevel  * System Controller Boards.
331708Sstevel  * I/O to the SCB is through the PCF8584 I2C controller.
341708Sstevel  * The SCB I2C interface and driver interface are provided by the
351708Sstevel  * Xilinx XCS40XL.
361708Sstevel  *
371708Sstevel  * N.B.: The design choice of using STREAMS was dictated because
381708Sstevel  *	 the original system monitor card had to multiplex 2 pcf8574's
391708Sstevel  *	 as one device.
401708Sstevel  */
411708Sstevel 
421708Sstevel #include <sys/types.h>
431708Sstevel #include <sys/param.h>
441708Sstevel #include <sys/cred.h>
451708Sstevel #include <sys/log.h>
461708Sstevel #include <sys/uio.h>
471708Sstevel #include <sys/stat.h>
481708Sstevel #include <sys/vnode.h>
491708Sstevel #include <sys/file.h>
501708Sstevel #include <sys/open.h>
511708Sstevel #include <sys/kmem.h>
521708Sstevel #include <sys/kstat.h>
531708Sstevel #include <sys/signal.h>
541708Sstevel 
551708Sstevel #include <sys/stream.h>
561708Sstevel #include <sys/strsubr.h>
571708Sstevel #include <sys/strsun.h>
581708Sstevel #include <sys/poll.h>
591708Sstevel 
601708Sstevel #include <sys/debug.h>
611708Sstevel 
621708Sstevel #include <sys/conf.h>
631708Sstevel #include <sys/ddi.h>
641708Sstevel #include <sys/sunddi.h>
651708Sstevel #include <sys/modctl.h>
661708Sstevel 
671708Sstevel #include <sys/i2c/misc/i2c_svc.h>
681708Sstevel 
691708Sstevel #include <sys/mct_topology.h>
701708Sstevel #include <sys/netract_gen.h>
711708Sstevel #include <sys/scsbioctl.h>
721708Sstevel #include <sys/scsb.h>
731708Sstevel #include <sys/scsb_cbi.h>
741708Sstevel 
751708Sstevel #include <sys/hotplug/hpctrl.h>
761708Sstevel #include <sys/hsc.h>
771708Sstevel #include <sys/hscimpl.h>
781708Sstevel 
791708Sstevel #define	CPCI_HOTSWAP_SUPPORT
801708Sstevel 
811708Sstevel #define	ALARM_CARD_ON_SLOT	1
821708Sstevel #define	SCSB_FRU_OP_GET_REG	1
831708Sstevel #define	SCSB_FRU_OP_SET_REGBIT	2
841708Sstevel #define	SCSB_FRU_OP_GET_BITVAL	3
851708Sstevel #define	SCSB_FRU_OP_GET_REGDATA	4
861708Sstevel 
871708Sstevel /*
881708Sstevel  * (internal only)
891708Sstevel  * scsb build version format is "CCYYMMDD"
901708Sstevel  * for integer compares.
911708Sstevel  */
921708Sstevel #define	SCSB_BUILD_VERSION	"20001206"
931708Sstevel 
941708Sstevel #define	MUTEX_UNINIT	0
951708Sstevel #define	MUTEX_INIT	2
961708Sstevel 
971708Sstevel static	int scsb_err_threshold = 0; /* max allowed i2c errors */
981708Sstevel static	int scsb_freeze_count = 3; /* #I2C errors to indicate SCB removal */
991708Sstevel static	int scsb_shutdown_count = 5; /* #polls before passing shutdown evt */
1001708Sstevel static	int scsb_in_postintr = 0;	/* 1 if scsb is processing intr */
1011708Sstevel static	kmutex_t *scb_intr_mutex;	 /* SCSB interrupt mutex */
1021708Sstevel static	int	nct_mutex_init = MUTEX_UNINIT;
1031708Sstevel 
1041708Sstevel extern	int	scsb_hsc_board_healthy();
1051708Sstevel 
1061708Sstevel static	char	*scsb_name = SCSB_DEVICE_NAME;
1071708Sstevel static	char	*scsb_clone_name = SCSB_DEVICE_NAME "clone";
1081708Sstevel static	char	*scsb_build_version = SCSB_BUILD_VERSION;
1091708Sstevel /*
1101708Sstevel  * cb_ops section of scsb driver.
1111708Sstevel  */
1121708Sstevel static	int	sm_open(queue_t *, dev_t *, int, int, cred_t *);
1131708Sstevel static	int	sm_close(queue_t *, int, int, cred_t *);
1141708Sstevel 
1151708Sstevel static	int	sm_rput(queue_t *, mblk_t *);	/* from i2c below */
1161708Sstevel static	int	sm_wput(queue_t *, mblk_t *);	/* from above */
1171708Sstevel 
1181708Sstevel uint_t	scsb_intr_preprocess(caddr_t arg);
1191708Sstevel void	scsb_intr(caddr_t arg);
1201708Sstevel static	void	smf_ioctl(queue_t *, mblk_t *);
1211708Sstevel static	void	sm_ioc_rdwr(queue_t *, mblk_t *, int);
1221708Sstevel 
1231708Sstevel static int scsb_info(dev_info_t *, ddi_info_cmd_t, void *, void **);
1241708Sstevel static int scsb_attach(dev_info_t *, ddi_attach_cmd_t);
1251708Sstevel static int scsb_detach(dev_info_t *, ddi_detach_cmd_t);
1261708Sstevel static int initialize_scb(scsb_state_t *);
1271708Sstevel 
1281708Sstevel static dev_info_t *scsb_dip;		/* private copy of devinfo pointer */
1291708Sstevel 
1301708Sstevel static struct module_info info = {
1311708Sstevel 	0, SCSB_DEVICE_NAME, 0, INFPSZ, 512, 128
1321708Sstevel };
1331708Sstevel 
1341708Sstevel static struct qinit sm_rinit = {
1351708Sstevel 	sm_rput, NULL, sm_open, sm_close, NULL, &info
1361708Sstevel };
1371708Sstevel 
1381708Sstevel static struct qinit sm_winit = {
1391708Sstevel 	sm_wput, NULL, sm_open, sm_close, NULL, &info
1401708Sstevel };
1411708Sstevel 
1421708Sstevel struct streamtab sm_st  = {
1431708Sstevel 	&sm_rinit, &sm_winit, NULL, NULL
1441708Sstevel };
1451708Sstevel 
1461708Sstevel static struct cb_ops scsb_cb_ops = {
1471708Sstevel 
1481708Sstevel 	nulldev,		/* open */
1491708Sstevel 	nulldev,		/* close */
1501708Sstevel 	nodev,			/* strategy */
1511708Sstevel 	nodev,			/* print */
1521708Sstevel 	nodev,			/* dump */
1531708Sstevel 	nodev,			/* read */
1541708Sstevel 	nodev,			/* write */
1551708Sstevel 	nodev,			/* ioctl */
1561708Sstevel 	nodev,			/* devmap */
1571708Sstevel 	nodev,			/* mmap */
1581708Sstevel 	nodev, 			/* segmap */
1591708Sstevel 	nochpoll,		/* poll */
1601708Sstevel 	ddi_prop_op,		/* cb_prop_op */
1611708Sstevel 	&sm_st,			/* streamtab  */
1621708Sstevel 	D_MP,			/* Driver compatibility flag */
1631708Sstevel 	CB_REV,				/* rev */
1641708Sstevel 	nodev,				/* int (*cb_aread)() */
1651708Sstevel 	nodev				/* int (*cb_awrite)() */
1661708Sstevel };
1671708Sstevel 
1681708Sstevel static struct dev_ops scsb_ops = {
1691708Sstevel 
1701708Sstevel 	DEVO_REV,		/* devo_rev, */
1711708Sstevel 	0,			/* refcnt  */
1721708Sstevel 	scsb_info,		/* info */
1731708Sstevel 	nulldev,		/* identify */
1741708Sstevel 	nulldev,		/* probe */
1751708Sstevel 	scsb_attach,		/* attach */
1761708Sstevel 	scsb_detach,		/* detach */
1771708Sstevel 	nodev,			/* reset */
1781708Sstevel 	&scsb_cb_ops,		/* driver operations */
1797656SSherry.Moore@Sun.COM 	(struct bus_ops *)0,	/* bus operations */
1807656SSherry.Moore@Sun.COM 	NULL,			/* power */
1817656SSherry.Moore@Sun.COM 	ddi_quiesce_not_supported,	/* devo_quiesce */
1821708Sstevel };
1831708Sstevel 
1841708Sstevel /*
1851708Sstevel  * Module linkage information for the kernel.
1861708Sstevel  */
1871708Sstevel 
1881708Sstevel static struct modldrv modldrv = {
1891708Sstevel 	&mod_driverops, /* Type of module.  This one is a pseudo driver */
1901708Sstevel #ifdef DEBUG
1911708Sstevel 	"SCB/SSB driver DBG" SCSB_BUILD_VERSION,
1921708Sstevel #else
1937656SSherry.Moore@Sun.COM 	"v1.33 Netra ct System Control/Status Board driver",
1941708Sstevel #endif
1951708Sstevel 	&scsb_ops,	/* driver ops */
1961708Sstevel };
1971708Sstevel 
1981708Sstevel static struct modlinkage modlinkage = {
1991708Sstevel 	MODREV_1,
2001708Sstevel 	(void *)&modldrv,
2011708Sstevel 	NULL
2021708Sstevel };
2031708Sstevel 
2041708Sstevel /*
2051708Sstevel  * local declarations and definitions
2061708Sstevel  */
2071708Sstevel #if defined(DEBUG)
2081708Sstevel 	uint32_t	scsb_debug = 0x00000000;
2091708Sstevel #else
2101708Sstevel static	uint32_t	scsb_debug = 0;
2111708Sstevel #endif
2121708Sstevel 
2131708Sstevel static	hrtime_t scb_pre_s, scb_pre_e, scb_post_s, scb_post_e;
2141708Sstevel 
2151708Sstevel static	int		scsb_pil = SCSB_INTR_PIL;
2161708Sstevel static	int		hsc_pil  = SCSB_INTR_PIL;
2171708Sstevel static 	void		*scsb_state;
2181708Sstevel static 	uint32_t	scsb_global_state;
2191708Sstevel static 	uint32_t	scsb_event_code;	/* for event polling */
2201708Sstevel static 	struct system_info	mct_system_info;
2211708Sstevel static 	int		scsb_healthy_poll_count = 16;
2221708Sstevel 
2231708Sstevel static fru_id_t		fru_id_table[MCT_MAX_FRUS];
2241708Sstevel static uchar_t		scb_intr_regs[SCTRL_MAX_GROUP_NUMREGS];
2251708Sstevel 
2261708Sstevel static	uint32_t	evc_fifo[EVC_FIFO_SIZE];
2271708Sstevel static	uint32_t	evc_fifo_count = 0;
2281708Sstevel static	uint32_t	*evc_rptr = evc_fifo;
2291708Sstevel static	uint32_t	*evc_wptr = evc_fifo;
2301708Sstevel static	void		*evc_procs[EVC_PROCS_MAX];
2311708Sstevel static	int		evc_proc_count = 0;
2321708Sstevel static timeout_id_t scsb_intr_tid;
2331708Sstevel 
2341708Sstevel int nct_i2c_transfer(i2c_client_hdl_t i2c_hdl, i2c_transfer_t *i2c_tran);
2351708Sstevel 
2361708Sstevel /*
2371708Sstevel  * kstat functions
2381708Sstevel  */
2391708Sstevel static	int	scsb_alloc_kstats(scsb_state_t *);
2401708Sstevel static	void	scsb_free_kstats(scsb_state_t *);
2411708Sstevel static	int	update_ks_leddata(kstat_t *, int);
2421708Sstevel static	int	update_ks_state(kstat_t *, int);
2431708Sstevel static	int	update_ks_topology(kstat_t *, int);
2441708Sstevel static	int	update_ks_evcreg(kstat_t *, int);
2451708Sstevel 
2461708Sstevel /*
2471708Sstevel  * local functions
2481708Sstevel  */
2491708Sstevel static	void	free_resources(dev_info_t *, scsb_state_t *, int);
2501708Sstevel static	i2c_transfer_t	*scsb_alloc_i2ctx(i2c_client_hdl_t, uint_t);
2511708Sstevel static	fru_info_t	*find_fru_info(fru_id_t fru_id);
2521708Sstevel static	int	scsb_fake_intr(scsb_state_t *, uint32_t);
2531708Sstevel static	int	scsb_get_status(scsb_state_t *, scsb_status_t *);
2541708Sstevel static	int	scsb_leds_switch(scsb_state_t *, scsb_ustate_t);
2551708Sstevel static	void	scsb_freeze(scsb_state_t *scsb);
2561708Sstevel static	void	scsb_freeze_check(scsb_state_t *scsb);
2571708Sstevel static	void	scsb_restore(scsb_state_t *scsb);
2581708Sstevel static	int	scsb_polled_int(scsb_state_t *, int, uint32_t *);
2591708Sstevel static	int	scsb_check_config_status(scsb_state_t *scsb);
2601708Sstevel static	int	scsb_set_scfg_pres_leds(scsb_state_t *, fru_info_t *);
2611708Sstevel static	void	scsb_set_topology(scsb_state_t *);
2621708Sstevel static	void	scsb_free_topology(scsb_state_t *);
2631708Sstevel int	scsb_read_bhealthy(scsb_state_t *scsb);
2641708Sstevel int	scsb_read_slot_health(scsb_state_t *, int);
2651708Sstevel static	void	tonga_slotnum_check(scsb_state_t *scsb, scsb_uinfo_t *suip);
2661708Sstevel static	int	tonga_psl_to_ssl(scsb_state_t *scsb, int slotnum);
2671708Sstevel static	uchar_t	tonga_slotnum_led_shift(scsb_state_t *scsb, uchar_t data);
2681708Sstevel static	int	scsb_clear_intptrs(scsb_state_t *scsb);
2691708Sstevel static	int	scsb_clear_intmasks(scsb_state_t *scsb);
2701708Sstevel static	int	scsb_setall_intmasks(scsb_state_t *scsb);
2711708Sstevel static	int	scsb_write_mask(scsb_state_t *, uchar_t, uchar_t, uchar_t,
2721708Sstevel 				uchar_t);
2731708Sstevel static	int	scsb_rdwr_register(scsb_state_t *, int, uchar_t, int,
2741708Sstevel 				uchar_t *, int);
2751708Sstevel static	int	scsb_readall_regs(scsb_state_t *);
2761708Sstevel static	int	scsb_get_led_regnum(scsb_state_t *, scsb_uinfo_t *, uchar_t *,
2771708Sstevel 				int *, scsb_led_t);
2781708Sstevel static	void	scsb_free_i2ctx(i2c_client_hdl_t, i2c_transfer_t *);
2791708Sstevel static	void	check_fru_info(scsb_state_t *, int);
2801708Sstevel static	void	update_fru_info(scsb_state_t *, fru_info_t *);
2811708Sstevel static	int	event_to_index(uint32_t);
2821708Sstevel static	void	add_event_code(scsb_state_t *, uint32_t);
2831708Sstevel static	uint32_t	del_event_code();
2841708Sstevel static	uint32_t	get_event_code();
2851708Sstevel static	int	add_event_proc(scsb_state_t *, pid_t);
2861708Sstevel static	int	del_event_proc(scsb_state_t *, pid_t);
2871708Sstevel static	void	rew_event_proc(scsb_state_t *);
2881708Sstevel static	int	event_proc_count(scsb_state_t *);
2891708Sstevel static	int	find_evc_proc(pid_t pid);
2901708Sstevel static	void	signal_evc_procs(scsb_state_t *);
2911708Sstevel static	int	check_event_procs();
2921708Sstevel static	int	scsb_is_alarm_card_slot(scsb_state_t *, int);
2931708Sstevel 	int	scsb_get_slot_state(scsb_state_t *, int, int *);
2941708Sstevel static	int	scsb_fru_op(scsb_state_t *, scsb_utype_t, int, int, int);
2951708Sstevel static	int	scsb_queue_put(queue_t *, int, uint32_t *, char *);
2961708Sstevel static	int	scsb_queue_ops(scsb_state_t *, int, int, void *, char *);
2971708Sstevel static	int scsb_blind_read(scsb_state_t *, int, uchar_t, int, uchar_t *, int);
2981708Sstevel static	int scsb_toggle_psmint(scsb_state_t *, int);
2991708Sstevel static	int scsb_quiesce_psmint(scsb_state_t *);
3001708Sstevel static	int scsb_invoke_intr_chain();
3011708Sstevel int	scsb_intr_register(int (*)(void *), void *, fru_id_t);
3021708Sstevel void scsb_intr_unregister(fru_id_t);
3031708Sstevel 
3041708Sstevel #ifdef	DEBUG
3051708Sstevel static	void	mct_topology_dump(scsb_state_t *, int);
3061708Sstevel static	void	scsb_failing_event(scsb_state_t *scsb);
3071708Sstevel #endif
3081708Sstevel 
3091708Sstevel int
_init(void)3101708Sstevel _init(void)
3111708Sstevel {
3121708Sstevel 	int	i, status;
3131708Sstevel 
3141708Sstevel 	if (scsb_debug & 0x0005)
3151708Sstevel 		cmn_err(CE_NOTE, "scsb: _init()");
316*11311SSurya.Prakki@Sun.COM 	(void) ddi_soft_state_init(&scsb_state, sizeof (scsb_state_t),
3177656SSherry.Moore@Sun.COM 	    SCSB_NO_OF_BOARDS);
318*11311SSurya.Prakki@Sun.COM 	(void) hsc_init();
3191708Sstevel 	if ((status = mod_install(&modlinkage)) != 0) {
3201708Sstevel 		if (scsb_debug & 0x0006)
3211708Sstevel 			cmn_err(CE_NOTE, "scsb: _init(): mod_install failed");
3221708Sstevel 		ddi_soft_state_fini(&scsb_state);
323*11311SSurya.Prakki@Sun.COM 		(void) hsc_fini();
3241708Sstevel 		return (status);
3251708Sstevel 	}
3261708Sstevel 	/*
3271708Sstevel 	 * initialize the FRU ID Table, using real FRU IDs where available
3281708Sstevel 	 * such as I2C Addresses for FRUs with I2C support
3291708Sstevel 	 */
3301708Sstevel 	for (i = 0; i < MCT_MAX_FRUS; ++i)
3311708Sstevel 		fru_id_table[i] = i + 1;
3321708Sstevel 	fru_id_table[event_to_index(SCTRL_EVENT_PS1)] = (fru_id_t)MCT_I2C_PS1;
3331708Sstevel 	fru_id_table[event_to_index(SCTRL_EVENT_PS2)] = (fru_id_t)MCT_I2C_PS2;
3341708Sstevel 	fru_id_table[event_to_index(SCTRL_EVENT_FAN1)] = (fru_id_t)MCT_I2C_FAN1;
3351708Sstevel 	fru_id_table[event_to_index(SCTRL_EVENT_FAN2)] = (fru_id_t)MCT_I2C_FAN2;
3361708Sstevel 	fru_id_table[event_to_index(SCTRL_EVENT_FAN3)] = (fru_id_t)MCT_I2C_FAN3;
3371708Sstevel 	fru_id_table[event_to_index(SCTRL_EVENT_SCB)] = (fru_id_t)MCT_I2C_SCB;
3381708Sstevel 	return (status);
3391708Sstevel }
3401708Sstevel 
3411708Sstevel int
_fini(void)3421708Sstevel _fini(void)
3431708Sstevel {
3441708Sstevel 	int	status;
3451708Sstevel 
3461708Sstevel 	if (scsb_debug & 0x0005)
3471708Sstevel 		cmn_err(CE_NOTE, "scsb: _fini()");
3481708Sstevel 
3491708Sstevel 	if ((status = mod_remove(&modlinkage)) == 0) {
3501708Sstevel 		ddi_soft_state_fini(&scsb_state);
351*11311SSurya.Prakki@Sun.COM 		(void) hsc_fini();
3521708Sstevel 	}
3531708Sstevel 	if (scsb_debug & 0x0006)
3541708Sstevel 		cmn_err(CE_NOTE, "scsb: _fini, error %x\n", status);
3551708Sstevel 
3561708Sstevel 	return (status);
3571708Sstevel }
3581708Sstevel 
3591708Sstevel int
_info(struct modinfo * modinfop)3601708Sstevel _info(struct modinfo *modinfop)
3611708Sstevel {
3621708Sstevel 	if (scsb_debug & 0x0005)
3631708Sstevel 		cmn_err(CE_NOTE, "scsb: _info()");
3641708Sstevel 
3651708Sstevel 	return (mod_info(&modlinkage, modinfop));
3661708Sstevel }
3671708Sstevel 
3681708Sstevel static int
scsb_attach(dev_info_t * dip,ddi_attach_cmd_t cmd)3691708Sstevel scsb_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
3701708Sstevel {
3711708Sstevel 	int		instance;
3721708Sstevel 	scsb_state_t	*scsb;
3731708Sstevel 	register int	i;
3741708Sstevel 	int		*regs;
3751708Sstevel 	uint_t		len;
3761708Sstevel 	uchar_t		reg, wdata, rmask;
3771708Sstevel 
3781708Sstevel 	instance = ddi_get_instance(dip);
3791708Sstevel 
3801708Sstevel 	if (scsb_debug & 0x0005)
3811708Sstevel 		cmn_err(CE_NOTE, "scsb_attach[%d]", instance);
3821708Sstevel 
3831708Sstevel 	if (cmd != DDI_ATTACH) {
3841708Sstevel 		if (scsb_debug & 0x0006)
3851708Sstevel 			cmn_err(CE_NOTE,
3867656SSherry.Moore@Sun.COM 			    "scsb_attach[%d]: cmd 0x%x != DDI_ATTACH",
3877656SSherry.Moore@Sun.COM 			    instance, cmd);
3881708Sstevel 		return (DDI_FAILURE);
3891708Sstevel 	}
3901708Sstevel 
3911708Sstevel 	if (ddi_soft_state_zalloc(scsb_state, instance) != DDI_SUCCESS) {
3921708Sstevel 		cmn_err(CE_WARN, "scsb%d: cannot allocate soft state",
3937656SSherry.Moore@Sun.COM 		    instance);
3941708Sstevel 		return (DDI_FAILURE);
3951708Sstevel 	}
3961708Sstevel 
3971708Sstevel 	scsb = (scsb_state_t *)ddi_get_soft_state(scsb_state, instance);
3981708Sstevel 	if (scsb == NULL) {
3991708Sstevel 		cmn_err(CE_WARN, "scsb%d: cannot get soft state", instance);
4001708Sstevel 		ddi_soft_state_free(scsb_state, instance);
4011708Sstevel 		return (DDI_FAILURE);
4021708Sstevel 	}
4031708Sstevel 	scsb->scsb_instance = instance;
4041708Sstevel 	scsb->scsb_state = 0;	/* just checking strange mutex behavior */
4051708Sstevel 
4061708Sstevel 	/*
4071708Sstevel 	 * make sure this is the SCB's known address
4081708Sstevel 	 */
4091708Sstevel 	if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
4107656SSherry.Moore@Sun.COM 	    "reg", &regs, &len) != DDI_PROP_SUCCESS) {
4111708Sstevel 		cmn_err(CE_WARN,
4127656SSherry.Moore@Sun.COM 		    "scsb%d: Failed to get \"reg\" property", instance);
4131708Sstevel 		ddi_soft_state_free(scsb_state, instance);
4141708Sstevel 		return (DDI_FAILURE);
4151708Sstevel 	}
4161708Sstevel 	scsb->scsb_i2c_addr = regs[1] & SCSB_I2C_ADDR_MASK;
4171708Sstevel 	if (scsb->scsb_i2c_addr != SCSB_I2C_ADDR) {
4181708Sstevel 		cmn_err(CE_WARN, "scsb%d: I2C Addr reg %x %x must be %x",
4197656SSherry.Moore@Sun.COM 		    instance, regs[0], regs[1], SCSB_I2C_ADDR);
4201708Sstevel 		ddi_soft_state_free(scsb_state, instance);
4211708Sstevel 		ddi_prop_free(regs);
4221708Sstevel 		return (DDI_FAILURE);
4231708Sstevel 	}
4241708Sstevel 	/* done with array lookup, free resource */
4251708Sstevel 	ddi_prop_free(regs);
4261708Sstevel 	/*
4271708Sstevel 	 * initialize synchronization mutex and condition var.
4281708Sstevel 	 * for this instance.
4291708Sstevel 	 */
4301708Sstevel 	mutex_init(&scsb->scsb_mutex, NULL, MUTEX_DRIVER, NULL);
4311708Sstevel 	scsb->scsb_state |= SCSB_UMUTEX;
4321708Sstevel 	cv_init(&scsb->scsb_cv, NULL, CV_DRIVER, NULL);
4331708Sstevel 	scsb->scsb_state |= SCSB_CONDVAR;
4341708Sstevel 
4351708Sstevel 	/*
4361708Sstevel 	 * 1. Read interrupt property of the board and register its handler.
4371708Sstevel 	 * 2. Get scsb private handle for communication via I2C Services.
4381708Sstevel 	 * 3. Allocate and save an i2c_transfer_t for I2C transfers.
4391708Sstevel 	 */
4401708Sstevel 	/* 1 */
4411708Sstevel 	if (ddi_prop_exists(DDI_DEV_T_ANY, dip,
4427656SSherry.Moore@Sun.COM 	    DDI_PROP_NOTPROM | DDI_PROP_DONTPASS,
4437656SSherry.Moore@Sun.COM 	    "interrupt-priorities") != 1) {
4441708Sstevel 		int tmp[2];
4451708Sstevel 		tmp[0] = scsb_pil;
4461708Sstevel 		tmp[1] = hsc_pil;
4471708Sstevel 		(void) ddi_prop_update_int_array(DDI_DEV_T_NONE, dip,
4487656SSherry.Moore@Sun.COM 		"interrupt-priorities", tmp, 2);
4491708Sstevel 		scsb->scsb_state |= SCSB_PROP_CREATE;
4501708Sstevel 	}
4511708Sstevel 	if ((i = ddi_prop_get_int(DDI_DEV_T_ANY, dip,
4527656SSherry.Moore@Sun.COM 	    DDI_PROP_DONTPASS, "interrupts", -1)) >= 0)
4531708Sstevel 		scsb->scsb_state |= SCSB_P06_INTR_ON;
4541708Sstevel 	else
4551708Sstevel 		scsb->scsb_state |= SCSB_P06_NOINT_KLUGE;
4561708Sstevel 
4571708Sstevel 	/*
4581708Sstevel 	 * Look for the device-err-threshold property which specifies
4591708Sstevel 	 * on how many errors will scsb send a warning event about it's
4601708Sstevel 	 * health. The scsb_err_threshold is 10 by default.
4611708Sstevel 	 */
4621708Sstevel 	if ((i = ddi_prop_get_int(DDI_DEV_T_ANY, dip,
4637656SSherry.Moore@Sun.COM 	    DDI_PROP_DONTPASS, "device-err-threshold", -1)) >= 0) {
4641708Sstevel 		scsb_err_threshold = i;
4651708Sstevel #ifdef	DEBUG
4661708Sstevel 		cmn_err(CE_NOTE, "?scsb_attach: Found device-err-threshold"
4677656SSherry.Moore@Sun.COM 		    " property, value %d", scsb_err_threshold);
4681708Sstevel #endif
4691708Sstevel 	}
4701708Sstevel 	scsb->scsb_i2c_errcnt = 0;
4711708Sstevel 	scsb->scsb_err_flag = B_FALSE;
4721708Sstevel 	scsb->scsb_kstat_flag = B_FALSE;
4731708Sstevel 
4741708Sstevel 	/*
4751708Sstevel 	 * If all went well, create the minor node for user level access.
4761708Sstevel 	 */
4771708Sstevel 	if (ddi_create_minor_node(dip, scsb_name, S_IFCHR, instance,
4787656SSherry.Moore@Sun.COM 	    "ddi_ctl:pcihpc", NULL) == DDI_FAILURE) {
4791708Sstevel 		cmn_err(CE_WARN, "scsb_attach: Failed to create minor node");
4801708Sstevel 		free_resources(dip, scsb, instance);
4811708Sstevel 		return (DDI_FAILURE);
4821708Sstevel 	}
4831708Sstevel 	scsb->scsb_state |= SCSB_MINOR_NODE;
4841708Sstevel 	scsb->scsb_dev = dip;
4851708Sstevel 	if (ddi_create_minor_node(dip, scsb_clone_name, S_IFCHR,
4867656SSherry.Moore@Sun.COM 	    instance|SCSB_CLONE, "ddi_ctl:pcihpc", NULL)
4877656SSherry.Moore@Sun.COM 	    == DDI_FAILURE) {
4881708Sstevel 		cmn_err(CE_WARN, "scsb_attach: Failed to create clone node");
4891708Sstevel 		free_resources(dip, scsb, instance);
4901708Sstevel 		return (DDI_FAILURE);
4911708Sstevel 	}
4921708Sstevel 	/* CLONE */
4931708Sstevel 	bzero(scsb->clone_devs, sizeof (clone_dev_t) * SCSB_CLONES_MAX);
4941708Sstevel 	/* 2 */
4951708Sstevel 	if (i2c_client_register(dip, &scsb->scsb_phandle) != I2C_SUCCESS) {
4961708Sstevel 		cmn_err(CE_WARN,
4977656SSherry.Moore@Sun.COM 		    "scsb_attach: Failed I2C Services registration");
4981708Sstevel 		free_resources(dip, scsb, instance);
4991708Sstevel 		return (DDI_FAILURE);
5001708Sstevel 	}
5011708Sstevel 	scsb->scsb_state |= SCSB_I2C_PHANDLE;
5021708Sstevel 	/* 3 */
5031708Sstevel 	if ((scsb->scsb_i2ctp = scsb_alloc_i2ctx(scsb->scsb_phandle,
5047656SSherry.Moore@Sun.COM 	    I2C_SLEEP)) == NULL) {
5051708Sstevel 		cmn_err(CE_WARN,
5067656SSherry.Moore@Sun.COM 		    "scsb%d: i2c_transfer allocation failed", instance);
5071708Sstevel 		free_resources(dip, scsb, instance);
5081708Sstevel 		return (DDI_FAILURE);
5091708Sstevel 	}
5101708Sstevel 	scsb->scsb_state |= SCSB_I2C_TRANSFER;
5111708Sstevel 	/*
5121708Sstevel 	 * Now it's time to INITIALIZE the boards.
5131708Sstevel 	 *
5141708Sstevel 	 *  1. make sure we can do I2C bus transfers to/from the SCB.
5151708Sstevel 	 *	Read the SCB PROM version for a check.
5161708Sstevel 	 *  2. set SCB_INITIALIZED bit in SysCommand registers (SYS_CMD_BASE)
5171708Sstevel 	 *  3. clear all LED Data registers (8) by writing 0's to turn off
5181708Sstevel 	 *	all LEDs on the SSB.
5191708Sstevel 	 *  4. read System Configuration Status registers (SCTRL_CFG)
5201708Sstevel 	 *	to find present FRUs and set corresponding FRU bits at
5211708Sstevel 	 *	LED_DATA_BASE.
5221708Sstevel 	 *	Also enable devices in Topology map for the current MP_ID
5231708Sstevel 	 *	and set the OK LEDs on the SSB.
5241708Sstevel 	 *  5. read Brd_Hlthy registers (2 @ BRD_HLTHY_BASE)
5251708Sstevel 	 *  6. Disable PSM Interrupts during initialization, mask all
5261708Sstevel 	 *	interrupts, and clear Interrupt Pointer registers
5271708Sstevel 	 *	by writing 0xFF to each register.
5281708Sstevel 	 *  7. set SCB EEPROM address bits SPA2-SPA0 at SYS_CMD_BASE + 1
5291708Sstevel 	 *  8. Install the interrupt handler if appropriate.
5301708Sstevel 	 *  9. clear appropriate bits in Interrupt Mask register for those
5311708Sstevel 	 *	devices that can be present for this MP_ID Topology.
5321708Sstevel 	 * 10. enable PSM Interrupt by writing '1' to PSM_INT_EN bit at
5331708Sstevel 	 *	SYS_CMD_BASE + 1
5341708Sstevel 	 *	Also update all shadow registers for test utility
5351708Sstevel 	 *	if scsb_debug is set.
5361708Sstevel 	 * 11. Check if Alarm Card present at boot and set flags
5371708Sstevel 	 * 12. Call hsc_attach() for slot registration.
5381708Sstevel 	 * 13. Allocate, initialze, and install the kstat structures.
5391708Sstevel 	 * 14. Set scsb_state_t flags to indicate SCB is ready
5401708Sstevel 	 *	and announce the driver is loaded.
5411708Sstevel 	 */
5421708Sstevel 
5431708Sstevel 	/* 1. through 7. */
5441708Sstevel 	if (initialize_scb(scsb) != DDI_SUCCESS) {
5451708Sstevel 		if (!(scsb_debug)) {
5461708Sstevel 			free_resources(dip, scsb, instance);
5471708Sstevel 			return (DDI_FAILURE);
5481708Sstevel 		}
5491708Sstevel 	}
5501708Sstevel 	/* 8. */
5511708Sstevel 	/*
5521708Sstevel 	 * P0.6 No Interrupt Support
5531708Sstevel 	 * Instead of installing the handler, it will be called from a user
5541708Sstevel 	 * program via smf_ioctl().  This flag provides knowledge of the
5551708Sstevel 	 * necessary workarounds to several scsb routines.
5561708Sstevel 	 */
5571708Sstevel 	/*
5581708Sstevel 	 * Now Install interrupt handler
5591708Sstevel 	 */
5601708Sstevel 	if (scsb->scsb_state & SCSB_P06_INTR_ON) {
5611708Sstevel 		if (ddi_get_iblock_cookie(dip, instance,
5627656SSherry.Moore@Sun.COM 		    &scsb->scsb_iblock) == DDI_SUCCESS) {
5631708Sstevel 			mutex_init(&scsb->scsb_imutex, NULL, MUTEX_DRIVER,
5647656SSherry.Moore@Sun.COM 			    (void *)scsb->scsb_iblock);
5651708Sstevel 			scsb->scsb_state |= SCSB_IMUTEX;
5661708Sstevel 			if (ddi_add_intr(dip, instance, &scsb->scsb_iblock,
5677656SSherry.Moore@Sun.COM 			    NULL, scsb_intr_preprocess,
5687656SSherry.Moore@Sun.COM 			    (caddr_t)scsb) != DDI_SUCCESS) {
5691708Sstevel 				cmn_err(CE_WARN,
5707656SSherry.Moore@Sun.COM 				    "scsb_attach: failed interrupt "
5717656SSherry.Moore@Sun.COM 				    "handler registration");
5721708Sstevel 				free_resources(dip, scsb, instance);
5731708Sstevel 				return (DDI_FAILURE);
5741708Sstevel 			}
5751708Sstevel 			scb_intr_mutex = &scsb->scsb_imutex;
5761708Sstevel 			nct_mutex_init |= MUTEX_INIT;
5771708Sstevel 		} else {
5781708Sstevel 			cmn_err(CE_WARN, "scsb_attach: failed interrupt "
5797656SSherry.Moore@Sun.COM 			    "mutex initialization");
5801708Sstevel 			if (scsb_debug) {
5811708Sstevel 				scsb->scsb_state |= SCSB_P06_NOINT_KLUGE;
5821708Sstevel 				scsb->scsb_state &= ~SCSB_P06_INTR_ON;
5831708Sstevel 			} else {
5841708Sstevel 				free_resources(dip, scsb, instance);
5851708Sstevel 				return (DDI_FAILURE);
5861708Sstevel 			}
5871708Sstevel 		}
5881708Sstevel 	}
5891708Sstevel 	/* 9. */
5901708Sstevel 	if (i = scsb_clear_intmasks(scsb)) {
5911708Sstevel 		cmn_err(CE_WARN,
5927656SSherry.Moore@Sun.COM 		    "scsb%d: I2C TRANSFER Failed", instance);
5931708Sstevel 		if (!scsb_debug) {
5941708Sstevel 			free_resources(dip, scsb, instance);
5951708Sstevel 			return (DDI_FAILURE);
5961708Sstevel 		}
5971708Sstevel 	}
5981708Sstevel 
5991708Sstevel 	/* 10. */
6001708Sstevel 	/*
6011708Sstevel 	 * For P0.6 No Interrupt Support, don't enable PSM Interrupt
6021708Sstevel 	 */
6031708Sstevel 	if (!(scsb->scsb_state & SCSB_P06_NOINT_KLUGE)) {
6041708Sstevel 		rmask = 0x00;
6051708Sstevel 		wdata = 1 << SYS_OFFSET(SCTRL_SYS_PSM_INT_ENABLE);
6061708Sstevel 		i = SYS_REG_INDEX(SCTRL_SYS_PSM_INT_ENABLE,
6077656SSherry.Moore@Sun.COM 		    SCTRL_SYS_CMD_BASE);
6081708Sstevel 		reg = SCSB_REG_ADDR(i);
6091708Sstevel 		if (i = scsb_write_mask(scsb, reg, rmask, wdata, (uchar_t)0)) {
6101708Sstevel 			cmn_err(CE_WARN,
6117656SSherry.Moore@Sun.COM 			    "scsb%d: I2C TRANSFER Failed", instance);
6121708Sstevel 			if (!scsb_debug) {
6131708Sstevel 				free_resources(dip, scsb, instance);
6141708Sstevel 				return (DDI_FAILURE);
6151708Sstevel 			}
6161708Sstevel 		} else
6171708Sstevel 			scsb->scsb_state |= SCSB_PSM_INT_ENABLED;
6181708Sstevel 	}
6191708Sstevel 	if (scsb_debug) {
6201708Sstevel 		/*
6211708Sstevel 		 * For smctrl test utility,
6221708Sstevel 		 * so all data is available in shadow registers
6231708Sstevel 		 *
6241708Sstevel 		 * DEBUG_MODE enables private testing interfaces
6251708Sstevel 		 * DIAGS_MODE permits limited testing interfaces
6261708Sstevel 		 */
6271708Sstevel 		scsb->scsb_state |= SCSB_DEBUG_MODE;
6281708Sstevel 		mutex_enter(&scsb->scsb_mutex);
6291708Sstevel 		if (scsb_readall_regs(scsb))
6301708Sstevel 			cmn_err(CE_WARN,
6317656SSherry.Moore@Sun.COM 			    "scsb_attach: scsb_readall FAILED");
6321708Sstevel 		mutex_exit(&scsb->scsb_mutex);
6331708Sstevel 	}
6341708Sstevel 	/* 11. */
6351708Sstevel 	/* Check if Alarm Card present at boot and set flags */
6361708Sstevel 	if (scsb_fru_op(scsb, ALARM, 1, SCTRL_SYSCFG_BASE,
6377656SSherry.Moore@Sun.COM 	    SCSB_FRU_OP_GET_BITVAL))
6381708Sstevel 		scsb->scsb_hsc_state |= SCSB_ALARM_CARD_PRES;
6391708Sstevel 
6401708Sstevel 	/* 12. */
6411708Sstevel 	if (scsb_debug & 0x0004)
6421708Sstevel 		cmn_err(CE_NOTE,
6437656SSherry.Moore@Sun.COM 		    "scsb_attach: registering cPCI slots");
6441708Sstevel 	if (scsb_hsc_attach(dip, scsb, instance) != DDI_SUCCESS) {
6451708Sstevel 		if (scsb_debug & 0x00008000) {
6461708Sstevel 			cmn_err(CE_WARN,
6471708Sstevel 			"scsb: Hotswap controller initialisation"
6487656SSherry.Moore@Sun.COM 			    " failed\n");
6491708Sstevel 		}
6501708Sstevel 	} else
6511708Sstevel 		scsb->scsb_hsc_state |= SCSB_HSC_INIT;
6521708Sstevel 	/* 13. */
6531708Sstevel 	/*
6541708Sstevel 	 * allocate and install the kstat data structures
6551708Sstevel 	 */
6561708Sstevel 	if (scsb_alloc_kstats(scsb) != DDI_SUCCESS) {
6571708Sstevel 		if (scsb_debug & 0x0006)
6581708Sstevel 			cmn_err(CE_WARN, "scsb_attach: ERROR adding kstats");
6591708Sstevel 	}
6601708Sstevel 	/* 14. */
6611708Sstevel 	scsb->scsb_state |= SCSB_UP;
6621708Sstevel 	scsb_global_state |= SCSB_UP;
6631708Sstevel 	ddi_report_dev(scsb->scsb_dev);
6641708Sstevel 	cmn_err(CE_CONT, "?%s%d: "
6651708Sstevel 	"Prom Version %s, Midplane Id %x\n",
6667656SSherry.Moore@Sun.COM 	    ddi_driver_name(scsb->scsb_dev),
6677656SSherry.Moore@Sun.COM 	    scsb->scsb_instance,
6687656SSherry.Moore@Sun.COM 	    (scsb->scsb_state & SCSB_P06_PROM) ? "0.6" :
6697656SSherry.Moore@Sun.COM 	    (scsb->scsb_state & SCSB_P10_PROM) ? "1.0" :
6707656SSherry.Moore@Sun.COM 	    (scsb->scsb_state & SCSB_P15_PROM) ? "1.5" :
6717656SSherry.Moore@Sun.COM 	    (scsb->scsb_state & SCSB_P20_PROM) ? "2.0" : "Unknown",
6727656SSherry.Moore@Sun.COM 	    mct_system_info.mid_plane.fru_id);
6731708Sstevel 	return (DDI_SUCCESS);
6741708Sstevel }
6751708Sstevel 
6761708Sstevel /*
6771708Sstevel  * This funciton is called from scsb_attach(), and from scsb_intr() as part
6781708Sstevel  * of Hot Insertion support, to check the SCB PROM ID register and set
6791708Sstevel  * scsb_state bits and register table pointers as necessary.
6801708Sstevel  */
6811708Sstevel static int
scb_check_version(scsb_state_t * scsb)6821708Sstevel scb_check_version(scsb_state_t *scsb)
6831708Sstevel {
6841708Sstevel 	int		hotswap = 0;
6851708Sstevel 	uchar_t		data;
6861708Sstevel 	if (scsb->scsb_state & SCSB_UP) {
6871708Sstevel 		/*
6881708Sstevel 		 * If driver is UP, then this call is from scsb_intr()
6891708Sstevel 		 * as part of Hot Insertion support.
6901708Sstevel 		 */
6911708Sstevel 		hotswap = 1;
6921708Sstevel 	}
6931708Sstevel 	/* Read the SCB PROM ID */
6941708Sstevel 	if (scsb_rdwr_register(scsb, I2C_WR_RD, (uchar_t)SCTRL_PROM_VERSION, 1,
6957656SSherry.Moore@Sun.COM 	    &data, 1)) {
6961708Sstevel 		if (!(hotswap && scsb->scsb_state & SCSB_FROZEN))
6971708Sstevel 			cmn_err(CE_WARN, "scsb%d: I2C TRANSFER Failed",
6987656SSherry.Moore@Sun.COM 			    scsb->scsb_instance);
6991708Sstevel 		if (scsb_debug & 0x0006) {
7001708Sstevel 				cmn_err(CE_WARN,
7011708Sstevel 				    "scsb_attach(%d): failed read of PROM ID",
7027656SSherry.Moore@Sun.COM 				    scsb->scsb_instance);
7031708Sstevel 		}
7041708Sstevel 		return (DDI_FAILURE);
7051708Sstevel 	}
7061708Sstevel 	/*
7071708Sstevel 	 * compare with stored version number, and if different,
7081708Sstevel 	 * report a warning and keep the driver FROZEN
7091708Sstevel 	 */
7101708Sstevel 	if (hotswap) {
7111708Sstevel 		if (((mct_system_info.fru_info_list[SCB])[0].fru_version & 0xf)
7127656SSherry.Moore@Sun.COM 		    == (data & 0xf)) {
7131708Sstevel 			return (DDI_SUCCESS);
7141708Sstevel 		}
7151708Sstevel 		if (scsb_debug & 0x00020000) {
7161708Sstevel 			cmn_err(CE_NOTE,
7177656SSherry.Moore@Sun.COM 			    "scb_check_version: SCB version %d "
7187656SSherry.Moore@Sun.COM 			    "replacing version %d", data,
7197656SSherry.Moore@Sun.COM 			    (mct_system_info.fru_info_list[SCB])[0].
7207656SSherry.Moore@Sun.COM 			    fru_version & 0xf);
7211708Sstevel 		}
7221708Sstevel 	}
7231708Sstevel 	if ((data & 0xf) == SCTRL_PROM_P06) {
7241708Sstevel 		scsb->scsb_state |= SCSB_P06_PROM;
7251708Sstevel 	} else if ((data & 0xf) == SCTRL_PROM_P10) {
7261708Sstevel 		scsb->scsb_state |= SCSB_P10_PROM;
7271708Sstevel 	} else if ((data & 0xf) == SCTRL_PROM_P15) {
7281708Sstevel 		scsb->scsb_state |= SCSB_P15_PROM;
7291708Sstevel 	} else if ((data & 0xf) == SCTRL_PROM_P20) {
7301708Sstevel 		scsb->scsb_state |= SCSB_P20_PROM;
7311708Sstevel 	}
7321708Sstevel 	if (!(scsb->scsb_state & SCSB_SCB_PRESENT))
7331708Sstevel 		scsb->scsb_state |= SCSB_SCB_PRESENT;
7341708Sstevel 	if (IS_SCB_P10) {
7351708Sstevel 		scb_reg_index  = scb_10_reg_index;
7361708Sstevel 		scb_numregs    = scb_10_numregs;
7371708Sstevel 		scb_fru_offset = scb_10_fru_offset;
7381708Sstevel 		scb_sys_offset = scb_10_sys_offset;
7391708Sstevel 	} else { /* if (IS_SCB_P15) */
7401708Sstevel 		scb_reg_index  = scb_15_reg_index;
7411708Sstevel 		scb_numregs    = scb_15_numregs;
7421708Sstevel 		scb_fru_offset = scb_15_fru_offset;
7431708Sstevel 		scb_sys_offset = scb_15_sys_offset;
7441708Sstevel 	}
7451708Sstevel 	if (!(IS_SCB_P15) && !(IS_SCB_P10)) {
7461708Sstevel 		cmn_err(CE_WARN, "scsb%d: SCB Version %d not recognized",
7477656SSherry.Moore@Sun.COM 		    scsb->scsb_instance, data);
7481708Sstevel 		if (hotswap)
7491708Sstevel 			scsb->scsb_state |= SCSB_FROZEN;
7501708Sstevel 		if (!(scsb_debug)) {
7511708Sstevel 			return (DDI_FAILURE);
7521708Sstevel 		}
7531708Sstevel 		/*
7541708Sstevel 		 * DEBUG: Assume SCB15
7551708Sstevel 		 */
7561708Sstevel 		scsb->scsb_state |= SCSB_P15_PROM;
7571708Sstevel 	}
7581708Sstevel 	return (DDI_SUCCESS);
7591708Sstevel }
7601708Sstevel 
7611708Sstevel /*
7621708Sstevel  * SCB initialization steps to be called from scsb_attach()
7631708Sstevel  * or from scsb_intr() calling scsb_restore() on Hot Insertion.
7641708Sstevel  */
7651708Sstevel static int
initialize_scb(scsb_state_t * scsb)7661708Sstevel initialize_scb(scsb_state_t *scsb)
7671708Sstevel {
7681708Sstevel 	register int	i;
7691708Sstevel 	uchar_t		reg, wdata, rmask;
7701708Sstevel 	/*
7711708Sstevel 	 * If called from scsb_intr(), we've already done this
7721708Sstevel 	 */
7731708Sstevel 	if (!(scsb->scsb_state & SCSB_IN_INTR))
7741708Sstevel 		if (scb_check_version(scsb) != DDI_SUCCESS)
7751708Sstevel 			return (DDI_FAILURE);
7761708Sstevel 	/*
7771708Sstevel 	 * 2. Set the SCB_INIT bit in the System Command register
7781708Sstevel 	 */
7791708Sstevel 	rmask = 0x00;	/* P1.0: 0x60; */
7801708Sstevel 	wdata = 1 << SYS_OFFSET(SCTRL_SYS_SCB_INIT);
7811708Sstevel 	i = SYS_REG_INDEX(SCTRL_SYS_SCB_INIT, SCTRL_SYS_CMD_BASE);
7821708Sstevel 	reg = SCSB_REG_ADDR(i);
7831708Sstevel 	if (i = scsb_write_mask(scsb, reg, rmask, wdata, 0)) {
7841708Sstevel 		cmn_err(CE_WARN,
7857656SSherry.Moore@Sun.COM 		    "scsb%d: I2C TRANSFER Failed", scsb->scsb_instance);
7861708Sstevel 		if (scsb_debug & 0x0006) {
7871708Sstevel 			cmn_err(CE_NOTE,
7887656SSherry.Moore@Sun.COM 			"scsb_attach: failed to set SCB_INIT");
7891708Sstevel 		}
7901708Sstevel 		return (DDI_FAILURE);
7911708Sstevel 	}
7921708Sstevel 	/* 3. For P1.0 and previous system, turn off all LEDs */
7931708Sstevel 	if (IS_SCB_P10) {
7941708Sstevel 		if (scsb_debug & 0x0004) {
7951708Sstevel 			cmn_err(CE_NOTE, "scsb_attach(%d): turning LEDs off",
7967656SSherry.Moore@Sun.COM 			    scsb->scsb_instance);
7971708Sstevel 		}
7981708Sstevel 		if (i = scsb_leds_switch(scsb, OFF)) {
7991708Sstevel 			cmn_err(CE_WARN, "scsb%d: I2C TRANSFER Failed",
8007656SSherry.Moore@Sun.COM 			    scsb->scsb_instance);
8011708Sstevel 			return (DDI_FAILURE);
8021708Sstevel 		}
8031708Sstevel 	}
8041708Sstevel 	/* 4. Read the SYSCFG registers, update FRU info and SSB LEDs */
8051708Sstevel 	if (scsb_debug & 0x0004)
8061708Sstevel 		cmn_err(CE_NOTE, "scsb_attach(%d): reading config registers",
8077656SSherry.Moore@Sun.COM 		    scsb->scsb_instance);
8081708Sstevel 	if ((i = scsb_check_config_status(scsb)) == 0) {
8091708Sstevel 		if (!(scsb->scsb_state & SCSB_TOPOLOGY)) {
8101708Sstevel 			scsb_set_topology(scsb);
8111708Sstevel 			if (scsb_debug & 0x0004)
8121708Sstevel 				cmn_err(CE_NOTE, "scsb_attach(%d): mpid = 0x%x",
8137656SSherry.Moore@Sun.COM 				    scsb->scsb_instance,
8147656SSherry.Moore@Sun.COM 				    mct_system_info.mid_plane.fru_id);
8151708Sstevel 		} else {
8161708Sstevel 			fru_info_t	*fru_ptr;
8171708Sstevel 			/*
8181708Sstevel 			 * walk through FRUs and update FRU info
8191708Sstevel 			 */
8201708Sstevel 			for (i = 0; i < SCSB_UNIT_TYPES; ++i) {
8211708Sstevel 				fru_ptr = mct_system_info.fru_info_list[i];
8221708Sstevel 				while (fru_ptr != NULL) {
8231708Sstevel 					update_fru_info(scsb, fru_ptr);
8241708Sstevel 					fru_ptr = fru_ptr->next;
8251708Sstevel 				}
8261708Sstevel 			}
8271708Sstevel 		}
8281708Sstevel 		i = scsb_set_scfg_pres_leds(scsb, NULL);
8291708Sstevel 	}
8301708Sstevel 	if (i) {
8311708Sstevel 		cmn_err(CE_WARN, "scsb%d: I2C TRANSFER Failed",
8327656SSherry.Moore@Sun.COM 		    scsb->scsb_instance);
8331708Sstevel 		return (DDI_FAILURE);
8341708Sstevel 	}
8351708Sstevel 	/* 5. read the Board Healthy registers */
8361708Sstevel 	if (scsb_debug & 0x0004)
8371708Sstevel 		cmn_err(CE_NOTE, "scsb_attach(%d): reading Brd_Hlthy registers",
8387656SSherry.Moore@Sun.COM 		    scsb->scsb_instance);
8391708Sstevel 	i = scsb_read_bhealthy(scsb);
8401708Sstevel 	if (i) {
8411708Sstevel 		cmn_err(CE_WARN, "scsb%d: I2C TRANSFER Failed",
8427656SSherry.Moore@Sun.COM 		    scsb->scsb_instance);
8431708Sstevel 		return (DDI_FAILURE);
8441708Sstevel 	}
8451708Sstevel 	/* 6. Clear Interrupt Source registers */
8461708Sstevel 	/*
8471708Sstevel 	 * Due to some registration problems, we must first disable
8481708Sstevel 	 * global interrupts which may be the default reset value
8491708Sstevel 	 * itself. However, this is a safe step to do in case of
8501708Sstevel 	 * implementation changes.
8511708Sstevel 	 *
8521708Sstevel 	 * Disable Global SCB Interrupts now
8531708Sstevel 	 */
8541708Sstevel 	rmask = 0x00;	/* P1.0: 0x60; */
8551708Sstevel 	wdata = 1 << SYS_OFFSET(SCTRL_SYS_PSM_INT_ENABLE);
8561708Sstevel 	i = SYS_REG_INDEX(SCTRL_SYS_PSM_INT_ENABLE, SCTRL_SYS_CMD_BASE);
8571708Sstevel 	reg = SCSB_REG_ADDR(i);
8581708Sstevel 	if (i = scsb_write_mask(scsb, reg, rmask, (uchar_t)0, wdata)) {
8591708Sstevel 		cmn_err(CE_WARN, "scsb%d: Cannot turn off PSM_INT",
8607656SSherry.Moore@Sun.COM 		    scsb->scsb_instance);
8611708Sstevel 		return (DDI_FAILURE);
8621708Sstevel 	}
8631708Sstevel 	/* Mask all interrupt sources */
8641708Sstevel 	if (i = scsb_setall_intmasks(scsb)) {
8651708Sstevel 		cmn_err(CE_WARN, "scsb%d: I2C TRANSFER Failed",
8667656SSherry.Moore@Sun.COM 		    scsb->scsb_instance);
8671708Sstevel 		return (DDI_FAILURE);
8681708Sstevel 	}
8691708Sstevel 	/* Clear any latched interrupts */
8701708Sstevel 	if (i = scsb_clear_intptrs(scsb)) {
8711708Sstevel 		cmn_err(CE_WARN, "scsb%d: I2C TRANSFER Failed",
8727656SSherry.Moore@Sun.COM 		    scsb->scsb_instance);
8731708Sstevel 		return (DDI_FAILURE);
8741708Sstevel 	}
8751708Sstevel 	/* 7. set SCB EEPROM address: NOT USED */
8761708Sstevel 	return (DDI_SUCCESS);
8771708Sstevel }
8781708Sstevel 
8791708Sstevel /*
8801708Sstevel  * Based on MC conditions, scsb_detach should eventually be made to always
8811708Sstevel  * return FAILURE, as the driver should not be allowed to detach after some
8821708Sstevel  * hs slots have been used.
8831708Sstevel  */
8841708Sstevel static int
scsb_detach(dev_info_t * dip,ddi_detach_cmd_t cmd)8851708Sstevel scsb_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
8861708Sstevel {
8871708Sstevel 	int		instance;
8881708Sstevel 	scsb_state_t	*scsb;
8891708Sstevel 	uchar_t		reg, wdata;
8901708Sstevel 
8911708Sstevel 	/*
8921708Sstevel 	 * TBD: make sure there are no outstanding operations on the system
8931708Sstevel 	 * monitor card before detaching.
8941708Sstevel 	 */
8951708Sstevel 	instance = ddi_get_instance(dip);
8961708Sstevel 	if (scsb_debug & 0x0005)
8971708Sstevel 		cmn_err(CE_NOTE, "scsb_detach[%d]", instance);
8981708Sstevel 	if (cmd != DDI_DETACH) {
8991708Sstevel 		if (scsb_debug & 0x0006)
9001708Sstevel 			cmn_err(CE_NOTE,
9017656SSherry.Moore@Sun.COM 			    "scsb_detach(%d): command %x is not DDI_DETACH\n",
9027656SSherry.Moore@Sun.COM 			    instance, cmd);
9031708Sstevel 		return (DDI_FAILURE);
9041708Sstevel 	}
9051708Sstevel 	scsb = (scsb_state_t *)ddi_get_soft_state(scsb_state, instance);
9061708Sstevel 	scsb->scsb_state &= ~SCSB_UP;
9071708Sstevel 	scsb_global_state &= ~SCSB_UP;
9081708Sstevel 	if (scsb->scsb_hsc_state & SCSB_HSC_INIT) {
909*11311SSurya.Prakki@Sun.COM 		(void) scsb_hsc_detach(dip, scsb, instance);
9101708Sstevel 		scsb->scsb_hsc_state &= ~SCSB_HSC_INIT;
9111708Sstevel 	}
9121708Sstevel 	if (scsb->scsb_state & SCSB_PSM_INT_ENABLED) {
9131708Sstevel 		/*
9141708Sstevel 		 * Disable Global SCB Interrupts now
9151708Sstevel 		 */
9161708Sstevel 		wdata = 1 << SYS_OFFSET(SCTRL_SYS_PSM_INT_ENABLE);
9171708Sstevel 		reg = SYS_REG_INDEX(SCTRL_SYS_PSM_INT_ENABLE,
9187656SSherry.Moore@Sun.COM 		    SCTRL_SYS_CMD_BASE);
9191708Sstevel 		if (scsb_write_mask(scsb, reg, (uchar_t)0, (uchar_t)0, wdata)) {
9201708Sstevel 			cmn_err(CE_WARN,
9217656SSherry.Moore@Sun.COM 			    "scsb%d: Cannot turn off PSM_INT", instance);
9221708Sstevel 			if (!scsb_debug) {
9231708Sstevel 				(void) free_resources(dip, scsb, instance);
9241708Sstevel 				return (DDI_FAILURE);
9251708Sstevel 			}
9261708Sstevel 		}
9271708Sstevel 		/* Mask all interrupts */
9281708Sstevel 		if (scsb_setall_intmasks(scsb)) {
9291708Sstevel 			cmn_err(CE_WARN,
9307656SSherry.Moore@Sun.COM 			    "scsb%d: I2C TRANSFER Failed", instance);
9311708Sstevel 			if (!scsb_debug) {
9321708Sstevel 				(void) free_resources(dip, scsb, instance);
9331708Sstevel 				return (DDI_FAILURE);
9341708Sstevel 			}
9351708Sstevel 		}
9361708Sstevel 		/* Clear all latched interrupts */
9371708Sstevel 		if (scsb_clear_intptrs(scsb)) {
9381708Sstevel 			cmn_err(CE_WARN,
9397656SSherry.Moore@Sun.COM 			    "scsb%d: I2C TRANSFER Failed", instance);
9401708Sstevel 			if (!scsb_debug) {
9411708Sstevel 				(void) free_resources(dip, scsb, instance);
9421708Sstevel 				return (DDI_FAILURE);
9431708Sstevel 			}
9441708Sstevel 		}
9451708Sstevel 	}
9461708Sstevel 	if (scsb->scsb_opens && scsb->scsb_rq != NULL)
9471708Sstevel 		qprocsoff(scsb->scsb_rq);
9481708Sstevel 	/* CLONE */
9491708Sstevel 	(void) scsb_queue_ops(scsb, QPROCSOFF, 0, NULL, NULL);
9501708Sstevel 	/*
9511708Sstevel 	 * free the allocated resources
9521708Sstevel 	 */
9531708Sstevel 	free_resources(dip, scsb, instance);
9541708Sstevel 	return (DDI_SUCCESS);
9551708Sstevel }
9561708Sstevel 
9571708Sstevel static void
free_resources(dev_info_t * dip,scsb_state_t * scsb,int instance)9581708Sstevel free_resources(dev_info_t *dip, scsb_state_t *scsb, int instance)
9591708Sstevel {
9601708Sstevel 	if (scsb_debug & 0x0005) {
9611708Sstevel 		cmn_err(CE_NOTE, "free_resources[%d], scsb_state=0x%x",
9627656SSherry.Moore@Sun.COM 		    instance, scsb->scsb_state);
9631708Sstevel 		drv_usecwait(500000);
9641708Sstevel 	}
9651708Sstevel 	if (scsb->scsb_state & SCSB_P06_INTR_ON &&
9667656SSherry.Moore@Sun.COM 	    scsb->scsb_state & SCSB_IMUTEX) {
9671708Sstevel 		scsb->scsb_state &= ~SCSB_P06_INTR_ON;
9681708Sstevel 		ddi_remove_intr(dip, 0, scsb->scsb_iblock);
9691708Sstevel 	}
9701708Sstevel 	if (scsb->scsb_state & SCSB_KSTATS) {
9711708Sstevel 		scsb_free_kstats(scsb);
9721708Sstevel 		scsb->scsb_state &= ~SCSB_KSTATS;
9731708Sstevel 	}
9741708Sstevel 	if (scsb->scsb_state & SCSB_TOPOLOGY) {
9751708Sstevel 		scsb_free_topology(scsb);
9761708Sstevel 		scsb->scsb_state &= ~SCSB_TOPOLOGY;
9771708Sstevel 	}
9781708Sstevel 
9791708Sstevel 	nct_mutex_init = MUTEX_UNINIT;
9801708Sstevel 	if (scsb->scsb_state & SCSB_IMUTEX) {
9811708Sstevel 		scsb->scsb_state &= ~SCSB_IMUTEX;
9821708Sstevel 		mutex_destroy(&scsb->scsb_imutex);
9831708Sstevel 	}
9841708Sstevel 	if (scsb->scsb_state & SCSB_I2C_TRANSFER) {
9851708Sstevel 		scsb->scsb_state &= ~SCSB_I2C_TRANSFER;
9861708Sstevel 		i2c_transfer_free(scsb->scsb_phandle, scsb->scsb_i2ctp);
9871708Sstevel 	}
9881708Sstevel 	if (scsb->scsb_state & SCSB_I2C_PHANDLE) {
9891708Sstevel 		scsb->scsb_state &= ~SCSB_I2C_PHANDLE;
9901708Sstevel 		i2c_client_unregister(scsb->scsb_phandle);
9911708Sstevel 	}
9921708Sstevel 	if (scsb->scsb_state & SCSB_MINOR_NODE) {
9931708Sstevel 		scsb->scsb_state &= ~SCSB_MINOR_NODE;
9941708Sstevel 		ddi_remove_minor_node(dip, NULL);
9951708Sstevel 	}
9961708Sstevel 	if (scsb->scsb_state & SCSB_PROP_CREATE) {
9971708Sstevel 		scsb->scsb_state &= ~SCSB_PROP_CREATE;
9981708Sstevel 		(void) ddi_prop_remove(DDI_DEV_T_NONE, dip,
9997656SSherry.Moore@Sun.COM 		    "interrupt-priorities");
10001708Sstevel 	}
10011708Sstevel 	/* ddi_prop_remove_all(dip); */
10021708Sstevel 	if (scsb->scsb_state & SCSB_CONDVAR) {
10031708Sstevel 		scsb->scsb_state &= ~SCSB_CONDVAR;
10041708Sstevel 		cv_destroy(&scsb->scsb_cv);
10051708Sstevel 	}
10061708Sstevel 	if (scsb->scsb_state & SCSB_UMUTEX) {
10071708Sstevel 		scsb->scsb_state &= ~SCSB_UMUTEX;
10081708Sstevel 		mutex_destroy(&scsb->scsb_mutex);
10091708Sstevel 	}
10101708Sstevel 	ddi_soft_state_free(scsb_state, instance);
10111708Sstevel }
10121708Sstevel 
10131708Sstevel /*
10141708Sstevel  * Just for testing scsb's poll function
10151708Sstevel  */
10161708Sstevel static int
scsb_fake_intr(scsb_state_t * scsb,uint32_t evcode)10171708Sstevel scsb_fake_intr(scsb_state_t *scsb, uint32_t evcode)
10181708Sstevel {
10191708Sstevel 	if (evcode == 0)
10201708Sstevel 		evcode = scsb_event_code;
10211708Sstevel 	else
10221708Sstevel 		scsb_event_code = evcode;
10231708Sstevel 	if (scsb_debug & 0x4001) {
10241708Sstevel 		cmn_err(CE_NOTE, "scsb_fake_intr: event = 0x%x, scsb_rq=0x%p",
1025*11311SSurya.Prakki@Sun.COM 		    scsb_event_code, (void *)scsb->scsb_rq);
10261708Sstevel 	}
10271708Sstevel 	/*
10281708Sstevel 	 * Allow access to shadow registers even though SCB is removed
10291708Sstevel 	 *
10301708Sstevel 	 * if (scsb->scsb_state & SCSB_FROZEN) {
10311708Sstevel 	 *	return (EAGAIN);
10321708Sstevel 	 * }
10331708Sstevel 	 */
10341708Sstevel 	if (scsb_debug & 0x00040000) {
10351708Sstevel 		check_fru_info(scsb, evcode);
10361708Sstevel 		add_event_code(scsb, evcode);
10371708Sstevel 	}
10381708Sstevel 	/* just inform user-level via poll about this event */
10391708Sstevel 	if (scsb_queue_ops(scsb, QPUT_INT32, 1, &evcode, "scsb_fake_intr")
10407656SSherry.Moore@Sun.COM 	    == QOP_FAILED)
10411708Sstevel 		return (ENOMEM);
10421708Sstevel 	return (0);
10431708Sstevel }
10441708Sstevel 
10451708Sstevel /* ARGSUSED */
10461708Sstevel static int
scsb_info(dev_info_t * dip,ddi_info_cmd_t infocmd,void * arg,void ** result)10471708Sstevel scsb_info(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result)
10481708Sstevel {
10491708Sstevel 	int	retval = DDI_FAILURE;
10501708Sstevel 
10511708Sstevel 	if (scsb_debug & 0x0001)
10521708Sstevel 		cmn_err(CE_NOTE, "scsb_info()");
10531708Sstevel 
10541708Sstevel 	switch (infocmd) {
10551708Sstevel 	case DDI_INFO_DEVT2DEVINFO:
10561708Sstevel 		if (getminor((dev_t)arg) == 0 && scsb_dip != NULL) {
10571708Sstevel 			*result = (void *) scsb_dip;
10581708Sstevel 			retval = DDI_SUCCESS;
10591708Sstevel 		}
10601708Sstevel 		break;
10611708Sstevel 
10621708Sstevel 	case DDI_INFO_DEVT2INSTANCE:
10631708Sstevel 		if (getminor((dev_t)arg) == 0) {
10641708Sstevel 			*result = (void *)0;
10651708Sstevel 			retval = DDI_SUCCESS;
10661708Sstevel 		}
10671708Sstevel 		break;
10681708Sstevel 
10691708Sstevel 	default:
10701708Sstevel 		break;
10711708Sstevel 	}
10721708Sstevel 
10731708Sstevel 	return (retval);
10741708Sstevel }
10751708Sstevel 
10761708Sstevel 
10771708Sstevel /*
10781708Sstevel  * SCSB STREAMS routines
10791708Sstevel  */
10801708Sstevel /*ARGSUSED*/
10811708Sstevel static int
sm_open(queue_t * q,dev_t * devp,int flag,int sflag,cred_t * credp)10821708Sstevel sm_open(queue_t *q, dev_t *devp, int flag, int sflag, cred_t *credp)
10831708Sstevel {
10841708Sstevel 	int		instance, clone;
10851708Sstevel 	minor_t		minor_dev;
10861708Sstevel 	clone_dev_t	*clptr;
10871708Sstevel 	scsb_state_t	*scsb;
10881708Sstevel 
10891708Sstevel 	minor_dev = getminor(*devp);
10901708Sstevel 	instance = SCSB_GET_INSTANCE(minor_dev);
10911708Sstevel 	scsb = ddi_get_soft_state(scsb_state, instance);
10921708Sstevel 	if (scsb == NULL)
10931708Sstevel 		return (ENXIO);
10941708Sstevel 
10951708Sstevel 	if (scsb_debug & 0x0009) {
1096*11311SSurya.Prakki@Sun.COM 		cmn_err(CE_NOTE, "sm_open(%d) q=0x%p", instance, (void *)q);
10971708Sstevel 	}
10981708Sstevel 	if (!(scsb->scsb_state & SCSB_UP)) {
10991708Sstevel 		return (ENODEV);
11001708Sstevel 	}
11011708Sstevel 	/*
11021708Sstevel 	 * Don't fail the open if SCB removed since we still want to satisfy
11031708Sstevel 	 * read requests from the shadow registers, the last know register
11041708Sstevel 	 * contents.  On new SCB insertion, all will be re-initialized,
11051708Sstevel 	 * including envmond and it's policies.
11061708Sstevel 	 *
11071708Sstevel 	 * if (scsb->scsb_state & SCSB_FROZEN) {
11081708Sstevel 	 *	return (EAGAIN);
11091708Sstevel 	 * }
11101708Sstevel 	 */
11111708Sstevel 	ASSERT(credp != NULL);
11121708Sstevel 	/*
11131708Sstevel 	 * XXX check for root access here, return EPERM if not root open
11141708Sstevel 	 */
11151708Sstevel 	if (sflag == MODOPEN) {
11161708Sstevel 		/* scsb module is being pushed */
11171708Sstevel 		if (scsb_debug & 0x0008)
11181708Sstevel 			cmn_err(CE_NOTE, "sm_open(%d): MODOPEN", instance);
11191708Sstevel 		/*
11201708Sstevel 		 * this is no longer supported
11211708Sstevel 		 */
11221708Sstevel 		return (ENXIO);
11231708Sstevel 	} else if (sflag == CLONEOPEN) {
11241708Sstevel 		/* scsb is being opened as a clonable driver */
11251708Sstevel 		if (scsb_debug & 0x0008)
11261708Sstevel 			cmn_err(CE_NOTE, "sm_open(%d): CLONEOPEN", instance);
11271708Sstevel 		/*
11281708Sstevel 		 * The cloned stream is not handled via the clone driver.
11291708Sstevel 		 * See the minor device code below.
11301708Sstevel 		 */
11311708Sstevel 		return (ENXIO);
11321708Sstevel 	} else if (minor_dev & SCSB_CLONE) {
11331708Sstevel 		/*
11341708Sstevel 		 * First check for the SCSB_CLONE device.
11351708Sstevel 		 *	Find an available clone_devs[] entry, or return ENXIO.
11361708Sstevel 		 *	Make new dev_t and store in *devp.
11371708Sstevel 		 */
11381708Sstevel 		if (scsb_debug & 0x0008)
11391708Sstevel 			cmn_err(CE_NOTE,
11407656SSherry.Moore@Sun.COM 			    "sm_open(%d): SCSB_CLONE OPEN", instance);
11411708Sstevel 		mutex_enter(&scsb->scsb_mutex);
11421708Sstevel 		if ((clone = scsb_queue_ops(scsb, QFIRST_AVAILABLE, 0, NULL,
11437656SSherry.Moore@Sun.COM 		"scsb_open")) == QOP_FAILED) {
11441708Sstevel 			mutex_exit(&scsb->scsb_mutex);
11451708Sstevel 			return (ENXIO);
11461708Sstevel 		}
11471708Sstevel 		clptr = &scsb->clone_devs[clone];
11481708Sstevel 		clptr->cl_flags = SCSB_OPEN;
11491708Sstevel 		clptr->cl_rq = RD(q);
11501708Sstevel 		clptr->cl_minor = SCSB_MAKE_MINOR(instance, clone);
11511708Sstevel 		*devp = makedevice(getmajor(*devp), clptr->cl_minor);
11521708Sstevel 		scsb->scsb_clopens++;
11531708Sstevel 		if (scsb_debug & 0x0008)
11541708Sstevel 			cmn_err(CE_NOTE,
11557656SSherry.Moore@Sun.COM 			    "sm_open(%d): new clone device minor: 0x%x"
11567656SSherry.Moore@Sun.COM 			    " stream queue is 0x%p",
1157*11311SSurya.Prakki@Sun.COM 			    instance, clptr->cl_minor, (void *)q);
11581708Sstevel 	} else {
11591708Sstevel 		/* scsb is being opened as a regular driver */
11601708Sstevel 		if (scsb_debug & 0x0008)
11611708Sstevel 			cmn_err(CE_NOTE, "sm_open(%d): DEVOPEN", instance);
11621708Sstevel 		mutex_enter(&scsb->scsb_mutex);
11631708Sstevel 		if (scsb->scsb_state & SCSB_EXCL) {
11641708Sstevel 			if (scsb_debug & 0x0008)
11651708Sstevel 				cmn_err(CE_NOTE,
11661708Sstevel 				    "sm_open(%d): can't open, state is EXCL",
11671708Sstevel 				    instance);
11681708Sstevel 			mutex_exit(&scsb->scsb_mutex);
11691708Sstevel 			return (EBUSY);
11701708Sstevel 		}
11711708Sstevel 		if (flag & FEXCL) {
11721708Sstevel 			if (scsb_debug & 0x0008)
11731708Sstevel 				cmn_err(CE_NOTE, "sm_open(%d): is EXCL",
11747656SSherry.Moore@Sun.COM 				    instance);
11751708Sstevel 			if (scsb->scsb_state & SCSB_OPEN) {
11761708Sstevel 				if (scsb_debug & 0x0008)
11771708Sstevel 					cmn_err(CE_NOTE,
11787656SSherry.Moore@Sun.COM 					    "sm_open(%d): cannot open EXCL",
11797656SSherry.Moore@Sun.COM 					    instance);
11801708Sstevel 				mutex_exit(&scsb->scsb_mutex);
11811708Sstevel 				return (EBUSY);
11821708Sstevel 			}
11831708Sstevel 			scsb->scsb_state |= SCSB_EXCL;
11841708Sstevel 		}
11851708Sstevel 		if (scsb->scsb_opens && scsb->scsb_rq != NULL &&
11867656SSherry.Moore@Sun.COM 		    scsb->scsb_rq != RD(q)) {
11871708Sstevel 			if (scsb_debug & 0x000a)
11881708Sstevel 				cmn_err(CE_WARN, "sm_open[%d]: q (0x%p) != "
11897656SSherry.Moore@Sun.COM 				    "scsb_rq (0x%p)",
1190*11311SSurya.Prakki@Sun.COM 				    instance, (void *)RD(q),
1191*11311SSurya.Prakki@Sun.COM 				    (void *)scsb->scsb_rq);
11921708Sstevel 		}
11931708Sstevel 		scsb->scsb_rq = RD(q);
11941708Sstevel 		scsb->scsb_opens++;
11951708Sstevel 	}
11961708Sstevel 	scsb->scsb_state |= SCSB_OPEN;
11971708Sstevel 	mutex_exit(&scsb->scsb_mutex);
11981708Sstevel 	RD(q)->q_ptr = WR(q)->q_ptr = scsb;
11991708Sstevel 	qprocson(q);
12001708Sstevel 	return (0);
12011708Sstevel }
12021708Sstevel 
12031708Sstevel /*ARGSUSED*/
12041708Sstevel static int
sm_close(queue_t * q,int flag,int otyp,cred_t * credp)12051708Sstevel sm_close(queue_t *q, int flag, int otyp, cred_t *credp)
12061708Sstevel {
12071708Sstevel 	scsb_state_t	*scsb;
12081708Sstevel 	int		clone;
12091708Sstevel 	clone_dev_t	*clptr = NULL;
12101708Sstevel 
12111708Sstevel 	scsb = (scsb_state_t *)q->q_ptr;
12121708Sstevel 	if (scsb_debug & 0x0009)
1213*11311SSurya.Prakki@Sun.COM 		cmn_err(CE_NOTE, "sm_close[%d](0x%p)", scsb->scsb_instance,
1214*11311SSurya.Prakki@Sun.COM 		    (void *)q);
12151708Sstevel 	if (scsb->scsb_clopens) {
12161708Sstevel 		mutex_enter(&scsb->scsb_mutex);
12171708Sstevel 		if ((clone = scsb_queue_ops(scsb, QFIND_QUEUE, 0,
12187656SSherry.Moore@Sun.COM 		    (void *) RD(q), "scsb_close")) != QOP_FAILED) {
12191708Sstevel 			clptr = &scsb->clone_devs[clone];
12201708Sstevel 			clptr->cl_flags = 0;
12211708Sstevel 			clptr->cl_rq = NULL;
12221708Sstevel 			scsb->scsb_clopens--;
12231708Sstevel 		}
12241708Sstevel 		mutex_exit(&scsb->scsb_mutex);
12251708Sstevel 		if (scsb_debug & 0x0008 && clone < SCSB_CLONES_MAX &&
12267656SSherry.Moore@Sun.COM 		    clone >= SCSB_CLONES_FIRST)
12271708Sstevel 			cmn_err(CE_NOTE, "sm_close(%d): SCSB_CLONE 0x%x",
12287656SSherry.Moore@Sun.COM 			    scsb->scsb_instance, clptr->cl_minor);
12291708Sstevel 	}
12301708Sstevel 	if (clptr == NULL && scsb->scsb_opens) {
12311708Sstevel 		if (scsb_debug & 0x0008)
12321708Sstevel 			cmn_err(CE_NOTE, "sm_close(%d): DEVOPEN, opens=%d",
12337656SSherry.Moore@Sun.COM 			    scsb->scsb_instance, scsb->scsb_opens);
12341708Sstevel 		if (RD(q) != scsb->scsb_rq) {
12351708Sstevel 			if (scsb_debug & 0x0008)
12361708Sstevel 				cmn_err(CE_WARN,
12377656SSherry.Moore@Sun.COM 				    "sm_close(%d): DEVOPEN, q != scsb_rq",
12387656SSherry.Moore@Sun.COM 				    scsb->scsb_instance);
12391708Sstevel 		}
12401708Sstevel 		mutex_enter(&scsb->scsb_mutex);
12411708Sstevel 		scsb->scsb_opens = 0;
12421708Sstevel 		if (scsb->scsb_state & SCSB_EXCL) {
12431708Sstevel 			scsb->scsb_state &= ~SCSB_EXCL;
12441708Sstevel 		}
12451708Sstevel 		scsb->scsb_rq = (queue_t *)NULL;
12461708Sstevel 		mutex_exit(&scsb->scsb_mutex);
12471708Sstevel 	}
12481708Sstevel 	if (scsb->scsb_opens == 0 && scsb->scsb_clopens == 0) {
12491708Sstevel 		scsb->scsb_state &= ~SCSB_OPEN;
12501708Sstevel 	}
12511708Sstevel 	RD(q)->q_ptr = WR(q)->q_ptr = NULL;
12521708Sstevel 	qprocsoff(q);
12531708Sstevel 	return (0);
12541708Sstevel }
12551708Sstevel 
12561708Sstevel /*ARGSUSED*/
12571708Sstevel static int
sm_rput(queue_t * q,mblk_t * mp)12581708Sstevel sm_rput(queue_t *q, mblk_t *mp)
12591708Sstevel {
12601708Sstevel 	if (scsb_debug & 0x0010)
12611708Sstevel 		cmn_err(CE_NOTE, "sm_rput");
12621708Sstevel 	return (0);
12631708Sstevel }
12641708Sstevel 
12651708Sstevel static int
sm_wput(queue_t * q,mblk_t * mp)12661708Sstevel sm_wput(queue_t *q, mblk_t *mp)
12671708Sstevel {
12681708Sstevel 	scsb_state_t	*scsb = (scsb_state_t *)WR(q)->q_ptr;
12691708Sstevel 
12701708Sstevel 	if (scsb_debug & 0x0010)
1271*11311SSurya.Prakki@Sun.COM 		cmn_err(CE_NOTE, "sm_wput(%d): mp %p", scsb->scsb_instance,
1272*11311SSurya.Prakki@Sun.COM 		    (void *)mp);
12731708Sstevel 
12741708Sstevel 	switch (mp->b_datap->db_type) {
12751708Sstevel 	default:
12761708Sstevel 		freemsg(mp);
12771708Sstevel 		break;
12781708Sstevel 
12791708Sstevel 	case M_FLUSH:	/* canonical flush handling */
12801708Sstevel 		if (*mp->b_rptr & FLUSHW) {
12811708Sstevel 			flushq(q, FLUSHDATA);
12821708Sstevel 			/* free any messages tied to scsb */
12831708Sstevel 		}
12841708Sstevel 
12851708Sstevel 		if (*mp->b_rptr & FLUSHR) {
12861708Sstevel 			*mp->b_rptr &= ~FLUSHW;
12871708Sstevel 			qreply(q, mp);
12881708Sstevel 		} else
12891708Sstevel 			freemsg(mp);
12901708Sstevel 		break;
12911708Sstevel 
12921708Sstevel 	case M_IOCTL:
12931708Sstevel 		if (scsb_debug & 0x0010)
12941708Sstevel 			cmn_err(CE_NOTE, "sm_wput(%d): M_IOCTL",
12957656SSherry.Moore@Sun.COM 			    scsb->scsb_instance);
12961708Sstevel 		/* do ioctl */
12971708Sstevel 		smf_ioctl(q, mp);
12981708Sstevel 		break;
12991708Sstevel 
13001708Sstevel 	case M_DATA:
13011708Sstevel 		if (scsb_debug & 0x0010)
13021708Sstevel 			cmn_err(CE_NOTE, "sm_wput(%d): M_DATA",
13037656SSherry.Moore@Sun.COM 			    scsb->scsb_instance);
13041708Sstevel 		if (!(scsb->scsb_state & SCSB_UP)) {
13051708Sstevel 			freemsg(mp);
13061708Sstevel 			return (0);
13071708Sstevel 		}
13081708Sstevel 		freemsg(mp);
13091708Sstevel 		break;
13101708Sstevel 
13111708Sstevel 	case M_CTL:
13121708Sstevel 		if (scsb_debug & 0x0010)
13131708Sstevel 			cmn_err(CE_NOTE, "sm_wput(%d): M_CTL",
13147656SSherry.Moore@Sun.COM 			    scsb->scsb_instance);
13151708Sstevel 		freemsg(mp);
13161708Sstevel 		break;
13171708Sstevel 	}
13181708Sstevel 
13191708Sstevel 	return (0);
13201708Sstevel }
13211708Sstevel 
13221708Sstevel 
13231708Sstevel /*
13241708Sstevel  * These are the system monitor upper ioctl functions.
13251708Sstevel  */
13261708Sstevel static void
smf_ioctl(queue_t * q,mblk_t * mp)13271708Sstevel smf_ioctl(queue_t *q, mblk_t *mp)
13281708Sstevel {
13291708Sstevel 	scsb_state_t	*scsb = (scsb_state_t *)q->q_ptr;
13301708Sstevel 	struct iocblk	*iocp = (struct iocblk *)mp->b_rptr;
13311708Sstevel 
13321708Sstevel 	if (scsb_debug & 0x0020)
13331708Sstevel 		cmn_err(CE_NOTE, "smf_ioctl(%d): (%p)->cmd=%x",
1334*11311SSurya.Prakki@Sun.COM 		    scsb->scsb_instance, (void *)mp, iocp->ioc_cmd);
13351708Sstevel 
13361708Sstevel 	if (!(scsb->scsb_state & SCSB_UP)) {
13371708Sstevel 		miocnak(q, mp, 0, ENXIO);
13381708Sstevel 		return;
13391708Sstevel 	}
13401708Sstevel 	/*
13411708Sstevel 	 * Don't fail ALL commands if the SCB removed, since we still want to
13421708Sstevel 	 * satisfy some requests from the shadow registers, the last known
13431708Sstevel 	 * register contents.
13441708Sstevel 	 *
13451708Sstevel 	 * if (scsb->scsb_state & SCSB_FROZEN) {
13461708Sstevel 	 *	iocp->ioc_error = EAGAIN;
13471708Sstevel 	 *	mp->b_datap->db_type = M_IOCNAK;
13481708Sstevel 	 *	qreply(q, mp);
13491708Sstevel 	 *	return;
13501708Sstevel 	 * }
13511708Sstevel 	 */
13521708Sstevel 
13531708Sstevel 	iocp->ioc_error = 0;
13541708Sstevel 	switch (iocp->ioc_cmd) {
13551708Sstevel 	default:
13561708Sstevel 		/* if we don't understand the ioctl */
13571708Sstevel 		if (scsb_debug & 0x0022)
13581708Sstevel 			cmn_err(CE_NOTE, "smf_ioctl(%d):unkown ioctl %x",
13597656SSherry.Moore@Sun.COM 			    scsb->scsb_instance, iocp->ioc_cmd);
13601708Sstevel 		iocp->ioc_error = EINVAL;
13611708Sstevel 		break;
13621708Sstevel 
13631708Sstevel 	case ENVC_IOC_GETMODE:
13641708Sstevel 	{
13651708Sstevel 		uint8_t *curr_mode;
13661708Sstevel 
13671708Sstevel 		iocp->ioc_error = miocpullup(mp, sizeof (uint8_t));
13681708Sstevel 		if (iocp->ioc_error != 0)
13691708Sstevel 			break;
13701708Sstevel 
13711708Sstevel 		curr_mode = (uint8_t *)mp->b_cont->b_rptr;
13721708Sstevel 		if (scsb->scsb_state & SCSB_DEBUG_MODE)
13731708Sstevel 			*curr_mode = (uint8_t)ENVC_DEBUG_MODE;
13741708Sstevel 		else if (scsb->scsb_state & SCSB_DIAGS_MODE)
13751708Sstevel 			*curr_mode = (uint8_t)ENVCTRL_DIAG_MODE;
13761708Sstevel 		else
13771708Sstevel 			*curr_mode = (uint8_t)ENVCTRL_NORMAL_MODE;
13781708Sstevel 
13791708Sstevel 		if (scsb_debug & 0x20) {
13801708Sstevel 			cmn_err(CE_NOTE, "IOC_GETMODE: returning mode 0x%x",
13817656SSherry.Moore@Sun.COM 			    *curr_mode);
13821708Sstevel 		}
13831708Sstevel 		break;
13841708Sstevel 	}
13851708Sstevel 
13861708Sstevel 	case ENVC_IOC_SETMODE:
13871708Sstevel 	{
13881708Sstevel 		uint8_t	*curr_mode;
13891708Sstevel 
13901708Sstevel 		iocp->ioc_error = miocpullup(mp, sizeof (uint8_t));
13911708Sstevel 		if (iocp->ioc_error != 0)
13921708Sstevel 			break;
13931708Sstevel 
13941708Sstevel 		curr_mode = (uint8_t *)mp->b_cont->b_rptr;
13951708Sstevel 		switch (*curr_mode) {
13961708Sstevel 		case ENVCTRL_NORMAL_MODE:
13971708Sstevel 			scsb->scsb_state &=
13987656SSherry.Moore@Sun.COM 			    ~(SCSB_DEBUG_MODE | SCSB_DIAGS_MODE);
13991708Sstevel 			break;
14001708Sstevel 		case ENVCTRL_DIAG_MODE:
14011708Sstevel 			scsb->scsb_state |=  SCSB_DIAGS_MODE;
14021708Sstevel 			scsb->scsb_state &= ~SCSB_DEBUG_MODE;
14031708Sstevel 			break;
14041708Sstevel 		case ENVC_DEBUG_MODE:
14051708Sstevel 			if (scsb->scsb_state &
14067656SSherry.Moore@Sun.COM 			    (SCSB_DIAGS_MODE | SCSB_DEBUG_MODE)) {
14071708Sstevel 				scsb->scsb_state &= ~SCSB_DIAGS_MODE;
14081708Sstevel 				scsb->scsb_state |=  SCSB_DEBUG_MODE;
14091708Sstevel 			} else {
14101708Sstevel 				iocp->ioc_error = EACCES;
14111708Sstevel 			}
14121708Sstevel 			break;
14131708Sstevel 		default:
14141708Sstevel 			if (scsb_debug & 0x22) {
14151708Sstevel 				cmn_err(CE_WARN,
14161708Sstevel 				    "IOC_SETMODE: Invalid mode 0x%x",
14171708Sstevel 				    *curr_mode);
14181708Sstevel 			}
14191708Sstevel 			iocp->ioc_error = EINVAL;
14201708Sstevel 			break;
14211708Sstevel 		}
14221708Sstevel 		break;
14231708Sstevel 	}
14241708Sstevel 
14251708Sstevel 	case ENVC_IOC_ACQUIRE_SLOT_LED_CTRL:
14261708Sstevel 		if (scsb->scsb_state & SCSB_APP_SLOTLED_CTRL)
14271708Sstevel 			iocp->ioc_error = EAGAIN;
14281708Sstevel 		else {
14291708Sstevel 			scsb->scsb_state |= SCSB_APP_SLOTLED_CTRL;
14301708Sstevel 			iocp->ioc_error = 0;
14311708Sstevel 		}
14321708Sstevel 		break;
14331708Sstevel 
14341708Sstevel 	case ENVC_IOC_RELEASE_SLOT_LED_CTRL:
14351708Sstevel 		scsb->scsb_state &= ~SCSB_APP_SLOTLED_CTRL;
14361708Sstevel 		iocp->ioc_error = 0;
14371708Sstevel 		break;
14381708Sstevel 
14391708Sstevel 	/*
14401708Sstevel 	 * Not an exposed interface, only used by development utilities.
14411708Sstevel 	 */
14421708Sstevel 	case SCSBIOC_GET_VERSIONS:
14431708Sstevel 	{
14441708Sstevel 		uint8_t *ppromid, promid;
14451708Sstevel 		scsb_ids_t *sids;
14461708Sstevel 
14471708Sstevel 		if (iocp->ioc_count == sizeof (uint8_t)) {
14481708Sstevel 			iocp->ioc_error = miocpullup(mp, sizeof (uint8_t));
14491708Sstevel 			if (iocp->ioc_error != 0)
14501708Sstevel 				break;
14511708Sstevel 
14521708Sstevel 			ppromid = (uint8_t *)mp->b_cont->b_rptr;
14531708Sstevel 			*ppromid = (uint8_t)(mct_system_info.
14547656SSherry.Moore@Sun.COM 			    fru_info_list[SCB])->fru_version;
14551708Sstevel 			promid = *ppromid;
14561708Sstevel 		} else {
14571708Sstevel 			iocp->ioc_error = miocpullup(mp, sizeof (scsb_ids_t));
14581708Sstevel 			if (iocp->ioc_error != 0)
14591708Sstevel 				break;
14601708Sstevel 
14611708Sstevel 			sids = (scsb_ids_t *)mp->b_cont->b_rptr;
14621708Sstevel 			bcopy(modldrv.drv_linkinfo, sids->modldrv_string,
14631708Sstevel 			    SCSB_MODSTR_LEN);
14641708Sstevel 			bcopy(scsb_build_version, sids->scsb_version,
14651708Sstevel 			    SCSB_VERSTR_LEN);
14661708Sstevel 			sids->promid = (uint8_t)(mct_system_info.
14671708Sstevel 			    fru_info_list[SCB])->fru_version;
14681708Sstevel 
14691708Sstevel 			promid = sids->promid;
14701708Sstevel 			if (scsb_debug & 0x20) {
14711708Sstevel 				cmn_err(CE_NOTE,
14721708Sstevel 				    "IOC_GET_VERSIONS: sizeof(scsb_ids_t) "
14731708Sstevel 				    "= %lu", sizeof (scsb_ids_t));
14741708Sstevel 			}
14751708Sstevel 		}
14761708Sstevel 		if (scsb_debug & 0x20) {
14771708Sstevel 			cmn_err(CE_NOTE,
14781708Sstevel 			    "IOC_GET_VERSIONS: SCB PROMID = 0x%x", promid);
14791708Sstevel 		}
14801708Sstevel 		break;
14811708Sstevel 	}
14821708Sstevel 
14831708Sstevel #ifdef	DEBUG
14841708Sstevel 	case ENVC_IOC_REGISTER_PID:
14851708Sstevel 		iocp->ioc_error = miocpullup(mp, sizeof (pid_t));
14861708Sstevel 		if (iocp->ioc_error == 0) {
14871708Sstevel 			if (add_event_proc(scsb, *(pid_t *)mp->b_cont->b_rptr))
14881708Sstevel 				iocp->ioc_error = ENOMEM;
14891708Sstevel 		}
14901708Sstevel 		break;
14911708Sstevel 
14921708Sstevel 	case ENVC_IOC_UNREGISTER_PID:
14931708Sstevel 		iocp->ioc_error = miocpullup(mp, sizeof (pid_t));
14941708Sstevel 		if (iocp->ioc_error == 0) {
14951708Sstevel 			if (del_event_proc(scsb, *(pid_t *)mp->b_cont->b_rptr))
14961708Sstevel 				iocp->ioc_error = EINVAL;
14971708Sstevel 		}
14981708Sstevel 		break;
14991708Sstevel 
15001708Sstevel 	case SCSBIOC_VALUE_MODE:
15011708Sstevel 	{
15021708Sstevel 		uint32_t *mode_vals;
15031708Sstevel 		int	three_vals = 0;
15041708Sstevel 
15051708Sstevel 		if (!(scsb->scsb_state & SCSB_DEBUG_MODE)) {
15061708Sstevel 			iocp->ioc_error = EINVAL;
15071708Sstevel 			break;
15081708Sstevel 		}
15091708Sstevel 
15101708Sstevel 		if (iocp->ioc_count == sizeof (uint32_t) * 3)
15111708Sstevel 			three_vals = 1;
15121708Sstevel 		else if (iocp->ioc_count != sizeof (uint32_t) * 2) {
15131708Sstevel 			iocp->ioc_error = EINVAL;
15141708Sstevel 			break;
15151708Sstevel 		}
15161708Sstevel 
15171708Sstevel 		iocp->ioc_error = miocpullup(mp, iocp->ioc_count);
15181708Sstevel 		if (iocp->ioc_error != 0)
15191708Sstevel 			break;
15201708Sstevel 
15211708Sstevel 		/*
15221708Sstevel 		 * check mode_vals[0] for get/set option.  setting
15231708Sstevel 		 * scsb_state is not valid for now.  0 == GET, 1 == SET
15241708Sstevel 		 */
15251708Sstevel 		mode_vals = (uint32_t *)mp->b_cont->b_rptr;
15261708Sstevel 		if (mode_vals[0]) {
15271708Sstevel 			scsb_debug = mode_vals[1];
15281708Sstevel 		} else {
15291708Sstevel 			mode_vals[0] = scsb->scsb_state;
15301708Sstevel 			if (three_vals) {
15311708Sstevel 				mode_vals[1] = scsb->scsb_hsc_state;
15321708Sstevel 				mode_vals[2] = scsb_debug;
15331708Sstevel 			} else
15341708Sstevel 				mode_vals[1] = scsb_debug;
15351708Sstevel 		}
15361708Sstevel 		if ((scsb_debug & 0x20) && three_vals) {
15371708Sstevel 			cmn_err(CE_NOTE, "IOC_VALUE_MODE: mode_vals: "
15381708Sstevel 			    "0x%x/0x%x/0x%x; ioc_count = 0x%lx",
15391708Sstevel 			    mode_vals[0], mode_vals[1], mode_vals[2],
15401708Sstevel 			    iocp->ioc_count);
15411708Sstevel 		}
15421708Sstevel 		break;
15431708Sstevel 	}
15441708Sstevel 
15451708Sstevel #ifdef DEBUG
15461708Sstevel 	case SCSBIOC_GET_SLOT_INFO:
15471708Sstevel 	{
15481708Sstevel 		hsc_slot_t	*slot_info = NULL;
15491708Sstevel 		uint32_t	*slot_vals;
15501708Sstevel 		int		pslotnum;
15511708Sstevel 
15521708Sstevel 		if (!(scsb->scsb_state & SCSB_DEBUG_MODE)) {
15531708Sstevel 			iocp->ioc_error = EINVAL;
15541708Sstevel 			break;
15551708Sstevel 		}
15561708Sstevel 
15571708Sstevel 		iocp->ioc_error = miocpullup(mp, sizeof (uint32_t) * 2);
15581708Sstevel 		if (iocp->ioc_error != 0)
15591708Sstevel 			break;
15601708Sstevel 
15611708Sstevel 		slot_vals = (uint32_t *)mp->b_cont->b_rptr;
15621708Sstevel 		pslotnum = (int)*slot_vals;
1563*11311SSurya.Prakki@Sun.COM 		hsc_ac_op((int)scsb->scsb_instance, pslotnum,
15641708Sstevel 		    SCSB_HSC_AC_GET_SLOT_INFO, &slot_info);
15651708Sstevel 		if (slot_info == NULL) {
15661708Sstevel 			iocp->ioc_error = ENODEV;
15671708Sstevel 			break;
15681708Sstevel 		}
15691708Sstevel 		*slot_vals = (uint32_t)slot_info->hs_flags;
15701708Sstevel 		*(++slot_vals) = (uint32_t)slot_info->hs_slot_state;
15711708Sstevel 		if (scsb_debug & 0x20) {
15721708Sstevel 			cmn_err(CE_NOTE, "IOC_GET_SLOT_STATE: slot_vals: "
15731708Sstevel 			    "0x%x/0x%x; ioc_count = 0x%lx",
15741708Sstevel 			    slot_vals[0], slot_vals[1], iocp->ioc_count);
15751708Sstevel 		}
15761708Sstevel 		break;
15771708Sstevel 	}
15781708Sstevel #endif /* DEBUG */
15791708Sstevel 
15801708Sstevel 	case SCSBIOC_GET_FAN_STATUS:
15811708Sstevel 	case SCSBIOC_GET_INTR_ARRAY:
15821708Sstevel 		/* for now we don't understand these ioctls */
15831708Sstevel 		if (scsb_debug & 0x0022)
15841708Sstevel 			cmn_err(CE_NOTE, "smf_ioctl(%d):unknown ioctl %x",
15857656SSherry.Moore@Sun.COM 			    scsb->scsb_instance, iocp->ioc_cmd);
15861708Sstevel 		iocp->ioc_error = EINVAL;
15871708Sstevel 		break;
15881708Sstevel #endif	/* DEBUG */
15891708Sstevel 
15901708Sstevel 	case SCSBIOC_LED_OK_GET:
15911708Sstevel 	case SCSBIOC_LED_NOK_GET:
15921708Sstevel 	case SCSBIOC_LED_OK_SET:
15931708Sstevel 	case SCSBIOC_LED_NOK_SET:
15941708Sstevel 	case SCSBIOC_BHEALTHY_GET:
15951708Sstevel 	case SCSBIOC_SLOT_OCCUPANCY:
15961708Sstevel 	case SCSBIOC_RESET_UNIT:
15971708Sstevel 		if (!(scsb->scsb_state & (SCSB_DIAGS_MODE | SCSB_DEBUG_MODE))) {
15981708Sstevel 			iocp->ioc_error = EACCES;
15991708Sstevel 			break;
16001708Sstevel 		}
16011708Sstevel 		/*FALLTHROUGH*/
16021708Sstevel 
16031708Sstevel 	case ENVC_IOC_GETDSKLED:
16041708Sstevel 	case ENVC_IOC_SETDSKLED:
16051708Sstevel 	case ENVC_IOC_SETFSP:
16061708Sstevel 	{
16071708Sstevel 		scsb_uinfo_t *suip;
16081708Sstevel 
16091708Sstevel 		iocp->ioc_error = miocpullup(mp, sizeof (scsb_uinfo_t));
16101708Sstevel 		if (iocp->ioc_error != 0)
16111708Sstevel 			break;
16121708Sstevel 
16131708Sstevel 		suip = (scsb_uinfo_t *)mp->b_cont->b_rptr;
16141708Sstevel 		switch (iocp->ioc_cmd) {
16151708Sstevel 		case SCSBIOC_LED_OK_GET:
16161708Sstevel 			iocp->ioc_error = scsb_led_get(scsb, suip, OK);
16171708Sstevel 			break;
16181708Sstevel 		case SCSBIOC_LED_NOK_GET:
16191708Sstevel 			iocp->ioc_error = scsb_led_get(scsb, suip, NOK);
16201708Sstevel 			break;
16211708Sstevel 		case SCSBIOC_LED_OK_SET:
16221708Sstevel 			iocp->ioc_error = scsb_led_set(scsb, suip, OK);
16231708Sstevel 			break;
16241708Sstevel 		case SCSBIOC_LED_NOK_SET:
16251708Sstevel 			iocp->ioc_error = scsb_led_set(scsb, suip, NOK);
16261708Sstevel 			break;
16271708Sstevel 		case SCSBIOC_BHEALTHY_GET:
16281708Sstevel 			iocp->ioc_error = scsb_bhealthy_slot(scsb, suip);
16291708Sstevel 			break;
16301708Sstevel 		case SCSBIOC_SLOT_OCCUPANCY:
16311708Sstevel 			iocp->ioc_error = scsb_slot_occupancy(scsb, suip);
16321708Sstevel 			break;
16331708Sstevel 		case SCSBIOC_RESET_UNIT:
16341708Sstevel 			iocp->ioc_error = scsb_reset_unit(scsb, suip);
16351708Sstevel 			break;
16361708Sstevel 		case ENVC_IOC_GETDSKLED:
16371708Sstevel 			if (suip->unit_type != DISK) {
16381708Sstevel 				iocp->ioc_error = EINVAL;
16391708Sstevel 				break;
16401708Sstevel 			}
16411708Sstevel 			iocp->ioc_error = scsb_led_get(scsb, suip, NOUSE);
16421708Sstevel 			break;
16431708Sstevel 		case ENVC_IOC_SETDSKLED:
16441708Sstevel 			if (suip->unit_type != DISK) {
16451708Sstevel 				iocp->ioc_error = EINVAL;
16461708Sstevel 				break;
16471708Sstevel 			}
16481708Sstevel 			iocp->ioc_error = scsb_led_set(scsb, suip, NOUSE);
16491708Sstevel 			break;
16501708Sstevel 		case ENVC_IOC_SETFSP:
16511708Sstevel 			if (scsb->scsb_state & SCSB_FROZEN) {
16521708Sstevel 				iocp->ioc_error = EAGAIN;
16531708Sstevel 				break;
16541708Sstevel 			}
16551708Sstevel 			iocp->ioc_error = scsb_led_set(scsb, suip, NOUSE);
16561708Sstevel 			break;
16571708Sstevel 		}
16581708Sstevel 		break;
16591708Sstevel 	}
16601708Sstevel 
16611708Sstevel 	case SCSBIOC_FAKE_INTR: {
16621708Sstevel 		uint32_t	ui;
16631708Sstevel 
16641708Sstevel 		if (!(scsb->scsb_state & SCSB_DEBUG_MODE)) {
16651708Sstevel 			iocp->ioc_error = EINVAL;
16661708Sstevel 			break;
16671708Sstevel 		}
16681708Sstevel 		if (mp->b_cont == NULL)
16691708Sstevel 			ui = 0;
16701708Sstevel 		else {
16711708Sstevel 			iocp->ioc_error = miocpullup(mp, sizeof (uint32_t));
16721708Sstevel 			if (iocp->ioc_error != 0)
16731708Sstevel 				break;
16741708Sstevel 			ui = *(uint32_t *)mp->b_cont->b_rptr;
16751708Sstevel 		}
16761708Sstevel 		iocp->ioc_error = scsb_fake_intr(scsb, ui);
16771708Sstevel 		break;
16781708Sstevel 	}
16791708Sstevel 
16801708Sstevel 	case SCSBIOC_GET_STATUS :
16811708Sstevel 		if (!(scsb->scsb_state & SCSB_DEBUG_MODE)) {
16821708Sstevel 			iocp->ioc_error = EINVAL;
16831708Sstevel 			break;
16841708Sstevel 		}
16851708Sstevel 		iocp->ioc_error = miocpullup(mp, sizeof (scsb_status_t));
16861708Sstevel 		if (iocp->ioc_error == 0)
16871708Sstevel 			iocp->ioc_error = scsb_get_status(scsb,
16881708Sstevel 			    (scsb_status_t *)mp->b_cont->b_rptr);
16891708Sstevel 		break;
16901708Sstevel 
16911708Sstevel 	case SCSBIOC_ALL_LEDS_ON :
16921708Sstevel 		if (!(scsb->scsb_state & (SCSB_DIAGS_MODE | SCSB_DEBUG_MODE)))
16931708Sstevel 			iocp->ioc_error = EACCES;
16941708Sstevel 		else
16951708Sstevel 			iocp->ioc_error = scsb_leds_switch(scsb, ON);
16961708Sstevel 		break;
16971708Sstevel 
16981708Sstevel 	case SCSBIOC_ALL_LEDS_OFF :
16991708Sstevel 		if (!(scsb->scsb_state & (SCSB_DIAGS_MODE | SCSB_DEBUG_MODE)))
17001708Sstevel 			iocp->ioc_error = EACCES;
17011708Sstevel 		else
17021708Sstevel 			iocp->ioc_error = scsb_leds_switch(scsb, OFF);
17031708Sstevel 		break;
17041708Sstevel 
17051708Sstevel 	case SCSBIOC_REG_READ:
17061708Sstevel 	case SCSBIOC_REG_WRITE:
17071708Sstevel 		if (!(scsb->scsb_state & (SCSB_DIAGS_MODE | SCSB_DEBUG_MODE))) {
17081708Sstevel 			iocp->ioc_error = EACCES;
17091708Sstevel 		} else {
17101708Sstevel 			scsb_ioc_rdwr_t	*iocrdwrp;
17111708Sstevel 
17121708Sstevel 			if (scsb->scsb_state & SCSB_FROZEN &&
17131708Sstevel 			    !(scsb->scsb_state & SCSB_DEBUG_MODE)) {
17141708Sstevel 				iocp->ioc_error = EAGAIN;
17151708Sstevel 				break;
17161708Sstevel 			}
17171708Sstevel 
17181708Sstevel 			iocp->ioc_error = miocpullup(mp, sizeof (*iocrdwrp));
17191708Sstevel 			if (iocp->ioc_error == 0) {
17201708Sstevel 				iocrdwrp =
17211708Sstevel 				    (scsb_ioc_rdwr_t *)mp->b_cont->b_rptr;
17221708Sstevel 
17231708Sstevel 				if (iocp->ioc_cmd == SCSBIOC_REG_READ) {
17241708Sstevel 					if (iocrdwrp->ioc_rlen > 0) {
17251708Sstevel 						sm_ioc_rdwr(q, mp, I2C_WR_RD);
17261708Sstevel 						return;
17271708Sstevel 					}
17281708Sstevel 				} else {
17291708Sstevel 					if (iocrdwrp->ioc_wlen > 0) {
17301708Sstevel 						sm_ioc_rdwr(q, mp, I2C_WR);
17311708Sstevel 						return;
17321708Sstevel 					}
17331708Sstevel 				}
17341708Sstevel 				iocp->ioc_error = EINVAL;
17351708Sstevel 				break;
17361708Sstevel 			}
17371708Sstevel 		}
17381708Sstevel 		break;
17391708Sstevel 
17401708Sstevel 	case SCSBIOC_SHUTDOWN_POLL:
17411708Sstevel 	case SCSBIOC_INTEVENT_POLL:
17421708Sstevel 		if (!(scsb->scsb_state & SCSB_DEBUG_MODE)) {
17431708Sstevel 			iocp->ioc_error = EINVAL;
17441708Sstevel 			break;
17451708Sstevel 		}
17461708Sstevel 		iocp->ioc_error = miocpullup(mp, sizeof (uint32_t));
17471708Sstevel 		if (iocp->ioc_error == 0)
17481708Sstevel 			iocp->ioc_error = scsb_polled_int(scsb, iocp->ioc_cmd,
17491708Sstevel 			    (uint32_t *)mp->b_cont->b_rptr);
17501708Sstevel 		break;
17511708Sstevel 
17521708Sstevel 	case SCSBIOC_RESTORE :
17531708Sstevel 		if (!(scsb->scsb_state & (SCSB_DIAGS_MODE | SCSB_DEBUG_MODE)))
17541708Sstevel 			iocp->ioc_error = EACCES;
17551708Sstevel 		else {
17561708Sstevel 			scsb_restore(scsb);
1757*11311SSurya.Prakki@Sun.COM 			(void) scsb_toggle_psmint(scsb, 1);
17581708Sstevel 			iocp->ioc_error = 0;
17591708Sstevel 		}
17601708Sstevel 		break;
17611708Sstevel 
17621708Sstevel 	case SCSBIOC_FREEZE :
17631708Sstevel 		if (!(scsb->scsb_state & (SCSB_DIAGS_MODE | SCSB_DEBUG_MODE)))
17641708Sstevel 			iocp->ioc_error = EACCES;
17651708Sstevel 		else {
17661708Sstevel 			scsb_freeze_check(scsb);
17671708Sstevel 			scsb_freeze(scsb);
17681708Sstevel 			iocp->ioc_error = 0;
17691708Sstevel 		}
17701708Sstevel 		break;
17711708Sstevel 
17721708Sstevel 	/*
17731708Sstevel 	 * envmond:alarmcard.so response to SCTRL_EVENT_ALARM_INSERTION
17741708Sstevel 	 */
17751708Sstevel 	case ENVC_IOC_ACCONF_RESTORED:
17761708Sstevel 		(void) scsb_hsc_ac_op(scsb, scsb->ac_slotnum,
17771708Sstevel 		    SCSB_HSC_AC_SET_BUSY);
17781708Sstevel 		break;
17791708Sstevel 
17801708Sstevel 	/*
17811708Sstevel 	 * envmond:alarmcard.so response to SCTRL_EVENT_ALARM_REMOVAL
17821708Sstevel 	 */
17831708Sstevel 	case ENVC_IOC_ACCONF_STORED:
17841708Sstevel 		if (scsb->scsb_state & SCSB_FROZEN) {
17851708Sstevel 			iocp->ioc_error = EAGAIN;
17861708Sstevel 			break;
17871708Sstevel 		}
17881708Sstevel 		(void) scsb_hsc_ac_op(scsb, scsb->ac_slotnum,
17891708Sstevel 		    SCSB_HSC_AC_UNCONFIGURE);
17901708Sstevel 		break;
17911708Sstevel 
17921708Sstevel #ifdef	DEBUG
17931708Sstevel 	case SCSBIOC_TOPOLOGY_DUMP:
17941708Sstevel 		if (!(scsb->scsb_state & SCSB_DEBUG_MODE))
17951708Sstevel 			iocp->ioc_error = EINVAL;
17961708Sstevel 		else {
17971708Sstevel 			mct_topology_dump(scsb, 1);
17981708Sstevel 			iocp->ioc_error = 0;
17991708Sstevel 		}
18001708Sstevel 		break;
18011708Sstevel #endif
18021708Sstevel 	}
18031708Sstevel 	if (iocp->ioc_error)
18041708Sstevel 		mp->b_datap->db_type = M_IOCNAK;
18051708Sstevel 	else
18061708Sstevel 		mp->b_datap->db_type = M_IOCACK;
18071708Sstevel 	qreply(q, mp);
18081708Sstevel }
18091708Sstevel 
18101708Sstevel static fru_info_t *
find_fru_info(fru_id_t fru_id)18111708Sstevel find_fru_info(fru_id_t fru_id)
18121708Sstevel {
18131708Sstevel 	int		i;
18141708Sstevel 	fru_info_t	*fru_ptr;
18151708Sstevel 
18161708Sstevel 	if (scsb_debug & 0x00100001)
18171708Sstevel 		cmn_err(CE_NOTE, "find_fru_info(0x%x)", fru_id);
18181708Sstevel 	if (fru_id == (fru_id_t)0)
18191708Sstevel 		return ((fru_info_t *)NULL);
18201708Sstevel 	for (i = 0; i < SCSB_UNIT_TYPES; ++i) {
18211708Sstevel 		fru_ptr = mct_system_info.fru_info_list[i];
18221708Sstevel 		while (fru_ptr != NULL) {
18231708Sstevel 			if (fru_ptr->fru_id == fru_id)
18241708Sstevel 				return (fru_ptr);
18251708Sstevel 			fru_ptr = fru_ptr->next;
18261708Sstevel 		}
18271708Sstevel 	}
18281708Sstevel 	return ((fru_info_t *)NULL);
18291708Sstevel }
18301708Sstevel 
18311708Sstevel 
18321708Sstevel struct scsb_cb_entry {
18331708Sstevel 	void			*cb_softstate_ptr;
18341708Sstevel 	fru_id_t		cb_fru_id;
18351708Sstevel 	scsb_fru_event_t	cb_event;
18361708Sstevel 	void			(*cb_func)
18371708Sstevel 				(void *, scsb_fru_event_t, scsb_fru_status_t);
18381708Sstevel 	fru_info_t		*cb_fru_ptr;
18391708Sstevel 	struct scsb_cb_entry	*cb_next;
18401708Sstevel };
18411708Sstevel 
18421708Sstevel #ifdef DEBUG
18431708Sstevel int	scsb_cb_count = 0;
18441708Sstevel #else
18451708Sstevel static
18461708Sstevel #endif
18471708Sstevel struct scsb_cb_entry	*scsb_cb_table;
18481708Sstevel 
18491708Sstevel /*
18501708Sstevel  * global function for interested FRU drivers to register a callback function,
18511708Sstevel  * to be called when FRU presence status changes.
18521708Sstevel  */
18531708Sstevel scsb_fru_status_t
scsb_fru_register(void (* cb_func)(void *,scsb_fru_event_t,scsb_fru_status_t),void * soft_ptr,fru_id_t fru_id)18541708Sstevel scsb_fru_register(void (*cb_func)(void *, scsb_fru_event_t, scsb_fru_status_t),
18551708Sstevel 			void *soft_ptr, fru_id_t fru_id)
18561708Sstevel {
18571708Sstevel 	struct scsb_cb_entry	*cbe_ptr;
18581708Sstevel 
18591708Sstevel 	if (scsb_debug & 0x00800001) {
18601708Sstevel 		cmn_err(CE_NOTE,
18617656SSherry.Moore@Sun.COM 		    "scsb_fru_register: FRU_ID 0x%x", (int)fru_id);
18621708Sstevel 	}
18631708Sstevel 	if (!(scsb_global_state & SCSB_UP)) {
18641708Sstevel 		return (FRU_NOT_AVAILABLE);
18651708Sstevel 	}
18661708Sstevel 	if (cb_func == NULL || fru_id == (fru_id_t)0)
18671708Sstevel 		return (FRU_NOT_AVAILABLE);
18681708Sstevel 	if (scsb_cb_table == NULL)
18691708Sstevel 		scsb_cb_table = (struct scsb_cb_entry *)
18707656SSherry.Moore@Sun.COM 		    kmem_zalloc(sizeof (struct scsb_cb_entry), KM_SLEEP);
18711708Sstevel 	cbe_ptr = scsb_cb_table;
18721708Sstevel 	while (cbe_ptr->cb_softstate_ptr != NULL) {
18731708Sstevel 		if (cbe_ptr->cb_next == (struct scsb_cb_entry *)NULL) {
18741708Sstevel 			cbe_ptr->cb_next = (struct scsb_cb_entry *)
18757656SSherry.Moore@Sun.COM 			    kmem_zalloc(sizeof (struct scsb_cb_entry),
18767656SSherry.Moore@Sun.COM 			    KM_SLEEP);
18771708Sstevel 			cbe_ptr = cbe_ptr->cb_next;
18781708Sstevel 			break;
18791708Sstevel 		}
18801708Sstevel 		cbe_ptr = cbe_ptr->cb_next;
18811708Sstevel 	}
18821708Sstevel 	cbe_ptr->cb_softstate_ptr = soft_ptr;
18831708Sstevel 	cbe_ptr->cb_fru_id = fru_id;
18841708Sstevel 	cbe_ptr->cb_func = cb_func;
18851708Sstevel 	cbe_ptr->cb_next = (struct scsb_cb_entry *)NULL;
18861708Sstevel 	cbe_ptr->cb_fru_ptr = find_fru_info(fru_id);
18871708Sstevel #ifdef DEBUG
18881708Sstevel 	scsb_cb_count++;
18891708Sstevel #endif
18901708Sstevel 	if (scsb_debug & 0x00800000) {
18911708Sstevel 		cmn_err(CE_NOTE,
18927656SSherry.Moore@Sun.COM 		    "scsb_fru_register: FRU_ID 0x%x, status=%d",
18937656SSherry.Moore@Sun.COM 		    (int)fru_id,
18947656SSherry.Moore@Sun.COM 		    (cbe_ptr->cb_fru_ptr == (fru_info_t *)NULL) ?
18957656SSherry.Moore@Sun.COM 		    0xff : cbe_ptr->cb_fru_ptr->fru_status);
18961708Sstevel 	}
18971708Sstevel 	if (cbe_ptr->cb_fru_ptr == (fru_info_t *)NULL)
18981708Sstevel 		return (FRU_NOT_AVAILABLE);
18991708Sstevel 	if (cbe_ptr->cb_fru_ptr->fru_status & FRU_PRESENT)
19001708Sstevel 		return (FRU_PRESENT);
19011708Sstevel 	return (FRU_NOT_PRESENT);
19021708Sstevel }
19031708Sstevel 
19041708Sstevel void
scsb_fru_unregister(void * soft_ptr,fru_id_t fru_id)19051708Sstevel scsb_fru_unregister(void *soft_ptr, fru_id_t fru_id)
19061708Sstevel {
19071708Sstevel 	struct scsb_cb_entry	*prev_ptr, *cbe_ptr;
19081708Sstevel 
19091708Sstevel 	if (scsb_debug & 0x00800001) {
19101708Sstevel 		cmn_err(CE_NOTE, "scsb_fru_unregister(0x%p, 0x%x)",
19117656SSherry.Moore@Sun.COM 		    soft_ptr, (int)fru_id);
19121708Sstevel 	}
19131708Sstevel 	if ((cbe_ptr = scsb_cb_table) == NULL || fru_id == (fru_id_t)0)
19141708Sstevel 		return;
19151708Sstevel 	prev_ptr = cbe_ptr;
19161708Sstevel 	do {
19171708Sstevel 		if (cbe_ptr->cb_softstate_ptr == soft_ptr &&
19187656SSherry.Moore@Sun.COM 		    cbe_ptr->cb_fru_id == fru_id) {
19191708Sstevel 			if (cbe_ptr == scsb_cb_table)
19201708Sstevel 				scsb_cb_table = cbe_ptr->cb_next;
19211708Sstevel 			else
19221708Sstevel 				prev_ptr->cb_next = cbe_ptr->cb_next;
19231708Sstevel 			kmem_free(cbe_ptr, sizeof (struct scsb_cb_entry));
19241708Sstevel #ifdef DEBUG
19251708Sstevel 			scsb_cb_count--;
19261708Sstevel #endif
19271708Sstevel 			return;
19281708Sstevel 		}
19291708Sstevel 		prev_ptr = cbe_ptr;
19301708Sstevel 	} while ((cbe_ptr = cbe_ptr->cb_next) != NULL);
19311708Sstevel }
19321708Sstevel 
19331708Sstevel /*
19341708Sstevel  * global function for interested FRU drivers to call to check
19351708Sstevel  * FRU presence status.
19361708Sstevel  */
19371708Sstevel scsb_fru_status_t
scsb_fru_status(uchar_t fru_id)19381708Sstevel scsb_fru_status(uchar_t fru_id)
19391708Sstevel {
19401708Sstevel 	fru_info_t		*fru_ptr;
19411708Sstevel 
19421708Sstevel 	fru_ptr = find_fru_info(fru_id);
19431708Sstevel 	if (scsb_debug & 0x00800001) {
19441708Sstevel 		cmn_err(CE_NOTE, "scsb_fru_status(0x%x): status=0x%x",
19457656SSherry.Moore@Sun.COM 		    fru_id, (fru_ptr == (fru_info_t *)NULL) ? 0xff :
19467656SSherry.Moore@Sun.COM 		    (int)fru_ptr->fru_status);
19471708Sstevel 	}
19481708Sstevel 	if (fru_ptr == (fru_info_t *)NULL)
19491708Sstevel 		return (FRU_NOT_AVAILABLE);
19501708Sstevel 	return (fru_ptr->fru_status);
19511708Sstevel }
19521708Sstevel 
19531708Sstevel /*
19541708Sstevel  * Global function for the other interruptible FRU device sharing the
19551708Sstevel  * same interrupt line to register the interrupt handler with scsb.
19561708Sstevel  * This enables all the handlers to be called whenever the interrupt
19571708Sstevel  * line is asserted by anyone shaing the interrupt line.
19581708Sstevel  */
19591708Sstevel 
19601708Sstevel /*
19611708Sstevel  * The interrupt handler table is currently a linked list. probably a
19621708Sstevel  * hash table will be more efficient. Usage of these facilities can
19631708Sstevel  * happen even before scsb is attached, so do not depend on scsb
19641708Sstevel  * structure being present.
19651708Sstevel  */
19661708Sstevel struct fru_intr_entry {
19671708Sstevel 	void	*softstate_ptr;
19681708Sstevel 	int	(*fru_intr_handler)(void *);
19691708Sstevel 	fru_id_t	fru_id;
19701708Sstevel 	struct fru_intr_entry	*fru_intr_next;
19711708Sstevel } *fru_intr_table = NULL;
19721708Sstevel 
19731708Sstevel int
scsb_intr_register(int (* intr_handler)(void *),void * soft_ptr,fru_id_t fru_id)19741708Sstevel scsb_intr_register(int (*intr_handler)(void *), void * soft_ptr,
19751708Sstevel 		fru_id_t fru_id)
19761708Sstevel {
19771708Sstevel 	struct fru_intr_entry *intr_table_entry;
19781708Sstevel 	intr_table_entry = (struct fru_intr_entry *)
19797656SSherry.Moore@Sun.COM 	    kmem_zalloc(sizeof (struct fru_intr_entry), KM_SLEEP);
19801708Sstevel 
19811708Sstevel 	if (intr_table_entry == NULL) {
19821708Sstevel 		return (DDI_FAILURE);
19831708Sstevel 	}
19841708Sstevel 
19851708Sstevel 	if (intr_handler == NULL || soft_ptr == NULL || fru_id == 0) {
19861708Sstevel 		kmem_free(intr_table_entry, sizeof (struct fru_intr_entry));
19871708Sstevel 		return (DDI_FAILURE);
19881708Sstevel 	}
19891708Sstevel 
19901708Sstevel 	intr_table_entry->softstate_ptr = soft_ptr;
19911708Sstevel 	intr_table_entry->fru_intr_handler = intr_handler;
19921708Sstevel 	intr_table_entry->fru_id = fru_id;
19931708Sstevel 	intr_table_entry->fru_intr_next = fru_intr_table;
19941708Sstevel 	fru_intr_table = intr_table_entry;
19951708Sstevel 
19961708Sstevel 	return (DDI_SUCCESS);
19971708Sstevel }
19981708Sstevel 
19991708Sstevel /*
20001708Sstevel  * Removed interrupt_handler of fru from interrupt call chain
20011708Sstevel  */
20021708Sstevel void
scsb_intr_unregister(fru_id_t fru_id)20031708Sstevel scsb_intr_unregister(fru_id_t fru_id)
20041708Sstevel {
20051708Sstevel 	struct fru_intr_entry *intr_entry = fru_intr_table,
20067656SSherry.Moore@Sun.COM 	    *prev_entry = intr_entry;
20071708Sstevel 
20081708Sstevel 	if (fru_id == 0) {
20091708Sstevel 		return;
20101708Sstevel 	}
20111708Sstevel 
20121708Sstevel 	do {
20131708Sstevel 		if (intr_entry->fru_id == fru_id) {
20141708Sstevel 			/* found a match, remove entry */
20151708Sstevel 			if (intr_entry == fru_intr_table)
20161708Sstevel 				fru_intr_table = intr_entry->fru_intr_next;
20171708Sstevel 			else
20181708Sstevel 				prev_entry->fru_intr_next =
20197656SSherry.Moore@Sun.COM 				    intr_entry->fru_intr_next;
20201708Sstevel 
20211708Sstevel 			kmem_free(intr_entry,
20227656SSherry.Moore@Sun.COM 			    sizeof (struct fru_intr_entry));
20231708Sstevel 			return;
20241708Sstevel 		}
20251708Sstevel 		prev_entry = intr_entry;
20261708Sstevel 
20271708Sstevel 	} while ((intr_entry = intr_entry->fru_intr_next) != NULL);
20281708Sstevel }
20291708Sstevel 
20301708Sstevel /*
20311708Sstevel  * Invoke all the registered interrupt handlers, whenever scsb_intr
20321708Sstevel  * is called. This function will go through the list of entries
20331708Sstevel  * in the fru interrupt table and invoke each function. Returns
20341708Sstevel  * whether interrupt is claimed or unclaimed.
20351708Sstevel  */
20361708Sstevel static int
scsb_invoke_intr_chain()20371708Sstevel scsb_invoke_intr_chain()
20381708Sstevel {
20391708Sstevel 	int retval = DDI_INTR_UNCLAIMED;
20401708Sstevel 	struct fru_intr_entry *intr_entry = fru_intr_table;
20411708Sstevel 
20421708Sstevel 	while (intr_entry != NULL) {
20431708Sstevel 		retval = (*intr_entry->
20447656SSherry.Moore@Sun.COM 		    fru_intr_handler)(intr_entry->softstate_ptr);
20451708Sstevel 		if (retval == DDI_INTR_CLAIMED) {
20461708Sstevel 			return (retval);
20471708Sstevel 		}
20481708Sstevel 
20491708Sstevel 		intr_entry = intr_entry->fru_intr_next;
20501708Sstevel 	}
20511708Sstevel 
20521708Sstevel 	return (retval);
20531708Sstevel }
20541708Sstevel 
20551708Sstevel 
20561708Sstevel /*
20571708Sstevel  * The scsb_ioc_rdwr_t is similar enough to an i2c_transfer_t that we can
20581708Sstevel  * translate the structures and use the i2c_transfer() service.
20591708Sstevel  */
20601708Sstevel static void
sm_ioc_rdwr(queue_t * q,mblk_t * mp,int op)20611708Sstevel sm_ioc_rdwr(queue_t *q, mblk_t *mp, int op)
20621708Sstevel {
20631708Sstevel 	scsb_state_t	*scsb = (scsb_state_t *)q->q_ptr;
20641708Sstevel 	struct iocblk	*iocp = (struct iocblk *)mp->b_rptr;
20651708Sstevel 	scsb_ioc_rdwr_t	*iocrdwrp;
20661708Sstevel 	int		len, error;
20671708Sstevel 	uchar_t		*uc, reg;
20681708Sstevel 
20691708Sstevel 	if (scsb_debug & 0x0040)
20701708Sstevel 		cmn_err(CE_CONT, "sm_ioc_rdwr[%d]:", scsb->scsb_instance);
20711708Sstevel 	iocrdwrp  = (scsb_ioc_rdwr_t *)mp->b_cont->b_rptr;
20721708Sstevel 	if (op == I2C_WR) {
20731708Sstevel 		len = iocrdwrp->ioc_wlen;
20741708Sstevel 		uc = iocrdwrp->ioc_wbuf;
20751708Sstevel 	} else {
20761708Sstevel 		len = iocrdwrp->ioc_rlen;
20771708Sstevel 		uc = iocrdwrp->ioc_rbuf;
20781708Sstevel 	}
20791708Sstevel 	/*
20801708Sstevel 	 * Check SCB register index boundries and requested len of read/write
20811708Sstevel 	 */
20821708Sstevel 	reg = iocrdwrp->ioc_regindex;
20831708Sstevel 	if (reg < SCSB_REG_ADDR_START || (reg + len) >
20847656SSherry.Moore@Sun.COM 	    (SCSB_REG_ADDR_START + SCTRL_TOTAL_NUMREGS))
20851708Sstevel 		error = EINVAL;
20861708Sstevel 	else
20871708Sstevel 		error = scsb_rdwr_register(scsb, op, reg, len, uc, 1);
20881708Sstevel 	if (error) {
20891708Sstevel 		if (scsb_debug & 0x0042)
20901708Sstevel 			cmn_err(CE_WARN,
20911708Sstevel 			    "sm_ioc_rdwr: rdwr_register failure: %d", error);
20921708Sstevel 		mp->b_datap->db_type = M_IOCNAK;
20931708Sstevel 	} else
20941708Sstevel 		mp->b_datap->db_type = M_IOCACK;
20951708Sstevel 	iocp->ioc_error = error;
20961708Sstevel 	qreply(q, mp);
20971708Sstevel }
20981708Sstevel 
20991708Sstevel /*
21001708Sstevel  * names for (scsb_utype_t) FRU types
21011708Sstevel  */
21021708Sstevel static char *led_name[SCSB_LED_TYPES] = { "NOK", "OK" };
21031708Sstevel static char *unit_type_name[SCSB_UNIT_TYPES] = {
21041708Sstevel 	"SLOT", "PDU", "POWER SUPPLY", "DISK", "FAN", "ALARM",
21051708Sstevel 	"SCB",  "SSB", "CFTM", "CRTM", "PRTM"
21061708Sstevel };
21071708Sstevel 
21081708Sstevel /*
21091708Sstevel  * Discover the register and bit-offset for LEDs and Reset registers,
21101708Sstevel  * according to unit_type, unit_number, and led_type.
21111708Sstevel  */
21121708Sstevel static int
scsb_get_led_regnum(scsb_state_t * scsb,scsb_uinfo_t * suip,uchar_t * regptr,int * unitptr,scsb_led_t led_type)21131708Sstevel scsb_get_led_regnum(scsb_state_t	*scsb,
21141708Sstevel 		    scsb_uinfo_t	*suip,
21151708Sstevel 		    uchar_t		*regptr,
21161708Sstevel 		    int			*unitptr,
21171708Sstevel 		    scsb_led_t		led_type)
21181708Sstevel {
21191708Sstevel 	int		code, base, error;
21201708Sstevel 
21211708Sstevel 	/* OK here means presence (OK) LEDs */
21221708Sstevel 	if (led_type == OK)
21231708Sstevel 		base = (SCTRL_LED_OK_BASE);
21241708Sstevel 	else
21251708Sstevel 		base = (SCTRL_LED_NOK_BASE);
21261708Sstevel 	error = 0;
21271708Sstevel 	if (scsb_debug & 0x0100) {
21281708Sstevel 		cmn_err(CE_NOTE, "get_led_regnum: suip <%x, %x, %x, %x>\n",
21297656SSherry.Moore@Sun.COM 		    suip->unit_type, suip->unit_number,
21307656SSherry.Moore@Sun.COM 		    led_type, suip->unit_state);
21311708Sstevel 	}
21321708Sstevel 	/*
21331708Sstevel 	 * It was requested that the scsb driver allow accesses to SCB device
21341708Sstevel 	 * registers for FRUs that cannot be present.
21351708Sstevel 	 * So except for SLOTs, if the unit_number check fails, we now
21361708Sstevel 	 * just log a message, but ONLY if scsb_debug error messages are
21371708Sstevel 	 * enabled.
21381708Sstevel 	 */
21391708Sstevel 	switch (suip->unit_type) {
21401708Sstevel 	case SLOT:
21411708Sstevel 		if (suip->unit_number < 1 || suip->unit_number >
21427656SSherry.Moore@Sun.COM 		    ((scsb->scsb_state & SCSB_IS_TONGA) ?
21437656SSherry.Moore@Sun.COM 		    TG_MAX_SLOTS : MC_MAX_SLOTS)) {
21441708Sstevel 			error = EINVAL;
21451708Sstevel 			break;
21461708Sstevel 		}
21471708Sstevel 		code = FRU_UNIT_TO_EVCODE(SLOT, suip->unit_number);
21481708Sstevel 		break;
21491708Sstevel 
21501708Sstevel 	case PDU:
21511708Sstevel 		if (suip->unit_number < 1 || suip->unit_number >
21527656SSherry.Moore@Sun.COM 		    ((scsb->scsb_state & SCSB_IS_TONGA) ?
21537656SSherry.Moore@Sun.COM 		    TG_MAX_PDU : MC_MAX_PDU)) {
21541708Sstevel 			if (scsb_debug & 0x0002) {
21551708Sstevel 				cmn_err(CE_WARN,
21567656SSherry.Moore@Sun.COM 				    "get_led_regnum: unit number %d "
21577656SSherry.Moore@Sun.COM 				    "is out of range", suip->unit_number);
21581708Sstevel 			}
21591708Sstevel 			error = EINVAL;
21601708Sstevel 			break;
21611708Sstevel 		}
21621708Sstevel 		code = FRU_UNIT_TO_EVCODE(PDU, suip->unit_number);
21631708Sstevel 		break;
21641708Sstevel 
21651708Sstevel 	case PS:
21661708Sstevel 		if ((suip->unit_number < 1 || suip->unit_number >
21677656SSherry.Moore@Sun.COM 		    ((scsb->scsb_state & SCSB_IS_TONGA) ?
21687656SSherry.Moore@Sun.COM 		    TG_MAX_PS : MC_MAX_PS))) {
21691708Sstevel 			if (scsb_debug & 0x0002) {
21701708Sstevel 				cmn_err(CE_WARN,
21717656SSherry.Moore@Sun.COM 				    "get_led_regnum: unit number %d "
21727656SSherry.Moore@Sun.COM 				    "is out of range", suip->unit_number);
21731708Sstevel 			}
21741708Sstevel 			error = EINVAL;
21751708Sstevel 			break;
21761708Sstevel 		}
21771708Sstevel 		code = FRU_UNIT_TO_EVCODE(PS, suip->unit_number);
21781708Sstevel 		break;
21791708Sstevel 
21801708Sstevel 	case DISK:
21811708Sstevel 		if ((suip->unit_number < 1 || suip->unit_number >
21827656SSherry.Moore@Sun.COM 		    ((scsb->scsb_state & SCSB_IS_TONGA) ?
21837656SSherry.Moore@Sun.COM 		    TG_MAX_DISK : MC_MAX_DISK))) {
21841708Sstevel 			if (scsb_debug & 0x0002) {
21851708Sstevel 				cmn_err(CE_WARN,
21867656SSherry.Moore@Sun.COM 				    "get_led_regnum: unit number %d "
21877656SSherry.Moore@Sun.COM 				    "is out of range", suip->unit_number);
21881708Sstevel 			}
21891708Sstevel 			if (!(scsb_debug & 0x20000000)) {
21901708Sstevel 				error = EINVAL;
21911708Sstevel 				break;
21921708Sstevel 			}
21931708Sstevel 		}
21941708Sstevel 		code = FRU_UNIT_TO_EVCODE(DISK, suip->unit_number);
21951708Sstevel 		break;
21961708Sstevel 
21971708Sstevel 	case FAN:
21981708Sstevel 		if (suip->unit_number < 1 || suip->unit_number >
21997656SSherry.Moore@Sun.COM 		    ((scsb->scsb_state & SCSB_IS_TONGA) ?
22007656SSherry.Moore@Sun.COM 		    TG_MAX_FAN : MC_MAX_FAN)) {
22011708Sstevel 			if (scsb_debug & 0x0002) {
22021708Sstevel 				cmn_err(CE_WARN,
22037656SSherry.Moore@Sun.COM 				    "get_led_regnum: unit number %d "
22047656SSherry.Moore@Sun.COM 				    "is out of range", suip->unit_number);
22051708Sstevel 			}
22061708Sstevel 			error = EINVAL;
22071708Sstevel 			break;
22081708Sstevel 		}
22091708Sstevel 		code = FRU_UNIT_TO_EVCODE(FAN, suip->unit_number);
22101708Sstevel 		break;
22111708Sstevel 
22121708Sstevel 	case CFTM:
22131708Sstevel 		if (suip->unit_number < 1 || suip->unit_number >
22147656SSherry.Moore@Sun.COM 		    ((scsb->scsb_state & SCSB_IS_TONGA) ?
22157656SSherry.Moore@Sun.COM 		    TG_MAX_CFTM : MC_MAX_CFTM)) {
22161708Sstevel 			if (scsb_debug & 0x0002) {
22171708Sstevel 				cmn_err(CE_WARN,
22187656SSherry.Moore@Sun.COM 				    "get_led_regnum: unit number %d "
22197656SSherry.Moore@Sun.COM 				    "is out of range", suip->unit_number);
22201708Sstevel 			}
22211708Sstevel 			error = EINVAL;
22221708Sstevel 			break;
22231708Sstevel 		}
22241708Sstevel 		code = FRU_UNIT_TO_EVCODE(CFTM, suip->unit_number);
22251708Sstevel 		break;
22261708Sstevel 
22271708Sstevel 	case SCB:
22281708Sstevel 		if (suip->unit_number < 1 || suip->unit_number >
22297656SSherry.Moore@Sun.COM 		    ((scsb->scsb_state & SCSB_IS_TONGA) ?
22307656SSherry.Moore@Sun.COM 		    TG_MAX_SCB : MC_MAX_SCB)) {
22311708Sstevel 			if (scsb_debug & 0x0002) {
22321708Sstevel 				cmn_err(CE_WARN,
22337656SSherry.Moore@Sun.COM 				    "get_led_regnum: unit number %d "
22347656SSherry.Moore@Sun.COM 				    "is out of range", suip->unit_number);
22351708Sstevel 			}
22361708Sstevel 			error = EINVAL;
22371708Sstevel 			break;
22381708Sstevel 		}
22391708Sstevel 		code = FRU_UNIT_TO_EVCODE(SCB, suip->unit_number);
22401708Sstevel 		break;
22411708Sstevel 
22421708Sstevel 	case ALARM:
22431708Sstevel 		error = EINVAL;
22441708Sstevel 		break;
22451708Sstevel 
22461708Sstevel 	default:
22471708Sstevel 		if (scsb_debug & 0x0102) {
22481708Sstevel 			cmn_err(CE_WARN,
22497656SSherry.Moore@Sun.COM 			    "scsb_get_led_regnum(): unknown unit type %d",
22507656SSherry.Moore@Sun.COM 			    suip->unit_type);
22511708Sstevel 		}
22521708Sstevel 		error = EINVAL;
22531708Sstevel 		break;
22541708Sstevel 	}
22551708Sstevel 	if (!error) {
22561708Sstevel 		*unitptr = FRU_OFFSET(code, base);
22571708Sstevel 		*regptr = FRU_REG_ADDR(code, base);
22581708Sstevel 		if (scsb_debug & 0x0100) {
22591708Sstevel 			cmn_err(CE_NOTE, "get_led_regnum: unitptr=%x, "
22607656SSherry.Moore@Sun.COM 			    "regptr=%x, code = %x\n",
22617656SSherry.Moore@Sun.COM 			    *unitptr, *regptr, code);
22621708Sstevel 		}
22631708Sstevel 	}
22641708Sstevel 	return (error);
22651708Sstevel }
22661708Sstevel 
22671708Sstevel /*
22681708Sstevel  * P1.0 and P1.5
22691708Sstevel  * Map 1.0 Tonga Slot Numbers: SCB to user interface and back.
22701708Sstevel  * User interface means positional slot numbers, as on P1.0 SSB,
22711708Sstevel  * which are used by hpcsvc/hsc and kstat/ioctl interfaces.
22721708Sstevel  */
22731708Sstevel 
22741708Sstevel /* HSC slotnum (Positional SLotnum) to SCB CFG bit-offset */
22751708Sstevel static	int	psl2sco[TG_MAX_SLOTS + 1] = { -1 };
22761708Sstevel 
22771708Sstevel /*
22781708Sstevel  * MAP Positional (HSC) slot number to SCB CFG register bit-offset
22791708Sstevel  */
22801708Sstevel static int
tonga_pslotnum_to_cfgbit(scsb_state_t * scsb,int sln)22811708Sstevel tonga_pslotnum_to_cfgbit(scsb_state_t *scsb, int sln)
22821708Sstevel {
22831708Sstevel 	int	base = SCTRL_SYSCFG_BASE;
22841708Sstevel 	if (!(scsb->scsb_state & SCSB_IS_TONGA)) {
22851708Sstevel 		return (sln);
22861708Sstevel 	}
22871708Sstevel 	if (sln < 1 || sln > TG_MAX_SLOTS) {
22881708Sstevel 		return (sln);
22891708Sstevel 	}
22901708Sstevel 	/*
22911708Sstevel 	 * Should move this to _init(), but for now,
22921708Sstevel 	 * check for initialized table
22931708Sstevel 	 */
22941708Sstevel 	if (psl2sco[0]) {
22951708Sstevel 		psl2sco[0] = 0;
22961708Sstevel 		psl2sco[1] = FRU_OFFSET(SCTRL_EVENT_SLOT5, base);
22971708Sstevel 		psl2sco[2] = FRU_OFFSET(SCTRL_EVENT_SLOT2, base);
22981708Sstevel 		psl2sco[3] = FRU_OFFSET(SCTRL_EVENT_SLOT1, base);
22991708Sstevel 		psl2sco[4] = FRU_OFFSET(SCTRL_EVENT_SLOT3, base);
23001708Sstevel 		psl2sco[5] = FRU_OFFSET(SCTRL_EVENT_SLOT4, base);
23011708Sstevel 	}
23021708Sstevel #ifdef DEBUG
23031708Sstevel 	if (scsb_debug & 0x10000000) {
23041708Sstevel 		cmn_err(CE_NOTE, "tonga_pslotnum_to_cfgbit: old/new: %d/%d",
23057656SSherry.Moore@Sun.COM 		    sln, psl2sco[sln]);
23061708Sstevel 	}
23071708Sstevel #endif
23081708Sstevel 	return (psl2sco[sln]);
23091708Sstevel }
23101708Sstevel 
23111708Sstevel /* positional slotnum to SCB slotnum */
23121708Sstevel static	int	psl2ssl[6] = {
23131708Sstevel 	0, 5, 2, 1, 3, 4
23141708Sstevel };
23151708Sstevel 
23161708Sstevel /* SCB slotnum to positional slotnum */
23171708Sstevel static	int	ssl2psl[6] = {
23181708Sstevel 	0, 3, 2, 4, 5, 1
23191708Sstevel };
23201708Sstevel 
23211708Sstevel /*
23221708Sstevel  * P1.0 and P1.5
23231708Sstevel  * HSC Slot numbers (physical positions or positional slotnum)
23241708Sstevel  *  to
23251708Sstevel  * SCB slot numbers (reset,present,healthy)
23261708Sstevel  *
23271708Sstevel  * These requests come mainly from application interface and
23281708Sstevel  * HSC using the scsb_uinfo_t structure.
23291708Sstevel  */
23301708Sstevel static void
tonga_slotnum_check(scsb_state_t * scsb,scsb_uinfo_t * suip)23311708Sstevel tonga_slotnum_check(scsb_state_t *scsb, scsb_uinfo_t *suip)
23321708Sstevel {
23331708Sstevel 	if (!(scsb->scsb_state & SCSB_IS_TONGA && scsb->scsb_state &
23347656SSherry.Moore@Sun.COM 	    (SCSB_P10_PROM | SCSB_P15_PROM | SCSB_P20_PROM))) {
23351708Sstevel 		return;
23361708Sstevel 	}
23371708Sstevel 	if (suip->unit_number < 1 || suip->unit_number > TG_MAX_SLOTS) {
23381708Sstevel 		return;
23391708Sstevel 	}
23401708Sstevel #ifdef DEBUG
23411708Sstevel 	if (scsb_debug & 0x10000000) {
23421708Sstevel 		cmn_err(CE_NOTE, "tonga_slotnum_check: old/new: %d/%d",
23437656SSherry.Moore@Sun.COM 		    suip->unit_number, psl2ssl[suip->unit_number]);
23441708Sstevel 	}
23451708Sstevel #endif
23461708Sstevel 	suip->unit_number = psl2ssl[suip->unit_number];
23471708Sstevel }
23481708Sstevel 
23491708Sstevel /*
23501708Sstevel  * P1.0 and P1.5
23511708Sstevel  */
23521708Sstevel static int
tonga_psl_to_ssl(scsb_state_t * scsb,int slotnum)23531708Sstevel tonga_psl_to_ssl(scsb_state_t *scsb, int slotnum)
23541708Sstevel {
23551708Sstevel 	if (!(scsb->scsb_state & SCSB_IS_TONGA && scsb->scsb_state &
23567656SSherry.Moore@Sun.COM 	    (SCSB_P10_PROM | SCSB_P15_PROM | SCSB_P20_PROM))) {
23571708Sstevel 		return (slotnum);
23581708Sstevel 	}
23591708Sstevel 	if (slotnum < 1 || slotnum > TG_MAX_SLOTS) {
23601708Sstevel 		return (slotnum);
23611708Sstevel 	}
23621708Sstevel #ifdef DEBUG
23631708Sstevel 	if (scsb_debug & 0x10000000) {
23641708Sstevel 		cmn_err(CE_NOTE, "tonga_psl_to_ssl: old/new: %d/%d",
23657656SSherry.Moore@Sun.COM 		    slotnum, psl2ssl[slotnum]);
23661708Sstevel 	}
23671708Sstevel #endif
23681708Sstevel 	return (psl2ssl[slotnum]);
23691708Sstevel }
23701708Sstevel 
23711708Sstevel /*
23721708Sstevel  * P1.0 and P1.5
23731708Sstevel  */
23741708Sstevel static int
tonga_ssl_to_psl(scsb_state_t * scsb,int slotnum)23751708Sstevel tonga_ssl_to_psl(scsb_state_t *scsb, int slotnum)
23761708Sstevel {
23771708Sstevel 	if (!(scsb->scsb_state & SCSB_IS_TONGA && scsb->scsb_state &
23787656SSherry.Moore@Sun.COM 	    (SCSB_P10_PROM | SCSB_P15_PROM | SCSB_P20_PROM))) {
23791708Sstevel 		return (slotnum);
23801708Sstevel 	}
23811708Sstevel 	if (slotnum < 1 || slotnum > TG_MAX_SLOTS) {
23821708Sstevel 		return (slotnum);
23831708Sstevel 	}
23841708Sstevel #ifdef DEBUG
23851708Sstevel 	if (scsb_debug & 0x10000000) {
23861708Sstevel 		cmn_err(CE_NOTE, "tonga_ssl_to_psl: old/new: %d/%d",
23877656SSherry.Moore@Sun.COM 		    slotnum, ssl2psl[slotnum]);
23881708Sstevel 	}
23891708Sstevel #endif
23901708Sstevel 	return (ssl2psl[slotnum]);
23911708Sstevel }
23921708Sstevel /*
23931708Sstevel  * tonga_slotnum_led_shift: this function remaps slot bits ONLY for Slots 1-5
23941708Sstevel  * and ONLY for the register sets in bit-offset groups 1,2:
23951708Sstevel  * LEDs, Confg/Status, Reset, BrdHlthy
23961708Sstevel  *
23971708Sstevel  * IN  bits: SCB slot numbers (led,reset,present,healthy)
23981708Sstevel  *  to
23991708Sstevel  * OUT bits: HSC Slot numbers (positional slot numbers as marked on the SSB)
24001708Sstevel  */
24011708Sstevel static uchar_t
tonga_slotnum_led_shift(scsb_state_t * scsb,uchar_t data)24021708Sstevel tonga_slotnum_led_shift(scsb_state_t *scsb, uchar_t data)
24031708Sstevel {
24041708Sstevel 	int	i;
24051708Sstevel 	uchar_t mask, new_data = 0;
24061708Sstevel #ifdef DEBUG
24071708Sstevel 	uchar_t	old_data = data;
24081708Sstevel #endif
24091708Sstevel 	if (!(scsb->scsb_state & SCSB_IS_TONGA)) {
24101708Sstevel 		return (data);
24111708Sstevel 	}
24121708Sstevel 	/*
24131708Sstevel 	 * P1.0 and P1.5 slot 1-5 offsets are the same
24141708Sstevel 	 */
24151708Sstevel 	for (i = 1; i <= TG_MAX_SLOTS; ++i) {
24161708Sstevel 		mask = 1 << (i - 1);
24171708Sstevel 		switch (i) {
24181708Sstevel 		case 1:		/* map to slot 3 */
24191708Sstevel 			new_data |= (data & mask) << 2;
24201708Sstevel 			data &= ~(mask);
24211708Sstevel 			break;
24221708Sstevel 		case 2:		/* map to slot 2 */
24231708Sstevel 			new_data |= (data & mask);
24241708Sstevel 			data &= ~(mask);
24251708Sstevel 			break;
24261708Sstevel 		case 3:		/* map to slot 4 */
24271708Sstevel 		case 4:		/* map to slot 5 */
24281708Sstevel 			new_data |= (data & mask) << 1;
24291708Sstevel 			data &= ~(mask);
24301708Sstevel 			break;
24311708Sstevel 		case 5:		/* map to slot 1 */
24321708Sstevel 			new_data |= (data & mask) >> 4;
24331708Sstevel 			data &= ~(mask);
24341708Sstevel 			break;
24351708Sstevel 		}
24361708Sstevel 	}
24371708Sstevel 	new_data |= data;	/* set any remaining bits */
24381708Sstevel #ifdef DEBUG
24391708Sstevel 	if (scsb_debug & 0x10000000) {
24401708Sstevel 		cmn_err(CE_NOTE, "tonga_slotnum_led_shift: old/new: 0x%x/0x%x",
24417656SSherry.Moore@Sun.COM 		    old_data, new_data);
24421708Sstevel 	}
24431708Sstevel #endif
24441708Sstevel 	return (new_data);
24451708Sstevel }
24461708Sstevel 
24471708Sstevel /*
24481708Sstevel  * P1.0 and P1.5
24491708Sstevel  */
24501708Sstevel int
scsb_led_get(scsb_state_t * scsb,scsb_uinfo_t * suip,scsb_led_t led_type)24511708Sstevel scsb_led_get(scsb_state_t *scsb, scsb_uinfo_t *suip, scsb_led_t led_type)
24521708Sstevel {
24531708Sstevel 	int		error;
24541708Sstevel 	int		unit_number;
24551708Sstevel 	uchar_t		reg;
24561708Sstevel 	int		index;
24571708Sstevel 
24581708Sstevel 	/*
24591708Sstevel 	 * Allow access to shadow registers even though SCB is removed
24601708Sstevel 	 *
24611708Sstevel 	 * if (scsb->scsb_state & SCSB_FROZEN) {
24621708Sstevel 	 *	return (EAGAIN);
24631708Sstevel 	 * }
24641708Sstevel 	 */
24651708Sstevel 	if (suip == NULL) {
24661708Sstevel 		return (EFAULT);
24671708Sstevel 	}
24681708Sstevel 	if (led_type == NOUSE) {
24691708Sstevel 		led_type = suip->led_type;
24701708Sstevel 	}
24711708Sstevel 	if (led_type != OK && led_type != NOK) {
24721708Sstevel 		cmn_err(CE_NOTE, "scsb_led_get(%d): unknown led type %x",
24737656SSherry.Moore@Sun.COM 		    scsb->scsb_instance, led_type);
24741708Sstevel 		return (EINVAL);
24751708Sstevel 	}
24761708Sstevel 	error = 0;
24771708Sstevel 	if (scsb_debug & 0x0100) {
24781708Sstevel 		cmn_err(CE_NOTE, "scsb_led_get: %s %s %d",
24797656SSherry.Moore@Sun.COM 		    led_name[led_type], unit_type_name[suip->unit_type],
24807656SSherry.Moore@Sun.COM 		    suip->unit_number);
24811708Sstevel 	}
24821708Sstevel 	/*
24831708Sstevel 	 * Map to Tonga Slot Number, if NOT P1.0 SCB
24841708Sstevel 	 * P1.0 SSB workaround
24851708Sstevel 	 */
24861708Sstevel 	if (suip->unit_type == SLOT && !(scsb->scsb_state & SCSB_P10_PROM)) {
24871708Sstevel 		tonga_slotnum_check(scsb, suip);
24881708Sstevel 	}
24891708Sstevel 	/* discover the register and index we need to operate on */
24901708Sstevel 	if ((error = scsb_get_led_regnum(scsb, suip, &reg, &unit_number,
24917656SSherry.Moore@Sun.COM 	    led_type)) == 0) {
24921708Sstevel 		index = SCSB_REG_INDEX(reg);
24931708Sstevel 		mutex_enter(&scsb->scsb_mutex);
24941708Sstevel 		if (scsb->scsb_data_reg[index] & (1 << unit_number)) {
24951708Sstevel 			suip->unit_state = ON;
24961708Sstevel 			if (led_type == OK) {
24971708Sstevel 				int code = FRU_UNIT_TO_EVCODE(suip->unit_type,
24987656SSherry.Moore@Sun.COM 				    suip->unit_number);
24991708Sstevel 				reg = FRU_REG_ADDR(code, SCTRL_BLINK_OK_BASE);
25001708Sstevel 				index = SCSB_REG_INDEX(reg);
25011708Sstevel 				if (scsb->scsb_data_reg[index] &
25027656SSherry.Moore@Sun.COM 				    (1 << unit_number))
25031708Sstevel 					suip->unit_state = BLINK;
25041708Sstevel 			}
25051708Sstevel 		} else {
25061708Sstevel 			suip->unit_state = OFF;
25071708Sstevel 		}
25081708Sstevel 		mutex_exit(&scsb->scsb_mutex);
25091708Sstevel 	}
25101708Sstevel 	return (error);
25111708Sstevel }
25121708Sstevel 
25131708Sstevel int
scsb_led_set(scsb_state_t * scsb,scsb_uinfo_t * suip,scsb_led_t led_type)25141708Sstevel scsb_led_set(scsb_state_t *scsb, scsb_uinfo_t *suip, scsb_led_t led_type)
25151708Sstevel {
25161708Sstevel 	int		error;
25171708Sstevel 	int		unit_number;
25181708Sstevel 	uchar_t		reg;
25191708Sstevel 	int		code, index;
25201708Sstevel 
25211708Sstevel 	/* we should really allow led state changes while frozen... */
25221708Sstevel 	if (scsb->scsb_state & SCSB_FROZEN)
25231708Sstevel 		return (EAGAIN);
25241708Sstevel 
25251708Sstevel 	if (suip == NULL) {
25261708Sstevel 		return (EFAULT);
25271708Sstevel 	}
25281708Sstevel 
25291708Sstevel 	/*
25301708Sstevel 	 * Sanity check, make sure we got plausible values for set command.
25311708Sstevel 	 * Also check for application only control of slot leds using NOUSE
25321708Sstevel 	 * interface
25331708Sstevel 	 */
25341708Sstevel 	if (led_type == NOUSE) {
25351708Sstevel 		led_type = suip->led_type;
25361708Sstevel 	} else if (suip->unit_type == SLOT &&
25377656SSherry.Moore@Sun.COM 	    scsb->scsb_state & SCSB_APP_SLOTLED_CTRL &&
25387656SSherry.Moore@Sun.COM 	    !(scsb->scsb_state &
25397656SSherry.Moore@Sun.COM 	    (SCSB_DIAGS_MODE | SCSB_DEBUG_MODE))) {
25401708Sstevel 		/*
25411708Sstevel 		 * kernel modules using this interface need to think they are
25421708Sstevel 		 * succeeding, so we won't return an error for this
25431708Sstevel 		 * application configuration
25441708Sstevel 		 */
25451708Sstevel 		return (0);
25461708Sstevel 	}
25471708Sstevel 	if (led_type != OK && led_type != NOK) {
25481708Sstevel 		return (EINVAL);
25491708Sstevel 	}
25501708Sstevel 	if (suip->unit_state != OFF && suip->unit_state != ON &&
25517656SSherry.Moore@Sun.COM 	    suip->unit_state != BLINK) {
25521708Sstevel 		return (EINVAL);
25531708Sstevel 	}
25541708Sstevel 	if (suip->unit_state == BLINK) {
25551708Sstevel 		if (led_type != OK)
25561708Sstevel 			return (EINVAL);
25571708Sstevel 		if (suip->unit_type != SLOT && scsb->scsb_state &
25587656SSherry.Moore@Sun.COM 		    (SCSB_P06_PROM | SCSB_P10_PROM))
25591708Sstevel 			return (EINVAL);
25601708Sstevel 	}
25611708Sstevel 	if (scsb_debug & 0x0100) {
25621708Sstevel 		cmn_err(CE_NOTE,
25637656SSherry.Moore@Sun.COM 		    "scsb_led_set: led %s, type %s, unit %d, state %s",
25647656SSherry.Moore@Sun.COM 		    led_name[led_type],
25657656SSherry.Moore@Sun.COM 		    unit_type_name[suip->unit_type], suip->unit_number,
25667656SSherry.Moore@Sun.COM 		    suip->unit_state == ON ? "ON":
25677656SSherry.Moore@Sun.COM 		    suip->unit_state == OFF ? "OFF": "BLINK");
25681708Sstevel 	}
25691708Sstevel 	/*
25701708Sstevel 	 * Map to Tonga Slot Number, if NOT P1.0 SCB
25711708Sstevel 	 * P1.0 SSB workaround
25721708Sstevel 	 */
25731708Sstevel 	if (suip->unit_type == SLOT && !(scsb->scsb_state & SCSB_P10_PROM)) {
25741708Sstevel 		tonga_slotnum_check(scsb, suip);
25751708Sstevel 	}
25761708Sstevel 	/*
25771708Sstevel 	 * discover the register and index we need to access
25781708Sstevel 	 */
25791708Sstevel 	if ((error = scsb_get_led_regnum(scsb, suip, &reg, &unit_number,
25807656SSherry.Moore@Sun.COM 	    led_type)) == 0) {
25811708Sstevel 		index = SCSB_REG_INDEX(reg);
25821708Sstevel 		mutex_enter(&scsb->scsb_mutex);
25831708Sstevel 		if (suip->unit_state == ON || suip->unit_state == BLINK)
25841708Sstevel 			scsb->scsb_data_reg[index] |=  (1 << unit_number);
25851708Sstevel 		else
25861708Sstevel 			scsb->scsb_data_reg[index] &= ~(1 << unit_number);
25871708Sstevel 
25881708Sstevel 		if (scsb_debug & 0x0100) {
25891708Sstevel 			cmn_err(CE_NOTE, "Writing %x to Reg %x",
25907656SSherry.Moore@Sun.COM 			    scsb->scsb_data_reg[index], reg);
25911708Sstevel 		}
25921708Sstevel 		error = scsb_rdwr_register(scsb, I2C_WR, reg, 1,
25937656SSherry.Moore@Sun.COM 		    &scsb->scsb_data_reg[index], 1);
25941708Sstevel 		if (error) {
25951708Sstevel 			cmn_err(CE_WARN, "%s#%d: Could not Update %s LEDs.",
25967656SSherry.Moore@Sun.COM 			    ddi_driver_name(scsb->scsb_dev),
25977656SSherry.Moore@Sun.COM 			    ddi_get_instance(scsb->scsb_dev),
25987656SSherry.Moore@Sun.COM 			    led_name[led_type]);
25991708Sstevel 			goto ledset_done;
26001708Sstevel 		}
26011708Sstevel 		if (led_type != OK ||
26027656SSherry.Moore@Sun.COM 		    (IS_SCB_P10 && suip->unit_type != SLOT) ||
26037656SSherry.Moore@Sun.COM 		    suip->unit_type == ALARM ||
26047656SSherry.Moore@Sun.COM 		    suip->unit_type == SSB ||
26057656SSherry.Moore@Sun.COM 		    suip->unit_type == CRTM ||
26067656SSherry.Moore@Sun.COM 		    suip->unit_type == PRTM) {
26071708Sstevel 			goto ledset_done;
26081708Sstevel 		}
26091708Sstevel 		code = FRU_UNIT_TO_EVCODE(suip->unit_type, suip->unit_number);
26101708Sstevel 		reg = FRU_REG_ADDR(code, SCTRL_BLINK_OK_BASE);
26111708Sstevel 		index = SCSB_REG_INDEX(reg);
26121708Sstevel 		if (suip->unit_state == BLINK)
26131708Sstevel 			scsb->scsb_data_reg[index] |=  (1 << unit_number);
26141708Sstevel 		else
26151708Sstevel 			scsb->scsb_data_reg[index] &= ~(1 << unit_number);
26161708Sstevel 		if (scsb_debug & 0x0100) {
26171708Sstevel 			cmn_err(CE_NOTE, "Writing %x to Reg %x",
26187656SSherry.Moore@Sun.COM 			    scsb->scsb_data_reg[index], reg);
26191708Sstevel 		}
26201708Sstevel 		error = scsb_rdwr_register(scsb, I2C_WR, reg, 1,
26217656SSherry.Moore@Sun.COM 		    &scsb->scsb_data_reg[index], 1);
26221708Sstevel 		if (error) {
26231708Sstevel 			cmn_err(CE_WARN, "%s#%d: Could not Blink %s LEDs.",
26247656SSherry.Moore@Sun.COM 			    ddi_driver_name(scsb->scsb_dev),
26257656SSherry.Moore@Sun.COM 			    ddi_get_instance(scsb->scsb_dev),
26267656SSherry.Moore@Sun.COM 			    led_name[led_type]);
26271708Sstevel 		}
26281708Sstevel ledset_done:
26291708Sstevel 		mutex_exit(&scsb->scsb_mutex);
26301708Sstevel 	}
26311708Sstevel 	return (error);
26321708Sstevel }
26331708Sstevel 
26341708Sstevel struct ps_auto_on {
26351708Sstevel 	scsb_state_t	*scsb;
26361708Sstevel 	scsb_utype_t	utype;
26371708Sstevel 	scsb_unum_t	unit;
26381708Sstevel };
26391708Sstevel 
26401708Sstevel static struct ps_auto_on pao;
26411708Sstevel 
26421708Sstevel static void
scsb_ps_auto_on(void * arg)26431708Sstevel scsb_ps_auto_on(void *arg)
26441708Sstevel {
26451708Sstevel 	struct ps_auto_on 	*ppao = (struct ps_auto_on *)arg;
26461708Sstevel 	uchar_t			rmask = 0;
26471708Sstevel 	uchar_t			ondata, sysreg;
26481708Sstevel 	int			tmp, bit_index;
26491708Sstevel 	/*
26501708Sstevel 	 * Turn on the PSU.
26511708Sstevel 	 * Notice: not checking Power Supply unit number
26521708Sstevel 	 */
26531708Sstevel 	bit_index = SCTRL_SYS_PS_ON_BASE + (ppao->unit - 1);
26541708Sstevel 	ondata = 1 << SYS_OFFSET(bit_index);
26551708Sstevel 	tmp = SYS_REG_INDEX(bit_index, SCTRL_SYS_CMD_BASE);
26561708Sstevel 	sysreg = SCSB_REG_ADDR(tmp);
26571708Sstevel 	if (scsb_write_mask(ppao->scsb, sysreg, rmask, ondata, (uchar_t)0)) {
26581708Sstevel 		cmn_err(CE_WARN, "scsb%d: " "I2C TRANSFER Failed",
26597656SSherry.Moore@Sun.COM 		    ppao->scsb->scsb_instance);
26601708Sstevel 	}
26611708Sstevel 	ppao->scsb->scsb_btid = 0;
26621708Sstevel }
26631708Sstevel 
26641708Sstevel /*
26651708Sstevel  * called with mutex held from
26661708Sstevel  * scsb_attach()	with int_fru_ptr == NULL
26671708Sstevel  * scsb_intr()		with int_fru_ptr == info for FRU that caused interrupt
26681708Sstevel  */
26691708Sstevel static int
scsb_set_scfg_pres_leds(scsb_state_t * scsb,fru_info_t * int_fru_ptr)26701708Sstevel scsb_set_scfg_pres_leds(scsb_state_t *scsb, fru_info_t *int_fru_ptr)
26711708Sstevel {
26721708Sstevel 	int		i, error = 0;
26731708Sstevel 	int		cfg_idx, led_idx, blink_idx, lid, bid;
26741708Sstevel 	int		cfg_bit, led_bit;
26751708Sstevel 	uchar_t		*puc, reg, led_reg, led_data[SCSB_LEDDATA_REGISTERS];
26761708Sstevel 	uchar_t		blink_bit, blink_reg, blink[SCSB_LEDDATA_REGISTERS];
26771708Sstevel 	uchar_t		update_reg = 0;
26781708Sstevel 	scsb_utype_t	fru_type;
26791708Sstevel 	fru_info_t	*fru_ptr;
26801708Sstevel 
26811708Sstevel 	if (scsb->scsb_state & SCSB_FROZEN &&
26827656SSherry.Moore@Sun.COM 	    !(scsb->scsb_state & SCSB_IN_INTR)) {
26831708Sstevel 		return (EAGAIN);
26841708Sstevel 	}
26851708Sstevel 	for (i = 0; i < SCTRL_LED_OK_NUMREGS; ++i) {
26861708Sstevel 		led_data[i] = 0;
26871708Sstevel 		blink[i] = 0;
26881708Sstevel 	}
26891708Sstevel 	led_reg = SCSB_REG_ADDR(SCTRL_LED_OK_BASE);
26901708Sstevel 	reg = SCSB_REG_ADDR(SCTRL_BLINK_OK_BASE);
26911708Sstevel 	lid = SCSB_REG_INDEX(led_reg);		/* the LED Index Delta */
26921708Sstevel 	bid = SCSB_REG_INDEX(reg);		/* the Blink Index Delta */
26931708Sstevel 	blink_reg = 0;
26941708Sstevel 	if (int_fru_ptr != NULL) {
26951708Sstevel 		update_reg = int_fru_ptr->i2c_info->ledata_reg;
26961708Sstevel 	}
26971708Sstevel 	for (fru_type = 0; fru_type < SCSB_UNIT_TYPES; ++fru_type) {
26981708Sstevel 		int	is_present;
26991708Sstevel 		fru_ptr = mct_system_info.fru_info_list[fru_type];
27001708Sstevel 		for (; fru_ptr != NULL; fru_ptr = fru_ptr->next) {
27011708Sstevel 			is_present = 0;
27021708Sstevel 			if (fru_type == SLOT && (scsb->scsb_state &
27037656SSherry.Moore@Sun.COM 			    SCSB_APP_SLOTLED_CTRL))
27041708Sstevel 				break;
27051708Sstevel 			if (fru_ptr->i2c_info == NULL)
27061708Sstevel 				continue;
27071708Sstevel 			if ((led_reg = fru_ptr->i2c_info->ledata_reg) == 0) {
27081708Sstevel 				/*
27091708Sstevel 				 * No LED exceptions: SSB,CRTM,PRTM
27101708Sstevel 				 */
27111708Sstevel 				continue;
27121708Sstevel 			}
27131708Sstevel 			if (update_reg && update_reg != led_reg)
27141708Sstevel 				continue;
27151708Sstevel 			led_idx = SCSB_REG_INDEX(led_reg) - lid;
27161708Sstevel 			led_bit = fru_ptr->i2c_info->ledata_bit;
27171708Sstevel 			if ((reg = fru_ptr->i2c_info->syscfg_reg) == 0) {
27181708Sstevel 				if (fru_type != SCB)
27191708Sstevel 					continue;
27201708Sstevel 				/*
27211708Sstevel 				 * exception: SCB
27221708Sstevel 				 */
27231708Sstevel 				if (scsb->scsb_state & SCSB_SCB_PRESENT) {
27241708Sstevel 					led_data[led_idx] |= 1 << led_bit;
27251708Sstevel 					is_present = 1;
27261708Sstevel 				} else {
27271708Sstevel 					led_data[led_idx] &= ~(1 << led_bit);
27281708Sstevel 				}
27291708Sstevel 				if (IS_SCB_P10)
27301708Sstevel 					continue;
27311708Sstevel 			} else {
27321708Sstevel 				cfg_idx = SCSB_REG_INDEX(reg);
27331708Sstevel 				cfg_bit = fru_ptr->i2c_info->syscfg_bit;
27341708Sstevel 				if (scsb->scsb_data_reg[cfg_idx] &
27357656SSherry.Moore@Sun.COM 				    (1 << cfg_bit)) {
27361708Sstevel 					is_present = 1;
27371708Sstevel 				}
27381708Sstevel 			}
27391708Sstevel 			if (is_present) {
27401708Sstevel 				/*
27411708Sstevel 				 * If the FRU is a Power Supply, AND
27421708Sstevel 				 * the call is from scsb_attach() OR
27431708Sstevel 				 * from scsb_intr() and FRUs match,
27441708Sstevel 				 * turn it on.
27451708Sstevel 				 */
27461708Sstevel 				if (fru_type == PS && (int_fru_ptr == NULL ||
27477656SSherry.Moore@Sun.COM 				    (int_fru_ptr == fru_ptr))) {
27481708Sstevel 					pao.scsb = scsb;
27491708Sstevel 					pao.utype = fru_type;
27501708Sstevel 					pao.unit = fru_ptr->fru_unit;
27511708Sstevel #ifdef	PS_ON_DELAY
27521708Sstevel 					/*
27531708Sstevel 					 * HW recommended not implementing
27541708Sstevel 					 * this delay for now.
27551708Sstevel 					 * The code is tested on PSUs:
27561708Sstevel 					 *	-06
27571708Sstevel 					 *	-07 rev 2
27581708Sstevel 					 *	-08 plus
27591708Sstevel 					 */
27601708Sstevel 					if (int_fru_ptr) {
27611708Sstevel 						/*
27621708Sstevel 						 * Hot insertion, so give it
27631708Sstevel 						 * the 3 seconds it needs to
27641708Sstevel 						 * become stable
27651708Sstevel 						 */
27661708Sstevel 						if (!scsb->scsb_btid)
27671708Sstevel 							scsb->scsb_btid =
27687656SSherry.Moore@Sun.COM 							    timeout(
27697656SSherry.Moore@Sun.COM 							    scsb_ps_auto_on,
27707656SSherry.Moore@Sun.COM 							    &pao, (4 *
27717656SSherry.Moore@Sun.COM 							    drv_usectohz(
27727656SSherry.Moore@Sun.COM 							    1000000)));
27731708Sstevel 					} else
27741708Sstevel #endif	/* PS_ON_DELAY */
27751708Sstevel 						scsb_ps_auto_on((void *)&pao);
27761708Sstevel 				}
27771708Sstevel 				/*
27781708Sstevel 				 * Special SLOT handling.
27791708Sstevel 				 * Make sure the OK LED is on for the CPU Slot
27801708Sstevel 				 * and for the FTC (CFTM) Slot for MonteCarlo.
27811708Sstevel 				 * Both will report as FRU_PRESENT.
27821708Sstevel 				 */
27831708Sstevel 				if (fru_type != SLOT || (fru_type == SLOT &&
27847656SSherry.Moore@Sun.COM 				    (fru_ptr->fru_type ==
27857656SSherry.Moore@Sun.COM 				    (scsb_utype_t)OC_CPU ||
27867656SSherry.Moore@Sun.COM 				    fru_ptr->fru_type ==
27877656SSherry.Moore@Sun.COM 				    (scsb_utype_t)OC_CTC))) {
27881708Sstevel 					/*
27891708Sstevel 					 * Set OK (green) LED register bit
27901708Sstevel 					 */
27911708Sstevel 					led_data[led_idx] |= 1 << led_bit;
27921708Sstevel 				}
27931708Sstevel 				if (IS_SCB_P10)
27941708Sstevel 					continue;
27951708Sstevel 				/*
27961708Sstevel 				 * Turn off BLINK register bit.
27971708Sstevel 				 * If single register update, then save the
27981708Sstevel 				 * corresponding blink register in blink_reg.
27991708Sstevel 				 */
28001708Sstevel 				reg = fru_ptr->i2c_info->blink_reg;
28011708Sstevel 				if (!reg)
28021708Sstevel 					continue;
28031708Sstevel 				blink_bit = fru_ptr->i2c_info->blink_bit;
28041708Sstevel 				blink_idx = SCSB_REG_INDEX(reg) - bid;
28051708Sstevel 				blink[blink_idx] |= 1 << blink_bit;
28061708Sstevel 				if (update_reg && update_reg == led_reg)
28071708Sstevel 					blink_reg = reg;
28081708Sstevel 			}
28091708Sstevel 		}
28101708Sstevel 	}
28111708Sstevel 	if (update_reg) {
28121708Sstevel 		reg = update_reg;
28131708Sstevel 		i = SCSB_REG_INDEX(reg);
28141708Sstevel 		puc = &led_data[i - lid];
28151708Sstevel 		i = 1;
28161708Sstevel 	} else {
28171708Sstevel 		reg = SCSB_REG_ADDR(SCTRL_LED_OK_BASE);
28181708Sstevel 		puc = led_data;
28191708Sstevel 		i = SCTRL_LED_OK_NUMREGS;
28201708Sstevel 	}
28211708Sstevel 	if (scsb_debug & 0x0100) {
28221708Sstevel 		cmn_err(CE_NOTE, "scsb_set_scfg_pres(): writing %d bytes "
28237656SSherry.Moore@Sun.COM 		    "to 0x%x", i, reg);
28241708Sstevel 	}
28251708Sstevel 	if ((error = scsb_rdwr_register(scsb, I2C_WR, reg, i, puc, 1)) != 0) {
28261708Sstevel 		if (scsb_debug & 0x0102)
28271708Sstevel 			cmn_err(CE_NOTE, "scsb_set_scfg_pres(): "
28287656SSherry.Moore@Sun.COM 			    "I2C write to 0x%x failed", reg);
28291708Sstevel 		error = EIO;
28301708Sstevel 	} else {
28311708Sstevel 		/*
28321708Sstevel 		 * Now see which BLINK bits need to be turned off for the
28331708Sstevel 		 * corresponding OK LED bits.
28341708Sstevel 		 */
28351708Sstevel 		reg = SCSB_REG_ADDR(SCTRL_BLINK_OK_BASE);
28361708Sstevel 		for (i = 0; i < SCTRL_BLINK_NUMREGS; ++i, ++reg) {
28371708Sstevel 			if (blink_reg && blink_reg != reg)
28381708Sstevel 				continue;
28391708Sstevel 			if (!blink[i]) {
28401708Sstevel 				continue;
28411708Sstevel 			}
28421708Sstevel 			if (scsb_debug & 0x0100) {
28431708Sstevel 				cmn_err(CE_NOTE, "scsb_set_scfg_pres(): turn "
28447656SSherry.Moore@Sun.COM 				    "OFF Blink bits 0x%x in 0x%x",
28457656SSherry.Moore@Sun.COM 				    blink[i], reg);
28461708Sstevel 			}
28471708Sstevel 			if (scsb_write_mask(scsb, reg, 0, 0, blink[i])) {
28481708Sstevel 				if (scsb_debug & 0x0102)
28491708Sstevel 					cmn_err(CE_NOTE,
28507656SSherry.Moore@Sun.COM 					    "scsb_set_scfg_pres(): "
28517656SSherry.Moore@Sun.COM 					    "Write to 0x%x failed", reg);
28521708Sstevel 				error = EIO;
28531708Sstevel 				break;
28541708Sstevel 			}
28551708Sstevel 		}
28561708Sstevel 	}
28571708Sstevel 	return (error);
28581708Sstevel }
28591708Sstevel 
28601708Sstevel static int
scsb_check_config_status(scsb_state_t * scsb)28611708Sstevel scsb_check_config_status(scsb_state_t *scsb)
28621708Sstevel {
28631708Sstevel 	int		error;
28641708Sstevel 	uchar_t		reg;
28651708Sstevel 	int		index, p06;
28661708Sstevel 
28671708Sstevel 	if (scsb_debug & 0x0201) {
28681708Sstevel 		cmn_err(CE_NOTE, "scsb_check_config_status:");
28691708Sstevel 	}
28701708Sstevel 	/*
28711708Sstevel 	 * Base of register set
28721708Sstevel 	 */
28731708Sstevel 	reg = SCSB_REG_ADDR(SCTRL_SYSCFG_BASE);
28741708Sstevel 	index = SCSB_REG_INDEX(reg);
28751708Sstevel 	/*
28761708Sstevel 	 * SCB P0.6 workaround: read registers twice, use 2nd value set
28771708Sstevel 	 */
28781708Sstevel 	mutex_enter(&scsb->scsb_mutex);
28791708Sstevel 	p06 = 2;
28801708Sstevel 	do {
28811708Sstevel 		if (error = scsb_rdwr_register(scsb, I2C_WR_RD, reg,
28827656SSherry.Moore@Sun.COM 		    SCTRL_CFG_NUMREGS, &scsb->scsb_data_reg[index], 1)) {
28831708Sstevel 			break;
28841708Sstevel 		}
28851708Sstevel 		if (p06 == 1) {
28861708Sstevel 			if (scsb_debug & 0x0200)
28871708Sstevel 				cmn_err(CE_NOTE,
28881708Sstevel 				"scsb_check_config_status: P0.6 workaround");
28891708Sstevel 		}
28901708Sstevel 		/*
28911708Sstevel 		 * If not P0.6 PROM, just break here
28921708Sstevel 		 */
28931708Sstevel 		if (!(scsb->scsb_state & SCSB_P06_PROM))
28941708Sstevel 			break;
28951708Sstevel 	} while (--p06);
28961708Sstevel 	mutex_exit(&scsb->scsb_mutex);
28971708Sstevel 
28981708Sstevel 	if (error == 0) {
28991708Sstevel 		if (!(scsb->scsb_state & SCSB_SCB_PRESENT))
29001708Sstevel 			scsb->scsb_state |= SCSB_SCB_PRESENT;
29011708Sstevel 		if (scsb_fru_op(scsb, SSB, 1, SCTRL_SYSCFG_BASE,
29027656SSherry.Moore@Sun.COM 		    SCSB_FRU_OP_GET_BITVAL))
29031708Sstevel 			scsb->scsb_state |= SCSB_SSB_PRESENT;
29041708Sstevel 		else
29051708Sstevel 			scsb->scsb_state &= ~SCSB_SSB_PRESENT;
29061708Sstevel 	}
29071708Sstevel 	return (error);
29081708Sstevel }
29091708Sstevel 
29101708Sstevel static void
scsb_set_topology(scsb_state_t * scsb)29111708Sstevel scsb_set_topology(scsb_state_t *scsb)
29121708Sstevel {
29131708Sstevel 	int		i, t, index, unit, is_tonga = 0;
29141708Sstevel 	int		alarm_slot_num, cpu_slot_num, ctc_slot_num;
29151708Sstevel 	fru_info_t	*fru_ptr, *last_ptr, *acslot_ptr, *ctcslot_ptr;
29161708Sstevel 	uchar_t		syscfg, led_reg, blink_reg, t_uchar;
29171708Sstevel 	uchar_t		bit_num, led_bit, blink_bit;
29181708Sstevel 	int		pad = 0;
29191708Sstevel 
29201708Sstevel 	/*
29211708Sstevel 	 * Get the presence status from the SysConfigStatus shadow registers
29221708Sstevel 	 * in scsb->scsb_data_reg[]
29231708Sstevel 	 */
29241708Sstevel 	/* Mid Plane */
29251708Sstevel 	i = SYS_REG_INDEX(SCTRL_CFG_MPID0, SCTRL_SYSCFG_BASE);
29261708Sstevel 	t_uchar = SCSB_REG_ADDR(i);
29271708Sstevel 	index = SCSB_REG_INDEX(t_uchar);
29281708Sstevel 	mct_system_info.mid_plane.fru_type = MIDPLANE;
29291708Sstevel 	mct_system_info.mid_plane.fru_version = (fru_version_t)0;
29301708Sstevel 	t = SYS_OFFSET(SCTRL_CFG_MPID0);
29311708Sstevel 	mct_system_info.mid_plane.fru_id = (int)((scsb->scsb_data_reg[index] &
29327656SSherry.Moore@Sun.COM 	    (SCTRL_MPID_MASK << t)) >> t);
29331708Sstevel 	switch (mct_system_info.mid_plane.fru_id) {
29341708Sstevel 	case SCTRL_MPID_HALF:		/* Monte Carlo		*/
29351708Sstevel 		if (scsb_debug & 0x00100005)
29361708Sstevel 			cmn_err(CE_NOTE, "scsb_set_topology: Monte Carlo");
29371708Sstevel 		cpu_slot_num = SC_MC_CPU_SLOT;
29381708Sstevel 		ctc_slot_num = SC_MC_CTC_SLOT;
29391708Sstevel 		alarm_slot_num = scsb->ac_slotnum = SC_MC_AC_SLOT;
29401708Sstevel 		mct_system_info.max_units[SLOT] = MC_MAX_SLOTS;
29411708Sstevel 		mct_system_info.max_units[ALARM] = MC_MAX_AC;
29421708Sstevel 		mct_system_info.max_units[DISK] = MC_MAX_DISK;
29431708Sstevel 		mct_system_info.max_units[FAN] = MC_MAX_FAN;
29441708Sstevel 		mct_system_info.max_units[PS] = MC_MAX_PS;
29451708Sstevel 		mct_system_info.max_units[PDU] = MC_MAX_PDU;
29461708Sstevel 		mct_system_info.max_units[SCB] = MC_MAX_SCB;
29471708Sstevel 		mct_system_info.max_units[SSB] = MC_MAX_SCB;
29481708Sstevel 		mct_system_info.max_units[CFTM] = MC_MAX_CFTM;
29491708Sstevel 		mct_system_info.max_units[CRTM] = MC_MAX_CRTM;
29501708Sstevel 		mct_system_info.max_units[PRTM] = MC_MAX_PRTM;
29511708Sstevel 		break;
29521708Sstevel 	case SCTRL_MPID_QUARTER_NODSK:	/* Tonga w/o disk	*/
29531708Sstevel 	case SCTRL_MPID_QUARTER:	/* Tonga w/  disk	*/
29541708Sstevel 		scsb->scsb_state |= SCSB_IS_TONGA;
29551708Sstevel 		is_tonga = 1;
29561708Sstevel 		ctc_slot_num = -1;
29571708Sstevel 		ctcslot_ptr = NULL;
29581708Sstevel 		if (scsb_debug & 0x00100005)
29591708Sstevel 			cmn_err(CE_NOTE, "scsb_set_topology: Tonga%s",
29607656SSherry.Moore@Sun.COM 			    mct_system_info.mid_plane.fru_id ==
29617656SSherry.Moore@Sun.COM 			    SCTRL_MPID_QUARTER_NODSK ?
29627656SSherry.Moore@Sun.COM 			    ", no disk" : " with disk");
29631708Sstevel 		cpu_slot_num = SC_TG_CPU_SLOT;
29641708Sstevel 		alarm_slot_num = scsb->ac_slotnum = SC_TG_AC_SLOT;
29651708Sstevel 		mct_system_info.max_units[SLOT] = TG_MAX_SLOTS;
29661708Sstevel 		mct_system_info.max_units[ALARM] = TG_MAX_AC;
29671708Sstevel 		mct_system_info.max_units[DISK] = TG_MAX_DISK;
29681708Sstevel 		mct_system_info.max_units[FAN] = TG_MAX_FAN;
29691708Sstevel 		mct_system_info.max_units[PS] = TG_MAX_PS;
29701708Sstevel 		mct_system_info.max_units[PDU] = TG_MAX_PDU;
29711708Sstevel 		mct_system_info.max_units[SCB] = TG_MAX_SCB;
29721708Sstevel 		mct_system_info.max_units[SSB] = TG_MAX_SCB;
29731708Sstevel 		mct_system_info.max_units[CFTM] = TG_MAX_CFTM;
29741708Sstevel 		mct_system_info.max_units[CRTM] = TG_MAX_CRTM;
29751708Sstevel 		mct_system_info.max_units[PRTM] = TG_MAX_PRTM;
29761708Sstevel 		break;
29771708Sstevel 	default:
29781708Sstevel 		cmn_err(CE_WARN, "%s#%d: Unknown MidPlane Id %x",
29797656SSherry.Moore@Sun.COM 		    ddi_driver_name(scsb->scsb_dev),
29807656SSherry.Moore@Sun.COM 		    ddi_get_instance(scsb->scsb_dev),
29817656SSherry.Moore@Sun.COM 		    mct_system_info.mid_plane.fru_id);
29821708Sstevel 		if (scsb_debug & 0x00100005)
29831708Sstevel 			cmn_err(CE_NOTE, "scsb_set_topology: 0x%x: unknown!",
29847656SSherry.Moore@Sun.COM 			    mct_system_info.mid_plane.fru_id);
29851708Sstevel 		return;
29861708Sstevel 	}
29871708Sstevel 	/*
29881708Sstevel 	 * cPCI Slots
29891708Sstevel 	 *
29901708Sstevel 	 * NOTE: The Tonga slot fru_unit needs to get mapped to the logical
29911708Sstevel 	 * slot number in slot_table[].  The field is not in the slot_table
29921708Sstevel 	 * at least until we know the format of the OBP slot table for the FCS
29931708Sstevel 	 * release.
29941708Sstevel 	 */
29951708Sstevel 	mct_system_info.fru_info_list[SLOT] = (fru_info_t *)
29967656SSherry.Moore@Sun.COM 	    kmem_zalloc(sizeof (fru_info_t) *
29977656SSherry.Moore@Sun.COM 	    (mct_system_info.max_units[SLOT] + pad), KM_SLEEP);
29981708Sstevel 	fru_ptr = mct_system_info.fru_info_list[SLOT];
29991708Sstevel 	for (unit = 1; unit <= mct_system_info.max_units[SLOT]; ++unit) {
30001708Sstevel 		int	iunit;
30011708Sstevel 		if (unit == cpu_slot_num) {
30021708Sstevel 			fru_ptr->fru_type = (scsb_utype_t)OC_CPU;
30031708Sstevel 		} else if (unit == ctc_slot_num) {
30041708Sstevel 			/* fru_ptr saved for Transition Card Presence check */
30051708Sstevel 			ctcslot_ptr = fru_ptr;
30061708Sstevel 			fru_ptr->fru_type = (scsb_utype_t)OC_UNKN;
30071708Sstevel 		} else if (unit == alarm_slot_num) {
30081708Sstevel 			/* fru_ptr saved for Alarm Card Presence check below */
30091708Sstevel 			acslot_ptr = fru_ptr;
30101708Sstevel 			fru_ptr->fru_type = (scsb_utype_t)OC_UNKN;
30111708Sstevel 		} else {
30121708Sstevel 			fru_ptr->fru_type = (scsb_utype_t)OC_UNKN;
30131708Sstevel 		}
30141708Sstevel 		/*
30151708Sstevel 		 * Get the slot event code (t), then use it to get the
30161708Sstevel 		 * slot bit-offsets for LED, BLINK, and SYSCFG registers.
30171708Sstevel 		 * On a P1.5 Tonga, the internal slot number must be used to
30181708Sstevel 		 * find the event code.
30191708Sstevel 		 * The P1.0 Tonga does not get mapped due to a SSB difference.
30201708Sstevel 		 */
30211708Sstevel 		if (IS_SCB_P15) {
30221708Sstevel 			iunit = tonga_psl_to_ssl(scsb, unit);
30231708Sstevel 			t = FRU_UNIT_TO_EVCODE(SLOT, iunit);
30241708Sstevel 		} else {
30251708Sstevel 			t = FRU_UNIT_TO_EVCODE(SLOT, unit);
30261708Sstevel 		}
30271708Sstevel 		led_bit = FRU_OFFSET(t, SCTRL_LED_OK_BASE);
30281708Sstevel 		blink_bit = FRU_OFFSET(t, SCTRL_BLINK_OK_BASE);
30291708Sstevel 		blink_reg = FRU_REG_ADDR(t, SCTRL_BLINK_OK_BASE);
30301708Sstevel 		if (is_tonga && unit <= TG_MAX_SLOTS) {
30311708Sstevel 			bit_num = tonga_pslotnum_to_cfgbit(scsb, unit);
30321708Sstevel 		} else {
30331708Sstevel 			bit_num = FRU_OFFSET(t, SCTRL_SYSCFG_BASE);
30341708Sstevel 		}
30351708Sstevel 		/*
30361708Sstevel 		 * get the registers addresses and shadow register index for
30371708Sstevel 		 * the SYSCFG register
30381708Sstevel 		 */
30391708Sstevel 		syscfg = FRU_REG_ADDR(t, SCTRL_SYSCFG_BASE);
30401708Sstevel 		index = SCSB_REG_INDEX(syscfg);
30411708Sstevel 		led_reg = FRU_REG_ADDR(t, SCTRL_LED_OK_BASE);
30421708Sstevel 		/*
30431708Sstevel 		 * check and set presence status
30441708Sstevel 		 */
30451708Sstevel 		if (scsb->scsb_state & SCSB_P06_PROM) {
30461708Sstevel 			fru_ptr->fru_status = FRU_NOT_PRESENT;
30471708Sstevel 		} else if (scsb->scsb_data_reg[index] & (1 << bit_num)) {
30481708Sstevel 			fru_ptr->fru_status = FRU_PRESENT;
30491708Sstevel 		} else {
30501708Sstevel 			fru_ptr->fru_status = FRU_NOT_PRESENT;
30511708Sstevel 		}
30521708Sstevel 		fru_ptr->fru_unit = (scsb_unum_t)unit;
30531708Sstevel 		fru_ptr->fru_id = fru_id_table[event_to_index(
30547656SSherry.Moore@Sun.COM 		    FRU_UNIT_TO_EVCODE(SLOT, unit))];
30551708Sstevel 		fru_ptr->fru_version = (fru_version_t)0;
30561708Sstevel 		fru_ptr->type_list = (fru_options_t *)NULL;
30571708Sstevel 		fru_ptr->i2c_info = (fru_i2c_info_t *)
30587656SSherry.Moore@Sun.COM 		    kmem_zalloc(sizeof (fru_i2c_info_t), KM_SLEEP);
30591708Sstevel 		fru_ptr->i2c_info->syscfg_reg = syscfg;
30601708Sstevel 		fru_ptr->i2c_info->syscfg_bit = bit_num;
30611708Sstevel 		fru_ptr->i2c_info->ledata_reg = led_reg;
30621708Sstevel 		fru_ptr->i2c_info->ledata_bit = led_bit;
30631708Sstevel 		fru_ptr->i2c_info->blink_reg = blink_reg;
30641708Sstevel 		fru_ptr->i2c_info->blink_bit = blink_bit;
30651708Sstevel 		last_ptr = fru_ptr;
30661708Sstevel 		fru_ptr++;
30671708Sstevel 		last_ptr->next = fru_ptr;
30681708Sstevel 	}
30691708Sstevel 	last_ptr->next = (fru_info_t *)NULL;
30701708Sstevel 	/*
30711708Sstevel 	 * PDU
30721708Sstevel 	 */
30731708Sstevel 	mct_system_info.fru_info_list[PDU] = (fru_info_t *)
30747656SSherry.Moore@Sun.COM 	    kmem_zalloc(sizeof (fru_info_t) *
30757656SSherry.Moore@Sun.COM 	    (mct_system_info.max_units[PDU] + pad), KM_SLEEP);
30761708Sstevel 	fru_ptr = mct_system_info.fru_info_list[PDU];
30771708Sstevel 	for (unit = 1; unit <= mct_system_info.max_units[PDU]; ++unit) {
30781708Sstevel 		fru_ptr->fru_type = PDU;
30791708Sstevel 		/* SCB15 */
30801708Sstevel 		/*
30811708Sstevel 		 * get the FRU event code (t), then use it to get the
30821708Sstevel 		 * FRU bit-offsets for LED and SYSCFG registers
30831708Sstevel 		 */
30841708Sstevel 		t = FRU_UNIT_TO_EVCODE(PDU, unit);
30851708Sstevel 		led_bit = FRU_OFFSET(t, SCTRL_LED_OK_BASE);
30861708Sstevel 		bit_num = FRU_OFFSET(t, SCTRL_SYSCFG_BASE);
30871708Sstevel 		if (IS_SCB_P15) {
30881708Sstevel 			blink_bit = FRU_OFFSET(t, SCTRL_BLINK_OK_BASE);
30891708Sstevel 			i = FRU_REG_INDEX(t, SCTRL_BLINK_OK_BASE);
30901708Sstevel 			blink_reg = SCSB_REG_ADDR(i);
30911708Sstevel 		} else {
30921708Sstevel 			blink_bit = 0;
30931708Sstevel 			blink_reg = 0;
30941708Sstevel 		}
30951708Sstevel 		/*
30961708Sstevel 		 * get the registers addresses and shadow register index for
30971708Sstevel 		 * the SYSCFG register
30981708Sstevel 		 */
30991708Sstevel 		i = FRU_REG_INDEX(t, SCTRL_SYSCFG_BASE);
31001708Sstevel 		syscfg = SCSB_REG_ADDR(i);
31011708Sstevel 		index = SCSB_REG_INDEX(syscfg);
31021708Sstevel 		i = FRU_REG_INDEX(t, SCTRL_LED_OK_BASE);
31031708Sstevel 		led_reg = SCSB_REG_ADDR(i);
31041708Sstevel 		/*
31051708Sstevel 		 * check and set presence status
31061708Sstevel 		 */
31071708Sstevel 		if (scsb->scsb_data_reg[index] & (1 << bit_num)) {
31081708Sstevel 			fru_ptr->fru_status = FRU_PRESENT;
31091708Sstevel 			fru_ptr->fru_version = (fru_version_t)0;
31101708Sstevel 		} else {
31111708Sstevel 			fru_ptr->fru_status = FRU_NOT_PRESENT;
31121708Sstevel 			fru_ptr->fru_version = (fru_version_t)0;
31131708Sstevel 		}
31141708Sstevel 		fru_ptr->fru_unit = (scsb_unum_t)unit;
31151708Sstevel 		fru_ptr->fru_id = fru_id_table[event_to_index(t)];
31161708Sstevel 		fru_ptr->type_list = (fru_options_t *)NULL;
31171708Sstevel 		fru_ptr->i2c_info = (fru_i2c_info_t *)
31187656SSherry.Moore@Sun.COM 		    kmem_zalloc(sizeof (fru_i2c_info_t), KM_SLEEP);
31191708Sstevel 		fru_ptr->i2c_info->syscfg_reg = syscfg;
31201708Sstevel 		fru_ptr->i2c_info->syscfg_bit = bit_num;
31211708Sstevel 		fru_ptr->i2c_info->ledata_reg = led_reg;
31221708Sstevel 		fru_ptr->i2c_info->ledata_bit = led_bit;
31231708Sstevel 		fru_ptr->i2c_info->blink_reg = blink_reg;
31241708Sstevel 		fru_ptr->i2c_info->blink_bit = blink_bit;
31251708Sstevel 		last_ptr = fru_ptr;
31261708Sstevel 		fru_ptr++;
31271708Sstevel 		last_ptr->next = fru_ptr;
31281708Sstevel 	}
31291708Sstevel 	last_ptr->next = (fru_info_t *)NULL;
31301708Sstevel 	/*
31311708Sstevel 	 * Power Supplies
31321708Sstevel 	 */
31331708Sstevel 	mct_system_info.fru_info_list[PS] = (fru_info_t *)
31347656SSherry.Moore@Sun.COM 	    kmem_zalloc(sizeof (fru_info_t) *
31357656SSherry.Moore@Sun.COM 	    (mct_system_info.max_units[PS] + pad), KM_SLEEP);
31361708Sstevel 	fru_ptr = mct_system_info.fru_info_list[PS];
31371708Sstevel 	for (unit = 1; unit <= mct_system_info.max_units[PS]; ++unit) {
31381708Sstevel 		/*
31391708Sstevel 		 * get the FRU event code (t), then use it to get the
31401708Sstevel 		 * FRU bit-offsets for LED and SYSCFG registers
31411708Sstevel 		 */
31421708Sstevel 		t = FRU_UNIT_TO_EVCODE(PS, unit);
31431708Sstevel 		led_bit = FRU_OFFSET(t, SCTRL_LED_OK_BASE);
31441708Sstevel 		bit_num = FRU_OFFSET(t, SCTRL_SYSCFG_BASE);
31451708Sstevel 		if (IS_SCB_P15) {
31461708Sstevel 			blink_bit = FRU_OFFSET(t, SCTRL_BLINK_OK_BASE);
31471708Sstevel 			i = FRU_REG_INDEX(t, SCTRL_BLINK_OK_BASE);
31481708Sstevel 			blink_reg = SCSB_REG_ADDR(i);
31491708Sstevel 		} else {
31501708Sstevel 			blink_bit = 0;
31511708Sstevel 			blink_reg = 0;
31521708Sstevel 		}
31531708Sstevel 		/*
31541708Sstevel 		 * get the registers addresses and shadow register index for
31551708Sstevel 		 * the SYSCFG register
31561708Sstevel 		 */
31571708Sstevel 		i = FRU_REG_INDEX(t, SCTRL_SYSCFG_BASE);
31581708Sstevel 		syscfg = SCSB_REG_ADDR(i);
31591708Sstevel 		index = SCSB_REG_INDEX(syscfg);
31601708Sstevel 		i = FRU_REG_INDEX(t, SCTRL_LED_OK_BASE);
31611708Sstevel 		led_reg = SCSB_REG_ADDR(i);
31621708Sstevel 		/*
31631708Sstevel 		 * check and set presence status
31641708Sstevel 		 */
31651708Sstevel 		if (scsb->scsb_data_reg[index] & (1 << bit_num)) {
31661708Sstevel 			fru_ptr->fru_status = FRU_PRESENT;
31671708Sstevel 		} else {
31681708Sstevel 			fru_ptr->fru_status = FRU_NOT_PRESENT;
31691708Sstevel 		}
31701708Sstevel 		fru_ptr->fru_type = PS;
31711708Sstevel 		fru_ptr->fru_unit = (scsb_unum_t)unit;
31721708Sstevel 		fru_ptr->fru_id = fru_id_table[event_to_index(t)];
31731708Sstevel 		fru_ptr->fru_version = (fru_version_t)0;
31741708Sstevel 		fru_ptr->type_list = (fru_options_t *)NULL;
31751708Sstevel 		fru_ptr->i2c_info = (fru_i2c_info_t *)
31767656SSherry.Moore@Sun.COM 		    kmem_zalloc(sizeof (fru_i2c_info_t), KM_SLEEP);
31771708Sstevel 		fru_ptr->i2c_info->syscfg_reg = syscfg;
31781708Sstevel 		fru_ptr->i2c_info->syscfg_bit = bit_num;
31791708Sstevel 		fru_ptr->i2c_info->ledata_reg = led_reg;
31801708Sstevel 		fru_ptr->i2c_info->ledata_bit = led_bit;
31811708Sstevel 		fru_ptr->i2c_info->blink_reg = blink_reg;
31821708Sstevel 		fru_ptr->i2c_info->blink_bit = blink_bit;
31831708Sstevel 		last_ptr = fru_ptr;
31841708Sstevel 		fru_ptr++;
31851708Sstevel 		last_ptr->next = fru_ptr;
31861708Sstevel 	}
31871708Sstevel 	last_ptr->next = (fru_info_t *)NULL;
31881708Sstevel 	/*
31891708Sstevel 	 * SCSI Disks and removable media
31901708Sstevel 	 */
31911708Sstevel 	mct_system_info.fru_info_list[DISK] = (fru_info_t *)
31927656SSherry.Moore@Sun.COM 	    kmem_zalloc(sizeof (fru_info_t) *
31937656SSherry.Moore@Sun.COM 	    (mct_system_info.max_units[DISK] + pad), KM_SLEEP);
31941708Sstevel 	fru_ptr = mct_system_info.fru_info_list[DISK];
31951708Sstevel 	for (unit = 1; unit <= mct_system_info.max_units[DISK]; ++unit) {
31961708Sstevel 		/* SCB15 */
31971708Sstevel 		/*
31981708Sstevel 		 * get the FRU event code (t), then use it to get the
31991708Sstevel 		 * FRU bit-offsets for LED and SYSCFG registers
32001708Sstevel 		 */
32011708Sstevel 		t = FRU_UNIT_TO_EVCODE(DISK, unit);
32021708Sstevel 		led_bit = FRU_OFFSET(t, SCTRL_LED_OK_BASE);
32031708Sstevel 		bit_num = FRU_OFFSET(t, SCTRL_SYSCFG_BASE);
32041708Sstevel 		if (IS_SCB_P15) {
32051708Sstevel 			blink_bit = FRU_OFFSET(t, SCTRL_BLINK_OK_BASE);
32061708Sstevel 			i = FRU_REG_INDEX(t, SCTRL_BLINK_OK_BASE);
32071708Sstevel 			blink_reg = SCSB_REG_ADDR(i);
32081708Sstevel 		} else {
32091708Sstevel 			blink_bit = 0;
32101708Sstevel 			blink_reg = 0;
32111708Sstevel 		}
32121708Sstevel 		/*
32131708Sstevel 		 * get the registers addresses and shadow register index for
32141708Sstevel 		 * the SYSCFG register
32151708Sstevel 		 */
32161708Sstevel 		i = FRU_REG_INDEX(t, SCTRL_SYSCFG_BASE);
32171708Sstevel 		syscfg = SCSB_REG_ADDR(i);
32181708Sstevel 		index = SCSB_REG_INDEX(syscfg);
32191708Sstevel 		i = FRU_REG_INDEX(t, SCTRL_LED_OK_BASE);
32201708Sstevel 		led_reg = SCSB_REG_ADDR(i);
32211708Sstevel 		/*
32221708Sstevel 		 * check and set presence status
32231708Sstevel 		 */
32241708Sstevel 		if (scsb->scsb_data_reg[index] & (1 << bit_num)) {
32251708Sstevel 			fru_ptr->fru_status = FRU_PRESENT;
32261708Sstevel 			fru_ptr->fru_version = (fru_version_t)0;
32271708Sstevel 		} else
32281708Sstevel 			fru_ptr->fru_status = FRU_NOT_PRESENT;
32291708Sstevel 		fru_ptr->fru_type = DISK;
32301708Sstevel 		fru_ptr->fru_unit = (scsb_unum_t)unit;
32311708Sstevel 		fru_ptr->fru_id = fru_id_table[event_to_index(t)];
32321708Sstevel 		fru_ptr->type_list = (fru_options_t *)NULL;
32331708Sstevel 		fru_ptr->i2c_info = (fru_i2c_info_t *)
32347656SSherry.Moore@Sun.COM 		    kmem_zalloc(sizeof (fru_i2c_info_t), KM_SLEEP);
32351708Sstevel 		fru_ptr->i2c_info->syscfg_reg = syscfg;
32361708Sstevel 		fru_ptr->i2c_info->syscfg_bit = bit_num;
32371708Sstevel 		fru_ptr->i2c_info->ledata_reg = led_reg;
32381708Sstevel 		fru_ptr->i2c_info->ledata_bit = led_bit;
32391708Sstevel 		fru_ptr->i2c_info->blink_reg = blink_reg;
32401708Sstevel 		fru_ptr->i2c_info->blink_bit = blink_bit;
32411708Sstevel 		last_ptr = fru_ptr;
32421708Sstevel 		fru_ptr++;
32431708Sstevel 		last_ptr->next = fru_ptr;
32441708Sstevel 	}
32451708Sstevel 	last_ptr->next = (fru_info_t *)NULL;
32461708Sstevel 	/*
32471708Sstevel 	 * Fan Trays
32481708Sstevel 	 */
32491708Sstevel 	mct_system_info.fru_info_list[FAN] = (fru_info_t *)
32507656SSherry.Moore@Sun.COM 	    kmem_zalloc(sizeof (fru_info_t) *
32517656SSherry.Moore@Sun.COM 	    (mct_system_info.max_units[FAN] + pad), KM_SLEEP);
32521708Sstevel 	fru_ptr = mct_system_info.fru_info_list[FAN];
32531708Sstevel 	for (unit = 1; unit <= mct_system_info.max_units[FAN]; ++unit) {
32541708Sstevel 		int		bit_num;
32551708Sstevel 		/* SCB15 */
32561708Sstevel 		/*
32571708Sstevel 		 * get the FRU event code (t), then use it to get the
32581708Sstevel 		 * FRU bit-offsets for LED and SYSCFG registers
32591708Sstevel 		 */
32601708Sstevel 		t = FRU_UNIT_TO_EVCODE(FAN, unit);
32611708Sstevel 		led_bit = FRU_OFFSET(t, SCTRL_LED_OK_BASE);
32621708Sstevel 		bit_num = FRU_OFFSET(t, SCTRL_SYSCFG_BASE);
32631708Sstevel 		if (IS_SCB_P15) {
32641708Sstevel 			blink_bit = FRU_OFFSET(t, SCTRL_BLINK_OK_BASE);
32651708Sstevel 			i = FRU_REG_INDEX(t, SCTRL_BLINK_OK_BASE);
32661708Sstevel 			blink_reg = SCSB_REG_ADDR(i);
32671708Sstevel 		} else {
32681708Sstevel 			blink_bit = 0;
32691708Sstevel 			blink_reg = 0;
32701708Sstevel 		}
32711708Sstevel 		/*
32721708Sstevel 		 * get the registers addresses and shadow register index for
32731708Sstevel 		 * the SYSCFG register
32741708Sstevel 		 */
32751708Sstevel 		i = FRU_REG_INDEX(t, SCTRL_SYSCFG_BASE);
32761708Sstevel 		syscfg = SCSB_REG_ADDR(i);
32771708Sstevel 		index = SCSB_REG_INDEX(syscfg);
32781708Sstevel 		i = FRU_REG_INDEX(t, SCTRL_LED_OK_BASE);
32791708Sstevel 		led_reg = SCSB_REG_ADDR(i);
32801708Sstevel 		/*
32811708Sstevel 		 * check and set presence status
32821708Sstevel 		 */
32831708Sstevel 		if (scsb->scsb_data_reg[index] & (1 << bit_num)) {
32841708Sstevel 			fru_ptr->fru_status = FRU_PRESENT;
32851708Sstevel 		} else {
32861708Sstevel 			fru_ptr->fru_status = FRU_NOT_PRESENT;
32871708Sstevel 		}
32881708Sstevel 		fru_ptr->fru_type = FAN;
32891708Sstevel 		fru_ptr->fru_unit = (scsb_unum_t)unit;
32901708Sstevel 		fru_ptr->fru_id = fru_id_table[event_to_index(t)];
32911708Sstevel 		fru_ptr->fru_version = (fru_version_t)0;
32921708Sstevel 		fru_ptr->type_list = (fru_options_t *)NULL;
32931708Sstevel 		fru_ptr->i2c_info = (fru_i2c_info_t *)
32947656SSherry.Moore@Sun.COM 		    kmem_zalloc(sizeof (fru_i2c_info_t), KM_SLEEP);
32951708Sstevel 		fru_ptr->i2c_info->syscfg_reg = syscfg;
32961708Sstevel 		fru_ptr->i2c_info->syscfg_bit = bit_num;
32971708Sstevel 		fru_ptr->i2c_info->ledata_reg = led_reg;
32981708Sstevel 		fru_ptr->i2c_info->ledata_bit = led_bit;
32991708Sstevel 		fru_ptr->i2c_info->blink_reg = blink_reg;
33001708Sstevel 		fru_ptr->i2c_info->blink_bit = blink_bit;
33011708Sstevel 		last_ptr = fru_ptr;
33021708Sstevel 		fru_ptr++;
33031708Sstevel 		last_ptr->next = fru_ptr;
33041708Sstevel 	}
33051708Sstevel 	last_ptr->next = (fru_info_t *)NULL;
33061708Sstevel 	/*
33071708Sstevel 	 * Alarm Cards
33081708Sstevel 	 */
33091708Sstevel 	mct_system_info.fru_info_list[ALARM] = (fru_info_t *)
33107656SSherry.Moore@Sun.COM 	    kmem_zalloc(sizeof (fru_info_t) *
33117656SSherry.Moore@Sun.COM 	    (mct_system_info.max_units[ALARM] + pad), KM_SLEEP);
33121708Sstevel 	fru_ptr = mct_system_info.fru_info_list[ALARM];
33131708Sstevel 	for (unit = 1; unit <= mct_system_info.max_units[ALARM]; ++unit) {
33141708Sstevel 		int		bit_num;
33151708Sstevel 
33161708Sstevel 		/*
33171708Sstevel 		 * get the FRU event code (t), then use it to get the
33181708Sstevel 		 * FRU bit-offsets for SYSCFG register
33191708Sstevel 		 */
33201708Sstevel 		t = FRU_UNIT_TO_EVCODE(ALARM, unit);
33211708Sstevel 		bit_num = FRU_OFFSET(t, SCTRL_SYSCFG_BASE);
33221708Sstevel 		/*
33231708Sstevel 		 * get the registers addresses and shadow register index for
33241708Sstevel 		 * the SYSCFG register
33251708Sstevel 		 */
33261708Sstevel 		i = FRU_REG_INDEX(t, SCTRL_SYSCFG_BASE);
33271708Sstevel 		syscfg = SCSB_REG_ADDR(i);
33281708Sstevel 		index = SCSB_REG_INDEX(syscfg);
33291708Sstevel 		/*
33301708Sstevel 		 * check and set presence status
33311708Sstevel 		 */
33321708Sstevel 		if (scsb->scsb_data_reg[index] & (1 << bit_num)) {
33331708Sstevel 			fru_ptr->fru_status = FRU_PRESENT;
33341708Sstevel 			if (acslot_ptr != NULL && acslot_ptr->fru_status ==
33357656SSherry.Moore@Sun.COM 			    FRU_PRESENT) {
33361708Sstevel 				acslot_ptr->fru_type = (scsb_utype_t)OC_AC;
33371708Sstevel 				/*
33381708Sstevel 				 * acslot_ptr->fru_id =
33397656SSherry.Moore@Sun.COM 				 *	fru_id_table[event_to_index(t)];
33401708Sstevel 				 */
33411708Sstevel 			}
33421708Sstevel 		} else {
33431708Sstevel 			fru_ptr->fru_status = FRU_NOT_PRESENT;
33441708Sstevel 		}
33451708Sstevel 
33461708Sstevel 		fru_ptr->fru_type = ALARM;
33471708Sstevel 		fru_ptr->fru_unit = (scsb_unum_t)unit;
33481708Sstevel 		fru_ptr->fru_id = fru_id_table[event_to_index(t)];
33491708Sstevel 		fru_ptr->fru_version = (fru_version_t)0;
33501708Sstevel 		fru_ptr->type_list = (fru_options_t *)NULL;
33511708Sstevel 		fru_ptr->i2c_info = (fru_i2c_info_t *)
33527656SSherry.Moore@Sun.COM 		    kmem_zalloc(sizeof (fru_i2c_info_t), KM_SLEEP);
33531708Sstevel 		fru_ptr->i2c_info->syscfg_reg = syscfg;
33541708Sstevel 		fru_ptr->i2c_info->syscfg_bit = bit_num;
33551708Sstevel 		fru_ptr->i2c_info->ledata_reg = 0;
33561708Sstevel 		fru_ptr->i2c_info->ledata_bit = 0;
33571708Sstevel 		fru_ptr->i2c_info->blink_reg = 0;
33581708Sstevel 		fru_ptr->i2c_info->blink_bit = 0;
33591708Sstevel 		last_ptr = fru_ptr;
33601708Sstevel 		fru_ptr++;
33611708Sstevel 		last_ptr->next = fru_ptr;
33621708Sstevel 	}
33631708Sstevel 	last_ptr->next = (fru_info_t *)NULL;
33641708Sstevel 	/*
33651708Sstevel 	 * SCB
33661708Sstevel 	 */
33671708Sstevel 	mct_system_info.fru_info_list[SCB] = (fru_info_t *)
33687656SSherry.Moore@Sun.COM 	    kmem_zalloc(sizeof (fru_info_t) *
33697656SSherry.Moore@Sun.COM 	    (mct_system_info.max_units[SCB] + pad), KM_SLEEP);
33701708Sstevel 	fru_ptr = mct_system_info.fru_info_list[SCB];
33711708Sstevel 	unit = 1;
33721708Sstevel 	/* SCB15 */
33731708Sstevel 	/*
33741708Sstevel 	 * get the FRU event code (t), then use it to get the
33751708Sstevel 	 * FRU bit-offset for LED register
33761708Sstevel 	 */
33771708Sstevel 	t = FRU_UNIT_TO_EVCODE(SCB, unit);
33781708Sstevel 	led_bit = FRU_OFFSET(t, SCTRL_LED_OK_BASE);
33791708Sstevel 	i = FRU_REG_INDEX(t, SCTRL_LED_OK_BASE);
33801708Sstevel 	led_reg = SCSB_REG_ADDR(i);
33811708Sstevel 	if (IS_SCB_P15) {
33821708Sstevel 		blink_bit = FRU_OFFSET(t, SCTRL_BLINK_OK_BASE);
33831708Sstevel 		i = FRU_REG_INDEX(t, SCTRL_BLINK_OK_BASE);
33841708Sstevel 		blink_reg = SCSB_REG_ADDR(i);
33851708Sstevel 	} else {
33861708Sstevel 		blink_bit = 0;
33871708Sstevel 		blink_reg = 0;
33881708Sstevel 	}
33891708Sstevel 	i = SYS_REG_INDEX(SCTRL_SCBID0, SCTRL_SCBID_BASE);
33901708Sstevel 	index = SCSB_REG_ADDR(i);
33911708Sstevel 	/*
33921708Sstevel 	 * check and set presence status
33931708Sstevel 	 */
33941708Sstevel 	if (scsb->scsb_state & SCSB_SCB_PRESENT) {
33951708Sstevel 		fru_ptr->fru_status = FRU_PRESENT;
33961708Sstevel 	} else {
33971708Sstevel 		fru_ptr->fru_status = FRU_NOT_PRESENT;
33981708Sstevel 	}
33991708Sstevel 	fru_ptr->fru_type = SCB;
34001708Sstevel 	fru_ptr->fru_unit = (scsb_unum_t)unit;
34011708Sstevel 	fru_ptr->fru_id = fru_id_table[event_to_index(t)];
34021708Sstevel 	/* get PROM_VERSION from shadow registers */
34031708Sstevel 	if (scsb_rdwr_register(scsb, I2C_WR_RD, index, 1, &t_uchar, 1))
34041708Sstevel 		fru_ptr->fru_version = (fru_version_t)0;
34051708Sstevel 	else
34061708Sstevel 		fru_ptr->fru_version = (fru_version_t)t_uchar;
34071708Sstevel 	fru_ptr->type_list = (fru_options_t *)NULL;
34081708Sstevel 	fru_ptr->i2c_info = (fru_i2c_info_t *)
34097656SSherry.Moore@Sun.COM 	    kmem_zalloc(sizeof (fru_i2c_info_t), KM_SLEEP);
34101708Sstevel 	fru_ptr->i2c_info->syscfg_reg = 0;
34111708Sstevel 	fru_ptr->i2c_info->syscfg_bit = 0;
34121708Sstevel 	fru_ptr->i2c_info->ledata_reg = led_reg;
34131708Sstevel 	fru_ptr->i2c_info->ledata_bit = led_bit;
34141708Sstevel 	fru_ptr->i2c_info->blink_reg = blink_reg;
34151708Sstevel 	fru_ptr->i2c_info->blink_bit = blink_bit;
34161708Sstevel 	fru_ptr->next = (fru_info_t *)NULL;
34171708Sstevel 	/*
34181708Sstevel 	 * SSB
34191708Sstevel 	 */
34201708Sstevel 	mct_system_info.fru_info_list[SSB] = (fru_info_t *)
34217656SSherry.Moore@Sun.COM 	    kmem_zalloc(sizeof (fru_info_t) *
34227656SSherry.Moore@Sun.COM 	    (mct_system_info.max_units[SSB] + pad), KM_SLEEP);
34231708Sstevel 	fru_ptr = mct_system_info.fru_info_list[SSB];
34241708Sstevel 	unit = 1;
34251708Sstevel 	/* SCB15 */
34261708Sstevel 	/*
34271708Sstevel 	 * get the FRU event code (t), then use it to get the
34281708Sstevel 	 * FRU bit-offset for SYSCFG register
34291708Sstevel 	 */
34301708Sstevel 	t = FRU_UNIT_TO_EVCODE(SSB, unit);
34311708Sstevel 	bit_num = FRU_OFFSET(t, SCTRL_SYSCFG_BASE);
34321708Sstevel 	/*
34331708Sstevel 	 * get the registers addresses and shadow register index for
34341708Sstevel 	 * the SYSCFG register
34351708Sstevel 	 */
34361708Sstevel 	i = FRU_REG_INDEX(t, SCTRL_SYSCFG_BASE);
34371708Sstevel 	syscfg = SCSB_REG_ADDR(i);
34381708Sstevel 	index = SCSB_REG_INDEX(syscfg);
34391708Sstevel 	/*
34401708Sstevel 	 * check and set presence status
34411708Sstevel 	 */
34421708Sstevel 	if (scsb->scsb_data_reg[index] & (1 << bit_num)) {
34431708Sstevel 		fru_ptr->fru_status = FRU_PRESENT;
34441708Sstevel 	} else {
34451708Sstevel 		fru_ptr->fru_status = FRU_NOT_PRESENT;
34461708Sstevel 	}
34471708Sstevel 	fru_ptr->fru_type = SSB;
34481708Sstevel 	fru_ptr->fru_unit = (scsb_unum_t)unit;
34491708Sstevel 	fru_ptr->fru_id = fru_id_table[event_to_index(t)];
34501708Sstevel 	fru_ptr->fru_version = (fru_version_t)0;
34511708Sstevel 	fru_ptr->type_list = (fru_options_t *)NULL;
34521708Sstevel 	fru_ptr->i2c_info = (fru_i2c_info_t *)
34537656SSherry.Moore@Sun.COM 	    kmem_zalloc(sizeof (fru_i2c_info_t), KM_SLEEP);
34541708Sstevel 	fru_ptr->i2c_info->syscfg_reg = syscfg;
34551708Sstevel 	fru_ptr->i2c_info->syscfg_bit = bit_num;
34561708Sstevel 	fru_ptr->i2c_info->ledata_reg = 0;
34571708Sstevel 	fru_ptr->i2c_info->ledata_bit = 0;
34581708Sstevel 	fru_ptr->i2c_info->blink_reg = 0;
34591708Sstevel 	fru_ptr->i2c_info->blink_bit = 0;
34601708Sstevel 	fru_ptr->next = (fru_info_t *)NULL;
34611708Sstevel 	/*
34621708Sstevel 	 * CFTM
34631708Sstevel 	 */
34641708Sstevel 	mct_system_info.fru_info_list[CFTM] = (fru_info_t *)
34657656SSherry.Moore@Sun.COM 	    kmem_zalloc(sizeof (fru_info_t) *
34667656SSherry.Moore@Sun.COM 	    (mct_system_info.max_units[CFTM] + pad), KM_SLEEP);
34671708Sstevel 	fru_ptr = mct_system_info.fru_info_list[CFTM];
34681708Sstevel 	unit = 1;
34691708Sstevel 	/* SCB15 */
34701708Sstevel 	/*
34711708Sstevel 	 * get the FRU event code (t), then use it to get the
34721708Sstevel 	 * FRU bit-offsets for LED and SYSCFG registers
34731708Sstevel 	 */
34741708Sstevel 	t = FRU_UNIT_TO_EVCODE(CFTM, unit);
34751708Sstevel 	led_bit = FRU_OFFSET(t, SCTRL_LED_OK_BASE);
34761708Sstevel 	bit_num = FRU_OFFSET(t, SCTRL_SYSCFG_BASE);
34771708Sstevel 	if (IS_SCB_P15) {
34781708Sstevel 		blink_bit = FRU_OFFSET(t, SCTRL_BLINK_OK_BASE);
34791708Sstevel 		i = FRU_REG_INDEX(t, SCTRL_BLINK_OK_BASE);
34801708Sstevel 		blink_reg = SCSB_REG_ADDR(i);
34811708Sstevel 	} else {
34821708Sstevel 		blink_bit = 0;
34831708Sstevel 		blink_reg = 0;
34841708Sstevel 	}
34851708Sstevel 	/*
34861708Sstevel 	 * get the registers addresses and shadow register index for
34871708Sstevel 	 * the SYSCFG register
34881708Sstevel 	 */
34891708Sstevel 	i = FRU_REG_INDEX(t, SCTRL_SYSCFG_BASE);
34901708Sstevel 	syscfg = SCSB_REG_ADDR(i);
34911708Sstevel 	index = SCSB_REG_INDEX(syscfg);
34921708Sstevel 	i = FRU_REG_INDEX(t, SCTRL_LED_OK_BASE);
34931708Sstevel 	led_reg = SCSB_REG_ADDR(i);
34941708Sstevel 	/*
34951708Sstevel 	 * check and set presence status
34961708Sstevel 	 */
34971708Sstevel 	if (scsb->scsb_data_reg[index] & (1 << bit_num)) {
34981708Sstevel 		fru_ptr->fru_status = FRU_PRESENT;
34991708Sstevel 		if (ctcslot_ptr != NULL && ctcslot_ptr->fru_status ==
35007656SSherry.Moore@Sun.COM 		    FRU_PRESENT) {
35011708Sstevel 			ctcslot_ptr->fru_type = (scsb_utype_t)OC_CTC;
35021708Sstevel 			scsb->scsb_hsc_state |= SCSB_HSC_CTC_PRES;
35031708Sstevel 		}
35041708Sstevel 	} else {
35051708Sstevel 		fru_ptr->fru_status = FRU_NOT_PRESENT;
35061708Sstevel 	}
35071708Sstevel 	fru_ptr->fru_type = CFTM;
35081708Sstevel 	fru_ptr->fru_unit = (scsb_unum_t)1;
35091708Sstevel 	fru_ptr->fru_id = fru_id_table[event_to_index(t)];
35101708Sstevel 	fru_ptr->fru_version = (fru_version_t)0;
35111708Sstevel 	fru_ptr->type_list = (fru_options_t *)NULL;
35121708Sstevel 	fru_ptr->i2c_info = (fru_i2c_info_t *)
35137656SSherry.Moore@Sun.COM 	    kmem_zalloc(sizeof (fru_i2c_info_t), KM_SLEEP);
35141708Sstevel 	fru_ptr->i2c_info->syscfg_reg = syscfg;
35151708Sstevel 	fru_ptr->i2c_info->syscfg_bit = bit_num;
35161708Sstevel 	fru_ptr->i2c_info->ledata_reg = led_reg;
35171708Sstevel 	fru_ptr->i2c_info->ledata_bit = led_bit;
35181708Sstevel 	fru_ptr->i2c_info->blink_reg = blink_reg;
35191708Sstevel 	fru_ptr->i2c_info->blink_bit = blink_bit;
35201708Sstevel 	fru_ptr->next = (fru_info_t *)NULL;
35211708Sstevel 	/*
35221708Sstevel 	 * CRTM
35231708Sstevel 	 */
35241708Sstevel 	mct_system_info.fru_info_list[CRTM] = (fru_info_t *)
35257656SSherry.Moore@Sun.COM 	    kmem_zalloc(sizeof (fru_info_t) *
35267656SSherry.Moore@Sun.COM 	    (mct_system_info.max_units[CRTM] + pad),
35277656SSherry.Moore@Sun.COM 	    KM_SLEEP);
35281708Sstevel 	fru_ptr = mct_system_info.fru_info_list[CRTM];
35291708Sstevel 	unit = 1;
35301708Sstevel 	/* SCB15 */
35311708Sstevel 	/*
35321708Sstevel 	 * get the FRU event code (t), then use it to get the
35331708Sstevel 	 * FRU bit-offsets for LED and SYSCFG registers
35341708Sstevel 	 */
35351708Sstevel 	t = FRU_UNIT_TO_EVCODE(CRTM, unit);
35361708Sstevel 	bit_num = FRU_OFFSET(t, SCTRL_SYSCFG_BASE);
35371708Sstevel 	/*
35381708Sstevel 	 * get the registers addresses and shadow register index for
35391708Sstevel 	 * the SYSCFG register
35401708Sstevel 	 */
35411708Sstevel 	i = FRU_REG_INDEX(t, SCTRL_SYSCFG_BASE);
35421708Sstevel 	syscfg = SCSB_REG_ADDR(i);
35431708Sstevel 	index = SCSB_REG_INDEX(syscfg);
35441708Sstevel 	/*
35451708Sstevel 	 * check and set presence status
35461708Sstevel 	 */
35471708Sstevel 	if (scsb->scsb_data_reg[index] & (1 << bit_num)) {
35481708Sstevel 		fru_ptr->fru_status = FRU_PRESENT;
35491708Sstevel 	} else {
35501708Sstevel 		fru_ptr->fru_status = FRU_NOT_PRESENT;
35511708Sstevel 	}
35521708Sstevel 	fru_ptr->fru_type = CRTM;
35531708Sstevel 	fru_ptr->fru_unit = (scsb_unum_t)unit;
35541708Sstevel 	fru_ptr->fru_id = fru_id_table[event_to_index(t)];
35551708Sstevel 	fru_ptr->fru_version = (fru_version_t)0;
35561708Sstevel 	fru_ptr->type_list = (fru_options_t *)NULL;
35571708Sstevel 	fru_ptr->i2c_info = (fru_i2c_info_t *)
35587656SSherry.Moore@Sun.COM 	    kmem_zalloc(sizeof (fru_i2c_info_t), KM_SLEEP);
35591708Sstevel 	fru_ptr->i2c_info->syscfg_reg = syscfg;
35601708Sstevel 	fru_ptr->i2c_info->syscfg_bit = bit_num;
35611708Sstevel 	fru_ptr->i2c_info->ledata_reg = 0;
35621708Sstevel 	fru_ptr->i2c_info->ledata_bit = 0;
35631708Sstevel 	fru_ptr->i2c_info->blink_reg = 0;
35641708Sstevel 	fru_ptr->i2c_info->blink_bit = 0;
35651708Sstevel 	fru_ptr->next = (fru_info_t *)NULL;
35661708Sstevel 	/*
35671708Sstevel 	 * PRTM
35681708Sstevel 	 */
35691708Sstevel 	mct_system_info.fru_info_list[PRTM] = (fru_info_t *)
35707656SSherry.Moore@Sun.COM 	    kmem_zalloc(sizeof (fru_info_t) *
35717656SSherry.Moore@Sun.COM 	    (mct_system_info.max_units[PRTM] + pad), KM_SLEEP);
35721708Sstevel 	fru_ptr = mct_system_info.fru_info_list[PRTM];
35731708Sstevel 	unit = 1;
35741708Sstevel 	/*
35751708Sstevel 	 * SCB15
35761708Sstevel 	 * get the FRU event code (t), then use it to get the
35771708Sstevel 	 * FRU bit-offsets for LED and SYSCFG registers
35781708Sstevel 	 */
35791708Sstevel 	t = FRU_UNIT_TO_EVCODE(PRTM, unit);
35801708Sstevel 	bit_num = FRU_OFFSET(t, SCTRL_SYSCFG_BASE);
35811708Sstevel 	/*
35821708Sstevel 	 * get the registers addresses and shadow register index for
35831708Sstevel 	 * the SYSCFG register
35841708Sstevel 	 */
35851708Sstevel 	i = FRU_REG_INDEX(t, SCTRL_SYSCFG_BASE);
35861708Sstevel 	syscfg = SCSB_REG_ADDR(i);
35871708Sstevel 	index = SCSB_REG_INDEX(syscfg);
35881708Sstevel 	/*
35891708Sstevel 	 * check and set presence status
35901708Sstevel 	 */
35911708Sstevel 	if (scsb->scsb_data_reg[index] & (1 << bit_num)) {
35921708Sstevel 		fru_ptr->fru_status = FRU_PRESENT;
35931708Sstevel 	} else {
35941708Sstevel 		fru_ptr->fru_status = FRU_NOT_PRESENT;
35951708Sstevel 	}
35961708Sstevel 	fru_ptr->fru_type = PRTM;
35971708Sstevel 	fru_ptr->fru_unit = (scsb_unum_t)unit;
35981708Sstevel 	fru_ptr->fru_id = fru_id_table[event_to_index(t)];
35991708Sstevel 	fru_ptr->fru_version = (fru_version_t)0;
36001708Sstevel 	fru_ptr->type_list = (fru_options_t *)NULL;
36011708Sstevel 	fru_ptr->i2c_info = (fru_i2c_info_t *)
36027656SSherry.Moore@Sun.COM 	    kmem_zalloc(sizeof (fru_i2c_info_t), KM_SLEEP);
36031708Sstevel 	fru_ptr->i2c_info->syscfg_reg = syscfg;
36041708Sstevel 	fru_ptr->i2c_info->syscfg_bit = bit_num;
36051708Sstevel 	fru_ptr->i2c_info->ledata_reg = 0;
36061708Sstevel 	fru_ptr->i2c_info->ledata_bit = 0;
36071708Sstevel 	fru_ptr->i2c_info->blink_reg = 0;
36081708Sstevel 	fru_ptr->i2c_info->blink_bit = 0;
36091708Sstevel 	fru_ptr->next = (fru_info_t *)NULL;
36101708Sstevel 
36111708Sstevel 	scsb->scsb_state |= SCSB_TOPOLOGY;
36121708Sstevel #ifdef DEBUG
36131708Sstevel 	mct_topology_dump(scsb, 0);
36141708Sstevel #endif
36151708Sstevel }
36161708Sstevel 
36171708Sstevel /*ARGSUSED*/
36181708Sstevel static void
scsb_free_topology(scsb_state_t * scsb)36191708Sstevel scsb_free_topology(scsb_state_t *scsb)
36201708Sstevel {
36211708Sstevel 	int		i;
36221708Sstevel 	fru_info_t	*fru_ptr;
36231708Sstevel 
36241708Sstevel 	if (scsb_debug & 0x00100005)
36251708Sstevel 		cmn_err(CE_NOTE, "scsb_free_topology:");
36261708Sstevel 	for (i = 0; i < SCSB_UNIT_TYPES; ++i) {
36271708Sstevel 		fru_ptr = mct_system_info.fru_info_list[i];
36281708Sstevel 		while (fru_ptr != NULL) {
36291708Sstevel 			if (fru_ptr->i2c_info != (fru_i2c_info_t *)NULL)
36301708Sstevel 				kmem_free(fru_ptr->i2c_info,
36317656SSherry.Moore@Sun.COM 				    sizeof (fru_i2c_info_t));
36321708Sstevel 			fru_ptr = fru_ptr->next;
36331708Sstevel 		}
36341708Sstevel 		if ((fru_ptr = mct_system_info.fru_info_list[i]) !=
36357656SSherry.Moore@Sun.COM 		    (fru_info_t *)NULL) {
36361708Sstevel 			kmem_free(fru_ptr, sizeof (fru_info_t) *
36377656SSherry.Moore@Sun.COM 			    mct_system_info.max_units[i]);
36381708Sstevel 			mct_system_info.fru_info_list[i] = (fru_info_t *)NULL;
36391708Sstevel 		}
36401708Sstevel 	}
36411708Sstevel }
36421708Sstevel 
36431708Sstevel #ifdef DEBUG
36441708Sstevel static void
mct_topology_dump(scsb_state_t * scsb,int force)36451708Sstevel mct_topology_dump(scsb_state_t *scsb, int force)
36461708Sstevel {
36471708Sstevel 	int		i;
36481708Sstevel 	fru_info_t	*fru_ptr;
36491708Sstevel 
36501708Sstevel 	if (!force && !(scsb_debug & 0x00200000))
36511708Sstevel 		return;
36521708Sstevel 	if (force && !(scsb->scsb_state & (SCSB_DIAGS_MODE | SCSB_DEBUG_MODE)))
36531708Sstevel 		return;
36541708Sstevel 	if (!(scsb->scsb_state & SCSB_TOPOLOGY)) {
36551708Sstevel 		cmn_err(CE_NOTE, "mct_topology_dump: Topology not set!");
36561708Sstevel 		return;
36571708Sstevel 	}
36581708Sstevel 	for (i = 0; i < SCSB_UNIT_TYPES; ++i) {
36591708Sstevel 		fru_ptr = mct_system_info.fru_info_list[i];
36601708Sstevel 		switch ((scsb_utype_t)i) {
36611708Sstevel 		case SLOT:
36621708Sstevel 			cmn_err(CE_NOTE, "MCT: Number of Slots: %d",
36637656SSherry.Moore@Sun.COM 			    mct_system_info.max_units[SLOT]);
36641708Sstevel 			break;
36651708Sstevel 		case ALARM:
36661708Sstevel 			cmn_err(CE_NOTE, "MCT: MAX Number of Alarm Cards: %d",
36677656SSherry.Moore@Sun.COM 			    mct_system_info.max_units[ALARM]);
36681708Sstevel 			break;
36691708Sstevel 		case DISK:
36701708Sstevel 			cmn_err(CE_NOTE, "MCT: MAX Number of SCSI Devices: %d",
36717656SSherry.Moore@Sun.COM 			    mct_system_info.max_units[DISK]);
36721708Sstevel 			break;
36731708Sstevel 		case FAN:
36741708Sstevel 			cmn_err(CE_NOTE, "MCT: MAX Number of Fan Trays: %d",
36757656SSherry.Moore@Sun.COM 			    mct_system_info.max_units[FAN]);
36761708Sstevel 			break;
36771708Sstevel 		case PDU:
36781708Sstevel 			cmn_err(CE_NOTE, "MCT: MAX Number of PDUs: %d",
36797656SSherry.Moore@Sun.COM 			    mct_system_info.max_units[PDU]);
36801708Sstevel 			break;
36811708Sstevel 		case PS:
36821708Sstevel 			cmn_err(CE_NOTE,
36837656SSherry.Moore@Sun.COM 			    "MCT: MAX Number of Power Supplies: %d",
36847656SSherry.Moore@Sun.COM 			    mct_system_info.max_units[PS]);
36851708Sstevel 			break;
36861708Sstevel 		case SCB:
36871708Sstevel 			cmn_err(CE_NOTE, "MCT: MAX Number of SCBs: %d",
36887656SSherry.Moore@Sun.COM 			    mct_system_info.max_units[SCB]);
36891708Sstevel 			break;
36901708Sstevel 		case SSB:
36911708Sstevel 			cmn_err(CE_NOTE, "MCT: MAX Number of SSBs: %d",
36927656SSherry.Moore@Sun.COM 			    mct_system_info.max_units[SSB]);
36931708Sstevel 			break;
36941708Sstevel 		}
36951708Sstevel 		while (fru_ptr != NULL) {
36961708Sstevel 			if (fru_ptr->fru_status & FRU_PRESENT) {
36971708Sstevel 				cmn_err(CE_NOTE,
36987656SSherry.Moore@Sun.COM 				    "MCT:   type=%d, unit=%d, id=0x%x, "
36997656SSherry.Moore@Sun.COM 				    "version=0x%x",
37007656SSherry.Moore@Sun.COM 				    fru_ptr->fru_type,
37017656SSherry.Moore@Sun.COM 				    fru_ptr->fru_unit,
37027656SSherry.Moore@Sun.COM 				    fru_ptr->fru_id,
37037656SSherry.Moore@Sun.COM 				    fru_ptr->fru_version);
37041708Sstevel 			}
37051708Sstevel 			fru_ptr = fru_ptr->next;
37061708Sstevel 		}
37071708Sstevel 	}
37081708Sstevel }
37091708Sstevel 
37101708Sstevel /*
37111708Sstevel  * Sends an event when the system controller board I2C errors
37121708Sstevel  * exceed the threshold.
37131708Sstevel  */
37141708Sstevel static void
scsb_failing_event(scsb_state_t * scsb)37151708Sstevel scsb_failing_event(scsb_state_t *scsb)
37161708Sstevel {
37171708Sstevel 	uint32_t scsb_event_code = SCTRL_EVENT_SCB;
37181708Sstevel 
37191708Sstevel 	add_event_code(scsb, scsb_event_code);
37201708Sstevel 	(void) scsb_queue_ops(scsb, QPUT_INT32, 1, &scsb_event_code,
37217656SSherry.Moore@Sun.COM 	"scsb_intr");
37221708Sstevel }
37231708Sstevel #endif
37241708Sstevel 
37251708Sstevel int
scsb_read_bhealthy(scsb_state_t * scsb)37261708Sstevel scsb_read_bhealthy(scsb_state_t *scsb)
37271708Sstevel {
37281708Sstevel 	int		error;
37291708Sstevel 	uchar_t		reg;
37301708Sstevel 	int		index;
37311708Sstevel 
37321708Sstevel 	if (scsb_debug & 0x8001) {
37331708Sstevel 		cmn_err(CE_NOTE, "scsb_read_bhealthy()");
37341708Sstevel 	}
37351708Sstevel 	reg = SCSB_REG_ADDR(SCTRL_BHLTHY_BASE);
37361708Sstevel 	index = SCSB_REG_INDEX(reg);
37371708Sstevel 	error = scsb_rdwr_register(scsb, I2C_WR_RD, reg,
37387656SSherry.Moore@Sun.COM 	    SCTRL_BHLTHY_NUMREGS, &scsb->scsb_data_reg[index], 1);
37391708Sstevel 	return (error);
37401708Sstevel }
37411708Sstevel 
37421708Sstevel /*
37431708Sstevel  * Returns the health status of a slot
37441708Sstevel  */
37451708Sstevel int
scsb_read_slot_health(scsb_state_t * scsb,int pslotnum)37461708Sstevel scsb_read_slot_health(scsb_state_t *scsb, int pslotnum)
37471708Sstevel {
37481708Sstevel 	int slotnum = tonga_psl_to_ssl(scsb, pslotnum);
37491708Sstevel 	return (scsb_fru_op(scsb, SLOT, slotnum,
37507656SSherry.Moore@Sun.COM 	    SCTRL_BHLTHY_BASE, SCSB_FRU_OP_GET_BITVAL));
37511708Sstevel }
37521708Sstevel 
37531708Sstevel /*
37541708Sstevel  * DIAGNOSTIC and DEBUG only.
37551708Sstevel  * Called from ioctl command (SCSBIOC_BHEALTHY_GET)
37561708Sstevel  */
37571708Sstevel int
scsb_bhealthy_slot(scsb_state_t * scsb,scsb_uinfo_t * suip)37581708Sstevel scsb_bhealthy_slot(scsb_state_t *scsb, scsb_uinfo_t *suip)
37591708Sstevel {
37601708Sstevel 	int		error = 0;
37611708Sstevel 	int		base, code, unit_number;
37621708Sstevel 	uchar_t		reg;
37631708Sstevel 	int		index;
37641708Sstevel 
37651708Sstevel 	if (scsb->scsb_state & SCSB_FROZEN)
37661708Sstevel 		return (EAGAIN);
37671708Sstevel 
37681708Sstevel 	/* operation valid for slots only */
37691708Sstevel 	if (suip == NULL || suip->unit_type != SLOT) {
37701708Sstevel 		return (EINVAL);
37711708Sstevel 	}
37721708Sstevel 
37731708Sstevel 	if (scsb_debug & 0x8001)
37741708Sstevel 		cmn_err(CE_NOTE, "scsb_bhealthy_slot: slot %d",
37757656SSherry.Moore@Sun.COM 		    suip->unit_number);
37761708Sstevel 	if (suip->unit_number > mct_system_info.max_units[SLOT]) {
37771708Sstevel 		return (EINVAL);
37781708Sstevel 	}
37791708Sstevel 	/*
37801708Sstevel 	 * Map 1.0 Tonga Slot Number, if necessary
37811708Sstevel 	 */
37821708Sstevel 	tonga_slotnum_check(scsb, suip);
37831708Sstevel 	base = SCTRL_BHLTHY_BASE;
37841708Sstevel 	code = FRU_UNIT_TO_EVCODE(suip->unit_type, suip->unit_number);
37851708Sstevel 	unit_number = FRU_OFFSET(code, base);
37861708Sstevel 	index = FRU_REG_INDEX(code, base);
37871708Sstevel 	reg = SCSB_REG_ADDR(index);
37881708Sstevel 	index = SCSB_REG_INDEX(reg);		/* shadow index */
37891708Sstevel 
37901708Sstevel 	if (scsb->scsb_state & SCSB_P10_PROM) {
37911708Sstevel 		error = scsb_read_bhealthy(scsb);
37921708Sstevel 	}
37931708Sstevel 	/* else shadow regs are updated by interrupt handler */
37941708Sstevel 	if (error == 0) {
37951708Sstevel 		if (scsb->scsb_data_reg[index] & (1 << unit_number))
37961708Sstevel 			suip->unit_state = ON;
37971708Sstevel 		else
37981708Sstevel 			suip->unit_state = OFF;
37991708Sstevel 	}
38001708Sstevel 	return (error);
38011708Sstevel }
38021708Sstevel 
38031708Sstevel /*
38041708Sstevel  * Called from HSC and ioctl command (SCSBIOC_RESET_UNIT)
38051708Sstevel  * to reset one specified slot
38061708Sstevel  */
38071708Sstevel int
scsb_reset_unit(scsb_state_t * scsb,scsb_uinfo_t * suip)38081708Sstevel scsb_reset_unit(scsb_state_t *scsb, scsb_uinfo_t *suip)
38091708Sstevel {
38101708Sstevel 	int		error;
38111708Sstevel 	int		unit_number;
38121708Sstevel 	uchar_t		reg;
38131708Sstevel 	int		index, slotnum, reset_state;
38141708Sstevel 
38151708Sstevel 	if (scsb->scsb_state & SCSB_FROZEN)
38161708Sstevel 		return (EAGAIN);
38171708Sstevel 	if (scsb_debug & 0x8001) {
38181708Sstevel 		cmn_err(CE_NOTE, "scsb_reset_slot(%d): slot %d, state %d\n",
38197656SSherry.Moore@Sun.COM 		    scsb->scsb_instance, suip->unit_number,
38207656SSherry.Moore@Sun.COM 		    suip->unit_state);
38211708Sstevel 	}
38221708Sstevel 	if (suip->unit_type != ALARM && !(scsb->scsb_state &
38237656SSherry.Moore@Sun.COM 	    (SCSB_DIAGS_MODE | SCSB_DEBUG_MODE))) {
38241708Sstevel 		return (EINVAL);
38251708Sstevel 	}
38261708Sstevel 	if (suip->unit_state != ON && suip->unit_state != OFF) {
38271708Sstevel 		return (EINVAL);
38281708Sstevel 	}
38291708Sstevel 	error = 0;
38301708Sstevel 	switch (suip->unit_type) {
38311708Sstevel 	case ALARM:
38321708Sstevel 	{
38331708Sstevel 		int	i, code;
38341708Sstevel 		if (suip->unit_number != 1)
38351708Sstevel 			return (EINVAL);
38361708Sstevel 		code = FRU_UNIT_TO_EVCODE(suip->unit_type, suip->unit_number);
38371708Sstevel 		unit_number = FRU_OFFSET(code, SCTRL_RESET_BASE);
38381708Sstevel 		i = ALARM_RESET_REG_INDEX(code, SCTRL_RESET_BASE);
38391708Sstevel 		reg = SCSB_REG_ADDR(i);
38401708Sstevel 		break;
38411708Sstevel 	}
38421708Sstevel 	case SLOT:
38431708Sstevel 		slotnum = suip->unit_number;
38441708Sstevel 		reset_state = (suip->unit_state == ON) ? SCSB_RESET_SLOT :
38457656SSherry.Moore@Sun.COM 		    SCSB_UNRESET_SLOT;
38461708Sstevel 		if (scsb->scsb_state & SCSB_IS_TONGA) {
38471708Sstevel 			if (slotnum > TG_MAX_SLOTS ||
38487656SSherry.Moore@Sun.COM 			    slotnum == SC_TG_CPU_SLOT) {
38491708Sstevel 				return (EINVAL);
38501708Sstevel 			}
38511708Sstevel 		} else {
38521708Sstevel 			if (slotnum > MC_MAX_SLOTS ||
38537656SSherry.Moore@Sun.COM 			    slotnum == SC_MC_CPU_SLOT ||
38547656SSherry.Moore@Sun.COM 			    (scsb->scsb_hsc_state & SCSB_HSC_CTC_PRES &&
38557656SSherry.Moore@Sun.COM 			    slotnum == SC_MC_CTC_SLOT)) {
38561708Sstevel 				return (EINVAL);
38571708Sstevel 			}
38581708Sstevel 		}
38591708Sstevel 		return (scsb_reset_slot(scsb, slotnum, reset_state));
38601708Sstevel 	default:
38611708Sstevel 		return (EINVAL);
38621708Sstevel 	}
38631708Sstevel 	index = SCSB_REG_INDEX(reg);
38641708Sstevel 	mutex_enter(&scsb->scsb_mutex);
38651708Sstevel 	if (suip->unit_state == ON)
38661708Sstevel 		scsb->scsb_data_reg[index] |= (1 << unit_number);
38671708Sstevel 	else /* OFF */
38681708Sstevel 		scsb->scsb_data_reg[index] &= ~(1 << unit_number);
38691708Sstevel 	if ((error = scsb_rdwr_register(scsb, I2C_WR, reg, 1,
38707656SSherry.Moore@Sun.COM 	    &scsb->scsb_data_reg[index], 0)) != 0) {
38711708Sstevel 		if (scsb_debug & 0x8002)
38721708Sstevel 			cmn_err(CE_WARN,
38737656SSherry.Moore@Sun.COM 			    "scsb_leds: write failure to 0x%x", reg);
38741708Sstevel 		return (error);
38751708Sstevel 	}
38761708Sstevel 	mutex_exit(&scsb->scsb_mutex);
38771708Sstevel 	return (error);
38781708Sstevel }
38791708Sstevel 
38801708Sstevel /*
38811708Sstevel  * Diagnostic and DEBUG
38821708Sstevel  * This is a helper function for the helper ioctl to pretend that
38831708Sstevel  * scsb h/w is doing its job!!!
38841708Sstevel  */
38851708Sstevel int
scsb_slot_occupancy(scsb_state_t * scsb,scsb_uinfo_t * suip)38861708Sstevel scsb_slot_occupancy(scsb_state_t *scsb, scsb_uinfo_t *suip)
38871708Sstevel {
38881708Sstevel 	int		error;
38891708Sstevel 	int		saved_unit_number;
38901708Sstevel 
38911708Sstevel 	if (!(scsb->scsb_state & (SCSB_DEBUG_MODE | SCSB_DIAGS_MODE)))
38921708Sstevel 		return (EACCES);
38931708Sstevel 	if (scsb->scsb_state & SCSB_FROZEN) {
38941708Sstevel 		return (EAGAIN);
38951708Sstevel 	}
38961708Sstevel 	error = 0;
38971708Sstevel 	switch (suip->unit_type) {
38981708Sstevel 	case ALARM:
38991708Sstevel 		if (suip->unit_number !=
39007656SSherry.Moore@Sun.COM 		    (mct_system_info.fru_info_list[ALARM])->fru_unit) {
39011708Sstevel 			return (EINVAL);
39021708Sstevel 		}
39031708Sstevel 		break;
39041708Sstevel 
39051708Sstevel 	case SLOT:
39061708Sstevel 		/*
39071708Sstevel 		 * All slots are acceptable, except slots 11 & 12.
39081708Sstevel 		 */
39091708Sstevel 		if (suip->unit_number < 1 || suip->unit_number >
39107656SSherry.Moore@Sun.COM 		    mct_system_info.max_units[ALARM]) {
39111708Sstevel 			error = EINVAL;
39121708Sstevel 			break;
39131708Sstevel 		}
39141708Sstevel 		/* Map 1.0 Tonga Slot Numbers if necessary */
39151708Sstevel 		saved_unit_number = suip->unit_number;
39161708Sstevel 		tonga_slotnum_check(scsb, suip);
39171708Sstevel 		break;
39181708Sstevel 
39191708Sstevel 	default:
39201708Sstevel 		error = EINVAL;
39211708Sstevel 		break;
39221708Sstevel 	}
39231708Sstevel 
39241708Sstevel 	if (error)
39251708Sstevel 		return (error);
39261708Sstevel 	if (suip->unit_state == ON) {
39271708Sstevel 		if (hsc_slot_occupancy(saved_unit_number, B_TRUE, 0, B_TRUE)
39287656SSherry.Moore@Sun.COM 		    != 0)
39291708Sstevel 			error = EFAULT;
39301708Sstevel 	} else {
39311708Sstevel 		if (hsc_slot_occupancy(saved_unit_number, B_FALSE, 0, B_FALSE)
39327656SSherry.Moore@Sun.COM 		    != 0)
39331708Sstevel 			error = EFAULT;
39341708Sstevel 	}
39351708Sstevel 
39361708Sstevel 	return (error);
39371708Sstevel }
39381708Sstevel 
39391708Sstevel static int
scsb_clear_intptrs(scsb_state_t * scsb)39401708Sstevel scsb_clear_intptrs(scsb_state_t *scsb)
39411708Sstevel {
39421708Sstevel 	int		i, error;
39431708Sstevel 	uchar_t		wbuf[SCTRL_MAX_GROUP_NUMREGS];
39441708Sstevel 	error = 0;
39451708Sstevel 	for (i = 1; i <= SCTRL_INTR_NUMREGS; ++i) {
39461708Sstevel 		wbuf[i] = 0xff;
39471708Sstevel 	}
39481708Sstevel 	if (error = scsb_rdwr_register(scsb, I2C_WR,
39497656SSherry.Moore@Sun.COM 	    SCSB_REG_ADDR(SCTRL_INTSRC_BASE),
39507656SSherry.Moore@Sun.COM 	    SCTRL_INTR_NUMREGS, wbuf, 1)) {
39511708Sstevel 		if (scsb_debug & 0x0402)
39521708Sstevel 			cmn_err(CE_NOTE, "scsb_clear_intptrs(): "
39537656SSherry.Moore@Sun.COM 			    "write to 0x%x failed",
39547656SSherry.Moore@Sun.COM 			    SCSB_REG_ADDR(SCTRL_INTSRC_BASE));
39551708Sstevel 	}
39561708Sstevel 	return (error);
39571708Sstevel }
39581708Sstevel 
39591708Sstevel static int
scsb_setall_intmasks(scsb_state_t * scsb)39601708Sstevel scsb_setall_intmasks(scsb_state_t *scsb)
39611708Sstevel {
39621708Sstevel 	int		error;
39631708Sstevel 	uchar_t		reg, wdata, rmask;
39641708Sstevel 	int		i;
39651708Sstevel 
39661708Sstevel 	/*
39671708Sstevel 	 * write loop for Interrupt Mask registers
39681708Sstevel 	 */
39691708Sstevel 	if (scsb_debug & 0x0401)
39701708Sstevel 		cmn_err(CE_NOTE, "setall_intmasks()");
39711708Sstevel 	error = 0;
39721708Sstevel 	rmask = 0;
39731708Sstevel 	wdata = 0xff;
39741708Sstevel 	reg = SCSB_REG_ADDR(SCTRL_INTMASK_BASE);
39751708Sstevel 	for (i = 0; i < SCTRL_MASK_NUMREGS; ++i, ++reg) {
39761708Sstevel 		if (error = scsb_write_mask(scsb, reg, rmask, wdata, 0)) {
39771708Sstevel 			if (scsb_debug & 0x0402)
39781708Sstevel 				cmn_err(CE_NOTE, "scsb_setall_intmasks: "
39797656SSherry.Moore@Sun.COM 				    "write to 0x%x failed: %d", reg, error);
39801708Sstevel 			error = EIO;
39811708Sstevel 			break;
39821708Sstevel 		}
39831708Sstevel 	}
39841708Sstevel 	return (error);
39851708Sstevel }
39861708Sstevel 
39871708Sstevel 
39881708Sstevel /*
39891708Sstevel  * Clear Interrupt masks based on the FRUs that could be installed
39901708Sstevel  * for this particular topology, determined by the MidPlane ID
39911708Sstevel  * from SCTRL_SYSCFG registers
39921708Sstevel  *	case SCTRL_MPID_HALF:
39931708Sstevel  *		1 CPU, 1 AlarmCard, 1 SCB/SSB, 2 PS, 3 FAN, 3 SCSI, 8 Slots
39941708Sstevel  *	case SCTRL_MPID_QUARTER:
39951708Sstevel  *		1 CPU, 1 AlarmCard, 1 SCB/SSB, 1 PS, 2 FAN, 1 SCSI, 4 Slots
39961708Sstevel  *	case SCTRL_MPID_QUARTER_NODSK:
39971708Sstevel  *		1 CPU, 1 AlarmCard, 1 SCB/SSB, 1 PS, 2 FAN, 0 SCSI, 4 Slots
39981708Sstevel  */
39991708Sstevel static int
scsb_clear_intmasks(scsb_state_t * scsb)40001708Sstevel scsb_clear_intmasks(scsb_state_t *scsb)
40011708Sstevel {
40021708Sstevel 	int		error;
40031708Sstevel 	uchar_t		msk_reg, reg, wdata, rmask;
40041708Sstevel 	uchar_t		mask_data[SCTRL_MAX_GROUP_NUMREGS];
40051708Sstevel 	int		tmp, idx, code, unit, offset, mbid;
40061708Sstevel 	scsb_utype_t    fru_type;
40071708Sstevel 	fru_info_t	*fru_ptr;
40081708Sstevel 
40091708Sstevel 	if (scsb->scsb_state & SCSB_FROZEN &&
40107656SSherry.Moore@Sun.COM 	    !(scsb->scsb_state & SCSB_IN_INTR)) {
40111708Sstevel 		return (EAGAIN);
40121708Sstevel 	}
40131708Sstevel 	error = 0;
40141708Sstevel 	for (tmp = 0; tmp < SCTRL_MASK_NUMREGS; ++tmp)
40151708Sstevel 		mask_data[tmp] = 0;
40161708Sstevel 	msk_reg = SCSB_REG_ADDR(SCTRL_INTMASK_BASE);
40171708Sstevel 	mbid    = SCSB_REG_INDEX(msk_reg); /* the Mask Base Index Delta */
40181708Sstevel 	if (scsb_debug & 0x0400) {
40191708Sstevel 		cmn_err(CE_NOTE, "clear_intmasks: msk_reg=0x%x; mbid=%d",
40207656SSherry.Moore@Sun.COM 		    msk_reg, mbid);
40211708Sstevel 	}
40221708Sstevel 	for (fru_type = 0; fru_type < SCSB_UNIT_TYPES; ++fru_type) {
40231708Sstevel 		if (fru_type == SCB)
40241708Sstevel 			continue;	/* handle below, 2 reg offsets */
40251708Sstevel 		fru_ptr = mct_system_info.fru_info_list[fru_type];
40261708Sstevel 		for (; fru_ptr != NULL; fru_ptr = fru_ptr->next) {
40271708Sstevel 			unit = fru_ptr->fru_unit;
40281708Sstevel 			code   = FRU_UNIT_TO_EVCODE(fru_type, unit);
40291708Sstevel 			offset = FRU_OFFSET(code, SCTRL_INTMSK_BASE);
40301708Sstevel 			reg    = FRU_REG_ADDR(code, SCTRL_INTMSK_BASE);
40311708Sstevel 			idx    = SCSB_REG_INDEX(reg);
40321708Sstevel 			tmp = idx - mbid;
40331708Sstevel 			mask_data[tmp] |= (1 << offset);
40341708Sstevel 			if (scsb_debug & 0x0400)
40351708Sstevel 				cmn_err(CE_NOTE,
40361708Sstevel 				"clear_intmasks:%d:%d: PRES mask[%d]:0x%x",
40377656SSherry.Moore@Sun.COM 				    fru_type, unit, tmp, mask_data[tmp]);
40381708Sstevel 			if ((fru_type == SLOT) && (IS_SCB_P15)) {
40391708Sstevel 				/*
40401708Sstevel 				 * Unmask the corresponding Slot HLTHY mask
40411708Sstevel 				 * Use Slot bit and register offsets,
40421708Sstevel 				 *  but with SCTRL_INTMASK_HLTHY_BASE
40431708Sstevel 				 */
40441708Sstevel 				reg = FRU_REG_ADDR(code,
40457656SSherry.Moore@Sun.COM 				    SCTRL_INTMASK_HLTHY_BASE);
40461708Sstevel 				idx = SCSB_REG_INDEX(reg);
40471708Sstevel 				tmp = idx - mbid;
40481708Sstevel 				mask_data[tmp] |= (1 << offset);
40491708Sstevel 				if (scsb_debug & 0x0400) {
40501708Sstevel 					cmn_err(CE_NOTE,
40511708Sstevel 				"clear_intmasks:Slot:%d: HLTHY mask[%d]:0x%x"
40521708Sstevel 				"; reg=0x%x, idx=%d, mbid=%d",
40537656SSherry.Moore@Sun.COM 					    unit, tmp, mask_data[tmp],
40547656SSherry.Moore@Sun.COM 					    reg, idx, mbid);
40551708Sstevel 				}
40561708Sstevel 			}
40571708Sstevel 		}
40581708Sstevel 	}
40591708Sstevel 	/*
40601708Sstevel 	 * Now unmask these non-fru interrupt events
40611708Sstevel 	 *	SCTRL_EVENT_PWRDWN	(almost normal)
40621708Sstevel 	 *	SCTRL_EVENT_REPLACE	(not used)
40631708Sstevel 	 *	SCTRL_EVENT_ALARM_INT	(not working in P0.6/P1.0)
40641708Sstevel 	 *	SCTRL_EVENT_SCB		(SCB 1.5 ONLY; plus SCB_INT_OFFSET)
40651708Sstevel 	 */
40661708Sstevel 	code   = SCTRL_EVENT_PWRDWN;
40671708Sstevel 	offset = FRU_OFFSET(code, SCTRL_INTMSK_BASE);
40681708Sstevel 	reg    = FRU_REG_ADDR(code, SCTRL_INTMSK_BASE);
40691708Sstevel 	idx    = SCSB_REG_INDEX(reg);
40701708Sstevel 	tmp = idx - mbid;
40711708Sstevel 	mask_data[tmp] |= (1 << offset);
40721708Sstevel 	if (IS_SCB_P15) {
40731708Sstevel 		code   = SCTRL_EVENT_SCB;
40741708Sstevel 		offset = FRU_OFFSET(code, SCTRL_INTMSK_BASE);
40751708Sstevel 		reg    = FRU_REG_ADDR(code, SCTRL_INTMSK_BASE) + SCB_INT_OFFSET;
40761708Sstevel 		idx    = SCSB_REG_INDEX(reg);
40771708Sstevel 		tmp = idx - mbid;
40781708Sstevel 		mask_data[tmp] |= (1 << offset);
40791708Sstevel 		code   = SCTRL_EVENT_ALARM_INT;
40801708Sstevel 		offset = FRU_OFFSET(code, SCTRL_INTMSK_BASE);
40811708Sstevel 		reg    = FRU_REG_ADDR(code, SCTRL_INTMSK_BASE);
40821708Sstevel 		idx    = SCSB_REG_INDEX(reg);
40831708Sstevel 		tmp = idx - mbid;
40841708Sstevel 		mask_data[tmp] |= (1 << offset);
40851708Sstevel 	}
40861708Sstevel 	for (tmp = 0; tmp < SCTRL_MASK_NUMREGS; ++tmp) {
40871708Sstevel 		rmask = 0;
40881708Sstevel 		wdata = mask_data[tmp];
40891708Sstevel 		if (scsb_debug & 0x0400)
40901708Sstevel 			cmn_err(CE_NOTE, "clear_intmasks:0x%x: ~(0x%x),0x%x",
40917656SSherry.Moore@Sun.COM 			    msk_reg, (~wdata) & 0xff, wdata);
40921708Sstevel 		mutex_enter(&scsb->scsb_mutex);
40931708Sstevel 		if (error = scsb_write_mask(scsb, msk_reg, rmask,
40947656SSherry.Moore@Sun.COM 		    (~wdata) & 0xff, wdata)) {
40951708Sstevel 			mutex_exit(&scsb->scsb_mutex);
40961708Sstevel 			if (scsb_debug & 0x0402)
40971708Sstevel 				cmn_err(CE_NOTE, "scsb_clear_intmasks: "
40987656SSherry.Moore@Sun.COM 				    "write to 0x%x failed: %d",
40997656SSherry.Moore@Sun.COM 				    msk_reg, error);
41001708Sstevel 			error = EIO;
41011708Sstevel 			break;
41021708Sstevel 		}
41031708Sstevel 		mutex_exit(&scsb->scsb_mutex);
41041708Sstevel 		++msk_reg;
41051708Sstevel 	}
41061708Sstevel 	return (error);
41071708Sstevel }
41081708Sstevel 
41091708Sstevel static int
scsb_get_status(scsb_state_t * scsb,scsb_status_t * smp)41101708Sstevel scsb_get_status(scsb_state_t *scsb, scsb_status_t *smp)
41111708Sstevel {
41121708Sstevel 	register int 	i;
41131708Sstevel 
41141708Sstevel 	if (smp == NULL) {
41151708Sstevel 		return (EFAULT);
41161708Sstevel 	}
41171708Sstevel 	if (scsb_debug & 0x40000000 &&
41187656SSherry.Moore@Sun.COM 	    (scsb->scsb_state & SCSB_DEBUG_MODE ||
41197656SSherry.Moore@Sun.COM 	    scsb->scsb_state & SCSB_DIAGS_MODE)) {
41201708Sstevel 		if (scsb->scsb_state & SCSB_FROZEN) {
41211708Sstevel 			return (EAGAIN);
41221708Sstevel 		}
41231708Sstevel 		mutex_enter(&scsb->scsb_mutex);
41241708Sstevel 		if (scsb_debug & 0x80000000) {
41251708Sstevel 			if ((i = scsb_readall_regs(scsb)) != 0 &&
41267656SSherry.Moore@Sun.COM 			    scsb->scsb_state & SCSB_DEBUG_MODE)
41271708Sstevel 				cmn_err(CE_WARN, "scsb_get_status: "
41287656SSherry.Moore@Sun.COM 				    "scsb_readall_regs() FAILED");
41291708Sstevel 		} else {
41301708Sstevel 			if ((i = scsb_check_config_status(scsb)) == 0) {
41311708Sstevel 				i = scsb_set_scfg_pres_leds(scsb, NULL);
41321708Sstevel 			}
41331708Sstevel 		}
41341708Sstevel 		mutex_exit(&scsb->scsb_mutex);
41351708Sstevel 		if (i) {
41361708Sstevel 			cmn_err(CE_WARN,
41377656SSherry.Moore@Sun.COM 			    "scsb_get_status: FAILED Presence LEDs update");
41381708Sstevel 			return (EIO);
41391708Sstevel 		}
41401708Sstevel 	}
41411708Sstevel 	for (i = 0; i < SCSB_DATA_REGISTERS; ++i)
41421708Sstevel 		smp->scsb_reg[i] = scsb->scsb_data_reg[i];
41431708Sstevel 	return (0);
41441708Sstevel }
41451708Sstevel 
41461708Sstevel /*
41471708Sstevel  * scsb_freeze_check:
41481708Sstevel  *	Turn all the leds off on the system monitor card, without changing
41491708Sstevel  *	the state of what we have for scsb. This routine is called only when
41501708Sstevel  *	replacing system monitor card, so the state of the card leds could be
41511708Sstevel  *	restored, using scsb_restore().
41521708Sstevel  *	Also, set state to SCSB_FROZEN which denies access to scsb while in
41531708Sstevel  *	freeze mode.
41541708Sstevel  */
41551708Sstevel static char  *BAD_BOARD_MSG =
41561708Sstevel 	"SCSB: Should NOT remove SCB(%d) while cPCI Slot %d is "
41571708Sstevel 	"in RESET with a possible bad board.";
41581708Sstevel static int	slots_in_reset[SCTRL_MAX_GROUP_NUMREGS];
41591708Sstevel 
41601708Sstevel static void
scsb_freeze_check(scsb_state_t * scsb)41611708Sstevel scsb_freeze_check(scsb_state_t *scsb)
41621708Sstevel {
41631708Sstevel 	register int	i;
41641708Sstevel 	int		offset;
41651708Sstevel 	int		unit, slotnum;
41661708Sstevel 	int		index;
41671708Sstevel 	fru_info_t	*fru_ptr;
41681708Sstevel 	uint32_t	code;
41691708Sstevel 	uchar_t		reg;
41701708Sstevel 
41711708Sstevel 	if (scsb_debug & 0x20001)
41721708Sstevel 		cmn_err(CE_NOTE, "scsb_freeze_check(%d):", scsb->scsb_instance);
41731708Sstevel 
41741708Sstevel 	if (scsb->scsb_state & SCSB_FROZEN) {
41751708Sstevel 		return;
41761708Sstevel 	}
41771708Sstevel 	mutex_enter(&scsb->scsb_mutex);
41781708Sstevel 	for (i = 0; i < SCTRL_MAX_GROUP_NUMREGS; ++i)
41791708Sstevel 		slots_in_reset[i] = 0;
41801708Sstevel 	/*
41811708Sstevel 	 * We allow the SCB to be removed only if none of
41821708Sstevel 	 * the cPCI resets are asserted for occupied slots.
41831708Sstevel 	 * There shouldn't be a bad board plugged in the system
41841708Sstevel 	 * while swapping the SCB.
41851708Sstevel 	 */
41861708Sstevel 	fru_ptr = mct_system_info.fru_info_list[SLOT];
41871708Sstevel 	for (unit = 1; unit <= mct_system_info.max_units[SLOT]; ++unit) {
41881708Sstevel 		if (IS_SCB_P15) {
41891708Sstevel 			slotnum = tonga_psl_to_ssl(scsb, unit);
41901708Sstevel 		} else {
41911708Sstevel 			slotnum = unit;
41921708Sstevel 		}
41931708Sstevel 		code = FRU_UNIT_TO_EVCODE(SLOT, slotnum);
41941708Sstevel 		offset = FRU_OFFSET(code, SCTRL_RESET_BASE);
41951708Sstevel 		reg = FRU_REG_ADDR(code, SCTRL_RESET_BASE);
41961708Sstevel 		index = SCSB_REG_INDEX(reg);
41971708Sstevel 		if (scsb->scsb_data_reg[index] & (1 << offset)) {
41981708Sstevel 			if (fru_ptr[unit - 1].fru_status == FRU_PRESENT) {
41991708Sstevel 				slots_in_reset[unit - 1] = unit;
42001708Sstevel 				cmn_err(CE_NOTE, BAD_BOARD_MSG,
42017656SSherry.Moore@Sun.COM 				    scsb->scsb_instance, unit);
42021708Sstevel 			}
42031708Sstevel 		}
42041708Sstevel 	}
42051708Sstevel 	mutex_exit(&scsb->scsb_mutex);
42061708Sstevel }
42071708Sstevel 
42081708Sstevel static void
scsb_freeze(scsb_state_t * scsb)42091708Sstevel scsb_freeze(scsb_state_t *scsb)
42101708Sstevel {
42111708Sstevel 	uint32_t	code;
42121708Sstevel 	if (scsb_debug & 0x00020002) {
42131708Sstevel 		cmn_err(CE_WARN, "scsb_freeze: SCB%d possibly removed",
42147656SSherry.Moore@Sun.COM 		    scsb->scsb_instance);
42151708Sstevel 	}
42161708Sstevel 	if (scsb->scsb_state & SCSB_FROZEN)
42171708Sstevel 		return;
42181708Sstevel 	scsb->scsb_state |= SCSB_FROZEN;
42191708Sstevel 	scsb->scsb_state &= ~SCSB_SCB_PRESENT;
4220*11311SSurya.Prakki@Sun.COM 	(void) scsb_hsc_freeze(scsb->scsb_dev);
42211708Sstevel 	/*
42221708Sstevel 	 * Send the EVENT_SCB since there is evidence that the
42231708Sstevel 	 * System Controller Board has been removed.
42241708Sstevel 	 */
42251708Sstevel 	code = SCTRL_EVENT_SCB;
42261708Sstevel 	if (!(scsb->scsb_state & SCSB_IN_INTR))
42271708Sstevel 		scsb_event_code = code;
42281708Sstevel 	check_fru_info(scsb, code);
42291708Sstevel 	add_event_code(scsb, code);
42301708Sstevel 	(void) scsb_queue_ops(scsb, QPUT_INT32, 1, &code, "scsb_freeze");
42311708Sstevel }
42321708Sstevel 
42331708Sstevel /*
42341708Sstevel  * scsb_restore will only be called from the interrupt handler context on
42351708Sstevel  * INIT_SCB interrupt for newly inserted SCB.
42361708Sstevel  * Called with mutex held.
42371708Sstevel  */
42381708Sstevel static void
scsb_restore(scsb_state_t * scsb)42391708Sstevel scsb_restore(scsb_state_t *scsb)
42401708Sstevel {
42411708Sstevel 	if (scsb_debug & 0x20001)
42421708Sstevel 		cmn_err(CE_NOTE, "scsb_restore(%d):", scsb->scsb_instance);
42431708Sstevel 
42441708Sstevel 	if (initialize_scb(scsb) != DDI_SUCCESS) {
42451708Sstevel 		if (scsb_debug & 0x00020002) {
42461708Sstevel 			cmn_err(CE_WARN, "scsb_restore: INIT Failed");
42471708Sstevel 		return;
42481708Sstevel 		}
42491708Sstevel 	}
42501708Sstevel 	/* 9. Clear all Interrupts */
42511708Sstevel 	if (scsb_clear_intmasks(scsb)) {
42521708Sstevel 		cmn_err(CE_WARN,
42537656SSherry.Moore@Sun.COM 		    "scsb%d: I2C TRANSFER Failed", scsb->scsb_instance);
42541708Sstevel 		if (scsb_debug & 0x00020002) {
42551708Sstevel 			cmn_err(CE_WARN, "scsb_restore: clear_intmasks Failed");
42561708Sstevel 		}
42571708Sstevel 		return;
42581708Sstevel 	}
42591708Sstevel 
42601708Sstevel 	/* 10. */
42611708Sstevel 	/* Check if Alarm Card present at boot and set flags */
42621708Sstevel 	if (scsb_fru_op(scsb, ALARM, 1, SCTRL_SYSCFG_BASE,
42637656SSherry.Moore@Sun.COM 	    SCSB_FRU_OP_GET_BITVAL))
42641708Sstevel 		scsb->scsb_hsc_state |= SCSB_ALARM_CARD_PRES;
42651708Sstevel 	else
42661708Sstevel 		scsb->scsb_hsc_state &= ~SCSB_ALARM_CARD_PRES;
42671708Sstevel 
42681708Sstevel 	scsb->scsb_state &= ~SCSB_FROZEN;
42691708Sstevel 	(void) scsb_hsc_restore(scsb->scsb_dev);
42701708Sstevel }
42711708Sstevel 
42721708Sstevel /*
42731708Sstevel  * Given an Event Code,
42741708Sstevel  * Return:
42751708Sstevel  *	FRU type    in LSByte
42761708Sstevel  *	unit number in MSByte
42771708Sstevel  */
42781708Sstevel uint16_t
event_to_type(uint32_t evcode)42791708Sstevel event_to_type(uint32_t evcode)
42801708Sstevel {
42811708Sstevel 	int		i, li, unit;
42821708Sstevel 	uint32_t	ec;
42831708Sstevel 	uint16_t	ret;
42841708Sstevel 	for (i = li = 0; i < SCSB_UNIT_TYPES; ++i) {
42851708Sstevel 		if (evcode == type_to_code1[i]) {
42861708Sstevel 			ret = (uint16_t)(0x0100 | i);
42871708Sstevel 			return (ret);
42881708Sstevel 		}
42891708Sstevel 		if (evcode < type_to_code1[i]) {
42901708Sstevel 			unit = 1;
42911708Sstevel 			ec = type_to_code1[li];
42921708Sstevel 			while (ec < evcode)
42931708Sstevel 				ec = ec << 1, ++unit;
42941708Sstevel 			ret = (unit << 8) | li;
42951708Sstevel 			return (ret);
42961708Sstevel 		}
42971708Sstevel 		li = i;
42981708Sstevel 	}
42991708Sstevel 	return ((uint16_t)0xffff);
43001708Sstevel }
43011708Sstevel 
43021708Sstevel /*
43031708Sstevel  * scsb interrupt handler for (MC) PSM_INT vector
43041708Sstevel  * P0.6: HW shipped to beta customers
43051708Sstevel  *	1. did not have Slot Occupant Presense support
43061708Sstevel  *	2. I2C interrupt-map properties not yet tested, using polling daemon
43071708Sstevel  *	3. Polling detects each event reliably twice.
43081708Sstevel  *	   clr_bits# are used to keep track of events to be ignored 2nd time
43091708Sstevel  *
43101708Sstevel  * retval flags allow all events to be checked, and still returning the
43111708Sstevel  * correct DDI value.
43121708Sstevel  *
43131708Sstevel  */
43141708Sstevel #define	SCSB_INTR_CLAIMED	1
43151708Sstevel #define	SCSB_INTR_UNCLAIMED	2
43161708Sstevel #define	SCSB_INTR_EVENT		4
43171708Sstevel 
43181708Sstevel /*
43191708Sstevel  * Does preprocessing of the interrupt. The only thing this
43201708Sstevel  * needs to do is to ask scsb to release the interrupt line.
43211708Sstevel  * and then schedule delayed actual processing using timeout()
43221708Sstevel  */
43231708Sstevel uint_t
scsb_intr_preprocess(caddr_t arg)43241708Sstevel scsb_intr_preprocess(caddr_t arg)
43251708Sstevel {
43261708Sstevel 	scsb_state_t	*scsb = (scsb_state_t *)arg;
43271708Sstevel 
43281708Sstevel 	scb_pre_s = gethrtime();
43291708Sstevel 
43301708Sstevel 	/*
43311708Sstevel 	 * If SCSB_IN_INTR is already set in scsb_state,
43321708Sstevel 	 * it means we are being interrupted by someone else. This can
43331708Sstevel 	 * happen only if the interrupt does not belong to scsb, and some
43341708Sstevel 	 * other device, e.g. a FAN or PS is interrupting. So, we
43351708Sstevel 	 * cancel the previous timeout().
43361708Sstevel 	 */
43371708Sstevel 
43381708Sstevel 	if (scsb->scsb_state & SCSB_IN_INTR) {
4339*11311SSurya.Prakki@Sun.COM 		(void) untimeout(scsb_intr_tid);
4340*11311SSurya.Prakki@Sun.COM 		(void) scsb_invoke_intr_chain();
4341*11311SSurya.Prakki@Sun.COM 		(void) scsb_toggle_psmint(scsb, 1);
43421708Sstevel 		scsb->scsb_state &= ~SCSB_IN_INTR;
43431708Sstevel 		goto intr_end;
43441708Sstevel 	}
43451708Sstevel 	scsb->scsb_state |= SCSB_IN_INTR;
43461708Sstevel 
43471708Sstevel 	/*
43481708Sstevel 	 * Stop scsb from interrupting first.
43491708Sstevel 	 */
43501708Sstevel 	if (scsb_quiesce_psmint(scsb) != DDI_SUCCESS) {
43511708Sstevel 		goto intr_end;
43521708Sstevel 	}
43531708Sstevel 
43541708Sstevel 	/*
43551708Sstevel 	 * Schedule a timeout to actually process the
43561708Sstevel 	 * interrupt.
43571708Sstevel 	 */
43581708Sstevel 	scsb_intr_tid = timeout((void (*)(void *))scsb_intr, arg,
43597656SSherry.Moore@Sun.COM 	    drv_usectohz(1000));
43601708Sstevel 
43611708Sstevel intr_end:
43621708Sstevel 
43631708Sstevel 	scb_pre_e = gethrtime();
43641708Sstevel 	return (DDI_INTR_CLAIMED);
43651708Sstevel }
43661708Sstevel 
43671708Sstevel static void scsb_healthy_intr(scsb_state_t *scsb, int pslotnum);
43681708Sstevel void
scsb_intr(caddr_t arg)43691708Sstevel scsb_intr(caddr_t arg)
43701708Sstevel {
43711708Sstevel 	scsb_state_t	*scsb = (scsb_state_t *)arg;
43721708Sstevel 	int		i, idx, offset, unit, numregs, error;
43731708Sstevel 	int		intr_idx, index, offset_base, retval, slotnum, val;
43741708Sstevel 	uint32_t	code;
43751708Sstevel 	uchar_t		intr_reg, tmp_reg, intr_addr, clr_bits = 0;
43761708Sstevel 	uchar_t		ac_slot = B_FALSE;
43771708Sstevel 	uchar_t		*int_masks;
43781708Sstevel 	uchar_t		cstatus_regs[SCTRL_MAX_GROUP_NUMREGS];
43791708Sstevel 	scsb_utype_t	fru_type;
43801708Sstevel 	fru_info_t	*fru_ptr;
43811708Sstevel 	int		ac_present;
43821708Sstevel 
43831708Sstevel 	/*
43841708Sstevel 	 * Avoid mayhem, make sure we have only one timeout thread running.
43851708Sstevel 	 */
43861708Sstevel 	mutex_enter(&scsb->scsb_mutex);
43871708Sstevel 	while (scsb_in_postintr)
43881708Sstevel 		cv_wait(&scsb->scsb_cv, &scsb->scsb_mutex);
43891708Sstevel 	scsb_in_postintr = 1;
43901708Sstevel 	mutex_exit(&scsb->scsb_mutex);
43911708Sstevel 
43921708Sstevel 	scb_post_s = gethrtime();
43931708Sstevel 	if (scsb_debug & 0x00002000)
43941708Sstevel 		cmn_err(CE_NOTE, "scsb_intr(%d)", scsb->scsb_instance);
43951708Sstevel 	retval = 0;
43961708Sstevel 	tmp_reg = 0;
43971708Sstevel 	/*
43981708Sstevel 	 * XXX: Problem, when we want to support swapping between SCB
43991708Sstevel 	 * versions, then we need to check the SCB PROM ID (CF) register here
44001708Sstevel 	 * before assuming the same SCB version was re-inserted.
44011708Sstevel 	 * We will have to duplicate some of the scb_initialization()
44021708Sstevel 	 * code to set the scsb_state PROM ID bits and to set up the
44031708Sstevel 	 * register table pointers.
44041708Sstevel 	 *
44051708Sstevel 	 * Only if NOT SSB_PRESENT, check the SCB PROM ID
44061708Sstevel 	 */
44071708Sstevel 	if (!(scsb->scsb_state & SCSB_SSB_PRESENT)) {
44081708Sstevel 		if (scb_check_version(scsb) != DDI_SUCCESS) {
44091708Sstevel #ifdef DEBUG
44101708Sstevel 			if (scsb->scsb_state & SCSB_SSB_PRESENT &&
44117656SSherry.Moore@Sun.COM 			    scsb->scsb_i2c_errcnt > scsb_err_threshold)
44121708Sstevel 				scsb_failing_event(scsb);
44131708Sstevel #endif
44141708Sstevel 			goto intr_error;
44151708Sstevel 		}
44161708Sstevel 	}
44171708Sstevel 	if (IS_SCB_P15) {
44181708Sstevel 		int_masks = scb_15_int_masks;
44191708Sstevel 	} else {
44201708Sstevel 		int_masks = scb_10_int_masks;
44211708Sstevel 	}
44221708Sstevel 	/*
44231708Sstevel 	 * Now check the INTSRC registers for set bits.
44241708Sstevel 	 * Do a quick check by OR'ing INTSRC registers together as we copy
44251708Sstevel 	 * them from the transfer buffer. For P1.0 or earlier we had already
44261708Sstevel 	 * read the interrupt source registers and wrote them back to stop
44271708Sstevel 	 * interrupt. So we need to do this step only for P1.5 or later.
44281708Sstevel 	 * We already read INTSRC6 to take care of SCB insertion case, so
44291708Sstevel 	 * do not read INTSRC6 again.
44301708Sstevel 	 */
44311708Sstevel 
44321708Sstevel 	if (IS_SCB_P15) {
44331708Sstevel 		intr_addr = SCSB_REG_ADDR(SCTRL_INTSRC_BASE);
44341708Sstevel 		/* read the interrupt register from scsb */
44351708Sstevel 		if (scsb_rdwr_register(scsb, I2C_WR_RD, intr_addr,
44367656SSherry.Moore@Sun.COM 		    SCTRL_INTR_NUMREGS - 1, scb_intr_regs, 1)) {
44371708Sstevel 			cmn_err(CE_WARN, "scsb_intr: "
44387656SSherry.Moore@Sun.COM 			    " Failed read of interrupt registers.");
44391708Sstevel #ifdef DEBUG
44401708Sstevel 			if (scsb->scsb_state & SCSB_SSB_PRESENT &&
44417656SSherry.Moore@Sun.COM 			    scsb->scsb_i2c_errcnt > scsb_err_threshold)
44421708Sstevel 				scsb_failing_event(scsb);
44431708Sstevel #endif
44441708Sstevel 			goto intr_error;
44451708Sstevel 		}
44461708Sstevel 	}
44471708Sstevel 
44481708Sstevel 	/*
44491708Sstevel 	 * We have seen that an interrupt source bit can be set
44501708Sstevel 	 * even though the corresponding interrupt mask bit
44511708Sstevel 	 * has been set to mask the interrupt. So we must
44521708Sstevel 	 * clear all bits set in the interrupt source register.
44531708Sstevel 	 */
44541708Sstevel 	for (i = 0; i < SCTRL_INTR_NUMREGS; ++i) {
44551708Sstevel 		retval |= scb_intr_regs[i];		/* Quick INTSRC check */
44561708Sstevel #ifdef DEBUG
44571708Sstevel 		if (scsb_debug & 0x08000000) {
44581708Sstevel 			if (tmp_reg || scb_intr_regs[i]) {
44591708Sstevel 				cmn_err(CE_NOTE, "scsb_intr: INTSRC%d=0x%x",
44607656SSherry.Moore@Sun.COM 				    i + 1, scb_intr_regs[i]);
44611708Sstevel 				++tmp_reg;
44621708Sstevel 			}
44631708Sstevel 		}
44641708Sstevel #endif
44651708Sstevel 	}
44661708Sstevel 	/*
44671708Sstevel 	 * Any bits from quick check? If this is not our interrupt,
44681708Sstevel 	 * something is wrong. FAN/PS interrupts are supposed to be
44691708Sstevel 	 * blocked, but we can not be sure. So, go ahead and call the
44701708Sstevel 	 * emergency interrupt handlers for FAN/PS devices and mask
44711708Sstevel 	 * their interrupts, if they aren't already masked.
44721708Sstevel 	 */
44731708Sstevel 	if (retval == 0) {
44741708Sstevel 		goto intr_error;
44751708Sstevel 	}
44761708Sstevel 
44771708Sstevel 	retval = 0;
44781708Sstevel 
44791708Sstevel 	/*
44801708Sstevel 	 * If SCB 1.5 or 2.0, check for the INIT_SCB Interrupt
44811708Sstevel 	 * to support Hot SCB Insertion.
44821708Sstevel 	 * The check was moved here during debugging of the SCB hot insertion.
44831708Sstevel 	 * Theoretically, this code could be moved back to the check for
44841708Sstevel 	 * SCTRL_EVENT_SCB in the processing loop below.
44851708Sstevel 	 */
44861708Sstevel 	if (IS_SCB_P15) {
44871708Sstevel 		int	iid;
44881708Sstevel 		iid = SCSB_REG_INDEX(intr_addr);
44891708Sstevel 		offset = FRU_OFFSET(SCTRL_EVENT_SCB, SCTRL_INTPTR_BASE);
44901708Sstevel 		tmp_reg = SCSB_REG_ADDR(SCTRL_INTSRC_SCB_P15);
44911708Sstevel 		intr_idx = SCSB_REG_INDEX(tmp_reg) - iid;
44921708Sstevel 		clr_bits = 1 << offset;
44931708Sstevel 		if (scb_intr_regs[intr_idx] & clr_bits) {
44941708Sstevel 			/*
44951708Sstevel 			 * Must be newly inserted SCB
44961708Sstevel 			 * Time to re-initialize.
44971708Sstevel 			 */
44981708Sstevel 			if (scsb_debug & 0x00023000) {
44991708Sstevel 				cmn_err(CE_NOTE,
45007656SSherry.Moore@Sun.COM 				    "scsb_intr(%d): INIT_SCB INT",
45017656SSherry.Moore@Sun.COM 				    scsb->scsb_instance);
45021708Sstevel 			}
45031708Sstevel 			scsb_restore(scsb);
45041708Sstevel 			retval |= (SCSB_INTR_CLAIMED | SCSB_INTR_EVENT);
45051708Sstevel 			/*
45061708Sstevel 			 * The INTSRC bit will be cleared by the
45071708Sstevel 			 * scsb_restore() function.
45081708Sstevel 			 * Also, leave the bit set in scb_intr_regs[] so we can
45091708Sstevel 			 * report the event code as we check for other
45101708Sstevel 			 * interrupt source bits.
45111708Sstevel 			 *
45121708Sstevel 			 * scsb_write_mask(scsb, tmp_reg, 0, clr_bits, 0);
45131708Sstevel 			 * scb_intr_regs[intr_idx] &= ~clr_bits;
45141708Sstevel 			 */
45151708Sstevel 		}
45161708Sstevel 		/*
45171708Sstevel 		 * In case this is a power down interrupt, check the validity
45181708Sstevel 		 * of the request to make sure it's not an I2C noise
45191708Sstevel 		 */
45201708Sstevel 		offset = FRU_OFFSET(SCTRL_EVENT_PWRDWN,
45217656SSherry.Moore@Sun.COM 		    SCTRL_INTPTR_BASE);
45221708Sstevel 		clr_bits = 1 << offset;
45231708Sstevel 		intr_reg = scb_intr_regs[intr_idx];
45241708Sstevel 		if (intr_reg & clr_bits) {
45251708Sstevel 			/*
45261708Sstevel 			 * A shutdown request has been detected. Poll
45271708Sstevel 			 * the corresponding register ? more times to
45281708Sstevel 			 * make sure it's a genuine shutdown request.
45291708Sstevel 			 */
45301708Sstevel 			for (i = 0; i < scsb_shutdown_count; i++) {
45311708Sstevel 				drv_usecwait(1000);
45321708Sstevel 				if (scsb_rdwr_register(scsb, I2C_WR_RD, tmp_reg,
45337656SSherry.Moore@Sun.COM 				    1, &intr_reg, 1)) {
45341708Sstevel 					cmn_err(CE_WARN, "Failed to read "
45357656SSherry.Moore@Sun.COM 					    " interrupt register");
45361708Sstevel 					goto intr_error;
45371708Sstevel 				}
45381708Sstevel 				if (scsb_debug & 0x08000000) {
45391708Sstevel 					cmn_err(CE_NOTE, "scsb_intr: "
45407656SSherry.Moore@Sun.COM 					    " INTSRC6[%d]=0x%x", i,
45417656SSherry.Moore@Sun.COM 					    intr_reg);
45421708Sstevel 				}
45431708Sstevel 				if (!(intr_reg & clr_bits)) {
45441708Sstevel 					scb_intr_regs[intr_idx] &= ~clr_bits;
45451708Sstevel 					break;
45461708Sstevel 				}
45471708Sstevel 			}
45481708Sstevel 		}
45491708Sstevel 	}
45501708Sstevel 	/*
45511708Sstevel 	 * if retval == 0, then we didn't call scsb_restore,
45521708Sstevel 	 * so we update the shadow copy of SYSCFG registers
45531708Sstevel 	 * We *MUST* read the syscfg registers before any attempt
45541708Sstevel 	 * to clear the interrupt source registers is made.
45551708Sstevel 	 */
45561708Sstevel 	if (retval == 0 && scsb_check_config_status(scsb)) {
45571708Sstevel 		cmn_err(CE_WARN,
45587656SSherry.Moore@Sun.COM 		    "scsb_intr: Failed read of config/status registers");
45591708Sstevel 		if (scsb->scsb_state & SCSB_P06_NOINT_KLUGE) {
45601708Sstevel 			if (!scsb_debug) {
45611708Sstevel 				goto intr_error;
45621708Sstevel 			}
45631708Sstevel 		}
45641708Sstevel #ifdef DEBUG
45651708Sstevel 		if (scsb->scsb_state & SCSB_SSB_PRESENT &&
45667656SSherry.Moore@Sun.COM 		    scsb->scsb_i2c_errcnt > scsb_err_threshold) {
45671708Sstevel 			scsb_failing_event(scsb);
45681708Sstevel 		}
45691708Sstevel #endif
45701708Sstevel 		/*
45711708Sstevel 		 * Allow to go on so we clear the INTSRC bits
45721708Sstevel 		 */
45731708Sstevel 	}
45741708Sstevel 
45751708Sstevel 	/*
45761708Sstevel 	 * Read the board healthy registers here, if any of the healthy
45771708Sstevel 	 * interrupts are set.
45781708Sstevel 	 */
45791708Sstevel 	if (IS_SCB_P15) {
45801708Sstevel 		intr_idx = intr_reg = 0;
45811708Sstevel 		intr_addr = SCSB_REG_ADDR(SCTRL_INTSRC_BASE);
45821708Sstevel 		index = SCSB_REG_INDEX(intr_addr);
45831708Sstevel 		for (i = 0; i < SCTRL_BHLTHY_NUMREGS; ++i, ++intr_idx) {
45841708Sstevel 			scsb->scsb_data_reg[index++] =
45857656SSherry.Moore@Sun.COM 			    scb_intr_regs[intr_idx] & int_masks[intr_idx];
45861708Sstevel 			intr_reg |= scb_intr_regs[i];
45871708Sstevel 		}
45881708Sstevel 
45891708Sstevel 		if (intr_reg &&	scsb_read_bhealthy(scsb) != 0) {
45901708Sstevel 			cmn_err(CE_WARN, "%s#%d: Error Reading Healthy# "
45917656SSherry.Moore@Sun.COM 			    " Registers", ddi_driver_name(scsb->scsb_dev),
45927656SSherry.Moore@Sun.COM 			    ddi_get_instance(scsb->scsb_dev));
45931708Sstevel #ifdef DEBUG
45941708Sstevel 			if (scsb->scsb_state & SCSB_SSB_PRESENT &&
45957656SSherry.Moore@Sun.COM 			    scsb->scsb_i2c_errcnt > scsb_err_threshold) {
45961708Sstevel 				scsb_failing_event(scsb);
45971708Sstevel 			}
45981708Sstevel #endif
45991708Sstevel 			goto intr_error;
46001708Sstevel 		}
46011708Sstevel 	}
46021708Sstevel 
46031708Sstevel 	/*
46041708Sstevel 	 * We clear the interrupt source registers now itself so that
46051708Sstevel 	 * future interrupts can be latched quickly, instead of after
46061708Sstevel 	 * finishing processing of all interrupt conditions. The global
46071708Sstevel 	 * interrupt mask however remain disabled.
46081708Sstevel 	 */
46091708Sstevel 	if (IS_SCB_P15) {
46101708Sstevel 		if (scsb_rdwr_register(scsb, I2C_WR, intr_addr,
46117656SSherry.Moore@Sun.COM 		    SCTRL_INTR_NUMREGS, scb_intr_regs, 1)) {
46121708Sstevel 			cmn_err(CE_WARN, "scsb_intr: Failed write to interrupt"
46137656SSherry.Moore@Sun.COM 			    " registers.");
46141708Sstevel #ifdef DEBUG
46151708Sstevel 			if (scsb->scsb_state & SCSB_SSB_PRESENT &&
46167656SSherry.Moore@Sun.COM 			    scsb->scsb_i2c_errcnt > scsb_err_threshold) {
46171708Sstevel 				scsb_failing_event(scsb);
46181708Sstevel 			}
46191708Sstevel #endif
46201708Sstevel 			goto intr_error;
46211708Sstevel 		}
46221708Sstevel 	}
46231708Sstevel 
46241708Sstevel 	/*
46251708Sstevel 	 * At this point, all interrupt source registers are read.
46261708Sstevel 	 * We only handle interrups which are not masked
46271708Sstevel 	 */
46281708Sstevel 	for (i = 0; i < SCTRL_INTR_NUMREGS; ++i) {
46291708Sstevel 		scb_intr_regs[i] &= int_masks[i];
46301708Sstevel 	}
46311708Sstevel 
46321708Sstevel 	/*
46331708Sstevel 	 * We are here means that there was some bit set in the interrupt
46341708Sstevel 	 * source register. So we must claim the interrupt no matter
46351708Sstevel 	 * whatever error we may encounter in the course of processing.
46361708Sstevel 	 */
46371708Sstevel 	retval |= SCSB_INTR_CLAIMED;
46381708Sstevel 
46391708Sstevel 	/* store config status data */
46401708Sstevel 	tmp_reg = SCSB_REG_ADDR(SCTRL_SYSCFG_BASE);
46411708Sstevel 	index = SCSB_REG_INDEX(tmp_reg);
46421708Sstevel 	for (i = 0; i < SCTRL_CFG_NUMREGS; ++i)
46431708Sstevel 		cstatus_regs[i] = scsb->scsb_data_reg[index + i];
46441708Sstevel 	/*
46451708Sstevel 	 * Clear the event code,
46461708Sstevel 	 * then check to see what kind(s) of events we were interrupted for.
46471708Sstevel 	 * Check all SCTRL_INTSRC registers
46481708Sstevel 	 */
46491708Sstevel 	scsb_event_code = 0;
46501708Sstevel 	clr_bits = 0;
46511708Sstevel 	intr_idx = 0;
46521708Sstevel 	numregs = SCTRL_INTR_NUMREGS;
46531708Sstevel 	index = SCSB_REG_INDEX(intr_addr);
46541708Sstevel 	/*
46551708Sstevel 	 * If SCB 1.5, adjust some variables to skip the SCTRL_BHLTHY_REGS
46561708Sstevel 	 * which will be handled last in this function.
46571708Sstevel 	 */
46581708Sstevel 	if (IS_SCB_P15) {
46591708Sstevel 		i = SCTRL_BHLTHY_NUMREGS;
46601708Sstevel 		intr_idx += i;
46611708Sstevel 		intr_addr += i;
46621708Sstevel 		index += i;
46631708Sstevel 	}
46641708Sstevel 	/*
46651708Sstevel 	 * For the rest of the INTSRC registers, we walk through the
46661708Sstevel 	 * scb_fru_offset[] table, matching register offsets with our offset
46671708Sstevel 	 * counter.  Then we check for the scb_fru_offset[] bit in intr_reg.
46681708Sstevel 	 * The scb_fru_offset[] index is now the SCTRL_EVENT code.
46691708Sstevel 	 * The code is then compared to type_to_code1[] entries to find the
46701708Sstevel 	 * fru_type.  The fru_type will help us recognize when to do
46711708Sstevel 	 * SLOT Hot Swap processing.
46721708Sstevel 	 *
46731708Sstevel 	 * offset_base:		the appropriate scb_fru_offset[] base index
46741708Sstevel 	 *			for the INTPTR_BASE register group
46751708Sstevel 	 * offset:		bit offset found in INTSRC register
46761708Sstevel 	 * intr_idx:		index to temporary INTSRC register copies
46771708Sstevel 	 * intr:		modified copy of current INTR register
46781708Sstevel 	 * intr_addr:		SCB register address of current INTR register
46791708Sstevel 	 * index:		index to current INTR shadow register
46801708Sstevel 	 * idx:			bit-number of current INTR event bit
46811708Sstevel 	 * uc:			uchar_t from scb_fru_offset[] table,
46821708Sstevel 	 *			containing register and FRU offsets.
46831708Sstevel 	 * j:			used to walk fru_offset[] table, which is also
46841708Sstevel 	 *			the bit-number of the current event code
46851708Sstevel 	 * code:		manufactured event code for current INT event
46861708Sstevel 	 */
46871708Sstevel 	offset_base = FRU_OFFSET_BASE(SCTRL_INTPTR_BASE);
46881708Sstevel 	for (offset = 0; intr_idx < numregs;
46897656SSherry.Moore@Sun.COM 	    ++offset, ++intr_idx, ++intr_addr, ++index) {
46901708Sstevel 		scsb->scsb_data_reg[index] = scb_intr_regs[intr_idx];
46911708Sstevel 		intr_reg = scb_intr_regs[intr_idx];
46921708Sstevel 		while (intr_reg) {	/* for each INTSRC bit that's set */
46931708Sstevel 			int		j;
46941708Sstevel 			uint16_t	ui;
46951708Sstevel 			uchar_t		uc;
46961708Sstevel 			idx = event_to_index((uint32_t)intr_reg); /* offset */
46971708Sstevel 			code = (1 << idx);		/* back to bit mask */
46981708Sstevel 			clr_bits |= code;
46991708Sstevel 			intr_reg = intr_reg & ~code;	/* clear this one   */
47001708Sstevel 			for (j = 0; j < MCT_MAX_FRUS; ++j) {
47011708Sstevel 				/*
47021708Sstevel 				 * Get register offset from table and check
47031708Sstevel 				 * for a match with our loop offset counter.
47041708Sstevel 				 * Then check for intr_reg bit-offset match
47051708Sstevel 				 * with bit-offset from table entry.
47061708Sstevel 				 */
47071708Sstevel 				uc = scb_fru_offset[offset_base + j];
47081708Sstevel 				if (offset != ((uc >> 4) & 0xf)) {
47091708Sstevel 					if (IS_SCB_P10)
47101708Sstevel 						continue;
47111708Sstevel 					if (j != FRU_INDEX(SCTRL_EVENT_SCB))
47121708Sstevel 						continue;
47131708Sstevel 					if (offset != ((uc >> 4) & 0xf)
47147656SSherry.Moore@Sun.COM 					    + SCB_INT_OFFSET)
47151708Sstevel 						continue;
47161708Sstevel 				}
47171708Sstevel 				if (idx == (uc & 0xf))
47181708Sstevel 					break;
47191708Sstevel 			}
47201708Sstevel 			if (uc == 0xff) {
47211708Sstevel 				/*
47221708Sstevel 				 * bit idx not recognized, check another.
47231708Sstevel 				 */
47241708Sstevel 				continue;
47251708Sstevel 			}
47261708Sstevel 			/*
47271708Sstevel 			 * We found the fru_offset[] entry, now use the index
47281708Sstevel 			 * to get the event code.
47291708Sstevel 			 */
47301708Sstevel 			code = (uint32_t)(1 << j);
47311708Sstevel 			if (scsb_debug & 0x00002000) {
47321708Sstevel 				cmn_err(CE_NOTE, "scsb_intr: code=0x%x", code);
47331708Sstevel 			}
47341708Sstevel 			/*
47351708Sstevel 			 * Now check for the NON-FRU type events.
47361708Sstevel 			 */
47371708Sstevel 			if (code ==  SCTRL_EVENT_PWRDWN) {
47381708Sstevel 				if (scsb_debug & 0x1002) {
47391708Sstevel 					cmn_err(CE_NOTE,
47407656SSherry.Moore@Sun.COM 					    "scsb_intr(%d): power down req."
47417656SSherry.Moore@Sun.COM 					    " INT.", scsb->scsb_instance);
47421708Sstevel 				}
47431708Sstevel 				scsb_event_code |= code;
47441708Sstevel 				if (scsb->scsb_state & SCSB_OPEN &&
47457656SSherry.Moore@Sun.COM 				    scsb->scsb_rq != (queue_t *)NULL) {
47461708Sstevel 					/*
47471708Sstevel 					 * inform applications using poll(2)
47481708Sstevel 					 * about this event, and provide the
47491708Sstevel 					 * event code to EnvMon scsb policy
47501708Sstevel 					 */
47511708Sstevel 					if (!(scsb_debug & 0x00040000))
47521708Sstevel 					(void) scsb_queue_put(scsb->scsb_rq, 1,
47537656SSherry.Moore@Sun.COM 					    &scsb_event_code, "scsb_intr");
47541708Sstevel 					goto intr_error;
47551708Sstevel 				}
47561708Sstevel 				continue;
47571708Sstevel 			} else if (code == SCTRL_EVENT_REPLACE) {
47581708Sstevel 				if (scsb_debug & 0x1002) {
47591708Sstevel 					cmn_err(CE_NOTE,
47607656SSherry.Moore@Sun.COM 					    "scsb_intr(%d): replacement "
47617656SSherry.Moore@Sun.COM 					    "req. INT.",
47627656SSherry.Moore@Sun.COM 					    scsb->scsb_instance);
47631708Sstevel 				}
47641708Sstevel 				scsb_freeze_check(scsb);
47651708Sstevel 				scsb_freeze(scsb);
47661708Sstevel 				scsb_event_code |= code;
47671708Sstevel 				retval |= (SCSB_INTR_CLAIMED | SCSB_INTR_EVENT);
47681708Sstevel 				continue;
47691708Sstevel 			} else if (code == SCTRL_EVENT_SCB) {
47701708Sstevel 				int	tmp;
47711708Sstevel 				/*
47721708Sstevel 				 * Must be newly inserted SCB
47731708Sstevel 				 * Time to re-initialize.
47741708Sstevel 				 */
47751708Sstevel 				if (scsb_debug & 0x1002) {
47761708Sstevel 					cmn_err(CE_NOTE,
47777656SSherry.Moore@Sun.COM 					    "scsb_intr(%d): INIT SCB INTR",
47787656SSherry.Moore@Sun.COM 					    scsb->scsb_instance);
47791708Sstevel 				}
47801708Sstevel 				/*
47811708Sstevel 				 * SCB initialization already handled, but we
47821708Sstevel 				 * set the event code bit here in order to
47831708Sstevel 				 * report the event to interested utilities.
47841708Sstevel 				 *
47851708Sstevel 				 * scsb_restore(scsb);
47861708Sstevel 				 * The INTSRC bit is already cleared,
47871708Sstevel 				 * so we won't do it again.
47881708Sstevel 				 */
47891708Sstevel 				tmp = FRU_OFFSET(SCTRL_EVENT_SCB,
47907656SSherry.Moore@Sun.COM 				    SCTRL_INTPTR_BASE);
47911708Sstevel 				clr_bits &= ~(1 << tmp);
47921708Sstevel 				scsb_event_code |= code;
47931708Sstevel 				retval |= (SCSB_INTR_CLAIMED | SCSB_INTR_EVENT);
47941708Sstevel 				continue;
47951708Sstevel 			} else if (code == SCTRL_EVENT_ALARM_INT) {
47961708Sstevel 				/*
47971708Sstevel 				 * P0.6/P1.0: SCTRL_INTR_ALARM_INT is always
47981708Sstevel 				 * set and cannot be cleared, so ignore it.
47991708Sstevel 				 */
48001708Sstevel 				if (!IS_SCB_P15) {
48011708Sstevel 					continue;
48021708Sstevel 				}
48031708Sstevel 				if (scsb_debug & 0x1002) {
48041708Sstevel 					cmn_err(CE_NOTE,
48057656SSherry.Moore@Sun.COM 					    "scsb_intr(%d): Alarm INT.",
48067656SSherry.Moore@Sun.COM 					    scsb->scsb_instance);
48071708Sstevel 				}
48081708Sstevel 				scsb_event_code |= code;
48091708Sstevel 				retval |= (SCSB_INTR_CLAIMED | SCSB_INTR_EVENT);
48101708Sstevel 				/*
48111708Sstevel 				 * XXX:
48121708Sstevel 				 * Must service the Alarm INT by clearing INT
48131708Sstevel 				 * condition on Alarm Card,
48141708Sstevel 				 * then clear the SCTRL_INTR_ALARM_INT bit here.
48151708Sstevel 				 * Waiting for specs and test environment.
48161708Sstevel 				 */
48171708Sstevel 				continue;
48181708Sstevel 			} else if ((ui = event_to_type(code)) == 0xffff) {
48191708Sstevel 				/*
48201708Sstevel 				 * FRU type not found
48211708Sstevel 				 */
48221708Sstevel 				break;
48231708Sstevel 			}
48241708Sstevel 			/*
48251708Sstevel 			 * Check for special processing
48261708Sstevel 			 * now that we found the FRU type.
48271708Sstevel 			 */
48281708Sstevel 			fru_type = (scsb_utype_t)(ui & 0xff);
48291708Sstevel 			unit = (ui >> 8) & 0xff;
48301708Sstevel 			if (scsb_debug & 0x00002000) {
48311708Sstevel 				cmn_err(CE_NOTE, "scsb_intr: "
48327656SSherry.Moore@Sun.COM 				    "FRU type/unit/code %d/%d/0x%x",
48337656SSherry.Moore@Sun.COM 				    fru_type, unit, code);
48341708Sstevel 			}
48351708Sstevel 			switch (fru_type) {
48361708Sstevel 			case PDU:
48371708Sstevel 				break;
48381708Sstevel 			case PS:
48391708Sstevel 				break;
48401708Sstevel 			case DISK:
48411708Sstevel 				break;
48421708Sstevel 			case FAN:
48431708Sstevel 				break;
48441708Sstevel 			case SSB:
48451708Sstevel 				/*
48461708Sstevel 				 * in check_fru_info() below, we see if the
48471708Sstevel 				 * SSB has been removed, then check for
48481708Sstevel 				 * occupied slots in reset to see if we should
48491708Sstevel 				 * WARN agains SCB removal
48501708Sstevel 				 */
48511708Sstevel 				break;
48521708Sstevel 			case CFTM:
48531708Sstevel 				break;
48541708Sstevel 			case CRTM:
48551708Sstevel 				break;
48561708Sstevel 			case PRTM:
48571708Sstevel 				break;
48581708Sstevel 			case SLOT:
48591708Sstevel 				slotnum = tonga_ssl_to_psl(scsb, unit);
48601708Sstevel 				if (scsb_debug & 0x00002000) {
48611708Sstevel 					cmn_err(CE_NOTE, "scsb_intr: "
48627656SSherry.Moore@Sun.COM 					    "unit/slot %d/%d",
48637656SSherry.Moore@Sun.COM 					    unit, slotnum);
48641708Sstevel 				}
48651708Sstevel 
48661708Sstevel 				/*
48671708Sstevel 				 * If the slot number is not valid, continue.
48681708Sstevel 				 */
48691708Sstevel 				if (scsb->scsb_state & SCSB_IS_TONGA) {
48701708Sstevel 					if (slotnum > TG_MAX_SLOTS ||
48717656SSherry.Moore@Sun.COM 					    slotnum == SC_TG_CPU_SLOT) {
48721708Sstevel 						continue;
48731708Sstevel 					}
48741708Sstevel 					/*
48751708Sstevel 					 * For a tonga, we need to return
48761708Sstevel 					 * the code corresponding to the
48771708Sstevel 					 * actual physical slot
48781708Sstevel 					 */
48791708Sstevel 					code = FRU_UNIT_TO_EVCODE(SLOT,
48807656SSherry.Moore@Sun.COM 					    slotnum);
48811708Sstevel 				} else {
48821708Sstevel 					if (slotnum > MC_MAX_SLOTS ||
48837656SSherry.Moore@Sun.COM 					    slotnum == SC_MC_CPU_SLOT ||
48847656SSherry.Moore@Sun.COM 					    (scsb->scsb_hsc_state &
48857656SSherry.Moore@Sun.COM 					    SCSB_HSC_CTC_PRES &&
48867656SSherry.Moore@Sun.COM 					    slotnum == SC_MC_CTC_SLOT)) {
48871708Sstevel 						continue;
48881708Sstevel 					}
48891708Sstevel 				}
48901708Sstevel 			/* FALLTHROUGH */
48911708Sstevel 			case ALARM:
48921708Sstevel 		/*
48931708Sstevel 		 * INDENT CHEATING, 2 indentations
48941708Sstevel 		 */
48951708Sstevel 		ac_present = 0;
48961708Sstevel 		/*
48971708Sstevel 		 * If it is an Alarm Card Interrupt, we just do some sanity
48981708Sstevel 		 * checks and then wait for the slot interrupt to take
48991708Sstevel 		 * connect or disconnect action.
49001708Sstevel 		 * XXX - Is there a gaurantee that ALARM int will occur first ?
49011708Sstevel 		 */
49021708Sstevel 		if (fru_type == ALARM) {
49031708Sstevel 			DEBUG2("AC Intr %d(%d)\n", scsb->ac_slotnum, idx+1);
49041708Sstevel 			val = scsb_fru_op(scsb, SLOT,
49057656SSherry.Moore@Sun.COM 			    tonga_ssl_to_psl(scsb, scsb->ac_slotnum),
49067656SSherry.Moore@Sun.COM 			    SCTRL_SYSCFG_BASE, SCSB_FRU_OP_GET_BITVAL);
49071708Sstevel 			ac_present = scsb_fru_op(scsb, ALARM, 1,
49087656SSherry.Moore@Sun.COM 			    SCTRL_SYSCFG_BASE,
49097656SSherry.Moore@Sun.COM 			    SCSB_FRU_OP_GET_BITVAL);
49101708Sstevel 			/*
49111708Sstevel 			 * It is observed that slot presence and Alarm
49121708Sstevel 			 * presence bits do not go ON at the same time.
49131708Sstevel 			 * Hence we wait till both events happen.
49141708Sstevel 			 */
49151708Sstevel #ifdef DEBUG
49161708Sstevel 			if ((((val) && (!ac_present)) ||
49177656SSherry.Moore@Sun.COM 			    ((!val) && (ac_present))) &&
49187656SSherry.Moore@Sun.COM 			    (scsb->scsb_hsc_state &
49197656SSherry.Moore@Sun.COM 			    SCSB_AC_SLOT_INTR_DONE))
49201708Sstevel 
49211708Sstevel 				cmn_err(CE_WARN, "?Alarm and Slot presence "
49227656SSherry.Moore@Sun.COM 				    "state bits do not match! (%x,%x)",
49237656SSherry.Moore@Sun.COM 				    val, ac_present);
49241708Sstevel #endif
49251708Sstevel 			if (scsb->scsb_hsc_state & SCSB_AC_SLOT_INTR_DONE)
49261708Sstevel 				scsb->scsb_hsc_state &= ~SCSB_AC_SLOT_INTR_DONE;
49271708Sstevel 			else
49281708Sstevel 				scsb->scsb_hsc_state |= SCSB_AC_SLOT_INTR_DONE;
49291708Sstevel 			break;	/* we break and wait for slot interrupt. */
49301708Sstevel 		}
49311708Sstevel 
49321708Sstevel 		/*
49331708Sstevel 		 * cPCI slot interrupt event
49341708Sstevel 		 */
49351708Sstevel 		if (scsb->scsb_state & SCSB_IS_TONGA) {
49361708Sstevel 			if (slotnum > TG_MAX_SLOTS ||
49377656SSherry.Moore@Sun.COM 			    slotnum == SC_TG_CPU_SLOT) {
49381708Sstevel 				continue;
49391708Sstevel 			}
49401708Sstevel 		} else {
49411708Sstevel 			if (slotnum > MC_MAX_SLOTS ||
49427656SSherry.Moore@Sun.COM 			    slotnum == SC_MC_CPU_SLOT ||
49437656SSherry.Moore@Sun.COM 			    (scsb->scsb_hsc_state & SCSB_HSC_CTC_PRES &&
49447656SSherry.Moore@Sun.COM 			    slotnum == SC_MC_CTC_SLOT)) {
49451708Sstevel 				continue;
49461708Sstevel 			}
49471708Sstevel 		}
49481708Sstevel 		if (scsb_is_alarm_card_slot(scsb, slotnum) == B_TRUE) {
49491708Sstevel 			DEBUG2("AC slot Intr %d(%d)\n", slotnum, idx+1);
49501708Sstevel 			ac_slot = B_TRUE;
49511708Sstevel 		}
49521708Sstevel 		val = scsb_fru_op(scsb, SLOT, unit, SCTRL_SYSCFG_BASE,
49537656SSherry.Moore@Sun.COM 		    SCSB_FRU_OP_GET_BITVAL);
49541708Sstevel 		if (ac_slot == B_TRUE) {
49551708Sstevel 			ac_present = scsb_fru_op(scsb, ALARM, 1,
49567656SSherry.Moore@Sun.COM 			    SCTRL_SYSCFG_BASE,
49577656SSherry.Moore@Sun.COM 			    SCSB_FRU_OP_GET_BITVAL);
49581708Sstevel #ifdef DEBUG
49591708Sstevel 			if ((((val) && (!ac_present)) ||
49607656SSherry.Moore@Sun.COM 			    ((!val) && (ac_present))) &&
49617656SSherry.Moore@Sun.COM 			    (scsb->scsb_hsc_state &
49627656SSherry.Moore@Sun.COM 			    SCSB_AC_SLOT_INTR_DONE)) {
49631708Sstevel 
49641708Sstevel 				cmn_err(CE_WARN, "?Alarm and Slot presence "
49657656SSherry.Moore@Sun.COM 				    "state bits do not match! (%x,%x)",
49667656SSherry.Moore@Sun.COM 				    val, ac_present);
49671708Sstevel 			}
49681708Sstevel #endif
49691708Sstevel 			if (scsb->scsb_hsc_state & SCSB_AC_SLOT_INTR_DONE)
49701708Sstevel 				scsb->scsb_hsc_state &= ~SCSB_AC_SLOT_INTR_DONE;
49711708Sstevel 			else
49721708Sstevel 				scsb->scsb_hsc_state |= SCSB_AC_SLOT_INTR_DONE;
49731708Sstevel 		}
49741708Sstevel 		if (val) {
49751708Sstevel 			if (ac_present) {
49761708Sstevel 				DEBUG1("AC insertion on slot %d!\n", slotnum);
49771708Sstevel 				if (scsb_debug & 0x00010000) {
49781708Sstevel 					cmn_err(CE_NOTE, "scsb_intr: "
49797656SSherry.Moore@Sun.COM 					"AC_PRES slot %d", slotnum);
49801708Sstevel 				}
49811708Sstevel 				scsb->scsb_hsc_state |= SCSB_ALARM_CARD_PRES;
49821708Sstevel 			}
49831708Sstevel #ifndef	lint
49841708Sstevel 			else
49851708Sstevel 				DEBUG1("IO Insertion on slot %d!\n", slotnum);
49861708Sstevel #endif
49871708Sstevel 			/*
49881708Sstevel 			 * Special case : check MPID type.
49891708Sstevel 			 * If MC midplane type,
49901708Sstevel 			 * check to make sure the Alarm Card present
49911708Sstevel 			 * bit is ON. If not, this is a regular IO card.
49921708Sstevel 			 */
49931708Sstevel 			(void) scsb_connect_slot(scsb, slotnum, B_FALSE);
49941708Sstevel 		} else {
49951708Sstevel 			if ((ac_slot == B_TRUE) &&
49967656SSherry.Moore@Sun.COM 			    (scsb->scsb_hsc_state & SCSB_ALARM_CARD_PRES)) {
49971708Sstevel 
49981708Sstevel 				DEBUG1("AC Removal on slot %d!\n", slotnum);
49991708Sstevel #ifdef DEBUG
50001708Sstevel 				if (scsb_debug & 0x00010000) {
50011708Sstevel 					cmn_err(CE_NOTE, "scsb_intr: "
50027656SSherry.Moore@Sun.COM 					    "!AC_PRES slot %d",
50037656SSherry.Moore@Sun.COM 					    slotnum);
50041708Sstevel 				}
50051708Sstevel #endif /* DEBUG */
50061708Sstevel 				scsb->scsb_hsc_state &= ~SCSB_ALARM_CARD_PRES;
50071708Sstevel 			}
50081708Sstevel #ifndef	lint
50091708Sstevel 			else
50101708Sstevel 				DEBUG1("IO Removal on slot %d!\n", slotnum);
50111708Sstevel #endif
50121708Sstevel 			(void) scsb_disconnect_slot(scsb, B_FALSE, slotnum);
50131708Sstevel 		}
50141708Sstevel 		/*
50151708Sstevel 		 * END INDENT CHEATING, 2 indentations
50161708Sstevel 		 */
50171708Sstevel 
50181708Sstevel 				break;
50191708Sstevel 			default:
50201708Sstevel 				/*
50211708Sstevel 				 * ERROR: Did not find cause of INTSRC bit
50221708Sstevel 				 */
50231708Sstevel 				if (scsb_debug & 0x00000002) {
50241708Sstevel 					cmn_err(CE_WARN,
50257656SSherry.Moore@Sun.COM 					    "scsb_intr: FRU type %d"
50267656SSherry.Moore@Sun.COM 					    " not recognized", fru_type);
50271708Sstevel 				}
50281708Sstevel 				continue;
50291708Sstevel 			}
50301708Sstevel 			scsb_event_code |= code;
50311708Sstevel 			retval |= (SCSB_INTR_CLAIMED | SCSB_INTR_EVENT);
50321708Sstevel 			if (fru_type == SLOT)
50331708Sstevel 				continue;
50341708Sstevel 			error = 0;
50351708Sstevel 			fru_ptr = mct_system_info.fru_info_list[fru_type];
50361708Sstevel 			for (; fru_ptr != NULL; fru_ptr = fru_ptr->next) {
50371708Sstevel 				if (unit != fru_ptr->fru_unit)
50381708Sstevel 					continue;
50391708Sstevel 				if (fru_ptr->i2c_info == NULL ||
50407656SSherry.Moore@Sun.COM 				    (tmp_reg = fru_ptr->i2c_info->
50417656SSherry.Moore@Sun.COM 				    ledata_reg) == 0)
50421708Sstevel 					continue;
50431708Sstevel 				error = scsb_set_scfg_pres_leds(scsb, fru_ptr);
50441708Sstevel 				if (error) {
50451708Sstevel 					cmn_err(CE_WARN, "scsb_intr(): "
50467656SSherry.Moore@Sun.COM 					    "I2C write error to 0x%x",
50477656SSherry.Moore@Sun.COM 					    tmp_reg);
50481708Sstevel 					if (!(scsb->scsb_state &
50497656SSherry.Moore@Sun.COM 					    SCSB_DEBUG_MODE)) {
50501708Sstevel 						goto intr_error;
50511708Sstevel 					}
50521708Sstevel 				}
50531708Sstevel 				break;
50541708Sstevel 			}
50551708Sstevel 		}
50561708Sstevel 		if (clr_bits) {
50571708Sstevel 			clr_bits = 0;
50581708Sstevel 		}
50591708Sstevel 	}
50601708Sstevel 	/*
50611708Sstevel 	 * Check for SCB 1.5 interrupt for SLOT HEALTHY changes
50621708Sstevel 	 */
50631708Sstevel 	clr_bits = 0;
50641708Sstevel 	intr_idx = 0;
50651708Sstevel 	numregs = SCTRL_INTR_NUMREGS;
50661708Sstevel 	intr_addr = SCSB_REG_ADDR(SCTRL_INTSRC_BASE);
50671708Sstevel 	index = SCSB_REG_INDEX(intr_addr);
50681708Sstevel 	if (IS_SCB_P15) {
50691708Sstevel 		for (i = 0; i < SCTRL_BHLTHY_NUMREGS;
50707656SSherry.Moore@Sun.COM 		    ++i, ++intr_idx, ++intr_addr) {
50711708Sstevel 			scsb->scsb_data_reg[index++] = scb_intr_regs[intr_idx];
50721708Sstevel 			intr_reg = scb_intr_regs[i];
50731708Sstevel 			while (intr_reg) {
50741708Sstevel 				idx = event_to_index((uint32_t)intr_reg);
50751708Sstevel 				code = (1 << idx);
50761708Sstevel 				clr_bits |= code;
50771708Sstevel 				intr_reg = intr_reg & ~code;
50781708Sstevel 				/* idx + 1 because bit 0 is for Slot 1 */
50791708Sstevel 				slotnum = tonga_ssl_to_psl(scsb, idx + 1);
50801708Sstevel 				if (scsb->scsb_state & SCSB_IS_TONGA) {
50811708Sstevel 					if (slotnum > TG_MAX_SLOTS ||
50827656SSherry.Moore@Sun.COM 					    slotnum == SC_TG_CPU_SLOT) {
50831708Sstevel 						continue;
50841708Sstevel 					}
50851708Sstevel 				} else {
50861708Sstevel 					if (slotnum > MC_MAX_SLOTS ||
50877656SSherry.Moore@Sun.COM 					    slotnum == SC_MC_CPU_SLOT ||
50887656SSherry.Moore@Sun.COM 					    (scsb->scsb_hsc_state &
50897656SSherry.Moore@Sun.COM 					    SCSB_HSC_CTC_PRES &&
50907656SSherry.Moore@Sun.COM 					    slotnum == SC_MC_CTC_SLOT)) {
50911708Sstevel 						continue;
50921708Sstevel 					}
50931708Sstevel 				}
50941708Sstevel 				scsb_healthy_intr(scsb, slotnum);
50951708Sstevel 			}
50961708Sstevel 			if (clr_bits) {
50971708Sstevel 				clr_bits = 0;
50981708Sstevel 			}
50991708Sstevel 		}
51001708Sstevel 	}
51011708Sstevel 	code = scsb_event_code;
51021708Sstevel 	if (retval & SCSB_INTR_EVENT &&
51037656SSherry.Moore@Sun.COM 	    !(scsb->scsb_state & SCSB_P06_NOINT_KLUGE)) {
51041708Sstevel 		check_fru_info(scsb, code);
51051708Sstevel 		add_event_code(scsb, code);
51061708Sstevel 		(void) scsb_queue_ops(scsb, QPUT_INT32, 1, &scsb_event_code,
51077656SSherry.Moore@Sun.COM 		"scsb_intr");
51081708Sstevel 	}
51091708Sstevel intr_error:
51101708Sstevel 	scb_post_e = gethrtime();
51111708Sstevel 
51121708Sstevel 	if (scsb_debug & 0x8000000)
51131708Sstevel 		cmn_err(CE_NOTE, "Summary of times in nsec: pre_time %llu, \
51141708Sstevel 			post_time %llu", scb_pre_e - scb_pre_s,
51157656SSherry.Moore@Sun.COM 		    scb_post_e - scb_post_s);
51161708Sstevel 
51171708Sstevel 
51181708Sstevel 	mutex_enter(&scsb->scsb_mutex);
51191708Sstevel 	scsb_in_postintr = 0;
51201708Sstevel 	cv_broadcast(&scsb->scsb_cv);
51211708Sstevel 	mutex_exit(&scsb->scsb_mutex);
51221708Sstevel 
51231708Sstevel 	/*
51241708Sstevel 	 * Re-enable interrupt now.
51251708Sstevel 	 */
5126*11311SSurya.Prakki@Sun.COM 	(void) scsb_toggle_psmint(scsb, 1);
51271708Sstevel 	scsb->scsb_state &= ~SCSB_IN_INTR;
51281708Sstevel }
51291708Sstevel 
51301708Sstevel static int
scsb_polled_int(scsb_state_t * scsb,int cmd,uint32_t * set)51311708Sstevel scsb_polled_int(scsb_state_t *scsb, int cmd, uint32_t *set)
51321708Sstevel {
51331708Sstevel 	if (scsb_debug & 0x4000)
51341708Sstevel 		cmn_err(CE_NOTE, "scsb_polled_int(scsb,0x%x)", cmd);
51351708Sstevel 	*set = 0;
51361708Sstevel 	if (cmd == SCSBIOC_SHUTDOWN_POLL) {
51371708Sstevel 		return (EINVAL);
51381708Sstevel 	}
51391708Sstevel 	if (cmd != SCSBIOC_INTEVENT_POLL) {
51401708Sstevel 		return (EINVAL);
51411708Sstevel 	}
51421708Sstevel 	if (scsb->scsb_state & SCSB_P06_NOINT_KLUGE) {
51431708Sstevel 		/*
51441708Sstevel 		 * scsb_intr() may modify scsb_event_code
51451708Sstevel 		 */
51461708Sstevel 		scsb_event_code = SCTRL_EVENT_NONE;
51471708Sstevel 		(void) scsb_intr((caddr_t)scsb);
51481708Sstevel 		*set = scsb_event_code;
51491708Sstevel 		scsb_event_code = 0;
51501708Sstevel 	} else {
51511708Sstevel 		/*
51521708Sstevel 		 * SCSB_P06_INTR_ON, we know there was an event
51531708Sstevel 		 * and we're retrieving the event code from the event FIFO.
51541708Sstevel 		 */
51551708Sstevel 		*set = get_event_code();
51561708Sstevel 	}
51571708Sstevel 	if (scsb_debug & 0x01004000) {
51581708Sstevel 		cmn_err(CE_NOTE, "scsb_polled_int: event_code = 0x%x", *set);
51591708Sstevel 	}
51601708Sstevel 	return (0);
51611708Sstevel }
51621708Sstevel 
51631708Sstevel static int
scsb_leds_switch(scsb_state_t * scsb,scsb_ustate_t op)51641708Sstevel scsb_leds_switch(scsb_state_t *scsb, scsb_ustate_t op)
51651708Sstevel {
51661708Sstevel 	register int 	i;
51671708Sstevel 	int		index;
51681708Sstevel 	uchar_t		reg, idata, rwbuf[SCTRL_MAX_GROUP_NUMREGS];
51691708Sstevel 
51701708Sstevel 	if (scsb->scsb_state & SCSB_FROZEN &&
51717656SSherry.Moore@Sun.COM 	    !(scsb->scsb_state & SCSB_IN_INTR)) {
51721708Sstevel 		return (EAGAIN);
51731708Sstevel 	}
51741708Sstevel 	if (scsb_debug & 0x0101) {
51751708Sstevel 		cmn_err(CE_NOTE, "scsb_leds_switch(%s):",
51767656SSherry.Moore@Sun.COM 		    op == ON ? "ON" : "OFF");
51771708Sstevel 	}
51781708Sstevel 	/* Step 1: turn ON/OFF all NOK LEDs. */
51791708Sstevel 	if (scsb_debug & 0x0100) {
51801708Sstevel 		cmn_err(CE_NOTE, "scsb%d: turning all NOK LEDs %s",
51817656SSherry.Moore@Sun.COM 		    scsb->scsb_instance,
51827656SSherry.Moore@Sun.COM 		    op == ON ? "ON" : "OFF");
51831708Sstevel 	}
51841708Sstevel 	if (op == ON)
51851708Sstevel 		idata = 0xff;
51861708Sstevel 	else	/* off */
51871708Sstevel 		idata = 0x00;
51881708Sstevel 	reg = SCSB_REG_ADDR(SCTRL_LED_NOK_BASE);
51891708Sstevel 	index = SCSB_REG_INDEX(reg);
51901708Sstevel 	for (i = 0; i < SCTRL_LED_NOK_NUMREGS;  ++i) {
51911708Sstevel 		rwbuf[i] = idata;
51921708Sstevel 		scsb->scsb_data_reg[index + i] = idata;
51931708Sstevel 	}
51941708Sstevel 	mutex_enter(&scsb->scsb_mutex);
51951708Sstevel 	i = scsb_rdwr_register(scsb, I2C_WR, reg, SCTRL_LED_NOK_NUMREGS,
51967656SSherry.Moore@Sun.COM 	    rwbuf, 1);
51971708Sstevel 	mutex_exit(&scsb->scsb_mutex);
51981708Sstevel 	if (i) {
51991708Sstevel 		if (scsb_debug & 0x0102)
52001708Sstevel 			cmn_err(CE_WARN, "scsb_leds_switch(): "
52017656SSherry.Moore@Sun.COM 			    "Failed to turn %s NOK LEDs",
52027656SSherry.Moore@Sun.COM 			    op == ON ? "ON" : "OFF");
52031708Sstevel 	}
52041708Sstevel 	/* Step 2: turn ON/OFF all OK LEDs. */
52051708Sstevel 	if (scsb_debug & 0x0100) {
52061708Sstevel 		cmn_err(CE_NOTE, "scsb%d: turning all OK LEDs %s",
52077656SSherry.Moore@Sun.COM 		    scsb->scsb_instance,
52087656SSherry.Moore@Sun.COM 		    op == ON ? "ON" : "OFF");
52091708Sstevel 	}
52101708Sstevel 	reg = SCSB_REG_ADDR(SCTRL_LED_OK_BASE);
52111708Sstevel 	index = SCSB_REG_INDEX(reg);
52121708Sstevel 	for (i = 0; i < SCTRL_LED_OK_NUMREGS;  ++i) {
52131708Sstevel 		rwbuf[i] = idata;
52141708Sstevel 		scsb->scsb_data_reg[index + i] = idata;
52151708Sstevel 	}
52161708Sstevel 	mutex_enter(&scsb->scsb_mutex);
52171708Sstevel 	i = scsb_rdwr_register(scsb, I2C_WR, reg, SCTRL_LED_OK_NUMREGS,
52187656SSherry.Moore@Sun.COM 	    rwbuf, 1);
52191708Sstevel 	mutex_exit(&scsb->scsb_mutex);
52201708Sstevel 	if (i) {
52211708Sstevel 		if (scsb_debug & 0x0102)
52221708Sstevel 			cmn_err(CE_WARN, "scsb_leds_switch(): "
52237656SSherry.Moore@Sun.COM 			    "Failed to turn %s NOK LEDs",
52247656SSherry.Moore@Sun.COM 			    op == ON ? "ON" : "OFF");
52251708Sstevel 	}
52261708Sstevel 	/* Step 3: turn OFF all BLINK LEDs. */
52271708Sstevel 	if (op == OFF) {
52281708Sstevel 		reg = SCSB_REG_ADDR(SCTRL_BLINK_OK_BASE);
52291708Sstevel 		index = SCSB_REG_INDEX(reg);
52301708Sstevel 		for (i = 0; i < SCTRL_BLINK_NUMREGS;  ++i) {
52311708Sstevel 			rwbuf[i] = idata;
52321708Sstevel 			scsb->scsb_data_reg[index + i] = idata;
52331708Sstevel 		}
52341708Sstevel 		mutex_enter(&scsb->scsb_mutex);
52351708Sstevel 		i = scsb_rdwr_register(scsb, I2C_WR, reg, SCTRL_BLINK_NUMREGS,
52367656SSherry.Moore@Sun.COM 		    rwbuf, 1);
52371708Sstevel 		mutex_exit(&scsb->scsb_mutex);
52381708Sstevel 		if (i) {
52391708Sstevel 			if (scsb_debug & 0x0102)
52401708Sstevel 				cmn_err(CE_WARN, "scsb_leds_switch(): "
52417656SSherry.Moore@Sun.COM 				    "Failed to turn %s BLINK BITs",
52427656SSherry.Moore@Sun.COM 				    op == ON ? "ON" : "OFF");
52431708Sstevel 		}
52441708Sstevel 	}
52451708Sstevel 	return (0);
52461708Sstevel }
52471708Sstevel 
52481708Sstevel static int
scsb_readall_regs(scsb_state_t * scsb)52491708Sstevel scsb_readall_regs(scsb_state_t *scsb)
52501708Sstevel {
52511708Sstevel 	int		error;
52521708Sstevel 	int		index;
52531708Sstevel 	uchar_t		reg;
52541708Sstevel 
52551708Sstevel 	if (!(scsb_debug & 0x40000000))
52561708Sstevel 		return (0);
52571708Sstevel 	if (scsb_debug & 0x0005) {
52581708Sstevel 		cmn_err(CE_NOTE, "scsb_readall_regs:");
52591708Sstevel 	}
52601708Sstevel 	if (scsb->scsb_state & SCSB_FROZEN) {
52611708Sstevel 		return (EAGAIN);
52621708Sstevel 	}
52631708Sstevel 	reg = SCSB_REG_ADDR_START;	/* 1st register in set */
52641708Sstevel 	index = SCSB_REG_INDEX(reg);
52651708Sstevel 	error = scsb_rdwr_register(scsb, I2C_WR_RD, reg, SCSB_DATA_REGISTERS,
52667656SSherry.Moore@Sun.COM 	    &scsb->scsb_data_reg[index], 1);
52671708Sstevel 	return (error);
52681708Sstevel }
52691708Sstevel 
52701708Sstevel 
52711708Sstevel /*
52721708Sstevel  * read 1-byte register, mask with read bits (rmask),
52731708Sstevel  * turn ON bits in on_mask, turn OFF bits in off_mask
52741708Sstevel  * write the byte back to register
52751708Sstevel  * NOTE: MUST be called with mutex held
52761708Sstevel  */
52771708Sstevel static int
scsb_write_mask(scsb_state_t * scsb,uchar_t reg,uchar_t rmask,uchar_t on_mask,uchar_t off_mask)52781708Sstevel scsb_write_mask(scsb_state_t *scsb,
52791708Sstevel 		uchar_t reg,
52801708Sstevel 		uchar_t rmask,
52811708Sstevel 		uchar_t on_mask,
52821708Sstevel 		uchar_t off_mask)
52831708Sstevel {
52841708Sstevel 	i2c_transfer_t	*i2cxferp;
52851708Sstevel 	int		index, error = 0;
52861708Sstevel 	uchar_t		reg_data;
52871708Sstevel 
52881708Sstevel 	if (scsb_debug & 0x0800) {
52891708Sstevel 		cmn_err(CE_NOTE, "scsb_write_mask(,%x,,%x,%x):",
52907656SSherry.Moore@Sun.COM 		    reg, on_mask, off_mask);
52911708Sstevel 	}
52921708Sstevel 	if (scsb->scsb_state & SCSB_FROZEN &&
52937656SSherry.Moore@Sun.COM 	    !(scsb->scsb_state & SCSB_IN_INTR)) {
52941708Sstevel 		return (EAGAIN);
52951708Sstevel 	}
52961708Sstevel 	/* select the register address and read the register */
52971708Sstevel 	i2cxferp = (i2c_transfer_t *)scsb->scsb_i2ctp;
52981708Sstevel 	i2cxferp->i2c_flags = I2C_WR_RD;
52991708Sstevel 	i2cxferp->i2c_wlen = 1;
53001708Sstevel 	i2cxferp->i2c_rlen = 1;
53011708Sstevel 	i2cxferp->i2c_wbuf[0] = reg;
53021708Sstevel 	i2cxferp->i2c_rbuf[0] = 0;
53031708Sstevel 	scsb->scsb_kstat_flag = B_TRUE;	/* we did a i2c transaction */
53041708Sstevel 	if (error = nct_i2c_transfer(scsb->scsb_phandle, i2cxferp)) {
53051708Sstevel 		error = EIO;
53061708Sstevel 		goto wm_error;
53071708Sstevel 	}
53081708Sstevel 	scsb->scsb_i2c_errcnt = 0;
53091708Sstevel 	if (scsb_debug & 0x0800)
53101708Sstevel 		cmn_err(CE_NOTE, "scsb_write_mask() read 0x%x",
53117656SSherry.Moore@Sun.COM 		    i2cxferp->i2c_rbuf[0]);
53121708Sstevel 	reg_data = i2cxferp->i2c_rbuf[0];
53131708Sstevel 	if (rmask)
53141708Sstevel 		reg_data &= rmask;
53151708Sstevel 	if (off_mask)
53161708Sstevel 		reg_data &= ~off_mask;
53171708Sstevel 	if (on_mask)
53181708Sstevel 		reg_data |= on_mask;
53191708Sstevel 	i2cxferp->i2c_flags = I2C_WR;
53201708Sstevel 	i2cxferp->i2c_wlen = 2;
53211708Sstevel 	i2cxferp->i2c_wbuf[0] = reg;
53221708Sstevel 	i2cxferp->i2c_wbuf[1] = reg_data;
53231708Sstevel 	if (error = nct_i2c_transfer(scsb->scsb_phandle, i2cxferp)) {
53241708Sstevel 		error = EIO;
53251708Sstevel 		goto wm_error;
53261708Sstevel 	}
53271708Sstevel 	/* keep shadow registers updated */
53281708Sstevel 	index = SCSB_REG_INDEX(reg);
53291708Sstevel 	scsb->scsb_data_reg[index] = reg_data;
53301708Sstevel 	if (scsb_debug & 0x0800)
53311708Sstevel 		cmn_err(CE_NOTE, "scsb_write_mask() wrote 0x%x", reg_data);
53321708Sstevel 	scsb->scsb_i2c_errcnt = 0;
53331708Sstevel 	return (error);
53341708Sstevel wm_error:
53351708Sstevel 	scsb->scsb_i2c_errcnt++;
53361708Sstevel 	if (scsb->scsb_i2c_errcnt > scsb_err_threshold)
53371708Sstevel 		scsb->scsb_err_flag = B_TRUE; /* latch error */
53381708Sstevel 	if (scsb->scsb_state & SCSB_SSB_PRESENT) {
53391708Sstevel 		if (scsb_debug & 0x0802)
53401708Sstevel 			cmn_err(CE_WARN,
53417656SSherry.Moore@Sun.COM 			    "scsb_write_mask(): reg %x %s error, data=%x",
53427656SSherry.Moore@Sun.COM 			    reg,
53437656SSherry.Moore@Sun.COM 			    i2cxferp->i2c_flags & I2C_WR ? "write" : "read",
53447656SSherry.Moore@Sun.COM 			    i2cxferp->i2c_flags & I2C_WR ?
53457656SSherry.Moore@Sun.COM 			    i2cxferp->i2c_wbuf[1] : i2cxferp->i2c_rbuf[0]);
53461708Sstevel 	} else {
53471708Sstevel 		if (scsb->scsb_i2c_errcnt >= scsb_freeze_count)
53481708Sstevel 			scsb_freeze(scsb);
53491708Sstevel 		return (EAGAIN);
53501708Sstevel 	}
53511708Sstevel 	return (error);
53521708Sstevel }
53531708Sstevel 
53541708Sstevel /*
53551708Sstevel  * read/write len consecutive single byte registers to/from rbuf
53561708Sstevel  * NOTE: should be called with mutex held
53571708Sstevel  */
53581708Sstevel static int
scsb_rdwr_register(scsb_state_t * scsb,int op,uchar_t reg,int len,uchar_t * rwbuf,int i2c_alloc)53591708Sstevel scsb_rdwr_register(scsb_state_t *scsb, int op, uchar_t reg, int len,
53601708Sstevel 				uchar_t *rwbuf, int i2c_alloc)
53611708Sstevel {
53621708Sstevel 	i2c_transfer_t	*i2cxferp;
53631708Sstevel 	int		i, rlen, wlen, index, error = 0;
53641708Sstevel 
53651708Sstevel 	if (scsb_debug & 0x0800) {
53661708Sstevel 		cmn_err(CE_NOTE, "scsb_rdwr_register(scsb,%s,%x,%x,buf):",
53677656SSherry.Moore@Sun.COM 		    (op == I2C_WR) ? "write" : "read",  reg, len);
53681708Sstevel 	}
53691708Sstevel 	if (scsb->scsb_state & SCSB_FROZEN &&
53707656SSherry.Moore@Sun.COM 	    !(scsb->scsb_state & SCSB_IN_INTR)) {
53711708Sstevel 		return (EAGAIN);
53721708Sstevel 	}
53731708Sstevel 	if (i2c_alloc) {
53741708Sstevel 		i2cxferp = scsb_alloc_i2ctx(scsb->scsb_phandle, I2C_NOSLEEP);
53751708Sstevel 		if (i2cxferp == NULL) {
53761708Sstevel 			if (scsb_debug & 0x0042)
53771708Sstevel 				cmn_err(CE_WARN, "scsb_rdwr_register: "
53787656SSherry.Moore@Sun.COM 				    "i2ctx allocation failure");
53791708Sstevel 			return (ENOMEM);
53801708Sstevel 		}
53811708Sstevel 	} else {
53821708Sstevel 		i2cxferp = scsb->scsb_i2ctp;
53831708Sstevel 	}
53841708Sstevel 	index = SCSB_REG_INDEX(reg);
53851708Sstevel 	switch (op) {
53861708Sstevel 	case I2C_WR:
53871708Sstevel 		wlen = len + 1;	/* add the address */
53881708Sstevel 		rlen = 0;
53891708Sstevel 		i2cxferp->i2c_wbuf[0] = reg;
53901708Sstevel 		for (i = 0; i < len; ++i) {
53911708Sstevel 			scsb->scsb_data_reg[index + i] =
53927656SSherry.Moore@Sun.COM 			    i2cxferp->i2c_wbuf[1 + i] = rwbuf[i];
53931708Sstevel 			if (scsb_debug & 0x0080)
53941708Sstevel 				cmn_err(CE_NOTE,
53951708Sstevel 				"scsb_rdwr_register: writing rwbuf[%d]=0x%x",
53967656SSherry.Moore@Sun.COM 				    i, rwbuf[i]);
53971708Sstevel 		}
53981708Sstevel 		break;
53991708Sstevel 	case I2C_WR_RD:
54001708Sstevel 		wlen = 1;	/* for the address */
54011708Sstevel 		rlen = len;
54021708Sstevel 		i2cxferp->i2c_wbuf[0] = reg;
54031708Sstevel 		break;
54041708Sstevel 	default:
54051708Sstevel 		if (i2c_alloc)
54061708Sstevel 			scsb_free_i2ctx(scsb->scsb_phandle, i2cxferp);
54071708Sstevel 		return (EINVAL);
54081708Sstevel 	}
54091708Sstevel 	/* select the register address */
54101708Sstevel 	i2cxferp->i2c_flags = op;
54111708Sstevel 	i2cxferp->i2c_rlen = rlen;
54121708Sstevel 	i2cxferp->i2c_wlen = wlen;
54131708Sstevel 	i2cxferp->i2c_wbuf[0] = reg;
54141708Sstevel 	scsb->scsb_kstat_flag = B_TRUE;	/* we did a i2c transaction */
54151708Sstevel 	if (error = nct_i2c_transfer(scsb->scsb_phandle, i2cxferp)) {
54161708Sstevel 		error = EIO;
54171708Sstevel 	} else if (rlen) {
54181708Sstevel 		/* copy to rwbuf[] and keep shadow registers updated */
54191708Sstevel 		for (i = 0; i < len; ++i) {
54201708Sstevel 			scsb->scsb_data_reg[index + i] = rwbuf[i] =
54217656SSherry.Moore@Sun.COM 			    i2cxferp->i2c_rbuf[i];
54221708Sstevel 			if (scsb_debug & 0x0080)
54231708Sstevel 				cmn_err(CE_NOTE,
54241708Sstevel 				"scsb_rdwr_register: read rwbuf[%d]=0x%x",
54257656SSherry.Moore@Sun.COM 				    i, rwbuf[i]);
54261708Sstevel 		}
54271708Sstevel 	}
54281708Sstevel 	if (i2c_alloc)
54291708Sstevel 		scsb_free_i2ctx(scsb->scsb_phandle, i2cxferp);
54301708Sstevel 	if (error) {
54311708Sstevel 		scsb->scsb_i2c_errcnt++;
54321708Sstevel 		if (scsb->scsb_i2c_errcnt > scsb_err_threshold)
54331708Sstevel 			scsb->scsb_err_flag = B_TRUE; /* latch error */
54341708Sstevel 		if (!(scsb->scsb_state & SCSB_SSB_PRESENT)) {
54351708Sstevel 			if (scsb->scsb_i2c_errcnt >= scsb_freeze_count)
54361708Sstevel 				scsb_freeze(scsb);
54371708Sstevel 			return (EAGAIN);
54381708Sstevel 		} else {
54391708Sstevel 			cmn_err(CE_WARN,
54407656SSherry.Moore@Sun.COM 			    "scsb_rdwr_register(): I2C read error from %x",
54417656SSherry.Moore@Sun.COM 			    reg);
54421708Sstevel 		}
54431708Sstevel 	} else {
54441708Sstevel 		scsb->scsb_i2c_errcnt = 0;
54451708Sstevel 	}
54461708Sstevel 
54471708Sstevel 	return (error);
54481708Sstevel }
54491708Sstevel 
54501708Sstevel /*
54511708Sstevel  * Called from scsb_intr()
54521708Sstevel  * First find the fru_info for this fru_id, and set fru_status for callback.
54531708Sstevel  * Then check for a registered call_back entry for this fru_id,
54541708Sstevel  * and if found, call it.
54551708Sstevel  * Recursize call until no EVENTS left in evcode.
54561708Sstevel  */
54571708Sstevel static	void
check_fru_info(scsb_state_t * scsb,int evcode)54581708Sstevel check_fru_info(scsb_state_t *scsb, int evcode)
54591708Sstevel {
54601708Sstevel 	struct scsb_cb_entry	*cbe_ptr;
54611708Sstevel 	fru_info_t		*fru_ptr;
54621708Sstevel 	fru_id_t		fru_id;
54631708Sstevel 	scsb_fru_status_t	fru_status;
54641708Sstevel 	int			i, new_evcode;
54651708Sstevel 
54661708Sstevel 	if (scsb_debug & 0x00100001)
54671708Sstevel 		cmn_err(CE_NOTE, "check_fru_info(scsb,0x%x)", evcode);
54681708Sstevel 	if (evcode == 0)
54691708Sstevel 		return;
54701708Sstevel 	i = event_to_index((uint32_t)evcode);
54711708Sstevel 	new_evcode = evcode & ~(1 << i);
54721708Sstevel 	if (i > MCT_MAX_FRUS) {
54731708Sstevel 		if (scsb_debug & 0x00100000)
54741708Sstevel 			cmn_err(CE_NOTE,
54757656SSherry.Moore@Sun.COM 			    "check_fru_info: index %d out of range", i);
54761708Sstevel 		check_fru_info(scsb, new_evcode);
54771708Sstevel 		return;
54781708Sstevel 	}
54791708Sstevel 	fru_id = fru_id_table[i];
54801708Sstevel 	fru_ptr = find_fru_info(fru_id);
54811708Sstevel 	if (fru_ptr == (fru_info_t *)NULL) {
54821708Sstevel 		check_fru_info(scsb, new_evcode);
54831708Sstevel 		return;
54841708Sstevel 	}
54851708Sstevel 	update_fru_info(scsb, fru_ptr);
54861708Sstevel 	if (fru_ptr->fru_status & FRU_PRESENT) {
54871708Sstevel 		fru_status = FRU_PRESENT;
54881708Sstevel 	} else {
54891708Sstevel 		fru_status = FRU_NOT_PRESENT;
54901708Sstevel 		if (fru_ptr->fru_type == SSB) {
54911708Sstevel 			/*
54921708Sstevel 			 * WARN against SCB removal if any
54931708Sstevel 			 * occupied slots are in reset
54941708Sstevel 			 */
54951708Sstevel 			scsb_freeze_check(scsb);
54961708Sstevel 		}
54971708Sstevel 	}
54981708Sstevel 	/*
54991708Sstevel 	 * check for an entry in the CallBack table
55001708Sstevel 	 */
55011708Sstevel 	for (cbe_ptr = scsb_cb_table; cbe_ptr != NULL;
55027656SSherry.Moore@Sun.COM 	    cbe_ptr = cbe_ptr->cb_next) {
55031708Sstevel 		if (cbe_ptr->cb_fru_id == fru_id &&
55047656SSherry.Moore@Sun.COM 		    cbe_ptr->cb_fru_ptr == fru_ptr) {
55051708Sstevel 			if (scsb_debug & 0x00800000)
55061708Sstevel 				cmn_err(CE_NOTE,
55077656SSherry.Moore@Sun.COM 				    "check_fru_info: callback for FRU_ID "
55087656SSherry.Moore@Sun.COM 				    "0x%x; device is %spresent",
55097656SSherry.Moore@Sun.COM 				    (int)fru_id,
55107656SSherry.Moore@Sun.COM 				    fru_status == FRU_PRESENT ?
55117656SSherry.Moore@Sun.COM 				    "" : "not ");
55121708Sstevel 			(*cbe_ptr->cb_func)(
55137656SSherry.Moore@Sun.COM 			    cbe_ptr->cb_softstate_ptr,
55147656SSherry.Moore@Sun.COM 			    cbe_ptr->cb_event,
55157656SSherry.Moore@Sun.COM 			    fru_status);
55161708Sstevel 			break;
55171708Sstevel 		}
55181708Sstevel 	}
55191708Sstevel 	check_fru_info(scsb, new_evcode);
55201708Sstevel }
55211708Sstevel 
55221708Sstevel /*
55231708Sstevel  * -----------------------------
55241708Sstevel  * scsb kstat support functions.
55251708Sstevel  * -----------------------------
55261708Sstevel  */
55271708Sstevel /*
55281708Sstevel  * Create and initialize the kstat data structures
55291708Sstevel  */
55301708Sstevel static int
scsb_alloc_kstats(scsb_state_t * scsb)55311708Sstevel scsb_alloc_kstats(scsb_state_t *scsb)
55321708Sstevel {
55331708Sstevel 	kstat_named_t   *kn;
55341708Sstevel 	/*
55351708Sstevel 	 * scsb_ks_leddata_t for "scsb_leddata"
55361708Sstevel 	 */
55371708Sstevel 	if (scsb_debug & 0x00080001)
55381708Sstevel 		cmn_err(CE_NOTE,
55397656SSherry.Moore@Sun.COM 		    "scsb_alloc_kstats: create scsb_leddata: %lu bytes",
55407656SSherry.Moore@Sun.COM 		    sizeof (scsb_ks_leddata_t));
55411708Sstevel 	if ((scsb->ks_leddata = kstat_create(scsb_name, scsb->scsb_instance,
55427656SSherry.Moore@Sun.COM 	    SCSB_KS_LEDDATA, "misc", KSTAT_TYPE_RAW,
55437656SSherry.Moore@Sun.COM 	    sizeof (scsb_ks_leddata_t), KSTAT_FLAG_PERSISTENT))
55447656SSherry.Moore@Sun.COM 	    == NULL) {
55451708Sstevel 		scsb->scsb_state |= SCSB_KSTATS;
55461708Sstevel 		scsb_free_kstats(scsb);
55471708Sstevel 		return (DDI_FAILURE);
55481708Sstevel 	}
55491708Sstevel 	scsb->ks_leddata->ks_update = update_ks_leddata;
55501708Sstevel 	scsb->ks_leddata->ks_private = (void *)scsb;
55511708Sstevel 	if (update_ks_leddata(scsb->ks_leddata, KSTAT_READ) != DDI_SUCCESS) {
55521708Sstevel 		scsb->scsb_state |= SCSB_KSTATS;
55531708Sstevel 		scsb_free_kstats(scsb);
55541708Sstevel 		return (DDI_FAILURE);
55551708Sstevel 	}
55561708Sstevel 	kstat_install(scsb->ks_leddata);
55571708Sstevel 	/*
55581708Sstevel 	 * scsb_ks_state_t for "scsb_state"
55591708Sstevel 	 */
55601708Sstevel 	if (scsb_debug & 0x00080000)
55611708Sstevel 		cmn_err(CE_NOTE,
55627656SSherry.Moore@Sun.COM 		    "scsb_alloc_kstats: create scsb_state: %lu bytes",
55637656SSherry.Moore@Sun.COM 		    sizeof (scsb_ks_state_t));
55641708Sstevel 	if ((scsb->ks_state = kstat_create(scsb_name, scsb->scsb_instance,
55657656SSherry.Moore@Sun.COM 	    SCSB_KS_STATE, "misc", KSTAT_TYPE_RAW,
55667656SSherry.Moore@Sun.COM 	    sizeof (scsb_ks_state_t), KSTAT_FLAG_PERSISTENT))
55677656SSherry.Moore@Sun.COM 	    == NULL) {
55681708Sstevel 		scsb->scsb_state |= SCSB_KSTATS;
55691708Sstevel 		scsb_free_kstats(scsb);
55701708Sstevel 		return (DDI_FAILURE);
55711708Sstevel 	}
55721708Sstevel 	scsb->ks_state->ks_update = update_ks_state;
55731708Sstevel 	scsb->ks_state->ks_private = (void *)scsb;
55741708Sstevel 	if (update_ks_state(scsb->ks_state, KSTAT_READ) != DDI_SUCCESS) {
55751708Sstevel 		scsb->scsb_state |= SCSB_KSTATS;
55761708Sstevel 		scsb_free_kstats(scsb);
55771708Sstevel 		return (DDI_FAILURE);
55781708Sstevel 	}
55791708Sstevel 	kstat_install(scsb->ks_state);
55801708Sstevel 	/*
55811708Sstevel 	 * mct_topology_t for "env_topology"
55821708Sstevel 	 */
55831708Sstevel 	if (scsb_debug & 0x00080000)
55841708Sstevel 		cmn_err(CE_NOTE,
55857656SSherry.Moore@Sun.COM 		    "scsb_alloc_kstats: create env_toploogy: %lu bytes",
55867656SSherry.Moore@Sun.COM 		    sizeof (mct_topology_t));
55871708Sstevel 	if ((scsb->ks_topology = kstat_create(scsb_name, scsb->scsb_instance,
55887656SSherry.Moore@Sun.COM 	    SCSB_KS_TOPOLOGY, "misc", KSTAT_TYPE_RAW,
55897656SSherry.Moore@Sun.COM 	    sizeof (mct_topology_t), KSTAT_FLAG_PERSISTENT))
55907656SSherry.Moore@Sun.COM 	    == NULL) {
55911708Sstevel 		scsb->scsb_state |= SCSB_KSTATS;
55921708Sstevel 		scsb_free_kstats(scsb);
55931708Sstevel 		return (DDI_FAILURE);
55941708Sstevel 	}
55951708Sstevel 	scsb->ks_topology->ks_update = update_ks_topology;
55961708Sstevel 	scsb->ks_topology->ks_private = (void *)scsb;
55971708Sstevel 	if (update_ks_topology(scsb->ks_topology, KSTAT_READ) != DDI_SUCCESS) {
55981708Sstevel 		scsb->scsb_state |= SCSB_KSTATS;
55991708Sstevel 		scsb_free_kstats(scsb);
56001708Sstevel 		return (DDI_FAILURE);
56011708Sstevel 	}
56021708Sstevel 	kstat_install(scsb->ks_topology);
56031708Sstevel 	/*
56041708Sstevel 	 * kstat_named_t * 2 for "scsb_evc_register"
56051708Sstevel 	 */
56061708Sstevel 	if (scsb_debug & 0x00080001)
56071708Sstevel 		cmn_err(CE_NOTE,
56081708Sstevel 		    "scsb_alloc_kstats: create scsb_evc_register: %lu bytes",
56097656SSherry.Moore@Sun.COM 		    sizeof (kstat_named_t) * 2);
56101708Sstevel 	if ((scsb->ks_evcreg = kstat_create(scsb_name, scsb->scsb_instance,
56117656SSherry.Moore@Sun.COM 	    SCSB_KS_EVC_REGISTER, "misc", KSTAT_TYPE_NAMED, 2,
56127656SSherry.Moore@Sun.COM 	    KSTAT_FLAG_PERSISTENT|KSTAT_FLAG_WRITABLE)) == NULL) {
56131708Sstevel 		scsb->scsb_state |= SCSB_KSTATS;
56141708Sstevel 		scsb_free_kstats(scsb);
56151708Sstevel 		return (DDI_FAILURE);
56161708Sstevel 	}
56171708Sstevel 	scsb->ks_evcreg->ks_update = update_ks_evcreg;
56181708Sstevel 	scsb->ks_evcreg->ks_private = (void *)scsb;
56191708Sstevel 	kn = KSTAT_NAMED_PTR(scsb->ks_evcreg);
56201708Sstevel 	kstat_named_init(&kn[0], "pid_register", KSTAT_DATA_INT64);
56211708Sstevel 	kstat_named_init(&kn[1], "pid_unregister", KSTAT_DATA_INT64);
56221708Sstevel 	kstat_install(scsb->ks_evcreg);
56231708Sstevel 	/*
56241708Sstevel 	 * Done, set the flag for scsb_detach() and other checks
56251708Sstevel 	 */
56261708Sstevel 	scsb->scsb_state |= SCSB_KSTATS;
56271708Sstevel 	return (DDI_SUCCESS);
56281708Sstevel }
56291708Sstevel 
56301708Sstevel static int
update_ks_leddata(kstat_t * ksp,int rw)56311708Sstevel update_ks_leddata(kstat_t *ksp, int rw)
56321708Sstevel {
56331708Sstevel 	scsb_state_t		*scsb;
56341708Sstevel 	scsb_ks_leddata_t	*pks_leddata;
56351708Sstevel 	int			i, numregs, index, error = DDI_SUCCESS;
56361708Sstevel 	uchar_t			reg;
56371708Sstevel 
56381708Sstevel 	scsb = (scsb_state_t *)ksp->ks_private;
56391708Sstevel 	if (scsb_debug & 0x00080001)
56401708Sstevel 		cmn_err(CE_NOTE, "update_ks_leddata: KS_UPDATE%sset",
56417656SSherry.Moore@Sun.COM 		    scsb->scsb_state & SCSB_KS_UPDATE ? " " : " not ");
56421708Sstevel 	/*
56431708Sstevel 	 * Since this is satisfied from the shadow registers, let it succeed
56441708Sstevel 	 * even if the SCB is not present.  It would be nice to return the
56451708Sstevel 	 * shadow values with a warning.
56461708Sstevel 	 *
56471708Sstevel 	 * if (scsb->scsb_state & SCSB_FROZEN) {
56481708Sstevel 	 *	return (DDI_FAILURE);
56491708Sstevel 	 * }
56501708Sstevel 	 */
56511708Sstevel 	if (rw == KSTAT_WRITE) {
56521708Sstevel 		return (EACCES);
56531708Sstevel 	}
56541708Sstevel 	mutex_enter(&scsb->scsb_mutex);
56551708Sstevel 	while (scsb->scsb_state & SCSB_KS_UPDATE) {
56561708Sstevel 		if (cv_wait_sig(&scsb->scsb_cv, &scsb->scsb_mutex) <= 0) {
56571708Sstevel 			mutex_exit(&scsb->scsb_mutex);
56581708Sstevel 			return (EINTR);
56591708Sstevel 		}
56601708Sstevel 	}
56611708Sstevel 	scsb->scsb_state |= SCSB_KS_UPDATE;
56621708Sstevel 	mutex_exit(&scsb->scsb_mutex);
56631708Sstevel 	if (scsb_debug & 0x00080001)
56641708Sstevel 		cmn_err(CE_NOTE, "update_ks_leddata: updating data");
56651708Sstevel 	pks_leddata = (scsb_ks_leddata_t *)ksp->ks_data;
56661708Sstevel 	/*
56671708Sstevel 	 * Call tonga_slotnum_led_shift() for each register that
56681708Sstevel 	 * contains Slot 1-5 information, the first register at each base:
56691708Sstevel 	 * NOK_BASE, OK_BASE, BLINK_OK_BASE
56701708Sstevel 	 * XXX: breaking register table access rules by not using macros.
56711708Sstevel 	 */
56721708Sstevel 	/* NOK */
56731708Sstevel 	reg = SCSB_REG_ADDR(SCTRL_LED_NOK_BASE);
56741708Sstevel 	index = SCSB_REG_INDEX(reg);
56751708Sstevel 	numregs = SCTRL_LED_NOK_NUMREGS;
56761708Sstevel 	i = 0;
56771708Sstevel 	if (IS_SCB_P15)
56781708Sstevel 		reg = tonga_slotnum_led_shift(scsb, scsb->scsb_data_reg[index]);
56791708Sstevel 	else
56801708Sstevel 		reg = scsb->scsb_data_reg[index];
56811708Sstevel 	pks_leddata->scb_led_regs[i] = reg;
56821708Sstevel 	for (++i, ++index; i < numregs; ++i, ++index)
56831708Sstevel 		pks_leddata->scb_led_regs[i] = scsb->scsb_data_reg[index];
56841708Sstevel 	/* OK */
56851708Sstevel 	reg = SCSB_REG_ADDR(SCTRL_LED_OK_BASE);
56861708Sstevel 	index = SCSB_REG_INDEX(reg);
56871708Sstevel 	numregs += SCTRL_LED_OK_NUMREGS;
56881708Sstevel 	if (IS_SCB_P15)
56891708Sstevel 		reg = tonga_slotnum_led_shift(scsb, scsb->scsb_data_reg[index]);
56901708Sstevel 	else
56911708Sstevel 		reg = scsb->scsb_data_reg[index];
56921708Sstevel 	pks_leddata->scb_led_regs[i] = reg;
56931708Sstevel 	for (++i, ++index; i < numregs; ++i, ++index)
56941708Sstevel 		pks_leddata->scb_led_regs[i] = scsb->scsb_data_reg[index];
56951708Sstevel 	/* BLINK */
56961708Sstevel 	reg = SCSB_REG_ADDR(SCTRL_BLINK_OK_BASE);
56971708Sstevel 	index = SCSB_REG_INDEX(reg);
56981708Sstevel 	numregs += SCTRL_BLINK_NUMREGS;
56991708Sstevel 	if (IS_SCB_P15)
57001708Sstevel 		reg = tonga_slotnum_led_shift(scsb, scsb->scsb_data_reg[index]);
57011708Sstevel 	else
57021708Sstevel 		reg = scsb->scsb_data_reg[index];
57031708Sstevel 	pks_leddata->scb_led_regs[i] = reg;
57041708Sstevel 	for (++i, ++index; i < numregs; ++i, ++index)
57051708Sstevel 		pks_leddata->scb_led_regs[i] = scsb->scsb_data_reg[index];
57061708Sstevel 	mutex_enter(&scsb->scsb_mutex);
57071708Sstevel 	scsb->scsb_state &= ~SCSB_KS_UPDATE;
57081708Sstevel 	cv_signal(&scsb->scsb_cv);
57091708Sstevel 	mutex_exit(&scsb->scsb_mutex);
57101708Sstevel 	if (scsb_debug & 0x00080001)
57111708Sstevel 		cmn_err(CE_NOTE, "update_ks_leddata: returning");
57121708Sstevel 	return (error);
57131708Sstevel }
57141708Sstevel 
57151708Sstevel static int
update_ks_evcreg(kstat_t * ksp,int rw)57161708Sstevel update_ks_evcreg(kstat_t *ksp, int rw)
57171708Sstevel {
57181708Sstevel 	scsb_state_t		*scsb;
57191708Sstevel 	int			error = 0;
57201708Sstevel 	kstat_named_t		*kn = KSTAT_NAMED_PTR(ksp);
57211708Sstevel 	pid_t			pid;
57221708Sstevel 
57231708Sstevel 	scsb = (scsb_state_t *)ksp->ks_private;
57241708Sstevel 	if (scsb_debug & 0x00080001)
57251708Sstevel 		cmn_err(CE_NOTE, "update_ks_evcreg: %s(%d), KS_UPDATE%sset",
57267656SSherry.Moore@Sun.COM 		    rw == KSTAT_READ ? "read" : "write", rw,
57277656SSherry.Moore@Sun.COM 		    scsb->scsb_state & SCSB_KS_UPDATE ? " " : " not ");
57281708Sstevel 	/*
57291708Sstevel 	 * Let this registration succeed
57301708Sstevel 	 *
57311708Sstevel 	 * if (scsb->scsb_state & SCSB_FROZEN) {
57321708Sstevel 	 *	return (DDI_FAILURE);
57331708Sstevel 	 * }
57341708Sstevel 	 */
57351708Sstevel 	mutex_enter(&scsb->scsb_mutex);
57361708Sstevel 	while (scsb->scsb_state & SCSB_KS_UPDATE) {
57371708Sstevel 		if (cv_wait_sig(&scsb->scsb_cv, &scsb->scsb_mutex) <= 0) {
57381708Sstevel 			mutex_exit(&scsb->scsb_mutex);
57391708Sstevel 			return (EINTR);
57401708Sstevel 		}
57411708Sstevel 	}
57421708Sstevel 	scsb->scsb_state |= SCSB_KS_UPDATE;
57431708Sstevel 	mutex_exit(&scsb->scsb_mutex);
57441708Sstevel 	if (rw == KSTAT_READ) {
57451708Sstevel 		kn[0].value.i64 = (int64_t)0;
57461708Sstevel 		kn[1].value.i64 = (int64_t)0;
57471708Sstevel 	} else if (rw == KSTAT_WRITE) {
57481708Sstevel 		/*
57491708Sstevel 		 * kn[0] is "pid_register", kn[1] is "pid_unregister"
57501708Sstevel 		 */
57511708Sstevel 		if (kn[0].value.i64 != 0 && kn[1].value.i64 == 0) {
57521708Sstevel 			pid = (pid_t)kn[0].value.i64;
57531708Sstevel 			if (add_event_proc(scsb, pid)) {
57541708Sstevel 				if (scsb_debug & 0x02000002) {
57551708Sstevel 					cmn_err(CE_WARN,
57567656SSherry.Moore@Sun.COM 					    "update_ks_evcreg: "
57577656SSherry.Moore@Sun.COM 					    "process add failed for %d",
57587656SSherry.Moore@Sun.COM 					    pid);
57591708Sstevel 				}
57601708Sstevel 				error = EOVERFLOW;
57611708Sstevel 			}
57621708Sstevel 		} else if (kn[0].value.i64 == 0 && kn[1].value.i64 != 0) {
57631708Sstevel 			pid = (pid_t)kn[1].value.i64;
57641708Sstevel 			if (del_event_proc(scsb, pid)) {
57651708Sstevel 				if (scsb_debug & 0x02000000) {
57661708Sstevel 					cmn_err(CE_NOTE,
57677656SSherry.Moore@Sun.COM 					    "update_ks_evcreg: "
57687656SSherry.Moore@Sun.COM 					    "process delete failed for %d",
57697656SSherry.Moore@Sun.COM 					    pid);
57701708Sstevel 				}
57711708Sstevel 				error = EOVERFLOW;
57721708Sstevel 			}
57731708Sstevel 		} else if (kn[0].value.i64 == 0 && kn[1].value.i64 == 0) {
57741708Sstevel 			/*
57751708Sstevel 			 * rewind the pointers and counts, zero the table.
57761708Sstevel 			 */
57771708Sstevel 			rew_event_proc(scsb);
57781708Sstevel 		} else {
57791708Sstevel 			error = EINVAL;
57801708Sstevel 		}
57811708Sstevel 	} else {
57821708Sstevel 		error = EINVAL;
57831708Sstevel 	}
57841708Sstevel 	mutex_enter(&scsb->scsb_mutex);
57851708Sstevel 	scsb->scsb_state &= ~SCSB_KS_UPDATE;
57861708Sstevel 	cv_signal(&scsb->scsb_cv);
57871708Sstevel 	mutex_exit(&scsb->scsb_mutex);
57881708Sstevel 	return (error);
57891708Sstevel }
57901708Sstevel 
57911708Sstevel static int
update_ks_state(kstat_t * ksp,int rw)57921708Sstevel update_ks_state(kstat_t *ksp, int rw)
57931708Sstevel {
57941708Sstevel 	scsb_state_t		*scsb;
57951708Sstevel 	scsb_ks_state_t		*pks_state;
57961708Sstevel 	int			error = DDI_SUCCESS;
57971708Sstevel 	uint32_t		current_evc;
57981708Sstevel 
57991708Sstevel 	scsb = (scsb_state_t *)ksp->ks_private;
58001708Sstevel 	if (scsb_debug & 0x00080001)
58011708Sstevel 		cmn_err(CE_NOTE, "update_ks_state: KS_UPDATE%sset",
58027656SSherry.Moore@Sun.COM 		    scsb->scsb_state & SCSB_KS_UPDATE ? " " : " not ");
58031708Sstevel 	/*
58041708Sstevel 	 * Let this succeed based on last known data
58051708Sstevel 	 *
58061708Sstevel 	 * if (scsb->scsb_state & SCSB_FROZEN) {
58071708Sstevel 	 *	return (DDI_FAILURE);
58081708Sstevel 	 * }
58091708Sstevel 	 */
58101708Sstevel 	if (rw == KSTAT_WRITE) {
58111708Sstevel 		return (EACCES);
58121708Sstevel 	}
58131708Sstevel 	mutex_enter(&scsb->scsb_mutex);
58141708Sstevel 	while (scsb->scsb_state & SCSB_KS_UPDATE) {
58151708Sstevel 		if (cv_wait_sig(&scsb->scsb_cv, &scsb->scsb_mutex) <= 0) {
58161708Sstevel 			mutex_exit(&scsb->scsb_mutex);
58171708Sstevel 			return (EINTR);
58181708Sstevel 		}
58191708Sstevel 	}
58201708Sstevel 	scsb->scsb_state |= SCSB_KS_UPDATE;
58211708Sstevel 	/*
58221708Sstevel 	 * If SSB not present and scsb not SCSB_FROZEN, check for SCB presence
58231708Sstevel 	 * by initiating an I2C read from the SCB.  If an error occurs,
58241708Sstevel 	 * scsb_freeze() will be called to update SCB info and scsb state.
58251708Sstevel 	 */
58261708Sstevel 	if (!(scsb->scsb_state & SCSB_SSB_PRESENT) &&
58277656SSherry.Moore@Sun.COM 	    !(scsb->scsb_state & SCSB_FROZEN)) {
58281708Sstevel 		uchar_t		data;
58291708Sstevel 		/* Read the SCB PROM ID */
58301708Sstevel 		if (data = scsb_rdwr_register(scsb, I2C_WR_RD,
58317656SSherry.Moore@Sun.COM 		    (uchar_t)SCTRL_PROM_VERSION, 1, &data, 1))
58321708Sstevel 			if (scsb_debug & 0x00080002)
58331708Sstevel 				cmn_err(CE_NOTE, "update_ks_state: SCB/I2C "
58347656SSherry.Moore@Sun.COM 				    "failure %d", data);
58351708Sstevel 	}
58361708Sstevel 	mutex_exit(&scsb->scsb_mutex);
58371708Sstevel 	pks_state = (scsb_ks_state_t *)ksp->ks_data;
58381708Sstevel 	pks_state->scb_present = (scsb->scsb_state & SCSB_SCB_PRESENT) ? 1 : 0;
58391708Sstevel 	pks_state->ssb_present = (scsb->scsb_state & SCSB_SSB_PRESENT) ? 1 : 0;
58401708Sstevel 	pks_state->scsb_frozen = (scsb->scsb_state & SCSB_FROZEN) ? 1 : 0;
58411708Sstevel 	if (scsb->scsb_state & SCSB_DEBUG_MODE)
58421708Sstevel 		pks_state->scsb_mode = (uint8_t)ENVC_DEBUG_MODE;
58431708Sstevel 	else if (scsb->scsb_state & SCSB_DIAGS_MODE)
58441708Sstevel 		pks_state->scsb_mode = (uint8_t)ENVCTRL_DIAG_MODE;
58451708Sstevel 	else
58461708Sstevel 		pks_state->scsb_mode = (uint8_t)ENVCTRL_NORMAL_MODE;
58471708Sstevel 	/*
58481708Sstevel 	 * If scsb_attach() has not completed the kstat installs,
58491708Sstevel 	 * then there are no event processes to check for.
58501708Sstevel 	 */
58511708Sstevel 	if (scsb->scsb_state & SCSB_KSTATS) {
58521708Sstevel 		switch (check_event_procs(&current_evc)) {
58531708Sstevel 		case EVC_NO_EVENT_CODE:
58541708Sstevel 			pks_state->event_code = 0;
58551708Sstevel 			break;
58561708Sstevel 		case EVC_NEW_EVENT_CODE:
58571708Sstevel 		/* FALLTHROUGH */
58581708Sstevel 		case EVC_NO_CURR_PROC:
58591708Sstevel 			pks_state->event_code = current_evc;
58601708Sstevel 			break;
58611708Sstevel 		case EVC_OR_EVENT_CODE:
58621708Sstevel 			pks_state->event_code |= current_evc;
58631708Sstevel 			break;
58641708Sstevel 		case EVC_FAILURE:
58651708Sstevel 			pks_state->event_code = 0;
58661708Sstevel 			error = DDI_FAILURE;
58671708Sstevel 			break;
58681708Sstevel 		}
58691708Sstevel 	} else {
58701708Sstevel 		pks_state->event_code = 0;
58711708Sstevel 	}
58721708Sstevel 	mutex_enter(&scsb->scsb_mutex);
58731708Sstevel 	scsb->scsb_state &= ~SCSB_KS_UPDATE;
58741708Sstevel 	cv_signal(&scsb->scsb_cv);
58751708Sstevel 	mutex_exit(&scsb->scsb_mutex);
58761708Sstevel 	return (error);
58771708Sstevel }
58781708Sstevel 
58791708Sstevel static int
update_ks_topology(kstat_t * ksp,int rw)58801708Sstevel update_ks_topology(kstat_t *ksp, int rw)
58811708Sstevel {
58821708Sstevel 	scsb_state_t		*scsb;
58831708Sstevel 	mct_topology_t		*pks_topo;
58841708Sstevel 	fru_info_t		*fru_ptr;
58851708Sstevel 	int			i, val, error = DDI_SUCCESS, slotnum;
58861708Sstevel 
58871708Sstevel 	scsb = (scsb_state_t *)ksp->ks_private;
58881708Sstevel 	if (scsb_debug & 0x00080001)
58891708Sstevel 		cmn_err(CE_NOTE, "update_ks_topology: KS_UPDATE%sset",
58907656SSherry.Moore@Sun.COM 		    scsb->scsb_state & SCSB_KS_UPDATE ? " " : " not ");
58911708Sstevel 	/*
58921708Sstevel 	 * Let this succeed based on last known data
58931708Sstevel 	 *
58941708Sstevel 	 * if (scsb->scsb_state & SCSB_FROZEN) {
58951708Sstevel 	 *	return (DDI_FAILURE);
58961708Sstevel 	 * }
58971708Sstevel 	 */
58981708Sstevel 	if (rw == KSTAT_WRITE) {
58991708Sstevel 		return (EACCES);
59001708Sstevel 	}
59011708Sstevel 	mutex_enter(&scsb->scsb_mutex);
59021708Sstevel 	while (scsb->scsb_state & SCSB_KS_UPDATE) {
59031708Sstevel 		if (cv_wait_sig(&scsb->scsb_cv, &scsb->scsb_mutex) <= 0) {
59041708Sstevel 			mutex_exit(&scsb->scsb_mutex);
59051708Sstevel 			return (EINTR);
59061708Sstevel 		}
59071708Sstevel 	}
59081708Sstevel 	scsb->scsb_state |= SCSB_KS_UPDATE;
59091708Sstevel 	/*
59101708Sstevel 	 * If SSB not present and scsb not SCSB_FROZEN, check for SCB presence
59111708Sstevel 	 * by initiating an I2C read from the SCB.  If an error occurs,
59121708Sstevel 	 * scsb_freeze() will be called to update SCB info and scsb state.
59131708Sstevel 	 */
59141708Sstevel 	if (!(scsb->scsb_state & SCSB_SSB_PRESENT) &&
59157656SSherry.Moore@Sun.COM 	    !(scsb->scsb_state & SCSB_FROZEN)) {
59161708Sstevel 		uchar_t		data;
59171708Sstevel 		/* Read the SCB PROM ID */
59181708Sstevel 		if (data = scsb_rdwr_register(scsb, I2C_WR_RD,
59197656SSherry.Moore@Sun.COM 		    (uchar_t)SCTRL_PROM_VERSION, 1, &data, 1))
59201708Sstevel 			if (scsb_debug & 0x00080002)
59211708Sstevel 				cmn_err(CE_NOTE, "update_ks_topology: SCB/I2C "
59227656SSherry.Moore@Sun.COM 				    "failure %d", data);
59231708Sstevel 	}
59241708Sstevel 	mutex_exit(&scsb->scsb_mutex);
59251708Sstevel 	pks_topo = (mct_topology_t *)ksp->ks_data;
59261708Sstevel 	for (i = SLOT; i < SCSB_UNIT_TYPES; ++i) {
59271708Sstevel 		pks_topo->max_units[i] = mct_system_info.max_units[i];
59281708Sstevel 	}
59291708Sstevel 
59301708Sstevel 	pks_topo->mid_plane.fru_status = FRU_PRESENT;
59311708Sstevel 	pks_topo->mid_plane.fru_unit = (scsb_unum_t)1;
59321708Sstevel 	pks_topo->mid_plane.fru_type = mct_system_info.mid_plane.fru_type;
59331708Sstevel 	pks_topo->mid_plane.fru_id = mct_system_info.mid_plane.fru_id;
59341708Sstevel 	pks_topo->mid_plane.fru_version = mct_system_info.mid_plane.fru_version;
59351708Sstevel 	pks_topo->mid_plane.fru_health = MCT_HEALTH_OK;
59361708Sstevel 	fru_ptr = mct_system_info.fru_info_list[SLOT];
59371708Sstevel 	for (i = 0; i < pks_topo->max_units[SLOT]; ++i, ++fru_ptr) {
59381708Sstevel 		pks_topo->mct_slots[i].fru_status = fru_ptr->fru_status;
59391708Sstevel 		pks_topo->mct_slots[i].fru_type = fru_ptr->fru_type;
59401708Sstevel 		pks_topo->mct_slots[i].fru_unit = fru_ptr->fru_unit;
59411708Sstevel 		pks_topo->mct_slots[i].fru_id = fru_ptr->fru_id;
59421708Sstevel 		pks_topo->mct_slots[i].fru_version = fru_ptr->fru_version;
59431708Sstevel 		/*
59441708Sstevel 		 * XXX: need to check healthy regs to set fru_health
59451708Sstevel 		 */
59461708Sstevel 		slotnum = tonga_psl_to_ssl(scsb, i+1);
59471708Sstevel 		val = scsb_fru_op(scsb, SLOT, slotnum, SCTRL_BHLTHY_BASE,
59487656SSherry.Moore@Sun.COM 		    SCSB_FRU_OP_GET_BITVAL);
59491708Sstevel 		pks_topo->mct_slots[i].fru_health = (val) ?
59507656SSherry.Moore@Sun.COM 		    MCT_HEALTH_OK : MCT_HEALTH_NOK;
59511708Sstevel 	}
59521708Sstevel 	fru_ptr = mct_system_info.fru_info_list[PDU];
59531708Sstevel 	for (i = 0; i < pks_topo->max_units[PDU]; ++i, ++fru_ptr) {
59541708Sstevel 		pks_topo->mct_pdu[i].fru_status = fru_ptr->fru_status;
59551708Sstevel 		pks_topo->mct_pdu[i].fru_type = fru_ptr->fru_type;
59561708Sstevel 		pks_topo->mct_pdu[i].fru_unit = fru_ptr->fru_unit;
59571708Sstevel 		pks_topo->mct_pdu[i].fru_id = fru_ptr->fru_id;
59581708Sstevel 		pks_topo->mct_pdu[i].fru_version = fru_ptr->fru_version;
59591708Sstevel 		pks_topo->mct_pdu[i].fru_health = MCT_HEALTH_NA;
59601708Sstevel 	}
59611708Sstevel 	fru_ptr = mct_system_info.fru_info_list[PS];
59621708Sstevel 	for (i = 0; i < pks_topo->max_units[PS]; ++i, ++fru_ptr) {
59631708Sstevel 		pks_topo->mct_ps[i].fru_status = fru_ptr->fru_status;
59641708Sstevel 		pks_topo->mct_ps[i].fru_type = fru_ptr->fru_type;
59651708Sstevel 		pks_topo->mct_ps[i].fru_unit = fru_ptr->fru_unit;
59661708Sstevel 		pks_topo->mct_ps[i].fru_id = fru_ptr->fru_id;
59671708Sstevel 		pks_topo->mct_ps[i].fru_version = fru_ptr->fru_version;
59681708Sstevel 		pks_topo->mct_ps[i].fru_health = MCT_HEALTH_NA;
59691708Sstevel 	}
59701708Sstevel 	fru_ptr = mct_system_info.fru_info_list[DISK];
59711708Sstevel 	for (i = 0; i < pks_topo->max_units[DISK]; ++i, ++fru_ptr) {
59721708Sstevel 		pks_topo->mct_disk[i].fru_status = fru_ptr->fru_status;
59731708Sstevel 		pks_topo->mct_disk[i].fru_type = fru_ptr->fru_type;
59741708Sstevel 		pks_topo->mct_disk[i].fru_unit = fru_ptr->fru_unit;
59751708Sstevel 		pks_topo->mct_disk[i].fru_id = fru_ptr->fru_id;
59761708Sstevel 		pks_topo->mct_disk[i].fru_version = fru_ptr->fru_version;
59771708Sstevel 		pks_topo->mct_disk[i].fru_health = MCT_HEALTH_NA;
59781708Sstevel 	}
59791708Sstevel 	fru_ptr = mct_system_info.fru_info_list[FAN];
59801708Sstevel 	for (i = 0; i < pks_topo->max_units[FAN]; ++i, ++fru_ptr) {
59811708Sstevel 		pks_topo->mct_fan[i].fru_status = fru_ptr->fru_status;
59821708Sstevel 		pks_topo->mct_fan[i].fru_type = fru_ptr->fru_type;
59831708Sstevel 		pks_topo->mct_fan[i].fru_unit = fru_ptr->fru_unit;
59841708Sstevel 		pks_topo->mct_fan[i].fru_id = fru_ptr->fru_id;
59851708Sstevel 		pks_topo->mct_fan[i].fru_version = fru_ptr->fru_version;
59861708Sstevel 		pks_topo->mct_fan[i].fru_health = MCT_HEALTH_NA;
59871708Sstevel 	}
59881708Sstevel 	fru_ptr = mct_system_info.fru_info_list[SCB];
59891708Sstevel 	for (i = 0; i < pks_topo->max_units[SCB]; ++i, ++fru_ptr) {
59901708Sstevel 		pks_topo->mct_scb[i].fru_status = fru_ptr->fru_status;
59911708Sstevel 		pks_topo->mct_scb[i].fru_type = fru_ptr->fru_type;
59921708Sstevel 		pks_topo->mct_scb[i].fru_unit = fru_ptr->fru_unit;
59931708Sstevel 		pks_topo->mct_scb[i].fru_id = fru_ptr->fru_id;
59941708Sstevel 		pks_topo->mct_scb[i].fru_version = fru_ptr->fru_version;
59951708Sstevel 		/*
59961708Sstevel 		 * To get the scsb health, if there was no i2c transaction
59971708Sstevel 		 * until this read, generate an i2c transaction.
59981708Sstevel 		 */
59991708Sstevel 		if (scsb->scsb_kstat_flag == B_FALSE) {
60001708Sstevel 			uchar_t		data;
6001*11311SSurya.Prakki@Sun.COM 			(void) scsb_blind_read(scsb, I2C_WR_RD,
60027656SSherry.Moore@Sun.COM 			    (uchar_t)SCTRL_PROM_VERSION, 1, &data, 1);
60031708Sstevel 		}
60041708Sstevel 		pks_topo->mct_scb[i].fru_health = ((scsb->scsb_err_flag ==
60057656SSherry.Moore@Sun.COM 		    B_TRUE || scsb->scsb_i2c_errcnt > scsb_err_threshold)
60067656SSherry.Moore@Sun.COM 		    ?  MCT_HEALTH_NOK : MCT_HEALTH_OK);
60071708Sstevel #ifdef DEBUG
60081708Sstevel 		if (pks_topo->mct_scb[i].fru_health == MCT_HEALTH_NOK)
60091708Sstevel 			cmn_err(CE_WARN, "SCSB kstat health:%d", pks_topo->
60107656SSherry.Moore@Sun.COM 			    mct_scb[i].fru_health);
60111708Sstevel #endif
60121708Sstevel 		scsb->scsb_err_flag = B_FALSE; /* clear error flag once read */
60131708Sstevel 		scsb->scsb_kstat_flag = B_FALSE; /* false? read from i2c */
60141708Sstevel 	}
60151708Sstevel 	fru_ptr = mct_system_info.fru_info_list[SSB];
60161708Sstevel 	for (i = 0; i < pks_topo->max_units[SSB]; ++i, ++fru_ptr) {
60171708Sstevel 		pks_topo->mct_ssb[i].fru_status = fru_ptr->fru_status;
60181708Sstevel 		pks_topo->mct_ssb[i].fru_type = fru_ptr->fru_type;
60191708Sstevel 		pks_topo->mct_ssb[i].fru_unit = fru_ptr->fru_unit;
60201708Sstevel 		pks_topo->mct_ssb[i].fru_id = fru_ptr->fru_id;
60211708Sstevel 		pks_topo->mct_ssb[i].fru_version = fru_ptr->fru_version;
60221708Sstevel 		pks_topo->mct_ssb[i].fru_health = MCT_HEALTH_NA;
60231708Sstevel 	}
60241708Sstevel 	fru_ptr = mct_system_info.fru_info_list[ALARM];
60251708Sstevel 	for (i = 0; i < pks_topo->max_units[ALARM]; ++i, ++fru_ptr) {
60261708Sstevel 		pks_topo->mct_alarm[i].fru_status = fru_ptr->fru_status;
60271708Sstevel 		pks_topo->mct_alarm[i].fru_type = fru_ptr->fru_type;
60281708Sstevel 		pks_topo->mct_alarm[i].fru_unit = fru_ptr->fru_unit;
60291708Sstevel 		pks_topo->mct_alarm[i].fru_id = fru_ptr->fru_id;
60301708Sstevel 		pks_topo->mct_alarm[i].fru_version = fru_ptr->fru_version;
60311708Sstevel 		pks_topo->mct_alarm[i].fru_health = MCT_HEALTH_NA;
60321708Sstevel 	}
60331708Sstevel 	fru_ptr = mct_system_info.fru_info_list[CFTM];
60341708Sstevel 	for (i = 0; i < pks_topo->max_units[CFTM]; ++i, ++fru_ptr) {
60351708Sstevel 		pks_topo->mct_cftm[i].fru_status = fru_ptr->fru_status;
60361708Sstevel 		pks_topo->mct_cftm[i].fru_type = fru_ptr->fru_type;
60371708Sstevel 		pks_topo->mct_cftm[i].fru_unit = fru_ptr->fru_unit;
60381708Sstevel 		pks_topo->mct_cftm[i].fru_id = fru_ptr->fru_id;
60391708Sstevel 		pks_topo->mct_cftm[i].fru_version = fru_ptr->fru_version;
60401708Sstevel 		pks_topo->mct_cftm[i].fru_health = MCT_HEALTH_NA;
60411708Sstevel 	}
60421708Sstevel 	fru_ptr = mct_system_info.fru_info_list[CRTM];
60431708Sstevel 	for (i = 0; i < pks_topo->max_units[CRTM]; ++i, ++fru_ptr) {
60441708Sstevel 		pks_topo->mct_crtm[i].fru_status = fru_ptr->fru_status;
60451708Sstevel 		pks_topo->mct_crtm[i].fru_type = fru_ptr->fru_type;
60461708Sstevel 		pks_topo->mct_crtm[i].fru_unit = fru_ptr->fru_unit;
60471708Sstevel 		pks_topo->mct_crtm[i].fru_id = fru_ptr->fru_id;
60481708Sstevel 		pks_topo->mct_crtm[i].fru_version = fru_ptr->fru_version;
60491708Sstevel 		pks_topo->mct_crtm[i].fru_health = MCT_HEALTH_NA;
60501708Sstevel 	}
60511708Sstevel 	fru_ptr = mct_system_info.fru_info_list[PRTM];
60521708Sstevel 	for (i = 0; i < pks_topo->max_units[PRTM]; ++i, ++fru_ptr) {
60531708Sstevel 		pks_topo->mct_prtm[i].fru_status = fru_ptr->fru_status;
60541708Sstevel 		pks_topo->mct_prtm[i].fru_type = fru_ptr->fru_type;
60551708Sstevel 		pks_topo->mct_prtm[i].fru_unit = fru_ptr->fru_unit;
60561708Sstevel 		pks_topo->mct_prtm[i].fru_id = fru_ptr->fru_id;
60571708Sstevel 		pks_topo->mct_prtm[i].fru_version = fru_ptr->fru_version;
60581708Sstevel 		pks_topo->mct_prtm[i].fru_health = MCT_HEALTH_NA;
60591708Sstevel 	}
60601708Sstevel 	mutex_enter(&scsb->scsb_mutex);
60611708Sstevel 	scsb->scsb_state &= ~SCSB_KS_UPDATE;
60621708Sstevel 	cv_signal(&scsb->scsb_cv);
60631708Sstevel 	mutex_exit(&scsb->scsb_mutex);
60641708Sstevel 	return (error);
60651708Sstevel }
60661708Sstevel 
60671708Sstevel static void
scsb_free_kstats(scsb_state_t * scsb)60681708Sstevel scsb_free_kstats(scsb_state_t *scsb)
60691708Sstevel {
60701708Sstevel 	if (!(scsb->scsb_state & SCSB_KSTATS))
60711708Sstevel 		return;
60721708Sstevel 	/*
60731708Sstevel 	 * free the allocated kstat data
60741708Sstevel 	 */
60751708Sstevel 	if (scsb->ks_evcreg != NULL) {
60761708Sstevel 		kstat_delete(scsb->ks_evcreg);
60771708Sstevel 	}
60781708Sstevel 	if (scsb->ks_topology != NULL) {
60791708Sstevel 		kstat_delete(scsb->ks_topology);
60801708Sstevel 	}
60811708Sstevel 	if (scsb->ks_state != NULL) {
60821708Sstevel 		kstat_delete(scsb->ks_state);
60831708Sstevel 	}
60841708Sstevel 	if (scsb->ks_leddata != NULL) {
60851708Sstevel 		kstat_delete(scsb->ks_leddata);
60861708Sstevel 	}
60871708Sstevel 	scsb->ks_leddata = NULL;
60881708Sstevel 	scsb->ks_state = NULL;
60891708Sstevel 	scsb->ks_topology = NULL;
60901708Sstevel 	scsb->ks_evcreg = NULL;
60911708Sstevel 	scsb->scsb_state &= ~SCSB_KSTATS;
60921708Sstevel }
60931708Sstevel 
60941708Sstevel 
60951708Sstevel /*
60961708Sstevel  * --------------------------------------
60971708Sstevel  * Miscellaneous scsb internal functions.
60981708Sstevel  * --------------------------------------
60991708Sstevel  *
61001708Sstevel  * allocate I2C transfer structure
61011708Sstevel  */
61021708Sstevel static i2c_transfer_t *
scsb_alloc_i2ctx(i2c_client_hdl_t phandle,uint_t sleep)61031708Sstevel scsb_alloc_i2ctx(i2c_client_hdl_t phandle, uint_t sleep)
61041708Sstevel {
61051708Sstevel 	i2c_transfer_t	*tp;
61061708Sstevel 
61071708Sstevel 	if (i2c_transfer_alloc(phandle, &tp, SCSB_DATA_REGISTERS + 2,
61087656SSherry.Moore@Sun.COM 	    SCSB_DATA_REGISTERS + 2, sleep) == I2C_FAILURE) {
61091708Sstevel 		return (NULL);
61101708Sstevel 	}
61111708Sstevel 	return (tp);
61121708Sstevel }
61131708Sstevel 
61141708Sstevel /*
61151708Sstevel  * free I2C transfer structure
61161708Sstevel  */
61171708Sstevel static void
scsb_free_i2ctx(i2c_client_hdl_t phandle,i2c_transfer_t * tp)61181708Sstevel scsb_free_i2ctx(i2c_client_hdl_t phandle, i2c_transfer_t *tp)
61191708Sstevel {
61201708Sstevel 	i2c_transfer_free(phandle, tp);
61211708Sstevel }
61221708Sstevel 
61231708Sstevel static	void
update_fru_info(scsb_state_t * scsb,fru_info_t * fru_ptr)61241708Sstevel update_fru_info(scsb_state_t *scsb, fru_info_t *fru_ptr)
61251708Sstevel {
61261708Sstevel 	int		index;
61271708Sstevel 	uchar_t		reg, bit;
61281708Sstevel 	fru_info_t	*acslot_ptr = NULL;
61291708Sstevel 	fru_id_t	acslot_id = 0;
61301708Sstevel 	if (scsb_debug & 0x00100001)
6131*11311SSurya.Prakki@Sun.COM 		cmn_err(CE_NOTE, "update_fru_info(scsb,0x%p)", (void *)fru_ptr);
61321708Sstevel 	if (fru_ptr == (fru_info_t *)NULL ||
61337656SSherry.Moore@Sun.COM 	    fru_ptr->i2c_info == (fru_i2c_info_t *)NULL)
61341708Sstevel 		return;
61351708Sstevel 	/*
61361708Sstevel 	 * If this is an Alarm Card update, then we also need to get
61371708Sstevel 	 * Alarm Card Slot fru_ptr to update it's fru_type, and maybe fru_id
61381708Sstevel 	 */
61391708Sstevel 	if (fru_ptr->fru_id == fru_id_table[FRU_INDEX(SCTRL_EVENT_ALARM)]) {
61401708Sstevel 		/*
61411708Sstevel 		 * SCTRL_EVENT_SLOT1 == 0x01 so
61421708Sstevel 		 * fru_id_table[] index for Slot 1 == 0
61431708Sstevel 		 */
61441708Sstevel 		acslot_id = fru_id_table[(scsb->ac_slotnum - 1)];
61451708Sstevel 		acslot_ptr = find_fru_info(acslot_id);
61461708Sstevel 	}
61471708Sstevel 	reg = fru_ptr->i2c_info->syscfg_reg;
61481708Sstevel 	bit = fru_ptr->i2c_info->syscfg_bit;
61491708Sstevel 	if (reg == 0 && fru_ptr->fru_type == SCB) {
61501708Sstevel 		if (scsb->scsb_state & SCSB_SCB_PRESENT)
61511708Sstevel 			fru_ptr->fru_status = FRU_PRESENT;
61521708Sstevel 		else
61531708Sstevel 			fru_ptr->fru_status = FRU_NOT_PRESENT;
61541708Sstevel 	} else if (reg) {
61551708Sstevel 		index = SCSB_REG_INDEX(reg);
61561708Sstevel 		if (scsb->scsb_data_reg[index] & (1 << bit)) {
61571708Sstevel 			fru_ptr->fru_status = FRU_PRESENT;
61581708Sstevel 			/*
61591708Sstevel 			 * XXX:	need to add version register, and maybe a
61601708Sstevel 			 *	 method, to the fru_ptr->i2c_info structure.
61611708Sstevel 			 *
61621708Sstevel 			 * fru_ptr->fru_version = (fru_version_t)0;
61631708Sstevel 			 */
61641708Sstevel 			/*
61651708Sstevel 			 * Because scsb_intr() sometimes gets the AC present
61661708Sstevel 			 * INT before the ACSLOT present INT,
61671708Sstevel 			 * do not check the ACSLOT fru_status
61681708Sstevel 			 *
61691708Sstevel 			 * if (acslot_ptr != NULL && acslot_ptr->fru_status ==
61701708Sstevel 			 *					FRU_PRESENT)
61711708Sstevel 			 */
61721708Sstevel 			if (acslot_ptr != NULL)
61731708Sstevel 				acslot_ptr->fru_type = (scsb_utype_t)OC_AC;
61741708Sstevel 		} else {
61751708Sstevel 			fru_ptr->fru_status = FRU_NOT_PRESENT;
61761708Sstevel 			/*
61771708Sstevel 			 * fru_ptr->fru_version = (fru_version_t)0;
61781708Sstevel 			 */
61791708Sstevel 			if (acslot_ptr != NULL) {
61801708Sstevel 				/* AC just removed, but AC Slot is occupied? */
61811708Sstevel 				if (acslot_ptr->fru_status == FRU_PRESENT)
61821708Sstevel 					/* for now it's unknown */
61831708Sstevel 					acslot_ptr->fru_type =
61847656SSherry.Moore@Sun.COM 					    (scsb_utype_t)OC_UNKN;
61851708Sstevel 				else
61861708Sstevel 					acslot_ptr->fru_type =
61877656SSherry.Moore@Sun.COM 					    (scsb_utype_t)OC_UNKN;
61881708Sstevel 			}
61891708Sstevel 		}
61901708Sstevel 	}
61911708Sstevel 	if (scsb_debug & 0x00100000)
61921708Sstevel 		cmn_err(CE_NOTE,
61937656SSherry.Moore@Sun.COM 		    "update_fru_info: type %d unit %d is %spresent",
61947656SSherry.Moore@Sun.COM 		    fru_ptr->fru_type, fru_ptr->fru_unit,
61957656SSherry.Moore@Sun.COM 		    fru_ptr->fru_status == FRU_PRESENT
61967656SSherry.Moore@Sun.COM 		    ? "" : "not ");
61971708Sstevel }
61981708Sstevel 
61991708Sstevel /*
62001708Sstevel  * Convert EVENT code to FRU index
62011708Sstevel  * by finding the highest bit number in 32 bit word
62021708Sstevel  */
62031708Sstevel static int
event_to_index(uint32_t evcode)62041708Sstevel event_to_index(uint32_t evcode)
62051708Sstevel {
62061708Sstevel 	int	i = 0;
62071708Sstevel 	if (evcode == 0)
62081708Sstevel 		return (MCT_MAX_FRUS - 1);
62091708Sstevel 	for (; (evcode >>= 1); i++)
62101708Sstevel 		;
62111708Sstevel 	return (i);
62121708Sstevel }
62131708Sstevel 
62141708Sstevel #ifdef DEBUG
62151708Sstevel void
scsb_debug_prnt(char * fmt,uintptr_t a1,uintptr_t a2,uintptr_t a3,uintptr_t a4,uintptr_t a5)62161708Sstevel scsb_debug_prnt(char *fmt, uintptr_t a1, uintptr_t a2, uintptr_t a3,
62171708Sstevel 	uintptr_t a4, uintptr_t a5)
62181708Sstevel {
62191708Sstevel 	if (scsb_debug & 0x8000 ||
62207656SSherry.Moore@Sun.COM 	    (*fmt == 'X' && scsb_debug & 0x00010000)) {
62211708Sstevel 		if (*fmt == 'X')
62221708Sstevel 			++fmt;
62231708Sstevel 		prom_printf("scsb: ");
62241708Sstevel 		prom_printf(fmt, a1, a2, a3, a4, a5);
62251708Sstevel 		prom_printf("\n");
62261708Sstevel 	}
62271708Sstevel }
62281708Sstevel #endif
62291708Sstevel 
62301708Sstevel /*
62311708Sstevel  * event code functions to deliver event codes
62321708Sstevel  * and to manage:
62331708Sstevel  *	the event code fifo
62341708Sstevel  *	the process handle table for registered processes interested in
62351708Sstevel  *	  event codes
62361708Sstevel  */
62371708Sstevel /*
62381708Sstevel  * Send signal to processes registered for event code delivery
62391708Sstevel  */
62401708Sstevel static void
signal_evc_procs(scsb_state_t * scsb)62411708Sstevel signal_evc_procs(scsb_state_t *scsb)
62421708Sstevel {
62431708Sstevel 	int	i = 0, c = 0;
62441708Sstevel 	if (evc_proc_count == 0)
62451708Sstevel 		return;
62461708Sstevel 	for (; i < EVC_PROCS_MAX; ++i) {
62471708Sstevel 		if (evc_procs[i] != NULL) {
62481708Sstevel 			if (proc_signal(evc_procs[i], SIGPOLL)) {
62491708Sstevel 				if (scsb_debug & 0x02000002)
62501708Sstevel 					cmn_err(CE_WARN,
62517656SSherry.Moore@Sun.COM 					    "scsb:signal_evc_procs: "
62527656SSherry.Moore@Sun.COM 					    "signal to %d failed",
62537656SSherry.Moore@Sun.COM 					    ((struct pid *)
62547656SSherry.Moore@Sun.COM 					    evc_procs[i])->pid_id);
62551708Sstevel 				(void) del_event_proc(scsb,
62567656SSherry.Moore@Sun.COM 				    ((struct pid *)evc_procs[i])->pid_id);
62571708Sstevel 			}
62581708Sstevel 			if (++c >= evc_proc_count) {
62591708Sstevel 				if (scsb_debug & 0x02000000) {
62601708Sstevel 					cmn_err(CE_NOTE,
62617656SSherry.Moore@Sun.COM 					    "signal_evc_procs: signaled "
62627656SSherry.Moore@Sun.COM 					    "%d/%d processes", c,
62637656SSherry.Moore@Sun.COM 					    evc_proc_count);
62641708Sstevel 				}
62651708Sstevel 				break;
62661708Sstevel 			}
62671708Sstevel 		}
62681708Sstevel 	}
62691708Sstevel }
62701708Sstevel 
62711708Sstevel /*
62721708Sstevel  * bump FIFO ptr, taking care of wrap around
62731708Sstevel  */
62741708Sstevel static uint32_t *
inc_fifo_ptr(uint32_t * ptr)62751708Sstevel inc_fifo_ptr(uint32_t *ptr)
62761708Sstevel {
62771708Sstevel 	if (++ptr >= evc_fifo + EVC_FIFO_SIZE)
62781708Sstevel 		ptr = evc_fifo;
62791708Sstevel 	return (ptr);
62801708Sstevel }
62811708Sstevel 
62821708Sstevel /* ARGSUSED */
62831708Sstevel static void
reset_evc_fifo(scsb_state_t * scsb)62841708Sstevel reset_evc_fifo(scsb_state_t *scsb)
62851708Sstevel {
62861708Sstevel 	evc_wptr = evc_fifo;
62871708Sstevel 	evc_rptr = evc_fifo;
62881708Sstevel 	evc_fifo_count = 0;
62891708Sstevel }
62901708Sstevel 
62911708Sstevel /*
62921708Sstevel  * Called from scsb_intr() when a new event occurs, to put new code in FIFO,
62931708Sstevel  * and signal any interested processes in evc_procs[].
62941708Sstevel  * Always succeeds.
62951708Sstevel  */
62961708Sstevel static void
add_event_code(scsb_state_t * scsb,uint32_t event_code)62971708Sstevel add_event_code(scsb_state_t *scsb, uint32_t event_code)
62981708Sstevel {
62991708Sstevel 	if (event_proc_count(scsb) == 0) {
63001708Sstevel 		return;
63011708Sstevel 	}
63021708Sstevel 	*evc_wptr = event_code;
63031708Sstevel 	evc_wptr = inc_fifo_ptr(evc_wptr);
63041708Sstevel 	if (++evc_fifo_count > EVC_FIFO_SIZE) {
63051708Sstevel 		--evc_fifo_count;		/* lose the oldest event */
63061708Sstevel 		evc_rptr = inc_fifo_ptr(evc_rptr);
63071708Sstevel 	}
63081708Sstevel 	if (scsb_debug & 0x01000000) {
63091708Sstevel 		cmn_err(CE_NOTE, "add_event_code: 0x%x, FIFO size = %d",
63107656SSherry.Moore@Sun.COM 		    event_code, evc_fifo_count);
63111708Sstevel 	}
63121708Sstevel 	signal_evc_procs(scsb);
63131708Sstevel }
63141708Sstevel 
63151708Sstevel /*
63161708Sstevel  * called from check_event_procs() when the last registered process
63171708Sstevel  * retrieved the oldest event
63181708Sstevel  */
63191708Sstevel static uint32_t
del_event_code()63201708Sstevel del_event_code()
63211708Sstevel {
63221708Sstevel 	uint32_t evc = 0;
63231708Sstevel 	if (!evc_fifo_count)
63241708Sstevel 		return (scsb_event_code);
63251708Sstevel 	evc = *evc_rptr;
63261708Sstevel 	evc_rptr = inc_fifo_ptr(evc_rptr);
63271708Sstevel 	--evc_fifo_count;
63281708Sstevel 	if (scsb_debug & 0x01000000) {
63291708Sstevel 		cmn_err(CE_NOTE, "del_event_code: 0x%x, FIFO size = %d",
63307656SSherry.Moore@Sun.COM 		    evc, evc_fifo_count);
63311708Sstevel 	}
63321708Sstevel 	return (evc);
63331708Sstevel }
63341708Sstevel 
63351708Sstevel /*
63361708Sstevel  * called from check_event_procs() to retrieve the current event code
63371708Sstevel  */
63381708Sstevel static uint32_t
get_event_code()63391708Sstevel get_event_code()
63401708Sstevel {
63411708Sstevel 	if (!evc_fifo_count)
63421708Sstevel 		return (0);
63431708Sstevel 	return (*evc_rptr);
63441708Sstevel }
63451708Sstevel 
63461708Sstevel /*
63471708Sstevel  * called from an application interface (ie: an ioctl command)
63481708Sstevel  * to register a process id interested in SCB events.
63491708Sstevel  * NOTE: proc_ref() must be called from USER context, so since this is a
63501708Sstevel  * streams driver, a kstat interface is used for process registration.
63511708Sstevel  * return:
63521708Sstevel  *	0 = event_proc was added
63531708Sstevel  *	1 = out of space
63541708Sstevel  */
63551708Sstevel /* ARGSUSED */
63561708Sstevel static int
add_event_proc(scsb_state_t * scsb,pid_t pid)63571708Sstevel add_event_proc(scsb_state_t *scsb, pid_t pid)
63581708Sstevel {
63591708Sstevel 	int	i = 0;
63601708Sstevel 	void	*curr_proc;
63611708Sstevel 	pid_t	curr_pid;
63621708Sstevel 	if (evc_proc_count >= EVC_PROCS_MAX)
63631708Sstevel 		return (1);
63641708Sstevel 	curr_proc = proc_ref();
63651708Sstevel 	curr_pid = (pid_t)(((struct pid *)curr_proc)->pid_id);
63661708Sstevel 	if (curr_pid != pid) {
63671708Sstevel 		if (scsb_debug & 0x02000000) {
63681708Sstevel 			cmn_err(CE_WARN,
63697656SSherry.Moore@Sun.COM 			    "add_event_proc: current %d != requestor %d",
63707656SSherry.Moore@Sun.COM 			    curr_pid, pid);
63711708Sstevel 		} else {
63721708Sstevel 			proc_unref(curr_proc);
63731708Sstevel 			return (1);
63741708Sstevel 		}
63751708Sstevel 	}
63761708Sstevel 	for (; i < EVC_PROCS_MAX; ++i) {
63771708Sstevel 		if (evc_procs[i] == NULL) {
63781708Sstevel 			evc_procs[i] = curr_proc;
63791708Sstevel 			evc_proc_count++;
63801708Sstevel 			if (scsb_debug & 0x02000000) {
63811708Sstevel 				cmn_err(CE_NOTE,
63827656SSherry.Moore@Sun.COM 				    "add_event_proc: %d; evc_proc_count=%d",
63837656SSherry.Moore@Sun.COM 				    pid, evc_proc_count);
63841708Sstevel 			}
63851708Sstevel 			return (0);
63861708Sstevel 		}
63871708Sstevel 	}
63881708Sstevel 	proc_unref(curr_proc);
63891708Sstevel 	return (1);
63901708Sstevel }
63911708Sstevel 
63921708Sstevel /*
63931708Sstevel  * called from an application interface (ie: an ioctl command)
63941708Sstevel  * to unregister a process id interested in SCB events.
63951708Sstevel  * return:
63961708Sstevel  *	0 = event_proc was deleted
63971708Sstevel  *	1 = event_proc was not found, or table was empty
63981708Sstevel  */
63991708Sstevel /* ARGSUSED */
64001708Sstevel static int
del_event_proc(scsb_state_t * scsb,pid_t pid)64011708Sstevel del_event_proc(scsb_state_t *scsb, pid_t pid)
64021708Sstevel {
64031708Sstevel 	int	i = 0;
64041708Sstevel 	int	cnt = 0;
64051708Sstevel 	void	*this_proc;
64061708Sstevel 	if (evc_proc_count == 0)
64071708Sstevel 		return (1);
64081708Sstevel 	for (; i < EVC_PROCS_MAX; ++i) {
64091708Sstevel 		if (evc_procs[i] == NULL)
64101708Sstevel 			continue;
64111708Sstevel 		this_proc = evc_procs[i];
64121708Sstevel 		if (pid == ((struct pid *)this_proc)->pid_id) {
64131708Sstevel 			evc_procs[i] = NULL;
64141708Sstevel 			if (--evc_proc_count == 0) {
64151708Sstevel 				/*
64161708Sstevel 				 * reset evc fifo cound and pointers
64171708Sstevel 				 */
64181708Sstevel 				reset_evc_fifo(scsb);
64191708Sstevel 			}
64201708Sstevel 			if (scsb_debug & 0x02000000) {
64211708Sstevel 				cmn_err(CE_NOTE,
64227656SSherry.Moore@Sun.COM 				    "del_event_proc: %d; evc_proc_count=%d",
64237656SSherry.Moore@Sun.COM 				    pid, evc_proc_count);
64241708Sstevel 			}
64251708Sstevel 			proc_unref(this_proc);
64261708Sstevel 			return (0);
64271708Sstevel 		}
64281708Sstevel 		if (++cnt >= evc_proc_count)
64291708Sstevel 			break;
64301708Sstevel 	}
64311708Sstevel 	return (1);
64321708Sstevel }
64331708Sstevel 
64341708Sstevel /*
64351708Sstevel  * Can be called from an application interface
64361708Sstevel  * to rewind the pointers and counters, and zero the table
64371708Sstevel  * return:
64381708Sstevel  */
64391708Sstevel /* ARGSUSED */
64401708Sstevel static void
rew_event_proc(scsb_state_t * scsb)64411708Sstevel rew_event_proc(scsb_state_t *scsb)
64421708Sstevel {
64431708Sstevel 	int	i = 0;
64441708Sstevel 	if (scsb_debug & 0x02000001) {
64451708Sstevel 		cmn_err(CE_NOTE, "rew_event_proc: evc_proc_count=%d",
64467656SSherry.Moore@Sun.COM 		    evc_proc_count);
64471708Sstevel 	}
64481708Sstevel 	for (; i < EVC_PROCS_MAX; ++i) {
64491708Sstevel 		if (evc_procs[i] != NULL) {
64501708Sstevel 			proc_unref(evc_procs[i]);
64511708Sstevel 			evc_procs[i] = NULL;
64521708Sstevel 		}
64531708Sstevel 	}
64541708Sstevel 	evc_proc_count = 0;
64551708Sstevel }
64561708Sstevel 
64571708Sstevel /* ARGSUSED */
64581708Sstevel static int
event_proc_count(scsb_state_t * scsb)64591708Sstevel event_proc_count(scsb_state_t *scsb)
64601708Sstevel {
64611708Sstevel 	return (evc_proc_count);
64621708Sstevel }
64631708Sstevel 
64641708Sstevel /*
64651708Sstevel  * return:
64661708Sstevel  *	1 = pid was found
64671708Sstevel  *	0 = pid was not found, or table was empty
64681708Sstevel  */
64691708Sstevel static int
find_evc_proc(pid_t pid)64701708Sstevel find_evc_proc(pid_t pid)
64711708Sstevel {
64721708Sstevel 	int	i = 0;
64731708Sstevel 	int	cnt = 0;
64741708Sstevel 	if (evc_proc_count == 0)
64751708Sstevel 		return (0);
64761708Sstevel 	for (; i < EVC_PROCS_MAX; ++i) {
64771708Sstevel 		if (evc_procs[i] == NULL)
64781708Sstevel 			continue;
64791708Sstevel 		if (pid == ((struct pid *)evc_procs[i])->pid_id)
64801708Sstevel 			return (1);
64811708Sstevel 		if (++cnt >= evc_proc_count)
64821708Sstevel 			break;
64831708Sstevel 	}
64841708Sstevel 	return (0);
64851708Sstevel }
64861708Sstevel 
64871708Sstevel /*
64881708Sstevel  * called from update_ks_state() to compare evc_proc_count with
64891708Sstevel  * evc_requests, also mainted by this same function
64901708Sstevel  * This function could check the current process id, since this will be a user
64911708Sstevel  * context call, and only bump evc_requests if the calling process is
64921708Sstevel  * registered for event code delivery.
64931708Sstevel  * return:
64941708Sstevel  *	EVC_NO_EVENT_CODE	: no event_code on fifo
64951708Sstevel  *	EVC_NO_CURR_PROC	: current process not in table,
64961708Sstevel  *				  but have an event_code
64971708Sstevel  *	EVC_NEW_EVENT_CODE	: return_evc is new ks_state->event_code
64981708Sstevel  *	EVC_OR_EVENT_CODE	: OR return_evc with ks_state->event_code
64991708Sstevel  *	EVC_FAILURE		: unrecoverable error condition.
65001708Sstevel  */
65011708Sstevel static int
check_event_procs(uint32_t * return_evc)65021708Sstevel check_event_procs(uint32_t *return_evc)
65031708Sstevel {
65041708Sstevel 	void		*curr_proc;
65051708Sstevel 	pid_t		curr_pid = 0;
65061708Sstevel 	int		return_val = 0;
65071708Sstevel 	static int	evc_requests = 0;
65081708Sstevel 	/*
65091708Sstevel 	 * get current process handle, and check the event_procs table
65101708Sstevel 	 */
65111708Sstevel 	if (evc_proc_count == 0) {
65121708Sstevel 		*return_evc = del_event_code();
65131708Sstevel 		return_val = EVC_NO_CURR_PROC;
65141708Sstevel 	} else {
65151708Sstevel 		curr_proc = proc_ref();
65161708Sstevel 		curr_pid = ((struct pid *)curr_proc)->pid_id;
65171708Sstevel 		proc_unref(curr_proc);
65181708Sstevel 		if (!find_evc_proc(curr_pid)) {
65191708Sstevel 			*return_evc = get_event_code();
65201708Sstevel 			return_val = EVC_NO_CURR_PROC;
65211708Sstevel 		} else if (++evc_requests >= evc_proc_count) {
65221708Sstevel 			evc_requests = 0;
65231708Sstevel 			*return_evc = del_event_code();
65241708Sstevel 			return_val = EVC_NEW_EVENT_CODE;
65251708Sstevel 		} else {
65261708Sstevel 			*return_evc = get_event_code();
65271708Sstevel 		}
65281708Sstevel 		if (!return_val)
65291708Sstevel 			return_val = EVC_OR_EVENT_CODE;
65301708Sstevel 	}
65311708Sstevel 	if (scsb_debug & 0x02000000) {
65321708Sstevel 		cmn_err(CE_NOTE, "check_event_procs: pid=%d, evc=0x%x, "
65337656SSherry.Moore@Sun.COM 		    "requests=%d, returning 0x%x", curr_pid,
65347656SSherry.Moore@Sun.COM 		    *return_evc, evc_requests, return_val);
65351708Sstevel 	}
65361708Sstevel 	return (return_val);
65371708Sstevel }
65381708Sstevel 
65391708Sstevel static int
scsb_queue_put(queue_t * rq,int count,uint32_t * data,char * caller)65401708Sstevel scsb_queue_put(queue_t *rq, int count, uint32_t *data, char *caller)
65411708Sstevel {
65421708Sstevel 	mblk_t		*mp;
65431708Sstevel 	if (scsb_debug & 0x4001) {
65441708Sstevel 		cmn_err(CE_NOTE, "scsb_queue_put(0x%p, %d, 0x%x, %s)",
6545*11311SSurya.Prakki@Sun.COM 		    (void *)rq, count, *data, caller);
65461708Sstevel 	}
65471708Sstevel 	mp = allocb(sizeof (uint32_t) * count, BPRI_HI);
65481708Sstevel 	if (mp == NULL) {
65491708Sstevel 		cmn_err(CE_WARN, "%s: allocb failed",
65507656SSherry.Moore@Sun.COM 		    caller);
65511708Sstevel 		return (B_FALSE);
65521708Sstevel 	}
65531708Sstevel 	while (count--) {
65541708Sstevel 		*((uint32_t *)mp->b_wptr) = *data;
65551708Sstevel 		mp->b_wptr += sizeof (*data);
65561708Sstevel 		++data;
65571708Sstevel 	}
65581708Sstevel 	putnext(rq, mp);
65591708Sstevel 	return (B_TRUE);
65601708Sstevel }
65611708Sstevel 
65621708Sstevel /* CLONE */
65631708Sstevel static int
scsb_queue_ops(scsb_state_t * scsb,int op,int oparg,void * opdata,char * caller)65641708Sstevel scsb_queue_ops(scsb_state_t	*scsb,
65651708Sstevel 		int		op,
65661708Sstevel 		int		oparg,
65671708Sstevel 		void		*opdata,
65681708Sstevel 		char		*caller)
65691708Sstevel {
65701708Sstevel 	clone_dev_t	*clptr;
65711708Sstevel 	int		clone, find_open, find_available, retval = QOP_FAILED;
65721708Sstevel 
65731708Sstevel 	switch (op) {
65741708Sstevel 	case QPUT_INT32:
65751708Sstevel 		if (scsb->scsb_opens && scsb->scsb_rq != NULL &&
65767656SSherry.Moore@Sun.COM 		    scsb_queue_put(scsb->scsb_rq, oparg,
65777656SSherry.Moore@Sun.COM 		    (uint32_t *)opdata, caller) == B_FALSE) {
65781708Sstevel 			return (QOP_FAILED);
65791708Sstevel 		}
65801708Sstevel 	/*FALLTHROUGH*/	/* to look for opened clones */
65811708Sstevel 	case QPROCSOFF:
65821708Sstevel 		retval = QOP_OK;
65831708Sstevel 	/*FALLTHROUGH*/
65841708Sstevel 	case QFIRST_OPEN:
65851708Sstevel 	case QFIND_QUEUE:
65861708Sstevel 		find_open = 1;
65871708Sstevel 		find_available = 0;
65881708Sstevel 		break;
65891708Sstevel 	case QFIRST_AVAILABLE:
65901708Sstevel 		find_available = 1;
65911708Sstevel 		find_open = 0;
65921708Sstevel 		break;
65931708Sstevel 	}
65941708Sstevel 	for (clone = SCSB_CLONES_FIRST; clone < SCSB_CLONES_MAX; clone++) {
65951708Sstevel 		clptr = &scsb->clone_devs[clone];
65961708Sstevel 		if (find_open && clptr->cl_flags & SCSB_OPEN) {
65971708Sstevel 			if (clptr->cl_rq == NULL) {
65981708Sstevel 				cmn_err(CE_WARN, "%s: Clone %d has no queue",
65997656SSherry.Moore@Sun.COM 				    caller, clptr->cl_minor);
66001708Sstevel 				return (QOP_FAILED);
66011708Sstevel 			}
66021708Sstevel 			switch (op) {
66031708Sstevel 			case QPROCSOFF:
66041708Sstevel 				qprocsoff(clptr->cl_rq);
66051708Sstevel 				break;
66061708Sstevel 			case QPUT_INT32:
66071708Sstevel 				if (scsb_queue_put(clptr->cl_rq, oparg,
66087656SSherry.Moore@Sun.COM 				    (uint32_t *)opdata, caller)
66097656SSherry.Moore@Sun.COM 				    == B_FALSE) {
66101708Sstevel 					retval = QOP_FAILED;
66111708Sstevel 				}
66121708Sstevel 				break;
66131708Sstevel 			case QFIRST_OPEN:
66141708Sstevel 				return (clone);
66151708Sstevel 			case QFIND_QUEUE:
66161708Sstevel 				if (clptr->cl_rq == (queue_t *)opdata) {
66171708Sstevel 					return (clone);
66181708Sstevel 				}
66191708Sstevel 				break;
66201708Sstevel 			}
66211708Sstevel 		} else if (find_available && clptr->cl_flags == 0) {
66221708Sstevel 			switch (op) {
66231708Sstevel 			case QFIRST_AVAILABLE:
66241708Sstevel 				return (clone);
66251708Sstevel 			}
66261708Sstevel 		}
66271708Sstevel 	}
66281708Sstevel 	return (retval);
66291708Sstevel }
66301708Sstevel 
66311708Sstevel /*
66321708Sstevel  * Find out if a bit is set for the FRU type and unit number in the register
66331708Sstevel  * set defined by the register base table index, base.
66341708Sstevel  * Returns TRUE if bit is set, or FALSE.
66351708Sstevel  */
66361708Sstevel static int
scsb_fru_op(scsb_state_t * scsb,scsb_utype_t fru_type,int unit,int base,int op)66371708Sstevel scsb_fru_op(scsb_state_t *scsb, scsb_utype_t fru_type, int unit, int base,
66381708Sstevel 									int op)
66391708Sstevel {
66401708Sstevel 	int		rc;
66411708Sstevel 	uchar_t		reg;
66421708Sstevel 	int		tmp, idx, code, offset;
66431708Sstevel 
66441708Sstevel #if 0
66451708Sstevel 		reg = SCSB_REG_ADDR(i);
66461708Sstevel 		ac_mask = 1 << FRU_OFFSET(SCTRL_EVENT_ALARM, SCTRL_RESET_BASE);
66471708Sstevel 		ac_val = scsb->scsb_data_reg[index+1] & ac_mask;
66481708Sstevel #endif
66491708Sstevel 	/* get the event code based on which we get the reg and bit offsets */
66501708Sstevel 	code   = FRU_UNIT_TO_EVCODE(fru_type, unit);
66511708Sstevel 	/* get the bit offset in the 8bit register corresponding to the event */
66521708Sstevel 	offset = FRU_OFFSET(code, base);
66531708Sstevel 	/* register offset from the base register, based on the event code */
66541708Sstevel 	if ((fru_type == ALARM) && (base == SCTRL_RESET_BASE))
66551708Sstevel 		tmp = ALARM_RESET_REG_INDEX(code, base);
66561708Sstevel 	else
66571708Sstevel 		tmp = FRU_REG_INDEX(code, base);
66581708Sstevel 	/* get the global offset of the register in the parent address space */
66591708Sstevel 	reg    = SCSB_REG_ADDR(tmp);
66601708Sstevel 	/* get the global index of the register in this SCSB's address space */
66611708Sstevel 	idx    = SCSB_REG_INDEX(reg);
66621708Sstevel 	DEBUG4("scsb_fru_op(start): code=%x, offset=%x, tmp=%x, reg=%x\n",
66637656SSherry.Moore@Sun.COM 	    code, offset, tmp, reg);
66641708Sstevel 	switch (op) {
66651708Sstevel 		case SCSB_FRU_OP_GET_REG:
66661708Sstevel 			rc = reg;
66671708Sstevel 			break;
66681708Sstevel 		case SCSB_FRU_OP_GET_BITVAL:
66691708Sstevel 			rc = (scsb->scsb_data_reg[idx] & (1 << offset))
66707656SSherry.Moore@Sun.COM 			    >> offset;
66711708Sstevel 			break;
66721708Sstevel 		case SCSB_FRU_OP_GET_REGDATA:
66731708Sstevel 			rc = scsb->scsb_data_reg[idx];
66741708Sstevel 			break;
66751708Sstevel 		case SCSB_FRU_OP_SET_REGBIT:
66761708Sstevel 			rc = (1 << offset) & 0xff;
66771708Sstevel 			break;
66781708Sstevel 		default:
66791708Sstevel 			break;
66801708Sstevel 	}
66811708Sstevel 	DEBUG4("scsb_fru_op: unit=%x, base=%x, op=%d, rc=%x\n", unit, base,
66827656SSherry.Moore@Sun.COM 	    op, rc);
66831708Sstevel 	return (rc);
66841708Sstevel }
66851708Sstevel 
66861708Sstevel /*
66871708Sstevel  * All HSC related functions can fail, but an attempt is made to atleast
66881708Sstevel  * return the right shadow state  on get-state function when SCB is removed.
66891708Sstevel  */
66901708Sstevel int
scsb_get_slot_state(scsb_state_t * scsb,int pslotnum,int * rstate)66911708Sstevel scsb_get_slot_state(scsb_state_t *scsb, int pslotnum, int *rstate)
66921708Sstevel {
66931708Sstevel 	int		slotnum, val = 0, rc;
66941708Sstevel 
66951708Sstevel 	/*
66961708Sstevel 	 * When SCB is removed, we could be called with the lock held.
66971708Sstevel 	 * We call check_config_status anyway since it is a read-only operation
66981708Sstevel 	 * and HSC could be invoking this function at interrupt context.
66991708Sstevel 	 * If scsb is already in the doing interrupt postprocess, wait..
67001708Sstevel 	 */
67011708Sstevel 
67021708Sstevel 	rc = scsb_check_config_status(scsb);
67031708Sstevel 
67041708Sstevel 	/* check if error is because SCB is removed */
67051708Sstevel 	if ((rc != EAGAIN) && (rc != DDI_SUCCESS))
67061708Sstevel 		return (DDI_FAILURE);
67071708Sstevel 	slotnum = tonga_psl_to_ssl(scsb, pslotnum);
67081708Sstevel 	val = scsb_fru_op(scsb, SLOT, slotnum, SCTRL_SYSCFG_BASE,
67097656SSherry.Moore@Sun.COM 	    SCSB_FRU_OP_GET_BITVAL);
67101708Sstevel 	if (! val) {
67111708Sstevel 		*rstate = HPC_SLOT_EMPTY;
67121708Sstevel 		return (0);
67131708Sstevel 	}
67141708Sstevel 	/*
67151708Sstevel 	 * now, lets determine if it is connected or disconnected.
67161708Sstevel 	 * If reset is asserted, then the slot is disconnected.
67171708Sstevel 	 */
67181708Sstevel 	rc = scsb_reset_slot(scsb, pslotnum, SCSB_GET_SLOT_RESET_STATUS);
67191708Sstevel 	/* check if error is because SCB is removed */
67201708Sstevel 	if ((rc != EAGAIN) && (rc != DDI_SUCCESS))
67211708Sstevel 		return (DDI_FAILURE);
67221708Sstevel 	val = scsb_fru_op(scsb, SLOT, slotnum, SCTRL_RESET_BASE,
67237656SSherry.Moore@Sun.COM 	    SCSB_FRU_OP_GET_BITVAL);
67241708Sstevel 	if (val)
67251708Sstevel 		*rstate = HPC_SLOT_DISCONNECTED;
67261708Sstevel 	else {
67271708Sstevel 		if (scsb_fru_op(scsb, SLOT, slotnum, SCTRL_BHLTHY_BASE,
67287656SSherry.Moore@Sun.COM 		    SCSB_FRU_OP_GET_BITVAL)) {
67291708Sstevel 			*rstate = HPC_SLOT_CONNECTED;
67301708Sstevel 		} else {
67311708Sstevel 			cmn_err(CE_WARN, "%s#%d: Reset Not Asserted on "
67327656SSherry.Moore@Sun.COM 			    "Healthy# Failed slot %d!",
67337656SSherry.Moore@Sun.COM 			    ddi_driver_name(scsb->scsb_dev),
67347656SSherry.Moore@Sun.COM 			    ddi_get_instance(scsb->scsb_dev), slotnum);
67351708Sstevel 			*rstate = HPC_SLOT_DISCONNECTED;
67361708Sstevel 		}
67371708Sstevel 	}
67381708Sstevel 	return (0);
67391708Sstevel }
67401708Sstevel 
67411708Sstevel int
scsb_reset_slot(scsb_state_t * scsb,int pslotnum,int reset_flag)67421708Sstevel scsb_reset_slot(scsb_state_t *scsb, int pslotnum, int reset_flag)
67431708Sstevel {
67441708Sstevel 	int		slotnum, error, val, alarm_card = 0;
67451708Sstevel 	i2c_transfer_t	*i2cxferp;
67461708Sstevel 	uchar_t		reg;
67471708Sstevel 	int		index, condition_exists = 0, ac_val;
67481708Sstevel 
67491708Sstevel 	if (scsb_debug & 0x8001)
67501708Sstevel 		cmn_err(CE_NOTE, "scsb_reset_slot(%d), flag %x", pslotnum,
67517656SSherry.Moore@Sun.COM 		    reset_flag);
67521708Sstevel 	if (scsb->scsb_state & SCSB_FROZEN)
67531708Sstevel 		return (EAGAIN);
67541708Sstevel 	if ((i2cxferp = scsb_alloc_i2ctx(scsb->scsb_phandle,
67557656SSherry.Moore@Sun.COM 	    I2C_NOSLEEP)) == NULL) {
67561708Sstevel 		return (ENOMEM);
67571708Sstevel 	}
67581708Sstevel 	slotnum = tonga_psl_to_ssl(scsb, pslotnum);
67591708Sstevel 
67601708Sstevel 	if (scsb_is_alarm_card_slot(scsb, pslotnum) == B_TRUE) {
67611708Sstevel 		DEBUG0("alarm card  reset/unreset op:\n");
67621708Sstevel 		alarm_card = 1;
67631708Sstevel 	}
67641708Sstevel 	reg = SCSB_REG_ADDR(SCTRL_RESET_BASE);
67651708Sstevel 	index = SCSB_REG_INDEX(reg);
67661708Sstevel 
67671708Sstevel 	mutex_enter(&scsb->scsb_mutex);
67681708Sstevel 	i2cxferp->i2c_flags = I2C_WR_RD;
67691708Sstevel 	i2cxferp->i2c_rlen = SCTRL_RESET_NUMREGS;
67701708Sstevel 	i2cxferp->i2c_wbuf[0] = reg;
67711708Sstevel 	i2cxferp->i2c_wlen = 1;
67721708Sstevel 	scsb->scsb_kstat_flag = B_TRUE;	/* we did an i2c transaction */
67731708Sstevel 	if ((error = nct_i2c_transfer(scsb->scsb_phandle, i2cxferp)) == 0) {
67741708Sstevel 		scsb->scsb_i2c_errcnt = 0;
67751708Sstevel 		/*
67761708Sstevel 		 * XXX: following statements assume 2 reset registers,
67771708Sstevel 		 * which is the case for our current SCB revisions.
67781708Sstevel 		 */
67791708Sstevel 		scsb->scsb_data_reg[index]   = i2cxferp->i2c_rbuf[0];
67801708Sstevel 		scsb->scsb_data_reg[index+1] = i2cxferp->i2c_rbuf[1];
67811708Sstevel 	} else {
67821708Sstevel 		scsb->scsb_i2c_errcnt++;
67831708Sstevel 		if (scsb->scsb_i2c_errcnt > scsb_err_threshold)
67841708Sstevel 			scsb->scsb_err_flag = B_TRUE; /* latch until kstat */
67851708Sstevel 		if (!(scsb->scsb_state & SCSB_SSB_PRESENT)) {
67861708Sstevel 			if (scsb->scsb_i2c_errcnt >= scsb_freeze_count)
67871708Sstevel 				mutex_exit(&scsb->scsb_mutex);
67881708Sstevel 				scsb_freeze(scsb);
67891708Sstevel 				mutex_enter(&scsb->scsb_mutex);
67901708Sstevel 		}
67911708Sstevel 		cmn_err(CE_WARN, "%s#%d: scsb_reset_slot: error"
67927656SSherry.Moore@Sun.COM 		    " reading Reset regs\n",
67937656SSherry.Moore@Sun.COM 		    ddi_driver_name(scsb->scsb_dev),
67947656SSherry.Moore@Sun.COM 		    ddi_get_instance(scsb->scsb_dev));
67951708Sstevel 		error = DDI_FAILURE;
67961708Sstevel 	}
67971708Sstevel 
67981708Sstevel 	DEBUG2("pre-reset regs = %x,%x\n", scsb->scsb_data_reg[index],
67997656SSherry.Moore@Sun.COM 	    scsb->scsb_data_reg[index+1]);
68001708Sstevel 	if ((reset_flag == SCSB_GET_SLOT_RESET_STATUS) || (error)) {
68011708Sstevel 		mutex_exit(&scsb->scsb_mutex);
68021708Sstevel 		scsb_free_i2ctx(scsb->scsb_phandle, i2cxferp);
68031708Sstevel 		return (error);
68041708Sstevel 	}
68051708Sstevel 
68061708Sstevel 	val = scsb_fru_op(scsb, SLOT, slotnum, SCTRL_RESET_BASE,
68077656SSherry.Moore@Sun.COM 	    SCSB_FRU_OP_GET_BITVAL);
68081708Sstevel 	if (alarm_card) {
68091708Sstevel 		ac_val = scsb_fru_op(scsb, ALARM, 1, SCTRL_RESET_BASE,
68107656SSherry.Moore@Sun.COM 		    SCSB_FRU_OP_GET_BITVAL);
68111708Sstevel 	}
68121708Sstevel 	if (val && (reset_flag == SCSB_RESET_SLOT)) {
68131708Sstevel 		if (alarm_card) {
68141708Sstevel 			if (ac_val) {
68151708Sstevel 				condition_exists = 1;
68161708Sstevel 				DEBUG0("Alarm_RST# already active.\n");
68171708Sstevel 			}
68181708Sstevel #ifndef	lint
68191708Sstevel 			else
68201708Sstevel 				DEBUG1("Alarm_RST# not active! "
68217656SSherry.Moore@Sun.COM 				    "Slot%d_RST# active!\n", pslotnum);
68221708Sstevel #endif
68231708Sstevel 		} else {
68241708Sstevel 			condition_exists = 1;
68251708Sstevel 			DEBUG1("Slot%d_RST# already active!\n", pslotnum);
68261708Sstevel 		}
68271708Sstevel 	}
68281708Sstevel 	else
68291708Sstevel 		if ((val == 0) && (reset_flag == SCSB_UNRESET_SLOT)) {
68301708Sstevel 			if (alarm_card) {
68311708Sstevel 				if (!ac_val) {
68321708Sstevel 					DEBUG0("Alarm_RST# not active.\n");
68331708Sstevel 					condition_exists = 1;
68341708Sstevel 				}
68351708Sstevel #ifndef	lint
68361708Sstevel 				else
68371708Sstevel 					DEBUG1("Alarm_RST# active"
68387656SSherry.Moore@Sun.COM 					    " Slot%d_RST# not active!\n",
68397656SSherry.Moore@Sun.COM 					    pslotnum);
68401708Sstevel #endif
68411708Sstevel 			} else {
68421708Sstevel 				condition_exists = 1;
68431708Sstevel 				DEBUG1("Slot%d_RST# already not active!\n",
68447656SSherry.Moore@Sun.COM 				    pslotnum);
68451708Sstevel 			}
68461708Sstevel 		}
68471708Sstevel 
68481708Sstevel 	if (! condition_exists) {
68491708Sstevel 		i2cxferp->i2c_flags = I2C_WR;
68501708Sstevel 		i2cxferp->i2c_wlen = 2;
68511708Sstevel 		i2cxferp->i2c_wbuf[0] = scsb_fru_op(scsb, SLOT, slotnum,
68527656SSherry.Moore@Sun.COM 		    SCTRL_RESET_BASE, SCSB_FRU_OP_GET_REG);
68531708Sstevel 		if (reset_flag == SCSB_RESET_SLOT) {
68541708Sstevel 			i2cxferp->i2c_wbuf[1] =
68557656SSherry.Moore@Sun.COM 			    scsb_fru_op(scsb, SLOT, slotnum,
68567656SSherry.Moore@Sun.COM 			    SCTRL_RESET_BASE,
68577656SSherry.Moore@Sun.COM 			    SCSB_FRU_OP_GET_REGDATA) |
68587656SSherry.Moore@Sun.COM 			    scsb_fru_op(scsb, SLOT, slotnum,
68597656SSherry.Moore@Sun.COM 			    SCTRL_RESET_BASE,
68607656SSherry.Moore@Sun.COM 			    SCSB_FRU_OP_SET_REGBIT);
68611708Sstevel #ifdef	DEBUG		/* dont reset Alarm Card line unless in debug mode */
68621708Sstevel 			if (alarm_card)
68631708Sstevel 				i2cxferp->i2c_wbuf[1] |=
68647656SSherry.Moore@Sun.COM 				    scsb_fru_op(scsb, ALARM, 1,
68657656SSherry.Moore@Sun.COM 				    SCTRL_RESET_BASE,
68667656SSherry.Moore@Sun.COM 				    SCSB_FRU_OP_SET_REGBIT);
68671708Sstevel #endif
68681708Sstevel 		} else {
68691708Sstevel 			i2cxferp->i2c_wbuf[1] =
68707656SSherry.Moore@Sun.COM 			    scsb_fru_op(scsb, SLOT, slotnum,
68717656SSherry.Moore@Sun.COM 			    SCTRL_RESET_BASE,
68727656SSherry.Moore@Sun.COM 			    SCSB_FRU_OP_GET_REGDATA) &
68737656SSherry.Moore@Sun.COM 			    ~(scsb_fru_op(scsb, SLOT, slotnum,
68747656SSherry.Moore@Sun.COM 			    SCTRL_RESET_BASE,
68757656SSherry.Moore@Sun.COM 			    SCSB_FRU_OP_SET_REGBIT));
68761708Sstevel #ifdef	DEBUG		/* dont Unreset Alarm Card line unless in debug mode */
68771708Sstevel 			if (alarm_card)
68781708Sstevel 				i2cxferp->i2c_wbuf[1] &=
68797656SSherry.Moore@Sun.COM 				    scsb_fru_op(scsb, ALARM, 1,
68807656SSherry.Moore@Sun.COM 				    SCTRL_RESET_BASE,
68817656SSherry.Moore@Sun.COM 				    SCSB_FRU_OP_SET_REGBIT);
68821708Sstevel #endif
68831708Sstevel 		}
68841708Sstevel 
68851708Sstevel 		if (error = nct_i2c_transfer(scsb->scsb_phandle, i2cxferp)) {
68861708Sstevel 			scsb->scsb_i2c_errcnt++;
68871708Sstevel 			if (scsb->scsb_i2c_errcnt > scsb_err_threshold)
68881708Sstevel 				scsb->scsb_err_flag = B_TRUE; /* latch error */
68891708Sstevel 			mutex_exit(&scsb->scsb_mutex);
68901708Sstevel 			if (!(scsb->scsb_state & SCSB_SSB_PRESENT)) {
68911708Sstevel 				if (scsb->scsb_i2c_errcnt >= scsb_freeze_count)
68921708Sstevel 					scsb_freeze(scsb);
68931708Sstevel 			}
68941708Sstevel 			cmn_err(CE_WARN, "%s#%d: reset_slot: error writing to"
68957656SSherry.Moore@Sun.COM 			    " Reset regs (op=%d, data=%x)\n",
68967656SSherry.Moore@Sun.COM 			    ddi_driver_name(scsb->scsb_dev),
68977656SSherry.Moore@Sun.COM 			    ddi_get_instance(scsb->scsb_dev),
68987656SSherry.Moore@Sun.COM 			    reset_flag, i2cxferp->i2c_wbuf[1]);
68991708Sstevel 			scsb_free_i2ctx(scsb->scsb_phandle, i2cxferp);
69001708Sstevel 			return (DDI_FAILURE);
69011708Sstevel 		}
69021708Sstevel 
69031708Sstevel 		scsb->scsb_i2c_errcnt = 0;
69041708Sstevel 		/* now read back and update our scsb structure */
69051708Sstevel 		i2cxferp->i2c_flags = I2C_WR_RD;
69061708Sstevel 		i2cxferp->i2c_rlen = SCTRL_RESET_NUMREGS;
69071708Sstevel 		i2cxferp->i2c_wbuf[0] = reg;
69081708Sstevel 		i2cxferp->i2c_wlen = 1;
69091708Sstevel 		if ((error = nct_i2c_transfer(scsb->scsb_phandle,
69107656SSherry.Moore@Sun.COM 		    i2cxferp)) == 0) {
69111708Sstevel 			scsb->scsb_i2c_errcnt = 0;
69121708Sstevel 			scsb->scsb_data_reg[index]   = i2cxferp->i2c_rbuf[0];
69131708Sstevel 			scsb->scsb_data_reg[index+1] = i2cxferp->i2c_rbuf[1];
69141708Sstevel 		} else {
69151708Sstevel 			scsb->scsb_i2c_errcnt++;
69161708Sstevel 			if (scsb->scsb_i2c_errcnt > scsb_err_threshold)
69171708Sstevel 				scsb->scsb_err_flag = B_TRUE; /* latch error */
69181708Sstevel 			mutex_exit(&scsb->scsb_mutex);
69191708Sstevel 			if (!(scsb->scsb_state & SCSB_SSB_PRESENT)) {
69201708Sstevel 				if (scsb->scsb_i2c_errcnt >= scsb_freeze_count)
69211708Sstevel 					scsb_freeze(scsb);
69221708Sstevel 			}
69231708Sstevel 			cmn_err(CE_WARN, "%s#%d: scsb_reset_slot: error"
69247656SSherry.Moore@Sun.COM 			    " reading Reset regs (post reset)\n",
69257656SSherry.Moore@Sun.COM 			    ddi_driver_name(scsb->scsb_dev),
69267656SSherry.Moore@Sun.COM 			    ddi_get_instance(scsb->scsb_dev));
69271708Sstevel 			scsb_free_i2ctx(scsb->scsb_phandle, i2cxferp);
69281708Sstevel 			return (DDI_FAILURE);
69291708Sstevel 		}
69301708Sstevel 		/* XXX: P1.5 */
69311708Sstevel 		DEBUG2("post-reset regs = %x,%x\n", scsb->scsb_data_reg[index],
69327656SSherry.Moore@Sun.COM 		    scsb->scsb_data_reg[index+1]);
69331708Sstevel 		val = scsb_fru_op(scsb, SLOT, slotnum, SCTRL_RESET_BASE,
69347656SSherry.Moore@Sun.COM 		    SCSB_FRU_OP_GET_BITVAL);
69351708Sstevel #ifdef	DEBUG
69361708Sstevel 		if (alarm_card)
69371708Sstevel 			ac_val = scsb_fru_op(scsb, ALARM, 1, SCTRL_RESET_BASE,
69387656SSherry.Moore@Sun.COM 			    SCSB_FRU_OP_GET_BITVAL);
69391708Sstevel #endif
69401708Sstevel 		if (val && (reset_flag == SCSB_UNRESET_SLOT)) {
69411708Sstevel 			cmn_err(CE_WARN, "Cannot UnReset Slot %d (reg=%x)\n",
69427656SSherry.Moore@Sun.COM 			    pslotnum,
69437656SSherry.Moore@Sun.COM 			    scsb_fru_op(scsb, SLOT, slotnum,
69447656SSherry.Moore@Sun.COM 			    SCTRL_RESET_BASE,
69457656SSherry.Moore@Sun.COM 			    SCSB_FRU_OP_GET_REGDATA));
69461708Sstevel #ifdef	DEBUG
69471708Sstevel 			if (alarm_card) {
69481708Sstevel 				if (ac_val)
69491708Sstevel 					cmn_err(CE_WARN, "Cannot Unreset "
69507656SSherry.Moore@Sun.COM 					    "Alarm_RST#.\n");
69511708Sstevel 			}
69521708Sstevel #endif
69531708Sstevel 		}
69541708Sstevel 		else
69551708Sstevel 			if ((val == 0) && (reset_flag == SCSB_RESET_SLOT)) {
69561708Sstevel 				cmn_err(CE_WARN, "Cannot Reset Slot %d, "
69577656SSherry.Moore@Sun.COM 				    "reg=%x\n", pslotnum,
69587656SSherry.Moore@Sun.COM 				    scsb_fru_op(scsb, SLOT, slotnum,
69597656SSherry.Moore@Sun.COM 				    SCTRL_RESET_BASE,
69607656SSherry.Moore@Sun.COM 				    SCSB_FRU_OP_GET_REGDATA));
69611708Sstevel #ifdef	DEBUG
69621708Sstevel 				if (alarm_card) {
69631708Sstevel 					if (!ac_val)
69641708Sstevel 						cmn_err(CE_WARN, "Cannot reset "
69657656SSherry.Moore@Sun.COM 						    "Alarm_RST#.\n");
69661708Sstevel 				}
69671708Sstevel #endif
69681708Sstevel 			}
69691708Sstevel 	}
69701708Sstevel 
69711708Sstevel 	mutex_exit(&scsb->scsb_mutex);
69721708Sstevel 	scsb_free_i2ctx(scsb->scsb_phandle, i2cxferp);
69731708Sstevel 
69741708Sstevel 	return (error);
69751708Sstevel }
69761708Sstevel 
69771708Sstevel int
scsb_connect_slot(scsb_state_t * scsb,int pslotnum,int healthy)69781708Sstevel scsb_connect_slot(scsb_state_t *scsb, int pslotnum, int healthy)
69791708Sstevel {
69801708Sstevel 	int slotnum, count = 0, val;
69811708Sstevel 	int slot_flag = 0;
69821708Sstevel 
69831708Sstevel 	/*
69841708Sstevel 	 * If Power needs to be handled, it should be done here.
69851708Sstevel 	 * Since there is no power handling for now, lets disable
69861708Sstevel 	 * reset, wait for healthy to come on and then call it
69871708Sstevel 	 * connected.
69881708Sstevel 	 * If HLTHY# does not come on (in how long is the question)
69891708Sstevel 	 * then we stay disconnected.
69901708Sstevel 	 */
69911708Sstevel 	slotnum = tonga_psl_to_ssl(scsb, pslotnum);
69921708Sstevel 
69931708Sstevel 	/*
69941708Sstevel 	 * P1.5 doesnt require polling healthy as we get an
69951708Sstevel 	 * interrupt. So we could just update our state as disconnected
69961708Sstevel 	 * and return waiting for the healthy# interrupt. To make it
69971708Sstevel 	 * more efficient, lets poll for healthy# a short while since we are
69981708Sstevel 	 * in the interrupt context anyway. If we dont get a healthy# we
69991708Sstevel 	 * return, and then wait for the interrupt. Probably the warning
70001708Sstevel 	 * message needs to be removed then. Need a PROM check flag here.
70011708Sstevel 	 */
70021708Sstevel 	while ((healthy == B_FALSE) && (count < scsb_healthy_poll_count)) {
70031708Sstevel 		if (scsb_read_bhealthy(scsb) != 0)
70041708Sstevel 			return (DDI_FAILURE);
70051708Sstevel 		val = scsb_fru_op(scsb, SLOT, slotnum, SCTRL_BHLTHY_BASE,
70067656SSherry.Moore@Sun.COM 		    SCSB_FRU_OP_GET_BITVAL);
70071708Sstevel 		if (val) {
70081708Sstevel 			healthy = B_TRUE;
70091708Sstevel 			break;
70101708Sstevel 		}
70111708Sstevel 		count++;
70121708Sstevel 		drv_usecwait(100);	/* cant delay(9f) in intr context */
70131708Sstevel 	}
70141708Sstevel 
70151708Sstevel 	if (healthy == B_FALSE && count == scsb_healthy_poll_count) {
70161708Sstevel 		if (scsb_debug & 0x00004000)
70171708Sstevel 			cmn_err(CE_WARN, "%s#%d: no HEALTHY# signal on"
70187656SSherry.Moore@Sun.COM 			    " slot %d", ddi_driver_name(scsb->scsb_dev),
70197656SSherry.Moore@Sun.COM 			    ddi_get_instance(scsb->scsb_dev), pslotnum);
70201708Sstevel 	}
70211708Sstevel 
70221708Sstevel 	if ((scsb_is_alarm_card_slot(scsb, pslotnum) == B_TRUE) &&
70237656SSherry.Moore@Sun.COM 	    (scsb->scsb_hsc_state & SCSB_ALARM_CARD_PRES))
70241708Sstevel 		slot_flag = ALARM_CARD_ON_SLOT;
70251708Sstevel 	return (hsc_slot_occupancy(pslotnum, 1, slot_flag, healthy));
70261708Sstevel }
70271708Sstevel 
70281708Sstevel int
scsb_disconnect_slot(scsb_state_t * scsb,int occupied,int slotnum)70291708Sstevel scsb_disconnect_slot(scsb_state_t *scsb, int occupied, int slotnum)
70301708Sstevel {
70311708Sstevel 	int slot_flag = 0;
70321708Sstevel 
70331708Sstevel 	/* Reset is must at extraction. Move on even if failure. */
70341708Sstevel 	if (scsb_reset_slot(scsb, slotnum, SCSB_RESET_SLOT) != 0) {
70351708Sstevel 		/*
70361708Sstevel 		 * If board is still in slot, which means there is a manual
70371708Sstevel 		 * disconnection in progress, return failure.
70381708Sstevel 		 * Otherwise, a board was removed anyway; so we need to
70391708Sstevel 		 * update the status and move on.
70401708Sstevel 		 */
70411708Sstevel 		if (occupied == B_TRUE)
70421708Sstevel 			return (DDI_FAILURE);
70431708Sstevel 	}
70441708Sstevel 	/*
70451708Sstevel 	 * the following bug needs to be fixed.
70461708Sstevel 	 * When this function is called from scsb_intr, scsb_state already
70471708Sstevel 	 * clears the 'AC card present' bit.
70481708Sstevel 	 * However, hsc module doesn't depend on slot_flag during removal.
70491708Sstevel 	 */
70501708Sstevel 	if ((scsb_is_alarm_card_slot(scsb, slotnum) == B_TRUE) &&
70517656SSherry.Moore@Sun.COM 	    (scsb->scsb_hsc_state & SCSB_ALARM_CARD_PRES))
70521708Sstevel 		slot_flag = ALARM_CARD_ON_SLOT;
70531708Sstevel 	return (hsc_slot_occupancy(slotnum, occupied, slot_flag, B_FALSE));
70541708Sstevel }
70551708Sstevel 
70561708Sstevel static int
scsb_is_alarm_card_slot(scsb_state_t * scsb,int slotnum)70571708Sstevel scsb_is_alarm_card_slot(scsb_state_t *scsb, int slotnum)
70581708Sstevel {
70591708Sstevel 	return ((scsb->ac_slotnum == slotnum)? B_TRUE:B_FALSE);
70601708Sstevel }
70611708Sstevel 
70621708Sstevel /*
70631708Sstevel  * Invoked both by the hsc and the scsb module to exchanges necessary
70641708Sstevel  * information regarding the alarm card.
70651708Sstevel  * scsb calls this function to unconfigure the alarm card while the
70661708Sstevel  * hsc calls this function at different times to check busy status,
70671708Sstevel  * and during post hotswap insert operation so that the user process
70681708Sstevel  * if one waiting can configure the alarm card.
70691708Sstevel  */
70701708Sstevel int
scsb_hsc_ac_op(scsb_state_t * scsb,int pslotnum,int op)70711708Sstevel scsb_hsc_ac_op(scsb_state_t *scsb, int pslotnum, int op)
70721708Sstevel {
70731708Sstevel 	int		rc = B_FALSE;
70741708Sstevel 	uint32_t	event_code;
70751708Sstevel 
70761708Sstevel 	if (!(scsb->scsb_hsc_state & SCSB_HSC_INIT &&
70777656SSherry.Moore@Sun.COM 	    scsb->scsb_hsc_state & SCSB_ALARM_CARD_PRES)) {
70781708Sstevel 		cmn_err(CE_WARN,
70797656SSherry.Moore@Sun.COM 		    "scsb: HSC not initialized or AC not present!");
70801708Sstevel 		return (rc);
70811708Sstevel 	}
70821708Sstevel 	switch (op) {
70831708Sstevel 		/* hsc -> scsb */
70841708Sstevel 		case SCSB_HSC_AC_BUSY:
70851708Sstevel 			if (scsb->scsb_hsc_state & SCSB_ALARM_CARD_IN_USE)
70861708Sstevel 				rc = B_TRUE;
70871708Sstevel 			break;
70881708Sstevel 
70891708Sstevel 		/* API -> scsb */
70901708Sstevel 		/*
70911708Sstevel 		 * NOTE: this could be called multiple times from envmond if
70921708Sstevel 		 * the daemon is reinitialized with SIGHUP, or stopped and
70931708Sstevel 		 * restarted.
70941708Sstevel 		 */
70951708Sstevel 		case SCSB_HSC_AC_SET_BUSY:
70961708Sstevel 			DEBUG0("AC SET BUSY\n");
70971708Sstevel 			if (scsb_debug & 0x00010000) {
70981708Sstevel 				cmn_err(CE_NOTE,
70997656SSherry.Moore@Sun.COM 				    "scsb_hsc_ac_op(SCSB_HSC_AC_SET_BUSY)");
71001708Sstevel 			}
71011708Sstevel 			scsb->scsb_hsc_state |= SCSB_ALARM_CARD_IN_USE;
71021708Sstevel 			rc = B_TRUE;
71031708Sstevel 			break;
71041708Sstevel 
71051708Sstevel 		/* hsc -> scsb */
71061708Sstevel 		case SCSB_HSC_AC_CONFIGURED:
71071708Sstevel 			DEBUG0("AC configured\n");
71081708Sstevel 			if (scsb_debug & 0x00010000) {
71091708Sstevel 				cmn_err(CE_NOTE,
71101708Sstevel 				"scsb_hsc_ac_op(SCSB_HSC_AC_CONFIGURED)");
71111708Sstevel 			}
71121708Sstevel 			/*
71131708Sstevel 			 * wakeup anyone waiting on AC to be configured
71141708Sstevel 			 * Send the ALARM_CARD_CONFIGURE Event to all scsb
71151708Sstevel 			 * open streams.
71161708Sstevel 			 */
71171708Sstevel 			event_code = SCTRL_EVENT_ALARM_INSERTION;
71181708Sstevel 			(void) scsb_queue_ops(scsb, QPUT_INT32, 1,
71197656SSherry.Moore@Sun.COM 			    &event_code, "scsb_hsc_ac_op");
71201708Sstevel 			rc = B_TRUE;
71211708Sstevel 			break;
71221708Sstevel 
71231708Sstevel 		/* hsc -> scsb */
71241708Sstevel 		case SCSB_HSC_AC_REMOVAL_ALERT:
71251708Sstevel 			DEBUG0("AC removal alert\n");
71261708Sstevel 			if (scsb_debug & 0x00010000) {
71271708Sstevel 				cmn_err(CE_NOTE,
71281708Sstevel 				"scsb_hsc_ac_op(SCSB_HSC_AC_REMOVAL_ALERT)");
71291708Sstevel 			}
71301708Sstevel 			/*
71311708Sstevel 			 * Inform (envmond)alarmcard.so that it should save
71321708Sstevel 			 * the AC configuration, stop the
71331708Sstevel 			 * heartbeat, and shutdown the RSC link.
71341708Sstevel 			 */
71351708Sstevel 			event_code = SCTRL_EVENT_ALARM_REMOVAL;
71361708Sstevel 			(void) scsb_queue_ops(scsb, QPUT_INT32, 1,
71377656SSherry.Moore@Sun.COM 			    &event_code, "scsb_hsc_ac_op");
71381708Sstevel 			rc = B_TRUE;
71391708Sstevel 			break;
71401708Sstevel 
71411708Sstevel 		/* API -> scsb -> hsc */
71421708Sstevel 		case SCSB_HSC_AC_UNCONFIGURE:
71431708Sstevel 			DEBUG0("AC unconfigure\n");
71441708Sstevel 			if (scsb_debug & 0x00010000) {
71451708Sstevel 				cmn_err(CE_NOTE,
71467656SSherry.Moore@Sun.COM 				    "scsb_hsc_ac_op(SCSB_HSC_AC_UNCONFIG"
71477656SSherry.Moore@Sun.COM 				    "URE), AC NOT BUSY");
71481708Sstevel 			}
71491708Sstevel 			/*
71501708Sstevel 			 * send notification back to HSC to
71511708Sstevel 			 * unconfigure the AC, now that the env monitor
71521708Sstevel 			 * has given permission to do so.
71531708Sstevel 			 */
71541708Sstevel 			scsb->scsb_hsc_state &= ~SCSB_ALARM_CARD_IN_USE;
7155*11311SSurya.Prakki@Sun.COM 			hsc_ac_op((int)scsb->scsb_instance, pslotnum,
71567656SSherry.Moore@Sun.COM 			    SCSB_HSC_AC_UNCONFIGURE, NULL);
71571708Sstevel 			rc = B_TRUE;
71581708Sstevel 			break;
71591708Sstevel 		default:
71601708Sstevel 			break;
71611708Sstevel 	}
71621708Sstevel 
71631708Sstevel 	return (rc);
71641708Sstevel }
71651708Sstevel 
71661708Sstevel static void
scsb_healthy_intr(scsb_state_t * scsb,int pslotnum)71671708Sstevel scsb_healthy_intr(scsb_state_t *scsb, int pslotnum)
71681708Sstevel {
71691708Sstevel 	int val, slotnum;
71701708Sstevel 	int healthy = B_FALSE;
71711708Sstevel 
71721708Sstevel 	DEBUG1("Healthy Intr on slot %d\n", pslotnum);
71731708Sstevel 	/*
71741708Sstevel 	 * The interrupt source register can have the healthy
71751708Sstevel 	 * bit set for non-existing slot, e.g slot 7 on Tonga.
71761708Sstevel 	 * It can also be seen on the Tonga CPU slot. So we make
71771708Sstevel 	 * sure we have a valid slot before proceeding.
71781708Sstevel 	 */
71791708Sstevel 	if (scsb->scsb_state & SCSB_IS_TONGA) {
71801708Sstevel 		if (pslotnum > TG_MAX_SLOTS || pslotnum == SC_TG_CPU_SLOT) {
71811708Sstevel 			if (scsb_debug & 0x08000000)
71821708Sstevel 				cmn_err(CE_NOTE, "Healthy interrupt bit set for"
71837656SSherry.Moore@Sun.COM 				    " slot %d", pslotnum);
71841708Sstevel 		return;
71851708Sstevel 		}
71861708Sstevel 	} else {
71871708Sstevel 		if (pslotnum > MC_MAX_SLOTS || pslotnum == SC_MC_CPU_SLOT ||
71887656SSherry.Moore@Sun.COM 		    (scsb->scsb_hsc_state & SCSB_HSC_CTC_PRES &&
71897656SSherry.Moore@Sun.COM 		    pslotnum == SC_MC_CTC_SLOT)) {
71901708Sstevel 			if (scsb_debug & 0x08000000)
71911708Sstevel 				cmn_err(CE_NOTE, "Healthy interrupt bit set for"
71927656SSherry.Moore@Sun.COM 				    " slot %d", pslotnum);
71931708Sstevel 		return;
71941708Sstevel 		}
71951708Sstevel 	}
71961708Sstevel 
71971708Sstevel 	/*
71981708Sstevel 	 * The board healthy registers are already read before entering
71991708Sstevel 	 * this routine
72001708Sstevel 	 */
72011708Sstevel 	slotnum = tonga_psl_to_ssl(scsb, pslotnum);
72021708Sstevel 
72031708Sstevel 	/*
72041708Sstevel 	 * P1.5. Following works since slots 1 through 8 are in the same reg
72051708Sstevel 	 */
72061708Sstevel 	val = scsb_fru_op(scsb, SLOT, slotnum, SCTRL_BHLTHY_BASE,
72077656SSherry.Moore@Sun.COM 	    SCSB_FRU_OP_GET_BITVAL);
72081708Sstevel 	if (val)
72091708Sstevel 		healthy = B_TRUE;
7210*11311SSurya.Prakki@Sun.COM 	(void) scsb_hsc_board_healthy(pslotnum, healthy);
72111708Sstevel }
72121708Sstevel 
72131708Sstevel /*
72141708Sstevel  * This function will try to read from scsb irrespective of whether
72151708Sstevel  * SSB is present or SCB is frozen, to get the health kstat information.
72161708Sstevel  */
72171708Sstevel static int
scsb_blind_read(scsb_state_t * scsb,int op,uchar_t reg,int len,uchar_t * rwbuf,int i2c_alloc)72181708Sstevel scsb_blind_read(scsb_state_t *scsb, int op, uchar_t reg, int len,
72191708Sstevel 				uchar_t *rwbuf, int i2c_alloc)
72201708Sstevel {
72211708Sstevel 	i2c_transfer_t	*i2cxferp;
72221708Sstevel 	int		i, rlen, wlen, error = 0;
72231708Sstevel 
72241708Sstevel 	if (scsb_debug & 0x0800) {
72251708Sstevel 		cmn_err(CE_NOTE, "scsb_rdwr_register(scsb,%s,%x,%x,buf):",
72267656SSherry.Moore@Sun.COM 		    (op == I2C_WR) ? "write" : "read",  reg, len);
72271708Sstevel 	}
72281708Sstevel 
72291708Sstevel 	if (i2c_alloc) {
72301708Sstevel 		i2cxferp = scsb_alloc_i2ctx(scsb->scsb_phandle, I2C_NOSLEEP);
72311708Sstevel 		if (i2cxferp == NULL) {
72321708Sstevel 			if (scsb_debug & 0x0042)
72331708Sstevel 				cmn_err(CE_WARN, "scsb_rdwr_register: "
72347656SSherry.Moore@Sun.COM 				    "i2ctx allocation failure");
72351708Sstevel 			return (ENOMEM);
72361708Sstevel 		}
72371708Sstevel 	} else {
72381708Sstevel 		i2cxferp = scsb->scsb_i2ctp;
72391708Sstevel 	}
72401708Sstevel 	switch (op) {
72411708Sstevel 	case I2C_WR:
72421708Sstevel 		wlen = len + 1;	/* add the address */
72431708Sstevel 		rlen = 0;
72441708Sstevel 		i2cxferp->i2c_wbuf[0] = reg;
72451708Sstevel 		for (i = 0; i < len; ++i) {
72461708Sstevel 				i2cxferp->i2c_wbuf[1 + i] = rwbuf[i];
72471708Sstevel 			if (scsb_debug & 0x0080)
72481708Sstevel 				cmn_err(CE_NOTE,
72491708Sstevel 				"scsb_rdwr_register: writing rwbuf[%d]=0x%x",
72507656SSherry.Moore@Sun.COM 				    i, rwbuf[i]);
72511708Sstevel 		}
72521708Sstevel 		break;
72531708Sstevel 	case I2C_WR_RD:
72541708Sstevel 		wlen = 1;	/* for the address */
72551708Sstevel 		rlen = len;
72561708Sstevel 		i2cxferp->i2c_wbuf[0] = reg;
72571708Sstevel 		break;
72581708Sstevel 	default:
72591708Sstevel 		if (i2c_alloc)
72601708Sstevel 			scsb_free_i2ctx(scsb->scsb_phandle, i2cxferp);
72611708Sstevel 		return (EINVAL);
72621708Sstevel 	}
72631708Sstevel 	/* select the register address */
72641708Sstevel 	i2cxferp->i2c_flags = op;
72651708Sstevel 	i2cxferp->i2c_rlen = rlen;
72661708Sstevel 	i2cxferp->i2c_wlen = wlen;
72671708Sstevel 	i2cxferp->i2c_wbuf[0] = reg;
72681708Sstevel 	scsb->scsb_kstat_flag = B_TRUE;	/* we did a i2c transaction */
72691708Sstevel 	if (error = nct_i2c_transfer(scsb->scsb_phandle, i2cxferp)) {
72701708Sstevel 		error = EIO;
72711708Sstevel 	} else if (rlen) {
72721708Sstevel 		/* copy to rwbuf[] */
72731708Sstevel 		for (i = 0; i < len; ++i) {
72741708Sstevel 			rwbuf[i] = i2cxferp->i2c_rbuf[i];
72751708Sstevel 			if (scsb_debug & 0x0080)
72761708Sstevel 				cmn_err(CE_NOTE,
72771708Sstevel 				"scsb_rdwr_register: read rwbuf[%d]=0x%x",
72787656SSherry.Moore@Sun.COM 				    i, rwbuf[i]);
72791708Sstevel 		}
72801708Sstevel 	}
72811708Sstevel 	if (i2c_alloc)
72821708Sstevel 		scsb_free_i2ctx(scsb->scsb_phandle, i2cxferp);
72831708Sstevel 	if (error) {
72841708Sstevel 		scsb->scsb_i2c_errcnt++;
72851708Sstevel 		if (scsb->scsb_i2c_errcnt > scsb_err_threshold)
72861708Sstevel 			scsb->scsb_err_flag = B_TRUE; /* latch error */
72871708Sstevel 	} else {
72881708Sstevel 		scsb->scsb_i2c_errcnt = 0;
72891708Sstevel 	}
72901708Sstevel 
72911708Sstevel 	return (error);
72921708Sstevel }
72931708Sstevel 
72941708Sstevel /*
72951708Sstevel  * This function will quiesce the PSM_INT line by masking the
72961708Sstevel  * global PSM_INT and writing 1 to SCB_INIT ( for P1.5 and later )
72971708Sstevel  * This effectively translates to writing 0x20 to 0xE1 register.
72981708Sstevel  */
72991708Sstevel static int
scsb_quiesce_psmint(scsb_state_t * scsb)73001708Sstevel scsb_quiesce_psmint(scsb_state_t *scsb)
73011708Sstevel {
73021708Sstevel 	register int	i;
73031708Sstevel 	uchar_t	reg, wdata = 0;
73041708Sstevel 	uchar_t	tmp_reg, intr_addr, clr_bits = 0;
73051708Sstevel 	int error, iid, intr_idx, offset;
73061708Sstevel 
73071708Sstevel 	/*
73081708Sstevel 	 * For P1.5, set the SCB_INIT bit in the System Command register,
73091708Sstevel 	 * and disable global PSM_INT. Before this we need to read the
73101708Sstevel 	 * interrupt source register corresponding to INIT_SCB and
73111708Sstevel 	 * clear if set.
73121708Sstevel 	 */
73131708Sstevel 	if (IS_SCB_P15) {
73141708Sstevel 		/*
73151708Sstevel 		 * Read INTSRC6 and write back 0x20 in case INIT_SCB is set
73161708Sstevel 		 */
73171708Sstevel 		intr_addr = SCSB_REG_ADDR(SCTRL_INTSRC_BASE);
73181708Sstevel 		tmp_reg = SCSB_REG_ADDR(SCTRL_INTSRC_SCB_P15);
73191708Sstevel 		iid = SCSB_REG_INDEX(intr_addr);
73201708Sstevel 		intr_idx = SCSB_REG_INDEX(tmp_reg) - iid;
73211708Sstevel 		offset = FRU_OFFSET(SCTRL_EVENT_SCB, SCTRL_INTPTR_BASE);
73221708Sstevel 		clr_bits = 1 << offset;
73231708Sstevel 
73241708Sstevel 		error = scsb_rdwr_register(scsb, I2C_WR_RD, tmp_reg,
73257656SSherry.Moore@Sun.COM 		    1, &scb_intr_regs[intr_idx], 0);
73261708Sstevel 		/*
73271708Sstevel 		 * Now mask the global PSM_INT and write INIT_SCB in case
73281708Sstevel 		 * this is an INIT_SCB interrupt
73291708Sstevel 		 */
73301708Sstevel 		wdata = 1 << SYS_OFFSET(SCTRL_SYS_SCB_INIT);
73311708Sstevel 		i = SYS_REG_INDEX(SCTRL_SYS_SCB_INIT, SCTRL_SYS_CMD_BASE);
73321708Sstevel 		reg = SCSB_REG_ADDR(i);
73331708Sstevel 		error = scsb_rdwr_register(scsb, I2C_WR, reg, 1,
73347656SSherry.Moore@Sun.COM 		    &wdata, 0);
73351708Sstevel 
73361708Sstevel 		if (scb_intr_regs[intr_idx] & clr_bits) {
73371708Sstevel 			/*
73381708Sstevel 			 * There is an SCB_INIT interrupt, which we must clear
73391708Sstevel 			 * first to keep SCB_INIT from keeping PSM_INT asserted.
73401708Sstevel 			 */
73411708Sstevel 			error = scsb_rdwr_register(scsb, I2C_WR, tmp_reg,
73427656SSherry.Moore@Sun.COM 			    1, &clr_bits, 0);
73431708Sstevel 		}
73441708Sstevel 
73451708Sstevel 		if (error) {
73461708Sstevel 			cmn_err(CE_WARN, "scsb%d:scsb_quiesce_psmint: "
73477656SSherry.Moore@Sun.COM 			    " I2C TRANSFER Failed", scsb->scsb_instance);
73481708Sstevel 			if (scsb_debug & 0x0006) {
73491708Sstevel 				cmn_err(CE_NOTE, "scsb_attach: "
73507656SSherry.Moore@Sun.COM 				    " failed to set SCB_INIT");
73511708Sstevel 			}
73521708Sstevel 		}
73531708Sstevel 		scsb->scsb_state &= ~SCSB_PSM_INT_ENABLED;
73541708Sstevel 	} else { /* P1.0 or earlier */
73551708Sstevel 		/*
73561708Sstevel 		 * read the interrupt source registers, and then
73571708Sstevel 		 * write them back.
73581708Sstevel 		 */
73591708Sstevel 		/* read the interrupt register from scsb */
73601708Sstevel 		if (error = scsb_rdwr_register(scsb, I2C_WR_RD, intr_addr,
73617656SSherry.Moore@Sun.COM 		    SCTRL_INTR_NUMREGS, scb_intr_regs, 0)) {
73621708Sstevel 			cmn_err(CE_WARN, "scsb_intr: "
73637656SSherry.Moore@Sun.COM 			    " Failed read of interrupt registers.");
73641708Sstevel 			scsb->scsb_state &= ~SCSB_IN_INTR;
73651708Sstevel 		}
73661708Sstevel 
73671708Sstevel 		/*
73681708Sstevel 		 * Write to the interrupt source registers to stop scsb
73691708Sstevel 		 * from interrupting.
73701708Sstevel 		 */
73711708Sstevel 		if (error = scsb_rdwr_register(scsb, I2C_WR, intr_addr,
73727656SSherry.Moore@Sun.COM 		    SCTRL_INTR_NUMREGS, scb_intr_regs, 0)) {
73731708Sstevel 			cmn_err(CE_WARN, "scsb_intr: Failed write to interrupt"
73747656SSherry.Moore@Sun.COM 			    " registers.");
73751708Sstevel 			scsb->scsb_state &= ~SCSB_IN_INTR;
73761708Sstevel 		}
73771708Sstevel 
73781708Sstevel 	}
73791708Sstevel 
73801708Sstevel 	if (error)
73811708Sstevel 		return (DDI_FAILURE);
73821708Sstevel 	else
73831708Sstevel 		return (DDI_SUCCESS);
73841708Sstevel }
73851708Sstevel 
73861708Sstevel /*
73871708Sstevel  * Enables or disables the global PSM_INT interrupt for P1.5, depending
73881708Sstevel  * on the flag, flag = 0 => disable, else enable.
73891708Sstevel  */
73901708Sstevel static int
scsb_toggle_psmint(scsb_state_t * scsb,int enable)73911708Sstevel scsb_toggle_psmint(scsb_state_t *scsb, int enable)
73921708Sstevel {
73931708Sstevel 	int i;
73941708Sstevel 	uchar_t reg, on = 0, rmask = 0x0, off = 0;
73951708Sstevel 
73961708Sstevel 	if (enable == B_TRUE) {
73971708Sstevel 		on = 1 << SYS_OFFSET(SCTRL_SYS_PSM_INT_ENABLE);
73981708Sstevel 	} else {
73991708Sstevel 		off = 1 << SYS_OFFSET(SCTRL_SYS_PSM_INT_ENABLE);
74001708Sstevel 	}
74011708Sstevel 
74021708Sstevel 	i = SYS_REG_INDEX(SCTRL_SYS_PSM_INT_ENABLE, SCTRL_SYS_CMD_BASE);
74031708Sstevel 	reg = SCSB_REG_ADDR(i);
74041708Sstevel 	if (scsb_write_mask(scsb, reg, rmask, on, off)) {
74051708Sstevel 		cmn_err(CE_WARN, "scsb_toggle_psmint: Cannot turn %s PSM_INT",
74067656SSherry.Moore@Sun.COM 		    enable == 1 ? "on" : "off");
74071708Sstevel 		return (DDI_FAILURE);
74081708Sstevel 	}
74091708Sstevel 	if (enable == 0) {
74101708Sstevel 		scsb->scsb_state &= ~SCSB_PSM_INT_ENABLED;
74111708Sstevel 	} else {
74121708Sstevel 		scsb->scsb_state |= SCSB_PSM_INT_ENABLED;
74131708Sstevel 	}
74141708Sstevel 
74151708Sstevel 	return (DDI_SUCCESS);
74161708Sstevel }
74171708Sstevel 
74181708Sstevel /*
74191708Sstevel  * This routine is to be used by all the drivers using this i2c bus
74201708Sstevel  * to synchronize their transfer operations.
74211708Sstevel  */
74221708Sstevel int
nct_i2c_transfer(i2c_client_hdl_t i2c_hdl,i2c_transfer_t * i2c_tran)74231708Sstevel nct_i2c_transfer(i2c_client_hdl_t i2c_hdl, i2c_transfer_t *i2c_tran)
74241708Sstevel {
74251708Sstevel 	int retval, initmux = nct_mutex_init;
74261708Sstevel 
74271708Sstevel 	/*
74281708Sstevel 	 * If scsb interrupt mutex is initialized, also hold the
74291708Sstevel 	 * interrupt mutex to let the i2c_transfer() to complete
74301708Sstevel 	 */
74311708Sstevel 
74321708Sstevel 	if (initmux & MUTEX_INIT) {
74331708Sstevel 		mutex_enter(scb_intr_mutex);
74341708Sstevel 	}
74351708Sstevel 
74361708Sstevel 	retval = i2c_transfer(i2c_hdl, i2c_tran);
74371708Sstevel 
74381708Sstevel 	if (initmux & MUTEX_INIT) {
74391708Sstevel 		mutex_exit(scb_intr_mutex);
74401708Sstevel 	}
74411708Sstevel 
74421708Sstevel 	return (retval);
74431708Sstevel }
7444