xref: /titanic_41/usr/src/uts/sun4u/starcat/io/drmach.c (revision 8682d1ef2a0960ed5a9f05b9448eaa3e68ac931f)
103831d35Sstevel /*
203831d35Sstevel  * CDDL HEADER START
303831d35Sstevel  *
403831d35Sstevel  * The contents of this file are subject to the terms of the
525cf1a30Sjl139090  * Common Development and Distribution License (the "License").
625cf1a30Sjl139090  * You may not use this file except in compliance with the License.
703831d35Sstevel  *
803831d35Sstevel  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
903831d35Sstevel  * or http://www.opensolaris.org/os/licensing.
1003831d35Sstevel  * See the License for the specific language governing permissions
1103831d35Sstevel  * and limitations under the License.
1203831d35Sstevel  *
1303831d35Sstevel  * When distributing Covered Code, include this CDDL HEADER in each
1403831d35Sstevel  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
1503831d35Sstevel  * If applicable, add the following below this CDDL HEADER, with the
1603831d35Sstevel  * fields enclosed by brackets "[]" replaced with your own identifying
1703831d35Sstevel  * information: Portions Copyright [yyyy] [name of copyright owner]
1803831d35Sstevel  *
1903831d35Sstevel  * CDDL HEADER END
2003831d35Sstevel  */
2107d06da5SSurya Prakki 
2203831d35Sstevel /*
2356f33205SJonathan Adams  * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
2403831d35Sstevel  * Use is subject to license terms.
2503831d35Sstevel  */
2603831d35Sstevel 
2703831d35Sstevel #include <sys/note.h>
2803831d35Sstevel #include <sys/debug.h>
2903831d35Sstevel #include <sys/types.h>
3003831d35Sstevel #include <sys/varargs.h>
3103831d35Sstevel #include <sys/errno.h>
3203831d35Sstevel #include <sys/cred.h>
3303831d35Sstevel #include <sys/dditypes.h>
3403831d35Sstevel #include <sys/devops.h>
3503831d35Sstevel #include <sys/modctl.h>
3603831d35Sstevel #include <sys/poll.h>
3703831d35Sstevel #include <sys/conf.h>
3803831d35Sstevel #include <sys/ddi.h>
3903831d35Sstevel #include <sys/sunddi.h>
4003831d35Sstevel #include <sys/sunndi.h>
4103831d35Sstevel #include <sys/ndi_impldefs.h>
4203831d35Sstevel #include <sys/stat.h>
4303831d35Sstevel #include <sys/kmem.h>
4403831d35Sstevel #include <sys/vmem.h>
4503831d35Sstevel #include <sys/disp.h>
4603831d35Sstevel #include <sys/processor.h>
4703831d35Sstevel #include <sys/cheetahregs.h>
4803831d35Sstevel #include <sys/cpuvar.h>
4903831d35Sstevel #include <sys/mem_config.h>
5003831d35Sstevel #include <sys/ddi_impldefs.h>
5103831d35Sstevel #include <sys/systm.h>
5203831d35Sstevel #include <sys/machsystm.h>
5303831d35Sstevel #include <sys/autoconf.h>
5403831d35Sstevel #include <sys/cmn_err.h>
5503831d35Sstevel #include <sys/sysmacros.h>
5603831d35Sstevel #include <sys/x_call.h>
5703831d35Sstevel #include <sys/promif.h>
5803831d35Sstevel #include <sys/prom_plat.h>
5903831d35Sstevel #include <sys/membar.h>
6003831d35Sstevel #include <vm/seg_kmem.h>
6103831d35Sstevel #include <sys/mem_cage.h>
6203831d35Sstevel #include <sys/stack.h>
6303831d35Sstevel #include <sys/archsystm.h>
6403831d35Sstevel #include <vm/hat_sfmmu.h>
6503831d35Sstevel #include <sys/pte.h>
6603831d35Sstevel #include <sys/mmu.h>
6703831d35Sstevel #include <sys/cpu_module.h>
6803831d35Sstevel #include <sys/obpdefs.h>
6903831d35Sstevel #include <sys/mboxsc.h>
7003831d35Sstevel #include <sys/plat_ecc_dimm.h>
7103831d35Sstevel 
7203831d35Sstevel #include <sys/hotplug/hpctrl.h>		/* XXX should be included by schpc.h */
7303831d35Sstevel #include <sys/schpc.h>
7403831d35Sstevel #include <sys/pci.h>
7503831d35Sstevel 
7603831d35Sstevel #include <sys/starcat.h>
7703831d35Sstevel #include <sys/cpu_sgnblk_defs.h>
7803831d35Sstevel #include <sys/drmach.h>
7903831d35Sstevel #include <sys/dr_util.h>
8003831d35Sstevel #include <sys/dr_mbx.h>
8103831d35Sstevel #include <sys/sc_gptwocfg.h>
8203831d35Sstevel #include <sys/iosramreg.h>
8303831d35Sstevel #include <sys/iosramio.h>
8403831d35Sstevel #include <sys/iosramvar.h>
8503831d35Sstevel #include <sys/axq.h>
8603831d35Sstevel #include <sys/post/scat_dcd.h>
8703831d35Sstevel #include <sys/kobj.h>
8803831d35Sstevel #include <sys/taskq.h>
8903831d35Sstevel #include <sys/cmp.h>
9003831d35Sstevel #include <sys/sbd_ioctl.h>
9103831d35Sstevel 
9203831d35Sstevel #include <sys/sysevent.h>
9303831d35Sstevel #include <sys/sysevent/dr.h>
9403831d35Sstevel #include <sys/sysevent/eventdefs.h>
9503831d35Sstevel 
9603831d35Sstevel #include <sys/pci/pcisch.h>
9703831d35Sstevel #include <sys/pci/pci_regs.h>
9803831d35Sstevel 
9903831d35Sstevel #include <sys/ontrap.h>
10003831d35Sstevel 
10103831d35Sstevel /* defined in ../ml/drmach.il.cpp */
10203831d35Sstevel extern void		bcopy32_il(uint64_t, uint64_t);
10303831d35Sstevel extern void		flush_ecache_il(int64_t physaddr, int size, int linesz);
10403831d35Sstevel extern void		flush_dcache_il(void);
10503831d35Sstevel extern void		flush_icache_il(void);
10603831d35Sstevel extern void		flush_pcache_il(void);
10703831d35Sstevel 
10803831d35Sstevel /* defined in ../ml/drmach_asm.s */
10903831d35Sstevel extern uint64_t		lddmcdecode(uint64_t physaddr);
11003831d35Sstevel extern uint64_t		lddsafconfig(void);
11103831d35Sstevel 
11203831d35Sstevel /* XXX here until provided by sys/dman.h */
11303831d35Sstevel extern int man_dr_attach(dev_info_t *);
11403831d35Sstevel extern int man_dr_detach(dev_info_t *);
11503831d35Sstevel 
11603831d35Sstevel #define	DRMACH_BNUM2EXP(bnum)		((bnum) >> 1)
11703831d35Sstevel #define	DRMACH_BNUM2SLOT(bnum)		((bnum) & 1)
11803831d35Sstevel #define	DRMACH_EXPSLOT2BNUM(exp, slot)	(((exp) << 1) + (slot))
11903831d35Sstevel 
12003831d35Sstevel #define	DRMACH_SLICE_MASK		0x1Full
12103831d35Sstevel #define	DRMACH_SLICE_TO_PA(s)		(((s) & DRMACH_SLICE_MASK) << 37)
12203831d35Sstevel #define	DRMACH_PA_TO_SLICE(a)		(((a) >> 37) & DRMACH_SLICE_MASK)
12303831d35Sstevel 
12403831d35Sstevel /*
12503831d35Sstevel  * DRMACH_MEM_SLICE_SIZE and DRMACH_MEM_USABLE_SLICE_SIZE define the
12603831d35Sstevel  * available address space and the usable address space for every slice.
12703831d35Sstevel  * There must be a distinction between the available and usable do to a
12803831d35Sstevel  * restriction imposed by CDC memory size.
12903831d35Sstevel  */
13003831d35Sstevel 
13103831d35Sstevel #define	DRMACH_MEM_SLICE_SIZE		(1ull << 37)	/* 128GB */
13203831d35Sstevel #define	DRMACH_MEM_USABLE_SLICE_SIZE	(1ull << 36)	/* 64GB */
13303831d35Sstevel 
13403831d35Sstevel #define	DRMACH_MC_NBANKS		4
13503831d35Sstevel 
13603831d35Sstevel #define	DRMACH_MC_ADDR(mp, bank)	((mp)->madr_pa + 16 + 8 * (bank))
13703831d35Sstevel #define	DRMACH_MC_ASI_ADDR(mp, bank)	(DRMACH_MC_ADDR(mp, bank) & 0xFF)
13803831d35Sstevel 
13903831d35Sstevel #define	DRMACH_EMU_ACT_STATUS_OFFSET	0x50
14003831d35Sstevel #define	DRMACH_EMU_ACT_STATUS_ADDR(mp)	\
14103831d35Sstevel 	((mp)->madr_pa + DRMACH_EMU_ACT_STATUS_OFFSET)
14203831d35Sstevel 
14303831d35Sstevel /*
14403831d35Sstevel  * The Cheetah's Safari Configuration Register and the Schizo's
14503831d35Sstevel  * Safari Control/Status Register place the LPA base and bound fields in
14603831d35Sstevel  * same bit locations with in their register word. This source code takes
14703831d35Sstevel  * advantage of this by defining only one set of LPA encoding/decoding macros
14803831d35Sstevel  * which are shared by various Cheetah and Schizo drmach routines.
14903831d35Sstevel  */
15003831d35Sstevel #define	DRMACH_LPA_BASE_MASK		(0x3Full	<< 3)
15103831d35Sstevel #define	DRMACH_LPA_BND_MASK		(0x3Full	<< 9)
15203831d35Sstevel 
15303831d35Sstevel #define	DRMACH_LPA_BASE_TO_PA(scr)	(((scr) & DRMACH_LPA_BASE_MASK) << 34)
15403831d35Sstevel #define	DRMACH_LPA_BND_TO_PA(scr)	(((scr) & DRMACH_LPA_BND_MASK) << 28)
15503831d35Sstevel #define	DRMACH_PA_TO_LPA_BASE(pa)	(((pa) >> 34) & DRMACH_LPA_BASE_MASK)
15603831d35Sstevel #define	DRMACH_PA_TO_LPA_BND(pa)	(((pa) >> 28) & DRMACH_LPA_BND_MASK)
15703831d35Sstevel 
15803831d35Sstevel #define	DRMACH_L1_SET_LPA(b)		\
15903831d35Sstevel 	(((b)->flags & DRMACH_NULL_PROC_LPA) == 0)
16003831d35Sstevel 
16103831d35Sstevel #define	DRMACH_CPU_SRAM_ADDR    	0x7fff0900000ull
16203831d35Sstevel #define	DRMACH_CPU_SRAM_SIZE    	0x20000ull
16303831d35Sstevel 
16403831d35Sstevel /*
16503831d35Sstevel  * Name properties for frequently accessed device nodes.
16603831d35Sstevel  */
16703831d35Sstevel #define	DRMACH_CPU_NAMEPROP		"cpu"
16803831d35Sstevel #define	DRMACH_CMP_NAMEPROP		"cmp"
16903831d35Sstevel #define	DRMACH_AXQ_NAMEPROP		"address-extender-queue"
17003831d35Sstevel #define	DRMACH_PCI_NAMEPROP		"pci"
17103831d35Sstevel 
17203831d35Sstevel /*
17303831d35Sstevel  * Maximum value of processor Safari Timeout Log (TOL) field of
17403831d35Sstevel  * Safari Config reg (7 secs).
17503831d35Sstevel  */
17603831d35Sstevel #define	DRMACH_SAF_TOL_MAX		7 * 1000000
17703831d35Sstevel 
17803831d35Sstevel /*
17903831d35Sstevel  * drmach_board_t flag definitions
18003831d35Sstevel  */
18103831d35Sstevel #define	DRMACH_NULL_PROC_LPA		0x1
18203831d35Sstevel 
18303831d35Sstevel typedef struct {
18403831d35Sstevel 	uint32_t	reg_addr_hi;
18503831d35Sstevel 	uint32_t	reg_addr_lo;
18603831d35Sstevel 	uint32_t	reg_size_hi;
18703831d35Sstevel 	uint32_t	reg_size_lo;
18803831d35Sstevel } drmach_reg_t;
18903831d35Sstevel 
19003831d35Sstevel typedef struct {
19103831d35Sstevel 	struct drmach_node	*node;
19203831d35Sstevel 	void			*data;
19303831d35Sstevel } drmach_node_walk_args_t;
19403831d35Sstevel 
19503831d35Sstevel typedef struct drmach_node {
19603831d35Sstevel 	void		*here;
19703831d35Sstevel 
19803831d35Sstevel 	pnode_t		 (*get_dnode)(struct drmach_node *node);
19903831d35Sstevel 	int		 (*walk)(struct drmach_node *node, void *data,
20003831d35Sstevel 				int (*cb)(drmach_node_walk_args_t *args));
20103831d35Sstevel 	dev_info_t	*(*n_getdip)(struct drmach_node *node);
20203831d35Sstevel 	int		 (*n_getproplen)(struct drmach_node *node, char *name,
20303831d35Sstevel 				int *len);
20403831d35Sstevel 	int		 (*n_getprop)(struct drmach_node *node, char *name,
20503831d35Sstevel 				void *buf, int len);
20603831d35Sstevel 	int		 (*get_parent)(struct drmach_node *node,
20703831d35Sstevel 				struct drmach_node *pnode);
20803831d35Sstevel } drmach_node_t;
20903831d35Sstevel 
21003831d35Sstevel typedef struct {
21103831d35Sstevel 	int		 min_index;
21203831d35Sstevel 	int		 max_index;
21303831d35Sstevel 	int		 arr_sz;
21403831d35Sstevel 	drmachid_t	*arr;
21503831d35Sstevel } drmach_array_t;
21603831d35Sstevel 
21703831d35Sstevel typedef struct {
21803831d35Sstevel 	void		*isa;
21903831d35Sstevel 
22003831d35Sstevel 	void		 (*dispose)(drmachid_t);
22103831d35Sstevel 	sbd_error_t	*(*release)(drmachid_t);
22203831d35Sstevel 	sbd_error_t	*(*status)(drmachid_t, drmach_status_t *);
22303831d35Sstevel 
22403831d35Sstevel 	char		 name[MAXNAMELEN];
22503831d35Sstevel } drmach_common_t;
22603831d35Sstevel 
22703831d35Sstevel struct drmach_board;
22803831d35Sstevel typedef struct drmach_board drmach_board_t;
22903831d35Sstevel 
23003831d35Sstevel typedef struct {
23103831d35Sstevel 	drmach_common_t	 cm;
23203831d35Sstevel 	const char	*type;
23303831d35Sstevel 	drmach_board_t	*bp;
23403831d35Sstevel 	drmach_node_t	*node;
23503831d35Sstevel 	int		 portid;
23603831d35Sstevel 	int		 unum;
23703831d35Sstevel 	int		 busy;
23803831d35Sstevel 	int		 powered;
23903831d35Sstevel } drmach_device_t;
24003831d35Sstevel 
24103831d35Sstevel typedef struct drmach_cpu {
24203831d35Sstevel 	drmach_device_t	 dev;
24303831d35Sstevel 	uint64_t	 scr_pa;
24403831d35Sstevel 	processorid_t	 cpuid;
24503831d35Sstevel 	int		 coreid;
24603831d35Sstevel } drmach_cpu_t;
24703831d35Sstevel 
24803831d35Sstevel typedef struct drmach_mem {
24903831d35Sstevel 	drmach_device_t	 dev;
25003831d35Sstevel 	struct drmach_mem *next;
25103831d35Sstevel 	uint64_t	 nbytes;
25203831d35Sstevel 	uint64_t	 madr_pa;
25303831d35Sstevel } drmach_mem_t;
25403831d35Sstevel 
25503831d35Sstevel typedef struct drmach_io {
25603831d35Sstevel 	drmach_device_t	 dev;
25703831d35Sstevel 	uint64_t	 scsr_pa; /* PA of Schizo Control/Status Register */
25803831d35Sstevel } drmach_io_t;
25903831d35Sstevel 
26003831d35Sstevel struct drmach_board {
26103831d35Sstevel 	drmach_common_t	 cm;
26203831d35Sstevel 	int		 bnum;
26303831d35Sstevel 	int		 assigned;
26403831d35Sstevel 	int		 powered;
26503831d35Sstevel 	int		 connected;
26603831d35Sstevel 	int		 empty;
26703831d35Sstevel 	int		 cond;
26803831d35Sstevel 	uint_t		 cpu_impl;
26903831d35Sstevel 	uint_t		 flags;
27003831d35Sstevel 	drmach_node_t	*tree;
27103831d35Sstevel 	drmach_array_t	*devices;
27203831d35Sstevel 	drmach_mem_t	*mem;
27303831d35Sstevel 	uint64_t	 stardrb_offset;
27403831d35Sstevel 	char		 type[BD_TYPELEN];
27503831d35Sstevel };
27603831d35Sstevel 
27703831d35Sstevel typedef struct {
27803831d35Sstevel 	int		 flags;
27903831d35Sstevel 	drmach_device_t	*dp;
28003831d35Sstevel 	sbd_error_t	*err;
28103831d35Sstevel 	dev_info_t	*fdip;
28203831d35Sstevel } drmach_config_args_t;
28303831d35Sstevel 
28403831d35Sstevel typedef struct {
28503831d35Sstevel 	drmach_board_t	*obj;
28603831d35Sstevel 	int		 ndevs;
28703831d35Sstevel 	void		*a;
28803831d35Sstevel 	sbd_error_t	*(*found)(void *a, const char *, int, drmachid_t);
28903831d35Sstevel 	sbd_error_t	*err;
29003831d35Sstevel } drmach_board_cb_data_t;
29103831d35Sstevel 
29203831d35Sstevel typedef struct drmach_casmslot {
29303831d35Sstevel 	int	valid;
29403831d35Sstevel 	int	slice;
29503831d35Sstevel } drmach_casmslot_t;
29603831d35Sstevel 
29703831d35Sstevel typedef enum {
29803831d35Sstevel 	DRMACH_CR_OK,
29903831d35Sstevel 	DRMACH_CR_MC_IDLE_ERR,
30003831d35Sstevel 	DRMACH_CR_IOPAUSE_ERR,
30103831d35Sstevel 	DRMACH_CR_ONTRAP_ERR
30203831d35Sstevel } drmach_cr_err_t;
30303831d35Sstevel 
30403831d35Sstevel typedef struct {
30503831d35Sstevel 	void		*isa;
30603831d35Sstevel 	caddr_t		 data;
30703831d35Sstevel 	drmach_mem_t	*s_mp;
30803831d35Sstevel 	drmach_mem_t	*t_mp;
30903831d35Sstevel 	struct memlist	*c_ml;
31003831d35Sstevel 	uint64_t	 s_copybasepa;
31103831d35Sstevel 	uint64_t	 t_copybasepa;
31203831d35Sstevel 	drmach_cr_err_t	 ecode;
31303831d35Sstevel 	void		*earg;
31403831d35Sstevel } drmach_copy_rename_t;
31503831d35Sstevel 
31603831d35Sstevel /*
31703831d35Sstevel  * The following global is read as a boolean value, non-zero is true.
31803831d35Sstevel  * If zero, DR copy-rename and cpu poweron will not set the processor
31903831d35Sstevel  * LPA settings (CBASE, CBND of Safari config register) to correspond
32003831d35Sstevel  * to the current memory slice map. LPAs of processors present at boot
32103831d35Sstevel  * will remain as programmed by POST. LPAs of processors on boards added
32203831d35Sstevel  * by DR will remain NULL, as programmed by POST. This can be used to
32303831d35Sstevel  * to override the per-board L1SSFLG_THIS_L1_NULL_PROC_LPA flag set by
32403831d35Sstevel  * POST in the LDCD (and copied to the GDCD by SMS).
32503831d35Sstevel  *
32603831d35Sstevel  * drmach_reprogram_lpa and L1SSFLG_THIS_L1_NULL_PROC_LPA do not apply
32703831d35Sstevel  * to Schizo device LPAs. These are always set by DR.
32803831d35Sstevel  */
32903831d35Sstevel static int		 drmach_reprogram_lpa = 1;
33003831d35Sstevel 
33103831d35Sstevel /*
33203831d35Sstevel  * There is a known HW bug where a Jaguar CPU in Safari port 0 (SBX/P0)
33303831d35Sstevel  * can fail to receive an XIR. To workaround this issue until a hardware
33403831d35Sstevel  * fix is implemented, we will exclude the selection of these CPUs.
33503831d35Sstevel  * Setting this to 0 will allow their selection again.
33603831d35Sstevel  */
33703831d35Sstevel static int		 drmach_iocage_exclude_jaguar_port_zero = 1;
33803831d35Sstevel 
33903831d35Sstevel static int		 drmach_initialized;
34003831d35Sstevel static drmach_array_t	*drmach_boards;
34103831d35Sstevel 
34203831d35Sstevel static int		 drmach_cpu_delay = 1000;
34303831d35Sstevel static int		 drmach_cpu_ntries = 50000;
34403831d35Sstevel 
34503831d35Sstevel static uint32_t		 drmach_slice_table[AXQ_MAX_EXP];
34603831d35Sstevel static kmutex_t		 drmach_slice_table_lock;
34703831d35Sstevel 
34803831d35Sstevel tte_t			 drmach_cpu_sram_tte[NCPU];
34903831d35Sstevel caddr_t			 drmach_cpu_sram_va;
35003831d35Sstevel 
35103831d35Sstevel /*
35203831d35Sstevel  * Setting to non-zero will enable delay before all disconnect ops.
35303831d35Sstevel  */
35403831d35Sstevel static int		 drmach_unclaim_delay_all;
35503831d35Sstevel /*
35603831d35Sstevel  * Default delay is slightly greater than the max processor Safari timeout.
35703831d35Sstevel  * This delay is intended to ensure the outstanding Safari activity has
35803831d35Sstevel  * retired on this board prior to a board disconnect.
35903831d35Sstevel  */
36003831d35Sstevel static clock_t		 drmach_unclaim_usec_delay = DRMACH_SAF_TOL_MAX + 10;
36103831d35Sstevel 
36203831d35Sstevel /*
36303831d35Sstevel  * By default, DR of non-Panther procs is not allowed into a Panther
36403831d35Sstevel  * domain with large page sizes enabled.  Setting this to 0 will remove
36503831d35Sstevel  * the restriction.
36603831d35Sstevel  */
36703831d35Sstevel static int		 drmach_large_page_restriction = 1;
36803831d35Sstevel 
36903831d35Sstevel /*
37003831d35Sstevel  * Used to pass updated LPA values to procs.
37103831d35Sstevel  * Protocol is to clear the array before use.
37203831d35Sstevel  */
37303831d35Sstevel volatile uchar_t	*drmach_xt_mb;
37403831d35Sstevel volatile uint64_t	 drmach_xt_ready;
37503831d35Sstevel static kmutex_t		 drmach_xt_mb_lock;
37603831d35Sstevel static int		 drmach_xt_mb_size;
37703831d35Sstevel 
37803831d35Sstevel uint64_t		 drmach_bus_sync_list[18 * 4 * 4 + 1];
37903831d35Sstevel static kmutex_t		 drmach_bus_sync_lock;
38003831d35Sstevel 
381*8682d1efSRichard Lowe static void		drmach_fini(void);
382*8682d1efSRichard Lowe 
38303831d35Sstevel static sbd_error_t	*drmach_device_new(drmach_node_t *,
38403831d35Sstevel 				drmach_board_t *, int, drmachid_t *);
38503831d35Sstevel static sbd_error_t	*drmach_cpu_new(drmach_device_t *, drmachid_t *);
38603831d35Sstevel static sbd_error_t	*drmach_mem_new(drmach_device_t *, drmachid_t *);
38703831d35Sstevel static sbd_error_t	*drmach_pci_new(drmach_device_t *, drmachid_t *);
38803831d35Sstevel static sbd_error_t	*drmach_io_new(drmach_device_t *, drmachid_t *);
38903831d35Sstevel 
390*8682d1efSRichard Lowe static sbd_error_t 	*drmach_board_release(drmachid_t);
391*8682d1efSRichard Lowe static sbd_error_t 	*drmach_board_status(drmachid_t, drmach_status_t *);
392*8682d1efSRichard Lowe 
393*8682d1efSRichard Lowe static void 		drmach_cpu_dispose(drmachid_t);
394*8682d1efSRichard Lowe static sbd_error_t 	*drmach_cpu_release(drmachid_t);
395*8682d1efSRichard Lowe static sbd_error_t 	*drmach_cpu_status(drmachid_t, drmach_status_t *);
396*8682d1efSRichard Lowe 
397*8682d1efSRichard Lowe static void 		drmach_mem_dispose(drmachid_t);
398*8682d1efSRichard Lowe static sbd_error_t 	*drmach_mem_release(drmachid_t);
399*8682d1efSRichard Lowe static sbd_error_t 	*drmach_mem_status(drmachid_t, drmach_status_t *);
400*8682d1efSRichard Lowe 
40103831d35Sstevel static dev_info_t	*drmach_node_ddi_get_dip(drmach_node_t *np);
40203831d35Sstevel static int		 drmach_node_ddi_get_prop(drmach_node_t *np,
40303831d35Sstevel 				char *name, void *buf, int len);
40403831d35Sstevel static int		 drmach_node_ddi_get_proplen(drmach_node_t *np,
40503831d35Sstevel 				char *name, int *len);
40603831d35Sstevel 
40703831d35Sstevel static dev_info_t	*drmach_node_obp_get_dip(drmach_node_t *np);
40803831d35Sstevel static int		 drmach_node_obp_get_prop(drmach_node_t *np,
40903831d35Sstevel 				char *name, void *buf, int len);
41003831d35Sstevel static int		 drmach_node_obp_get_proplen(drmach_node_t *np,
41103831d35Sstevel 				char *name, int *len);
41203831d35Sstevel 
41303831d35Sstevel static sbd_error_t	*drmach_mbox_trans(uint8_t msgtype, int bnum,
41403831d35Sstevel 				caddr_t obufp, int olen,
41503831d35Sstevel 				caddr_t ibufp, int ilen);
41603831d35Sstevel 
41703831d35Sstevel sbd_error_t		*drmach_io_post_attach(drmachid_t id);
41803831d35Sstevel sbd_error_t		*drmach_io_post_release(drmachid_t id);
41903831d35Sstevel 
42003831d35Sstevel static sbd_error_t	*drmach_iocage_setup(dr_testboard_req_t *,
42103831d35Sstevel 				drmach_device_t **dpp, cpu_flag_t *oflags);
42203831d35Sstevel static int		drmach_iocage_cpu_return(drmach_device_t *dp,
42303831d35Sstevel 				cpu_flag_t oflags);
42403831d35Sstevel static sbd_error_t	*drmach_iocage_mem_return(dr_testboard_reply_t *tbr);
42503831d35Sstevel void			drmach_iocage_mem_scrub(uint64_t nbytes);
42603831d35Sstevel 
42703831d35Sstevel static sbd_error_t 	*drmach_i_status(drmachid_t id, drmach_status_t *stat);
42803831d35Sstevel 
42903831d35Sstevel static void		drmach_slot1_lpa_set(drmach_board_t *bp);
43003831d35Sstevel 
43103831d35Sstevel static void		drmach_cpu_read(uint64_t arg1, uint64_t arg2);
43203831d35Sstevel static int		drmach_cpu_read_scr(drmach_cpu_t *cp, uint64_t *scr);
43303831d35Sstevel 
43403831d35Sstevel static void		 drmach_bus_sync_list_update(void);
43503831d35Sstevel static void		 drmach_slice_table_update(drmach_board_t *, int);
43603831d35Sstevel static int		 drmach_portid2bnum(int);
43703831d35Sstevel 
43803831d35Sstevel static void		drmach_msg_memslice_init(dr_memslice_t slice_arr[]);
43903831d35Sstevel static void		drmach_msg_memregs_init(dr_memregs_t regs_arr[]);
44003831d35Sstevel 
44103831d35Sstevel static int		drmach_panther_boards(void);
44203831d35Sstevel 
44303831d35Sstevel static int		drmach_name2type_idx(char *);
44403831d35Sstevel 
44503831d35Sstevel #ifdef DEBUG
44603831d35Sstevel 
44703831d35Sstevel #define	DRMACH_PR		if (drmach_debug) printf
44803831d35Sstevel #define	DRMACH_MEMLIST_DUMP	if (drmach_debug) MEMLIST_DUMP
44903831d35Sstevel int drmach_debug = 0;		 /* set to non-zero to enable debug messages */
45003831d35Sstevel #else
45103831d35Sstevel 
45203831d35Sstevel #define	DRMACH_PR		_NOTE(CONSTANTCONDITION) if (0) printf
45303831d35Sstevel #define	DRMACH_MEMLIST_DUMP	_NOTE(CONSTANTCONDITION) if (0) MEMLIST_DUMP
45403831d35Sstevel #endif /* DEBUG */
45503831d35Sstevel 
45603831d35Sstevel #define	DRMACH_OBJ(id)		((drmach_common_t *)id)
45703831d35Sstevel 
45803831d35Sstevel #define	DRMACH_IS_BOARD_ID(id)	\
45903831d35Sstevel 	((id != 0) &&		\
46003831d35Sstevel 	(DRMACH_OBJ(id)->isa == (void *)drmach_board_new))
46103831d35Sstevel 
46203831d35Sstevel #define	DRMACH_IS_CPU_ID(id)	\
46303831d35Sstevel 	((id != 0) &&		\
46403831d35Sstevel 	(DRMACH_OBJ(id)->isa == (void *)drmach_cpu_new))
46503831d35Sstevel 
46603831d35Sstevel #define	DRMACH_IS_MEM_ID(id)	\
46703831d35Sstevel 	((id != 0) &&		\
46803831d35Sstevel 	(DRMACH_OBJ(id)->isa == (void *)drmach_mem_new))
46903831d35Sstevel 
47003831d35Sstevel #define	DRMACH_IS_IO_ID(id)	\
47103831d35Sstevel 	((id != 0) &&		\
47203831d35Sstevel 	(DRMACH_OBJ(id)->isa == (void *)drmach_io_new))
47303831d35Sstevel 
47403831d35Sstevel #define	DRMACH_IS_DEVICE_ID(id)					\
47503831d35Sstevel 	((id != 0) &&						\
47603831d35Sstevel 	(DRMACH_OBJ(id)->isa == (void *)drmach_cpu_new ||	\
47703831d35Sstevel 	    DRMACH_OBJ(id)->isa == (void *)drmach_mem_new ||	\
47803831d35Sstevel 	    DRMACH_OBJ(id)->isa == (void *)drmach_io_new))
47903831d35Sstevel 
48003831d35Sstevel #define	DRMACH_IS_ID(id)					\
48103831d35Sstevel 	((id != 0) &&						\
48203831d35Sstevel 	(DRMACH_OBJ(id)->isa == (void *)drmach_board_new ||	\
48303831d35Sstevel 	    DRMACH_OBJ(id)->isa == (void *)drmach_cpu_new ||	\
48403831d35Sstevel 	    DRMACH_OBJ(id)->isa == (void *)drmach_mem_new ||	\
48503831d35Sstevel 	    DRMACH_OBJ(id)->isa == (void *)drmach_io_new))
48603831d35Sstevel 
48703831d35Sstevel #define	DRMACH_INTERNAL_ERROR() \
48803831d35Sstevel 	drerr_new(1, ESTC_INTERNAL, drmach_ie_fmt, __LINE__)
48903831d35Sstevel static char		*drmach_ie_fmt = "drmach.c %d";
49003831d35Sstevel 
49103831d35Sstevel static struct {
49203831d35Sstevel 	const char	 *name;
49303831d35Sstevel 	const char	 *type;
49403831d35Sstevel 	sbd_error_t	 *(*new)(drmach_device_t *, drmachid_t *);
49503831d35Sstevel } drmach_name2type[] = {
49603831d35Sstevel 	{"cmp",			    DRMACH_DEVTYPE_CMP,    NULL },
49703831d35Sstevel 	{"cpu",			    DRMACH_DEVTYPE_CPU,    drmach_cpu_new },
49803831d35Sstevel 	{"SUNW,UltraSPARC-III",	    DRMACH_DEVTYPE_CPU,    drmach_cpu_new },
49903831d35Sstevel 	{"SUNW,UltraSPARC-III+",    DRMACH_DEVTYPE_CPU,    drmach_cpu_new },
50003831d35Sstevel 	{"memory-controller",	    DRMACH_DEVTYPE_MEM,    drmach_mem_new },
50103831d35Sstevel 	{"pci",			    DRMACH_DEVTYPE_PCI,    drmach_pci_new },
50203831d35Sstevel 	{"SUNW,wci",		    DRMACH_DEVTYPE_WCI,    drmach_io_new  },
50303831d35Sstevel };
50403831d35Sstevel 
50503831d35Sstevel /*
50603831d35Sstevel  * drmach autoconfiguration data structures and interfaces
50703831d35Sstevel  */
50803831d35Sstevel 
50903831d35Sstevel extern struct mod_ops mod_miscops;
51003831d35Sstevel 
51103831d35Sstevel static struct modlmisc modlmisc = {
51203831d35Sstevel 	&mod_miscops,
513f500b196SRichard Bean 	"Sun Fire 15000 DR"
51403831d35Sstevel };
51503831d35Sstevel 
51603831d35Sstevel static struct modlinkage modlinkage = {
51703831d35Sstevel 	MODREV_1,
51803831d35Sstevel 	(void *)&modlmisc,
51903831d35Sstevel 	NULL
52003831d35Sstevel };
52103831d35Sstevel 
52203831d35Sstevel /*
52303831d35Sstevel  * drmach_boards_rwlock is used to synchronize read/write
52403831d35Sstevel  * access to drmach_boards array between status and board lookup
52503831d35Sstevel  * as READERS, and assign, and unassign threads as WRITERS.
52603831d35Sstevel  */
52703831d35Sstevel static krwlock_t	drmach_boards_rwlock;
52803831d35Sstevel 
52903831d35Sstevel static kmutex_t		drmach_i_lock;
53003831d35Sstevel static kmutex_t		drmach_iocage_lock;
53103831d35Sstevel static kcondvar_t 	drmach_iocage_cv;
53203831d35Sstevel static int		drmach_iocage_is_busy = 0;
53303831d35Sstevel uint64_t		drmach_iocage_paddr;
53403831d35Sstevel static caddr_t		drmach_iocage_vaddr;
53503831d35Sstevel static int		drmach_iocage_size = 0;
53603831d35Sstevel static int		drmach_is_cheetah = -1;
53703831d35Sstevel 
53803831d35Sstevel int
_init(void)53903831d35Sstevel _init(void)
54003831d35Sstevel {
54103831d35Sstevel 	int	err;
54203831d35Sstevel 
54303831d35Sstevel 	mutex_init(&drmach_i_lock, NULL, MUTEX_DRIVER, NULL);
54403831d35Sstevel 	rw_init(&drmach_boards_rwlock, NULL, RW_DEFAULT, NULL);
54503831d35Sstevel 	drmach_xt_mb_size = NCPU * sizeof (uchar_t);
54603831d35Sstevel 	drmach_xt_mb = (uchar_t *)vmem_alloc(static_alloc_arena,
54703831d35Sstevel 	    drmach_xt_mb_size, VM_SLEEP);
54803831d35Sstevel 	bzero((void *)drmach_xt_mb, drmach_xt_mb_size);
54903831d35Sstevel 	if ((err = mod_install(&modlinkage)) != 0) {
55003831d35Sstevel 		mutex_destroy(&drmach_i_lock);
55103831d35Sstevel 		rw_destroy(&drmach_boards_rwlock);
55203831d35Sstevel 		vmem_free(static_alloc_arena, (void *)drmach_xt_mb,
55303831d35Sstevel 		    drmach_xt_mb_size);
55403831d35Sstevel 	}
55503831d35Sstevel 
55603831d35Sstevel 	return (err);
55703831d35Sstevel }
55803831d35Sstevel 
55903831d35Sstevel int
_fini(void)56003831d35Sstevel _fini(void)
56103831d35Sstevel {
56203831d35Sstevel 	int		err;
56303831d35Sstevel 
56403831d35Sstevel 	if ((err = mod_remove(&modlinkage)) == 0)
56503831d35Sstevel 		drmach_fini();
56603831d35Sstevel 
56703831d35Sstevel 	return (err);
56803831d35Sstevel }
56903831d35Sstevel 
57003831d35Sstevel int
_info(struct modinfo * modinfop)57103831d35Sstevel _info(struct modinfo *modinfop)
57203831d35Sstevel {
57303831d35Sstevel 	return (mod_info(&modlinkage, modinfop));
57403831d35Sstevel }
57503831d35Sstevel 
57603831d35Sstevel /*
57703831d35Sstevel  * drmach_node_* routines serve the purpose of separating the
57803831d35Sstevel  * rest of the code from the device tree and OBP.  This is necessary
57903831d35Sstevel  * because of In-Kernel-Probing.  Devices probed after stod, are probed
58003831d35Sstevel  * by the in-kernel-prober, not OBP.  These devices, therefore, do not
58103831d35Sstevel  * have dnode ids.
58203831d35Sstevel  */
58303831d35Sstevel 
58403831d35Sstevel static int
drmach_node_obp_get_parent(drmach_node_t * np,drmach_node_t * pp)58503831d35Sstevel drmach_node_obp_get_parent(drmach_node_t *np, drmach_node_t *pp)
58603831d35Sstevel {
58703831d35Sstevel 	pnode_t		nodeid;
58803831d35Sstevel 	static char	*fn = "drmach_node_obp_get_parent";
58903831d35Sstevel 
59003831d35Sstevel 	nodeid = np->get_dnode(np);
59103831d35Sstevel 	if (nodeid == OBP_NONODE) {
59203831d35Sstevel 		cmn_err(CE_WARN, "%s: invalid dnode", fn);
59303831d35Sstevel 		return (-1);
59403831d35Sstevel 	}
59503831d35Sstevel 
59603831d35Sstevel 	bcopy(np, pp, sizeof (drmach_node_t));
59703831d35Sstevel 
59803831d35Sstevel 	pp->here = (void *)(uintptr_t)prom_parentnode(nodeid);
59903831d35Sstevel 	if (pp->here == OBP_NONODE) {
60003831d35Sstevel 		cmn_err(CE_WARN, "%s: invalid parent dnode", fn);
60103831d35Sstevel 		return (-1);
60203831d35Sstevel 	}
60303831d35Sstevel 
60403831d35Sstevel 	return (0);
60503831d35Sstevel }
60603831d35Sstevel 
60703831d35Sstevel static pnode_t
drmach_node_obp_get_dnode(drmach_node_t * np)60803831d35Sstevel drmach_node_obp_get_dnode(drmach_node_t *np)
60903831d35Sstevel {
61003831d35Sstevel 	return ((pnode_t)(uintptr_t)np->here);
61103831d35Sstevel }
61203831d35Sstevel 
61303831d35Sstevel typedef struct {
61403831d35Sstevel 	drmach_node_walk_args_t	*nwargs;
61503831d35Sstevel 	int 			(*cb)(drmach_node_walk_args_t *args);
61603831d35Sstevel 	int			err;
61703831d35Sstevel } drmach_node_ddi_walk_args_t;
61803831d35Sstevel 
61903831d35Sstevel int
drmach_node_ddi_walk_cb(dev_info_t * dip,void * arg)62003831d35Sstevel drmach_node_ddi_walk_cb(dev_info_t *dip, void *arg)
62103831d35Sstevel {
62203831d35Sstevel 	drmach_node_ddi_walk_args_t	*nargs;
62303831d35Sstevel 
62403831d35Sstevel 	nargs = (drmach_node_ddi_walk_args_t *)arg;
62503831d35Sstevel 
62603831d35Sstevel 	/*
62703831d35Sstevel 	 * dip doesn't have to be held here as we are called
62803831d35Sstevel 	 * from ddi_walk_devs() which holds the dip.
62903831d35Sstevel 	 */
63003831d35Sstevel 	nargs->nwargs->node->here = (void *)dip;
63103831d35Sstevel 
63203831d35Sstevel 	nargs->err = nargs->cb(nargs->nwargs);
63303831d35Sstevel 
63403831d35Sstevel 	/*
63503831d35Sstevel 	 * Set "here" to NULL so that unheld dip is not accessible
63603831d35Sstevel 	 * outside ddi_walk_devs()
63703831d35Sstevel 	 */
63803831d35Sstevel 	nargs->nwargs->node->here = NULL;
63903831d35Sstevel 
64003831d35Sstevel 	if (nargs->err)
64103831d35Sstevel 		return (DDI_WALK_TERMINATE);
64203831d35Sstevel 	else
64303831d35Sstevel 		return (DDI_WALK_CONTINUE);
64403831d35Sstevel }
64503831d35Sstevel 
64603831d35Sstevel static int
drmach_node_ddi_walk(drmach_node_t * np,void * data,int (* cb)(drmach_node_walk_args_t * args))64703831d35Sstevel drmach_node_ddi_walk(drmach_node_t *np, void *data,
64803831d35Sstevel 		int (*cb)(drmach_node_walk_args_t *args))
64903831d35Sstevel {
65003831d35Sstevel 	drmach_node_walk_args_t		args;
65103831d35Sstevel 	drmach_node_ddi_walk_args_t	nargs;
65203831d35Sstevel 
65303831d35Sstevel 	/* initialized args structure for callback */
65403831d35Sstevel 	args.node = np;
65503831d35Sstevel 	args.data = data;
65603831d35Sstevel 
65703831d35Sstevel 	nargs.nwargs = &args;
65803831d35Sstevel 	nargs.cb = cb;
65903831d35Sstevel 	nargs.err = 0;
66003831d35Sstevel 
66103831d35Sstevel 	/*
66203831d35Sstevel 	 * Root node doesn't have to be held in any way.
66303831d35Sstevel 	 */
664d3d50737SRafael Vanoni 	ddi_walk_devs(ddi_root_node(), drmach_node_ddi_walk_cb, (void *)&nargs);
66503831d35Sstevel 
66603831d35Sstevel 	return (nargs.err);
66703831d35Sstevel }
66803831d35Sstevel 
66903831d35Sstevel static int
drmach_node_obp_walk(drmach_node_t * np,void * data,int (* cb)(drmach_node_walk_args_t * args))67003831d35Sstevel drmach_node_obp_walk(drmach_node_t *np, void *data,
67103831d35Sstevel 		int (*cb)(drmach_node_walk_args_t *args))
67203831d35Sstevel {
67303831d35Sstevel 	pnode_t			nodeid;
67403831d35Sstevel 	int			rv;
67503831d35Sstevel 	drmach_node_walk_args_t	args;
67603831d35Sstevel 
67703831d35Sstevel 	/* initialized args structure for callback */
67803831d35Sstevel 	args.node = np;
67903831d35Sstevel 	args.data = data;
68003831d35Sstevel 
68103831d35Sstevel 	nodeid = prom_childnode(prom_rootnode());
68203831d35Sstevel 
68303831d35Sstevel 	/* save our new position within the tree */
68403831d35Sstevel 	np->here = (void *)(uintptr_t)nodeid;
68503831d35Sstevel 
68603831d35Sstevel 	rv = 0;
68703831d35Sstevel 	while (nodeid != OBP_NONODE) {
68803831d35Sstevel 
68903831d35Sstevel 		pnode_t child;
69003831d35Sstevel 
69103831d35Sstevel 		rv = (*cb)(&args);
69203831d35Sstevel 		if (rv)
69303831d35Sstevel 			break;
69403831d35Sstevel 
69503831d35Sstevel 		child = prom_childnode(nodeid);
69603831d35Sstevel 		np->here = (void *)(uintptr_t)child;
69703831d35Sstevel 
69803831d35Sstevel 		while (child != OBP_NONODE) {
69903831d35Sstevel 			rv = (*cb)(&args);
70003831d35Sstevel 			if (rv)
70103831d35Sstevel 				break;
70203831d35Sstevel 
70303831d35Sstevel 			child = prom_nextnode(child);
70403831d35Sstevel 			np->here = (void *)(uintptr_t)child;
70503831d35Sstevel 		}
70603831d35Sstevel 
70703831d35Sstevel 		nodeid = prom_nextnode(nodeid);
70803831d35Sstevel 
70903831d35Sstevel 		/* save our new position within the tree */
71003831d35Sstevel 		np->here = (void *)(uintptr_t)nodeid;
71103831d35Sstevel 	}
71203831d35Sstevel 
71303831d35Sstevel 	return (rv);
71403831d35Sstevel }
71503831d35Sstevel 
71603831d35Sstevel static int
drmach_node_ddi_get_parent(drmach_node_t * np,drmach_node_t * pp)71703831d35Sstevel drmach_node_ddi_get_parent(drmach_node_t *np, drmach_node_t *pp)
71803831d35Sstevel {
71903831d35Sstevel 	dev_info_t	*ndip;
72003831d35Sstevel 	static char	*fn = "drmach_node_ddi_get_parent";
72103831d35Sstevel 
72203831d35Sstevel 	ndip = np->n_getdip(np);
72303831d35Sstevel 	if (ndip == NULL) {
72403831d35Sstevel 		cmn_err(CE_WARN, "%s: NULL dip", fn);
72503831d35Sstevel 		return (-1);
72603831d35Sstevel 	}
72703831d35Sstevel 
72803831d35Sstevel 	bcopy(np, pp, sizeof (drmach_node_t));
72903831d35Sstevel 
73003831d35Sstevel 	pp->here = (void *)ddi_get_parent(ndip);
73103831d35Sstevel 	if (pp->here == NULL) {
73203831d35Sstevel 		cmn_err(CE_WARN, "%s: NULL parent dip", fn);
73303831d35Sstevel 		return (-1);
73403831d35Sstevel 	}
73503831d35Sstevel 
73603831d35Sstevel 	return (0);
73703831d35Sstevel }
73803831d35Sstevel 
73903831d35Sstevel /*ARGSUSED*/
74003831d35Sstevel static pnode_t
drmach_node_ddi_get_dnode(drmach_node_t * np)74103831d35Sstevel drmach_node_ddi_get_dnode(drmach_node_t *np)
74203831d35Sstevel {
74303831d35Sstevel 	return ((pnode_t)NULL);
74403831d35Sstevel }
74503831d35Sstevel 
74603831d35Sstevel static drmach_node_t *
drmach_node_new(void)74703831d35Sstevel drmach_node_new(void)
74803831d35Sstevel {
74903831d35Sstevel 	drmach_node_t *np;
75003831d35Sstevel 
75103831d35Sstevel 	np = kmem_zalloc(sizeof (drmach_node_t), KM_SLEEP);
75203831d35Sstevel 
75303831d35Sstevel 	if (drmach_initialized) {
75403831d35Sstevel 		np->get_dnode = drmach_node_ddi_get_dnode;
75503831d35Sstevel 		np->walk = drmach_node_ddi_walk;
75603831d35Sstevel 		np->n_getdip = drmach_node_ddi_get_dip;
75703831d35Sstevel 		np->n_getproplen = drmach_node_ddi_get_proplen;
75803831d35Sstevel 		np->n_getprop = drmach_node_ddi_get_prop;
75903831d35Sstevel 		np->get_parent = drmach_node_ddi_get_parent;
76003831d35Sstevel 	} else {
76103831d35Sstevel 		np->get_dnode = drmach_node_obp_get_dnode;
76203831d35Sstevel 		np->walk = drmach_node_obp_walk;
76303831d35Sstevel 		np->n_getdip = drmach_node_obp_get_dip;
76403831d35Sstevel 		np->n_getproplen = drmach_node_obp_get_proplen;
76503831d35Sstevel 		np->n_getprop = drmach_node_obp_get_prop;
76603831d35Sstevel 		np->get_parent = drmach_node_obp_get_parent;
76703831d35Sstevel 	}
76803831d35Sstevel 
76903831d35Sstevel 	return (np);
77003831d35Sstevel }
77103831d35Sstevel 
77203831d35Sstevel static void
drmach_node_dispose(drmach_node_t * np)77303831d35Sstevel drmach_node_dispose(drmach_node_t *np)
77403831d35Sstevel {
77503831d35Sstevel 	kmem_free(np, sizeof (*np));
77603831d35Sstevel }
77703831d35Sstevel 
77803831d35Sstevel /*
77903831d35Sstevel  * Check if a CPU node is part of a CMP.
78003831d35Sstevel  */
78103831d35Sstevel static int
drmach_is_cmp_child(dev_info_t * dip)78203831d35Sstevel drmach_is_cmp_child(dev_info_t *dip)
78303831d35Sstevel {
78403831d35Sstevel 	dev_info_t *pdip;
78503831d35Sstevel 
78603831d35Sstevel 	if (strcmp(ddi_node_name(dip), DRMACH_CPU_NAMEPROP) != 0) {
78703831d35Sstevel 		return (0);
78803831d35Sstevel 	}
78903831d35Sstevel 
79003831d35Sstevel 	pdip = ddi_get_parent(dip);
79103831d35Sstevel 
79203831d35Sstevel 	ASSERT(pdip);
79303831d35Sstevel 
79403831d35Sstevel 	if (strcmp(ddi_node_name(pdip), DRMACH_CMP_NAMEPROP) == 0) {
79503831d35Sstevel 		return (1);
79603831d35Sstevel 	}
79703831d35Sstevel 
79803831d35Sstevel 	return (0);
79903831d35Sstevel }
80003831d35Sstevel 
80103831d35Sstevel static dev_info_t *
drmach_node_obp_get_dip(drmach_node_t * np)80203831d35Sstevel drmach_node_obp_get_dip(drmach_node_t *np)
80303831d35Sstevel {
80403831d35Sstevel 	pnode_t		nodeid;
80503831d35Sstevel 	dev_info_t	*dip;
80603831d35Sstevel 
80703831d35Sstevel 	nodeid = np->get_dnode(np);
80803831d35Sstevel 	if (nodeid == OBP_NONODE)
80903831d35Sstevel 		return (NULL);
81003831d35Sstevel 
81103831d35Sstevel 	dip = e_ddi_nodeid_to_dip(nodeid);
81203831d35Sstevel 	if (dip) {
81303831d35Sstevel 		/*
81403831d35Sstevel 		 * The branch rooted at dip will have been previously
81503831d35Sstevel 		 * held, or it will be the child of a CMP. In either
81603831d35Sstevel 		 * case, the hold acquired in e_ddi_nodeid_to_dip()
81703831d35Sstevel 		 * is not needed.
81803831d35Sstevel 		 */
81903831d35Sstevel 		ddi_release_devi(dip);
82003831d35Sstevel 		ASSERT(drmach_is_cmp_child(dip) || e_ddi_branch_held(dip));
82103831d35Sstevel 	}
82203831d35Sstevel 
82303831d35Sstevel 	return (dip);
82403831d35Sstevel }
82503831d35Sstevel 
82603831d35Sstevel static dev_info_t *
drmach_node_ddi_get_dip(drmach_node_t * np)82703831d35Sstevel drmach_node_ddi_get_dip(drmach_node_t *np)
82803831d35Sstevel {
82903831d35Sstevel 	return ((dev_info_t *)np->here);
83003831d35Sstevel }
83103831d35Sstevel 
83203831d35Sstevel static int
drmach_node_walk(drmach_node_t * np,void * param,int (* cb)(drmach_node_walk_args_t * args))83303831d35Sstevel drmach_node_walk(drmach_node_t *np, void *param,
83403831d35Sstevel 		int (*cb)(drmach_node_walk_args_t *args))
83503831d35Sstevel {
83603831d35Sstevel 	return (np->walk(np, param, cb));
83703831d35Sstevel }
83803831d35Sstevel 
83903831d35Sstevel static int
drmach_node_ddi_get_prop(drmach_node_t * np,char * name,void * buf,int len)84003831d35Sstevel drmach_node_ddi_get_prop(drmach_node_t *np, char *name, void *buf, int len)
84103831d35Sstevel {
84203831d35Sstevel 	int		rv = 0;
84303831d35Sstevel 	dev_info_t	*ndip;
84403831d35Sstevel 	static char	*fn = "drmach_node_ddi_get_prop";
84503831d35Sstevel 
84603831d35Sstevel 	ndip = np->n_getdip(np);
84703831d35Sstevel 	if (ndip == NULL) {
84803831d35Sstevel 		cmn_err(CE_WARN, "%s: NULL dip", fn);
84903831d35Sstevel 		rv = -1;
85003831d35Sstevel 	} else if (ddi_getlongprop_buf(DDI_DEV_T_ANY, ndip,
85103831d35Sstevel 	    DDI_PROP_DONTPASS | DDI_PROP_NOTPROM, name,
85203831d35Sstevel 	    (caddr_t)buf, &len) != DDI_PROP_SUCCESS) {
85303831d35Sstevel 		rv = -1;
85403831d35Sstevel 	}
85503831d35Sstevel 
85603831d35Sstevel 	return (rv);
85703831d35Sstevel }
85803831d35Sstevel 
85903831d35Sstevel /* ARGSUSED */
86003831d35Sstevel static int
drmach_node_obp_get_prop(drmach_node_t * np,char * name,void * buf,int len)86103831d35Sstevel drmach_node_obp_get_prop(drmach_node_t *np, char *name, void *buf, int len)
86203831d35Sstevel {
86303831d35Sstevel 	int		rv = 0;
86403831d35Sstevel 	pnode_t		nodeid;
86503831d35Sstevel 	static char	*fn = "drmach_node_obp_get_prop";
86603831d35Sstevel 
86703831d35Sstevel 	nodeid = np->get_dnode(np);
86803831d35Sstevel 	if (nodeid == OBP_NONODE) {
86903831d35Sstevel 		cmn_err(CE_WARN, "%s: invalid dnode", fn);
87003831d35Sstevel 		rv = -1;
87103831d35Sstevel 	} else if (prom_getproplen(nodeid, (caddr_t)name) < 0) {
87203831d35Sstevel 		rv = -1;
87303831d35Sstevel 	} else {
87403831d35Sstevel 		(void) prom_getprop(nodeid, (caddr_t)name, (caddr_t)buf);
87503831d35Sstevel 	}
87603831d35Sstevel 
87703831d35Sstevel 	return (rv);
87803831d35Sstevel }
87903831d35Sstevel 
88003831d35Sstevel static int
drmach_node_ddi_get_proplen(drmach_node_t * np,char * name,int * len)88103831d35Sstevel drmach_node_ddi_get_proplen(drmach_node_t *np, char *name, int *len)
88203831d35Sstevel {
88303831d35Sstevel 	int		rv = 0;
88403831d35Sstevel 	dev_info_t	*ndip;
88503831d35Sstevel 
88603831d35Sstevel 	ndip = np->n_getdip(np);
88703831d35Sstevel 	if (ndip == NULL) {
88803831d35Sstevel 		rv = -1;
88903831d35Sstevel 	} else if (ddi_getproplen(DDI_DEV_T_ANY, ndip, DDI_PROP_DONTPASS,
89003831d35Sstevel 	    name, len) != DDI_PROP_SUCCESS) {
89103831d35Sstevel 		rv = -1;
89203831d35Sstevel 	}
89303831d35Sstevel 
89403831d35Sstevel 	return (rv);
89503831d35Sstevel }
89603831d35Sstevel 
89703831d35Sstevel static int
drmach_node_obp_get_proplen(drmach_node_t * np,char * name,int * len)89803831d35Sstevel drmach_node_obp_get_proplen(drmach_node_t *np, char *name, int *len)
89903831d35Sstevel {
90003831d35Sstevel 	pnode_t	 nodeid;
90103831d35Sstevel 	int	 rv;
90203831d35Sstevel 
90303831d35Sstevel 	nodeid = np->get_dnode(np);
90403831d35Sstevel 	if (nodeid == OBP_NONODE)
90503831d35Sstevel 		rv = -1;
90603831d35Sstevel 	else {
90703831d35Sstevel 		*len = prom_getproplen(nodeid, (caddr_t)name);
90803831d35Sstevel 		rv = (*len < 0 ? -1 : 0);
90903831d35Sstevel 	}
91003831d35Sstevel 
91103831d35Sstevel 	return (rv);
91203831d35Sstevel }
91303831d35Sstevel 
91403831d35Sstevel static drmachid_t
drmach_node_dup(drmach_node_t * np)91503831d35Sstevel drmach_node_dup(drmach_node_t *np)
91603831d35Sstevel {
91703831d35Sstevel 	drmach_node_t *dup;
91803831d35Sstevel 
91903831d35Sstevel 	dup = drmach_node_new();
92003831d35Sstevel 	dup->here = np->here;
92103831d35Sstevel 	dup->get_dnode = np->get_dnode;
92203831d35Sstevel 	dup->walk = np->walk;
92303831d35Sstevel 	dup->n_getdip = np->n_getdip;
92403831d35Sstevel 	dup->n_getproplen = np->n_getproplen;
92503831d35Sstevel 	dup->n_getprop = np->n_getprop;
92603831d35Sstevel 	dup->get_parent = np->get_parent;
92703831d35Sstevel 
92803831d35Sstevel 	return (dup);
92903831d35Sstevel }
93003831d35Sstevel 
93103831d35Sstevel /*
93203831d35Sstevel  * drmach_array provides convenient array construction, access,
93303831d35Sstevel  * bounds checking and array destruction logic.
93403831d35Sstevel  */
93503831d35Sstevel 
93603831d35Sstevel static drmach_array_t *
drmach_array_new(int min_index,int max_index)93703831d35Sstevel drmach_array_new(int min_index, int max_index)
93803831d35Sstevel {
93903831d35Sstevel 	drmach_array_t *arr;
94003831d35Sstevel 
94103831d35Sstevel 	arr = kmem_zalloc(sizeof (drmach_array_t), KM_SLEEP);
94203831d35Sstevel 
94303831d35Sstevel 	arr->arr_sz = (max_index - min_index + 1) * sizeof (void *);
94403831d35Sstevel 	if (arr->arr_sz > 0) {
94503831d35Sstevel 		arr->min_index = min_index;
94603831d35Sstevel 		arr->max_index = max_index;
94703831d35Sstevel 
94803831d35Sstevel 		arr->arr = kmem_zalloc(arr->arr_sz, KM_SLEEP);
94903831d35Sstevel 		return (arr);
95003831d35Sstevel 	} else {
95103831d35Sstevel 		kmem_free(arr, sizeof (*arr));
95203831d35Sstevel 		return (0);
95303831d35Sstevel 	}
95403831d35Sstevel }
95503831d35Sstevel 
95603831d35Sstevel static int
drmach_array_set(drmach_array_t * arr,int idx,drmachid_t val)95703831d35Sstevel drmach_array_set(drmach_array_t *arr, int idx, drmachid_t val)
95803831d35Sstevel {
95903831d35Sstevel 	if (idx < arr->min_index || idx > arr->max_index)
96003831d35Sstevel 		return (-1);
96103831d35Sstevel 	else {
96203831d35Sstevel 		arr->arr[idx - arr->min_index] = val;
96303831d35Sstevel 		return (0);
96403831d35Sstevel 	}
96503831d35Sstevel 	/*NOTREACHED*/
96603831d35Sstevel }
96703831d35Sstevel 
96803831d35Sstevel static int
drmach_array_get(drmach_array_t * arr,int idx,drmachid_t * val)96903831d35Sstevel drmach_array_get(drmach_array_t *arr, int idx, drmachid_t *val)
97003831d35Sstevel {
97103831d35Sstevel 	if (idx < arr->min_index || idx > arr->max_index)
97203831d35Sstevel 		return (-1);
97303831d35Sstevel 	else {
97403831d35Sstevel 		*val = arr->arr[idx - arr->min_index];
97503831d35Sstevel 		return (0);
97603831d35Sstevel 	}
97703831d35Sstevel 	/*NOTREACHED*/
97803831d35Sstevel }
97903831d35Sstevel 
98003831d35Sstevel static int
drmach_array_first(drmach_array_t * arr,int * idx,drmachid_t * val)98103831d35Sstevel drmach_array_first(drmach_array_t *arr, int *idx, drmachid_t *val)
98203831d35Sstevel {
98303831d35Sstevel 	int rv;
98403831d35Sstevel 
98503831d35Sstevel 	*idx = arr->min_index;
98603831d35Sstevel 	while ((rv = drmach_array_get(arr, *idx, val)) == 0 && *val == NULL)
98703831d35Sstevel 		*idx += 1;
98803831d35Sstevel 
98903831d35Sstevel 	return (rv);
99003831d35Sstevel }
99103831d35Sstevel 
99203831d35Sstevel static int
drmach_array_next(drmach_array_t * arr,int * idx,drmachid_t * val)99303831d35Sstevel drmach_array_next(drmach_array_t *arr, int *idx, drmachid_t *val)
99403831d35Sstevel {
99503831d35Sstevel 	int rv;
99603831d35Sstevel 
99703831d35Sstevel 	*idx += 1;
99803831d35Sstevel 	while ((rv = drmach_array_get(arr, *idx, val)) == 0 && *val == NULL)
99903831d35Sstevel 		*idx += 1;
100003831d35Sstevel 
100103831d35Sstevel 	return (rv);
100203831d35Sstevel }
100303831d35Sstevel 
100403831d35Sstevel static void
drmach_array_dispose(drmach_array_t * arr,void (* disposer)(drmachid_t))100503831d35Sstevel drmach_array_dispose(drmach_array_t *arr, void (*disposer)(drmachid_t))
100603831d35Sstevel {
100703831d35Sstevel 	drmachid_t	val;
100803831d35Sstevel 	int		idx;
100903831d35Sstevel 	int		rv;
101003831d35Sstevel 
101103831d35Sstevel 	rv = drmach_array_first(arr, &idx, &val);
101203831d35Sstevel 	while (rv == 0) {
101303831d35Sstevel 		(*disposer)(val);
101403831d35Sstevel 
101503831d35Sstevel 		/* clear the array entry */
101603831d35Sstevel 		rv = drmach_array_set(arr, idx, NULL);
101703831d35Sstevel 		ASSERT(rv == 0);
101803831d35Sstevel 
101903831d35Sstevel 		rv = drmach_array_next(arr, &idx, &val);
102003831d35Sstevel 	}
102103831d35Sstevel 
102203831d35Sstevel 	kmem_free(arr->arr, arr->arr_sz);
102303831d35Sstevel 	kmem_free(arr, sizeof (*arr));
102403831d35Sstevel }
102503831d35Sstevel 
102603831d35Sstevel 
102703831d35Sstevel static gdcd_t *
drmach_gdcd_new()102803831d35Sstevel drmach_gdcd_new()
102903831d35Sstevel {
103003831d35Sstevel 	gdcd_t *gdcd;
103103831d35Sstevel 
103203831d35Sstevel 	gdcd = kmem_zalloc(sizeof (gdcd_t), KM_SLEEP);
103303831d35Sstevel 
103403831d35Sstevel 	/* read the gdcd, bail if magic or ver #s are not what is expected */
103503831d35Sstevel 	if (iosram_rd(GDCD_MAGIC, 0, sizeof (gdcd_t), (caddr_t)gdcd)) {
103603831d35Sstevel bail:
103703831d35Sstevel 		kmem_free(gdcd, sizeof (gdcd_t));
103803831d35Sstevel 		return (NULL);
103903831d35Sstevel 	} else if (gdcd->h.dcd_magic != GDCD_MAGIC) {
104003831d35Sstevel 		goto bail;
104103831d35Sstevel 	} else if (gdcd->h.dcd_version != DCD_VERSION) {
104203831d35Sstevel 		goto bail;
104303831d35Sstevel 	}
104403831d35Sstevel 
104503831d35Sstevel 	return (gdcd);
104603831d35Sstevel }
104703831d35Sstevel 
104803831d35Sstevel static void
drmach_gdcd_dispose(gdcd_t * gdcd)104903831d35Sstevel drmach_gdcd_dispose(gdcd_t *gdcd)
105003831d35Sstevel {
105103831d35Sstevel 	kmem_free(gdcd, sizeof (gdcd_t));
105203831d35Sstevel }
105303831d35Sstevel 
105403831d35Sstevel /*ARGSUSED*/
105503831d35Sstevel sbd_error_t *
drmach_configure(drmachid_t id,int flags)105603831d35Sstevel drmach_configure(drmachid_t id, int flags)
105703831d35Sstevel {
105803831d35Sstevel 	drmach_device_t	*dp;
105903831d35Sstevel 	dev_info_t	*rdip;
106003831d35Sstevel 	sbd_error_t	*err = NULL;
106103831d35Sstevel 
106203831d35Sstevel 	/*
106303831d35Sstevel 	 * On Starcat, there is no CPU driver, so it is
106403831d35Sstevel 	 * not necessary to configure any CPU nodes.
106503831d35Sstevel 	 */
106603831d35Sstevel 	if (DRMACH_IS_CPU_ID(id)) {
106703831d35Sstevel 		return (NULL);
106803831d35Sstevel 	}
106903831d35Sstevel 
107003831d35Sstevel 	for (; id; ) {
107103831d35Sstevel 		dev_info_t	*fdip = NULL;
107203831d35Sstevel 
107303831d35Sstevel 		if (!DRMACH_IS_DEVICE_ID(id))
107403831d35Sstevel 			return (drerr_new(0, ESTC_INAPPROP, NULL));
107503831d35Sstevel 		dp = id;
107603831d35Sstevel 
107703831d35Sstevel 		rdip = dp->node->n_getdip(dp->node);
107803831d35Sstevel 
107903831d35Sstevel 		/*
108003831d35Sstevel 		 * We held this branch earlier, so at a minimum its
108103831d35Sstevel 		 * root should still be present in the device tree.
108203831d35Sstevel 		 */
108303831d35Sstevel 		ASSERT(rdip);
108403831d35Sstevel 
108503831d35Sstevel 		DRMACH_PR("drmach_configure: configuring DDI branch");
108603831d35Sstevel 
108703831d35Sstevel 		ASSERT(e_ddi_branch_held(rdip));
108803831d35Sstevel 		if (e_ddi_branch_configure(rdip, &fdip, 0) != 0) {
108903831d35Sstevel 			if (err == NULL) {
109003831d35Sstevel 				/*
109103831d35Sstevel 				 * Record first failure but don't stop
109203831d35Sstevel 				 */
109303831d35Sstevel 				char *path = kmem_alloc(MAXPATHLEN, KM_SLEEP);
109403831d35Sstevel 				dev_info_t *dip = (fdip != NULL) ? fdip : rdip;
109503831d35Sstevel 
109603831d35Sstevel 				(void) ddi_pathname(dip, path);
109703831d35Sstevel 				err = drerr_new(1, ESTC_DRVFAIL, path);
109803831d35Sstevel 
109903831d35Sstevel 				kmem_free(path, MAXPATHLEN);
110003831d35Sstevel 			}
110103831d35Sstevel 
110203831d35Sstevel 			/*
110303831d35Sstevel 			 * If non-NULL, fdip is returned held and must be
110403831d35Sstevel 			 * released.
110503831d35Sstevel 			 */
110603831d35Sstevel 			if (fdip != NULL) {
110703831d35Sstevel 				ddi_release_devi(fdip);
110803831d35Sstevel 			}
110903831d35Sstevel 		}
111003831d35Sstevel 
111103831d35Sstevel 		if (DRMACH_IS_MEM_ID(id)) {
111203831d35Sstevel 			drmach_mem_t	*mp = id;
111303831d35Sstevel 			id = mp->next;
111403831d35Sstevel 		} else {
111503831d35Sstevel 			id = NULL;
111603831d35Sstevel 		}
111703831d35Sstevel 	}
111803831d35Sstevel 
111903831d35Sstevel 	return (err);
112003831d35Sstevel }
112103831d35Sstevel 
112203831d35Sstevel static sbd_error_t *
drmach_device_new(drmach_node_t * node,drmach_board_t * bp,int portid,drmachid_t * idp)112303831d35Sstevel drmach_device_new(drmach_node_t *node,
112403831d35Sstevel 	drmach_board_t *bp, int portid, drmachid_t *idp)
112503831d35Sstevel {
112603831d35Sstevel 	int		i, rv, device_id, unum;
112703831d35Sstevel 	char		name[OBP_MAXDRVNAME];
112803831d35Sstevel 	drmach_device_t	proto;
112903831d35Sstevel 
113003831d35Sstevel 	rv = node->n_getprop(node, "name", name, OBP_MAXDRVNAME);
113103831d35Sstevel 	if (rv) {
113203831d35Sstevel 		sbd_error_t *err;
113303831d35Sstevel 
113403831d35Sstevel 		/* every node is expected to have a name */
113503831d35Sstevel 		err = drerr_new(1, ESTC_GETPROP,
113603831d35Sstevel 		    "dip: 0x%p: property %s",
113703831d35Sstevel 		    node->n_getdip(node), OBP_NAME);
113803831d35Sstevel 
113903831d35Sstevel 		return (err);
114003831d35Sstevel 	}
114103831d35Sstevel 
114203831d35Sstevel 	i = drmach_name2type_idx(name);
114303831d35Sstevel 
114403831d35Sstevel 	if (i < 0 || strcmp(name, "cmp") == 0) {
114503831d35Sstevel 		/*
114603831d35Sstevel 		 * Not a node of interest to dr - including "cmp",
114703831d35Sstevel 		 * but it is in drmach_name2type[], which lets gptwocfg
114803831d35Sstevel 		 * driver to check if node is OBP created.
114903831d35Sstevel 		 */
115003831d35Sstevel 		*idp = (drmachid_t)0;
115103831d35Sstevel 		return (NULL);
115203831d35Sstevel 	}
115303831d35Sstevel 
115403831d35Sstevel 	/*
115503831d35Sstevel 	 * Derive a best-guess unit number from the portid value.
115603831d35Sstevel 	 * Some drmach_*_new constructors (drmach_pci_new, for example)
115703831d35Sstevel 	 * will overwrite the prototype unum value with one that is more
115803831d35Sstevel 	 * appropriate for the device.
115903831d35Sstevel 	 */
116003831d35Sstevel 	device_id = portid & 0x1f;
116103831d35Sstevel 	if (device_id < 4)
116203831d35Sstevel 		unum = device_id;
116303831d35Sstevel 	else if (device_id == 8) {
116403831d35Sstevel 		unum = 0;
116503831d35Sstevel 	} else if (device_id == 9) {
116603831d35Sstevel 		unum = 1;
116703831d35Sstevel 	} else if (device_id == 0x1c) {
116803831d35Sstevel 		unum = 0;
116903831d35Sstevel 	} else if (device_id == 0x1d) {
117003831d35Sstevel 		unum = 1;
117103831d35Sstevel 	} else {
117203831d35Sstevel 		return (DRMACH_INTERNAL_ERROR());
117303831d35Sstevel 	}
117403831d35Sstevel 
117503831d35Sstevel 	bzero(&proto, sizeof (proto));
117603831d35Sstevel 	proto.type = drmach_name2type[i].type;
117703831d35Sstevel 	proto.bp = bp;
117803831d35Sstevel 	proto.node = node;
117903831d35Sstevel 	proto.portid = portid;
118003831d35Sstevel 	proto.unum = unum;
118103831d35Sstevel 
118203831d35Sstevel 	return (drmach_name2type[i].new(&proto, idp));
118303831d35Sstevel }
118403831d35Sstevel 
118503831d35Sstevel static void
drmach_device_dispose(drmachid_t id)118603831d35Sstevel drmach_device_dispose(drmachid_t id)
118703831d35Sstevel {
118803831d35Sstevel 	drmach_device_t *self = id;
118903831d35Sstevel 
119003831d35Sstevel 	self->cm.dispose(id);
119103831d35Sstevel }
119203831d35Sstevel 
119303831d35Sstevel static drmach_board_t *
drmach_board_new(int bnum)119403831d35Sstevel drmach_board_new(int bnum)
119503831d35Sstevel {
119603831d35Sstevel 	drmach_board_t	*bp;
119703831d35Sstevel 
119803831d35Sstevel 	bp = kmem_zalloc(sizeof (drmach_board_t), KM_SLEEP);
119903831d35Sstevel 
120003831d35Sstevel 	bp->cm.isa = (void *)drmach_board_new;
120103831d35Sstevel 	bp->cm.release = drmach_board_release;
120203831d35Sstevel 	bp->cm.status = drmach_board_status;
120303831d35Sstevel 
120403831d35Sstevel 	(void) drmach_board_name(bnum, bp->cm.name, sizeof (bp->cm.name));
120503831d35Sstevel 
120603831d35Sstevel 	bp->bnum = bnum;
120703831d35Sstevel 	bp->devices = NULL;
120803831d35Sstevel 	bp->tree = drmach_node_new();
120903831d35Sstevel 
121007d06da5SSurya Prakki 	(void) drmach_array_set(drmach_boards, bnum, bp);
121103831d35Sstevel 	return (bp);
121203831d35Sstevel }
121303831d35Sstevel 
121403831d35Sstevel static void
drmach_board_dispose(drmachid_t id)121503831d35Sstevel drmach_board_dispose(drmachid_t id)
121603831d35Sstevel {
121703831d35Sstevel 	drmach_board_t *bp;
121803831d35Sstevel 
121903831d35Sstevel 	ASSERT(DRMACH_IS_BOARD_ID(id));
122003831d35Sstevel 	bp = id;
122103831d35Sstevel 
122203831d35Sstevel 	if (bp->tree)
122303831d35Sstevel 		drmach_node_dispose(bp->tree);
122403831d35Sstevel 
122503831d35Sstevel 	if (bp->devices)
122603831d35Sstevel 		drmach_array_dispose(bp->devices, drmach_device_dispose);
122703831d35Sstevel 
122803831d35Sstevel 	kmem_free(bp, sizeof (*bp));
122903831d35Sstevel }
123003831d35Sstevel 
123103831d35Sstevel static sbd_error_t *
drmach_board_status(drmachid_t id,drmach_status_t * stat)123203831d35Sstevel drmach_board_status(drmachid_t id, drmach_status_t *stat)
123303831d35Sstevel {
123403831d35Sstevel 	sbd_error_t	*err = NULL;
123503831d35Sstevel 	drmach_board_t	*bp;
123603831d35Sstevel 	caddr_t		obufp;
123703831d35Sstevel 	dr_showboard_t	shb;
123803831d35Sstevel 
123903831d35Sstevel 	if (!DRMACH_IS_BOARD_ID(id))
124003831d35Sstevel 		return (drerr_new(0, ESTC_INAPPROP, NULL));
124103831d35Sstevel 
124203831d35Sstevel 	bp = id;
124303831d35Sstevel 
124403831d35Sstevel 	/*
124503831d35Sstevel 	 * we need to know if the board's connected before
124603831d35Sstevel 	 * issuing a showboard message.  If it's connected, we just
124703831d35Sstevel 	 * reply with status composed of cached info
124803831d35Sstevel 	 */
124903831d35Sstevel 
125003831d35Sstevel 	if (!bp->connected) {
125103831d35Sstevel 		obufp = kmem_zalloc(sizeof (dr_proto_hdr_t), KM_SLEEP);
125203831d35Sstevel 		err = drmach_mbox_trans(DRMSG_SHOWBOARD, bp->bnum, obufp,
125303831d35Sstevel 		    sizeof (dr_proto_hdr_t), (caddr_t)&shb,
125403831d35Sstevel 		    sizeof (dr_showboard_t));
125503831d35Sstevel 
125603831d35Sstevel 		kmem_free(obufp, sizeof (dr_proto_hdr_t));
125703831d35Sstevel 		if (err)
125803831d35Sstevel 			return (err);
125903831d35Sstevel 
126003831d35Sstevel 		bp->connected = (shb.bd_assigned && shb.bd_active);
126107d06da5SSurya Prakki 		(void) strncpy(bp->type, shb.board_type, sizeof (bp->type));
126203831d35Sstevel 		stat->assigned = bp->assigned = shb.bd_assigned;
126303831d35Sstevel 		stat->powered = bp->powered = shb.power_on;
126403831d35Sstevel 		stat->empty = bp->empty = shb.slot_empty;
126503831d35Sstevel 
126603831d35Sstevel 		switch (shb.test_status) {
126703831d35Sstevel 			case DR_TEST_STATUS_UNKNOWN:
126803831d35Sstevel 			case DR_TEST_STATUS_IPOST:
126903831d35Sstevel 			case DR_TEST_STATUS_ABORTED:
127003831d35Sstevel 				stat->cond = bp->cond = SBD_COND_UNKNOWN;
127103831d35Sstevel 				break;
127203831d35Sstevel 			case DR_TEST_STATUS_PASSED:
127303831d35Sstevel 				stat->cond = bp->cond = SBD_COND_OK;
127403831d35Sstevel 				break;
127503831d35Sstevel 			case DR_TEST_STATUS_FAILED:
127603831d35Sstevel 				stat->cond = bp->cond = SBD_COND_FAILED;
127703831d35Sstevel 				break;
127803831d35Sstevel 			default:
127903831d35Sstevel 				stat->cond = bp->cond = SBD_COND_UNKNOWN;
128003831d35Sstevel 				DRMACH_PR("Unknown test status=0x%x from SC\n",
128103831d35Sstevel 				    shb.test_status);
128203831d35Sstevel 				break;
128303831d35Sstevel 
128403831d35Sstevel 		}
128503831d35Sstevel 
128607d06da5SSurya Prakki 		(void) strncpy(stat->type, shb.board_type, sizeof (stat->type));
128707d06da5SSurya Prakki 		(void) snprintf(stat->info, sizeof (stat->info),
128807d06da5SSurya Prakki 		    "Test Level=%d", shb.test_level);
128903831d35Sstevel 	} else {
129003831d35Sstevel 		stat->assigned = bp->assigned;
129103831d35Sstevel 		stat->powered = bp->powered;
129203831d35Sstevel 		stat->empty = bp->empty;
129303831d35Sstevel 		stat->cond = bp->cond;
129407d06da5SSurya Prakki 		(void) strncpy(stat->type, bp->type, sizeof (stat->type));
129503831d35Sstevel 	}
129603831d35Sstevel 
129703831d35Sstevel 	stat->busy = 0;			/* assume not busy */
129803831d35Sstevel 	stat->configured = 0;		/* assume not configured */
129903831d35Sstevel 	if (bp->devices) {
130003831d35Sstevel 		int		 rv;
130103831d35Sstevel 		int		 d_idx;
130203831d35Sstevel 		drmachid_t	 d_id;
130303831d35Sstevel 
130403831d35Sstevel 		rv = drmach_array_first(bp->devices, &d_idx, &d_id);
130503831d35Sstevel 		while (rv == 0) {
130603831d35Sstevel 			drmach_status_t	d_stat;
130703831d35Sstevel 
130803831d35Sstevel 			err = drmach_i_status(d_id, &d_stat);
130903831d35Sstevel 			if (err)
131003831d35Sstevel 				break;
131103831d35Sstevel 
131203831d35Sstevel 			stat->busy |= d_stat.busy;
131303831d35Sstevel 			stat->configured |= d_stat.configured;
131403831d35Sstevel 
131503831d35Sstevel 			rv = drmach_array_next(bp->devices, &d_idx, &d_id);
131603831d35Sstevel 		}
131703831d35Sstevel 	}
131803831d35Sstevel 
131903831d35Sstevel 	return (err);
132003831d35Sstevel }
132103831d35Sstevel 
132203831d35Sstevel typedef struct drmach_msglist {
132303831d35Sstevel 	kcondvar_t		s_cv; 		/* condvar for sending msg */
132403831d35Sstevel 	kmutex_t		s_lock;		/* mutex for sending */
132503831d35Sstevel 	kcondvar_t		g_cv;		/* condvar for getting reply */
132603831d35Sstevel 	kmutex_t		g_lock;		/* mutex for getting reply */
132703831d35Sstevel 	struct drmach_msglist	*prev;		/* link to previous entry */
132803831d35Sstevel 	struct drmach_msglist	*next;		/* link to next entry */
132903831d35Sstevel 	struct drmach_msglist	*link;		/* link to related entry */
133003831d35Sstevel 	caddr_t			o_buf;		/* address of output buffer */
133103831d35Sstevel 	caddr_t			i_buf; 		/* address of input buffer */
133203831d35Sstevel 	uint32_t		o_buflen;	/* output buffer length */
133303831d35Sstevel 	uint32_t		i_buflen;	/* input buffer length */
133403831d35Sstevel 	uint32_t		msgid;		/* message identifier */
133503831d35Sstevel 	int			o_nretry;	/* number of sending retries */
133603831d35Sstevel 	int			f_error;	/* mailbox framework error */
133703831d35Sstevel 	uint8_t			e_code;		/* error code returned by SC */
133803831d35Sstevel 	uint8_t			p_flag	:1,	/* successfully putmsg */
133903831d35Sstevel 				m_reply	:1,	/* msg reply received */
134003831d35Sstevel 				unused	:6;
134103831d35Sstevel } drmach_msglist_t;
134203831d35Sstevel 
134303831d35Sstevel kmutex_t		drmach_g_mbox_mutex;	/* mutex for mailbox globals */
134403831d35Sstevel kmutex_t		drmach_ri_mbox_mutex;	/* mutex for mailbox reinit */
134503831d35Sstevel kmutex_t		drmach_msglist_mutex;	/* mutex for message list */
134603831d35Sstevel drmach_msglist_t	*drmach_msglist_first;	/* first entry in msg list */
134703831d35Sstevel drmach_msglist_t	*drmach_msglist_last;	/* last entry in msg list */
134803831d35Sstevel uint32_t		drmach_msgid;		/* current message id */
134903831d35Sstevel kthread_t		*drmach_getmsg_thread;	/* ptr to getmsg thread */
135003831d35Sstevel volatile int		drmach_getmsg_thread_run; /* run flag for getmsg thr */
135103831d35Sstevel kmutex_t		drmach_sendmsg_mutex;	/* mutex for sendmsg cv */
135203831d35Sstevel kcondvar_t		drmach_sendmsg_cv;	/* signaled to send new msg */
135303831d35Sstevel kthread_t		*drmach_sendmsg_thread; /* ptr to sendmsg thread */
135403831d35Sstevel volatile int		drmach_sendmsg_thread_run; /* run flag for sendmsg */
135503831d35Sstevel int			drmach_mbox_istate;	/* mailbox init state */
135603831d35Sstevel int			drmach_mbox_iflag;	/* set if init'd with SC */
135703831d35Sstevel int			drmach_mbox_ipending;	/* set if reinit scheduled */
135803831d35Sstevel 
135903831d35Sstevel /*
136003831d35Sstevel  * Timeout values (in seconds) used when waiting for replies (from the SC) to
136103831d35Sstevel  * requests that we sent.  Since we only receive boardevent messages, and they
136203831d35Sstevel  * are events rather than replies, there is no boardevent timeout.
136303831d35Sstevel  */
136403831d35Sstevel int	drmach_to_mbxinit	= 60;		/* 1 minute */
136503831d35Sstevel int	drmach_to_assign	= 60;		/* 1 minute */
136603831d35Sstevel int	drmach_to_unassign	= 60;		/* 1 minute */
136703831d35Sstevel int	drmach_to_claim		= 3600;		/* 1 hour */
136803831d35Sstevel int	drmach_to_unclaim	= 3600;		/* 1 hour */
136903831d35Sstevel int	drmach_to_poweron	= 480;		/* 8 minutes */
137003831d35Sstevel int	drmach_to_poweroff	= 480;		/* 8 minutes */
137103831d35Sstevel int	drmach_to_testboard	= 43200;	/* 12 hours */
137203831d35Sstevel int	drmach_to_aborttest	= 180;		/* 3 minutes */
137303831d35Sstevel int	drmach_to_showboard	= 180;		/* 3 minutes */
137403831d35Sstevel int	drmach_to_unconfig	= 180;		/* 3 minutes */
137503831d35Sstevel 
137603831d35Sstevel /*
137703831d35Sstevel  * Delay (in seconds) used after receiving a non-transient error indication from
137803831d35Sstevel  * an mboxsc_getmsg call in the thread that loops waiting for incoming messages.
137903831d35Sstevel  */
138003831d35Sstevel int	drmach_mbxerr_delay	= 15;		/* 15 seconds */
138103831d35Sstevel 
138203831d35Sstevel /*
138303831d35Sstevel  * Timeout values (in milliseconds) for mboxsc_putmsg and mboxsc_getmsg calls.
138403831d35Sstevel  */
138503831d35Sstevel clock_t	drmach_to_putmsg;			/* set in drmach_mbox_init */
138603831d35Sstevel clock_t	drmach_to_getmsg	= 31000;	/* 31 seconds */
138703831d35Sstevel 
138803831d35Sstevel /*
138903831d35Sstevel  * Normally, drmach_to_putmsg is set dynamically during initialization in
139003831d35Sstevel  * drmach_mbox_init.  This has the potentially undesirable side effect of
139103831d35Sstevel  * clobbering any value that might have been set in /etc/system.  To prevent
139203831d35Sstevel  * dynamic setting of drmach_to_putmsg (thereby allowing it to be tuned in
139303831d35Sstevel  * /etc/system), set drmach_use_tuned_putmsg_to to 1.
139403831d35Sstevel  */
139503831d35Sstevel int	drmach_use_tuned_putmsg_to	= 0;
139603831d35Sstevel 
139703831d35Sstevel 
139803831d35Sstevel /* maximum conceivable message size for future mailbox protocol versions */
139903831d35Sstevel #define	DRMACH_MAX_MBOX_MSG_SIZE	4096
140003831d35Sstevel 
140103831d35Sstevel /*ARGSUSED*/
140203831d35Sstevel void
drmach_mbox_prmsg(dr_mbox_msg_t * mbp,int dir)140303831d35Sstevel drmach_mbox_prmsg(dr_mbox_msg_t *mbp, int dir)
140403831d35Sstevel {
140503831d35Sstevel 	int		i, j;
140603831d35Sstevel 	dr_memregs_t	*memregs;
140703831d35Sstevel 	dr_proto_hdr_t	*php = &mbp->p_hdr;
140803831d35Sstevel 	dr_msg_t	*mp = &mbp->msgdata;
140903831d35Sstevel 
141003831d35Sstevel #ifdef DEBUG
141103831d35Sstevel 	switch (php->command) {
141203831d35Sstevel 		case DRMSG_BOARDEVENT:
141303831d35Sstevel 			if (dir) {
141403831d35Sstevel 				DRMACH_PR("ERROR!! outgoing BOARDEVENT\n");
141503831d35Sstevel 			} else {
141603831d35Sstevel 				DRMACH_PR("BOARDEVENT received:\n");
141703831d35Sstevel 				DRMACH_PR("init=%d ins=%d rem=%d asgn=%d\n",
141803831d35Sstevel 				    mp->dm_be.initialized,
141903831d35Sstevel 				    mp->dm_be.board_insertion,
142003831d35Sstevel 				    mp->dm_be.board_removal,
142103831d35Sstevel 				    mp->dm_be.slot_assign);
142203831d35Sstevel 				DRMACH_PR("unasgn=%d avail=%d unavail=%d\n",
142303831d35Sstevel 				    mp->dm_be.slot_unassign,
142403831d35Sstevel 				    mp->dm_be.slot_avail,
142503831d35Sstevel 				    mp->dm_be.slot_unavail);
142603831d35Sstevel 			}
142703831d35Sstevel 			break;
142803831d35Sstevel 		case DRMSG_MBOX_INIT:
142903831d35Sstevel 			if (dir) {
143003831d35Sstevel 				DRMACH_PR("MBOX_INIT Request:\n");
143103831d35Sstevel 			} else {
143203831d35Sstevel 				DRMACH_PR("MBOX_INIT Reply:\n");
143303831d35Sstevel 			}
143403831d35Sstevel 			break;
143503831d35Sstevel 		case DRMSG_ASSIGN:
143603831d35Sstevel 			if (dir) {
143703831d35Sstevel 				DRMACH_PR("ASSIGN Request:\n");
143803831d35Sstevel 			} else {
143903831d35Sstevel 				DRMACH_PR("ASSIGN Reply:\n");
144003831d35Sstevel 			}
144103831d35Sstevel 			break;
144203831d35Sstevel 		case DRMSG_UNASSIGN:
144303831d35Sstevel 			if (dir) {
144403831d35Sstevel 				DRMACH_PR("UNASSIGN Request:\n");
144503831d35Sstevel 			} else {
144603831d35Sstevel 				DRMACH_PR("UNASSIGN Reply:\n");
144703831d35Sstevel 			}
144803831d35Sstevel 			break;
144903831d35Sstevel 		case DRMSG_CLAIM:
145003831d35Sstevel 			if (!dir) {
145103831d35Sstevel 				DRMACH_PR("CLAIM Reply:\n");
145203831d35Sstevel 				break;
145303831d35Sstevel 			}
145403831d35Sstevel 
145503831d35Sstevel 			DRMACH_PR("CLAIM Request:\n");
145603831d35Sstevel 			for (i = 0; i < 18; ++i) {
145703831d35Sstevel 				DRMACH_PR("exp%d: val=%d slice=0x%x\n", i,
145803831d35Sstevel 				    mp->dm_cr.mem_slice[i].valid,
145903831d35Sstevel 				    mp->dm_cr.mem_slice[i].slice);
146003831d35Sstevel 				memregs = &(mp->dm_cr.mem_regs[i]);
146103831d35Sstevel 				for (j = 0; j < S0_LPORT_COUNT; j++) {
146203831d35Sstevel 					DRMACH_PR("  MC %2d: "
146303831d35Sstevel 					    "MADR[%d] = 0x%lx, "
146403831d35Sstevel 					    "MADR[%d] = 0x%lx\n", j,
146503831d35Sstevel 					    0, DRMACH_MCREG_TO_U64(
146603831d35Sstevel 					    memregs->madr[j][0]),
146703831d35Sstevel 					    1, DRMACH_MCREG_TO_U64(
146803831d35Sstevel 					    memregs->madr[j][1]));
146903831d35Sstevel 					DRMACH_PR("       : "
147003831d35Sstevel 					    "MADR[%d] = 0x%lx, "
147103831d35Sstevel 					    "MADR[%d] = 0x%lx\n",
147203831d35Sstevel 					    2, DRMACH_MCREG_TO_U64(
147303831d35Sstevel 					    memregs->madr[j][2]),
147403831d35Sstevel 					    3, DRMACH_MCREG_TO_U64(
147503831d35Sstevel 					    memregs->madr[j][3]));
147603831d35Sstevel 				}
147703831d35Sstevel 			}
147803831d35Sstevel 			break;
147903831d35Sstevel 		case DRMSG_UNCLAIM:
148003831d35Sstevel 			if (!dir) {
148103831d35Sstevel 				DRMACH_PR("UNCLAIM Reply:\n");
148203831d35Sstevel 				break;
148303831d35Sstevel 			}
148403831d35Sstevel 
148503831d35Sstevel 			DRMACH_PR("UNCLAIM Request:\n");
148603831d35Sstevel 			for (i = 0; i < 18; ++i) {
148703831d35Sstevel 				DRMACH_PR("exp%d: val=%d slice=0x%x\n", i,
148803831d35Sstevel 				    mp->dm_ur.mem_slice[i].valid,
148903831d35Sstevel 				    mp->dm_ur.mem_slice[i].slice);
149003831d35Sstevel 				memregs = &(mp->dm_ur.mem_regs[i]);
149103831d35Sstevel 				for (j = 0; j < S0_LPORT_COUNT; j++) {
149203831d35Sstevel 					DRMACH_PR("  MC %2d: "
149303831d35Sstevel 					    "MADR[%d] = 0x%lx, "
149403831d35Sstevel 					    "MADR[%d] = 0x%lx\n", j,
149503831d35Sstevel 					    0, DRMACH_MCREG_TO_U64(
149603831d35Sstevel 					    memregs->madr[j][0]),
149703831d35Sstevel 					    1, DRMACH_MCREG_TO_U64(
149803831d35Sstevel 					    memregs->madr[j][1]));
149903831d35Sstevel 					DRMACH_PR("       : "
150003831d35Sstevel 					    "MADR[%d] = 0x%lx, "
150103831d35Sstevel 					    "MADR[%d] = 0x%lx\n",
150203831d35Sstevel 					    2, DRMACH_MCREG_TO_U64(
150303831d35Sstevel 					    memregs->madr[j][2]),
150403831d35Sstevel 					    3, DRMACH_MCREG_TO_U64(
150503831d35Sstevel 					    memregs->madr[j][3]));
150603831d35Sstevel 				}
150703831d35Sstevel 			}
150803831d35Sstevel 			DRMACH_PR(" mem_clear=%d\n", mp->dm_ur.mem_clear);
150903831d35Sstevel 			break;
151003831d35Sstevel 		case DRMSG_UNCONFIG:
151103831d35Sstevel 			if (!dir) {
151203831d35Sstevel 				DRMACH_PR("UNCONFIG Reply:\n");
151303831d35Sstevel 				break;
151403831d35Sstevel 			}
151503831d35Sstevel 
151603831d35Sstevel 			DRMACH_PR("UNCONFIG Request:\n");
151703831d35Sstevel 			for (i = 0; i < 18; ++i) {
151803831d35Sstevel 				DRMACH_PR("exp%d: val=%d slice=0x%x\n", i,
151903831d35Sstevel 				    mp->dm_uc.mem_slice[i].valid,
152003831d35Sstevel 				    mp->dm_uc.mem_slice[i].slice);
152103831d35Sstevel 				memregs = &(mp->dm_uc.mem_regs[i]);
152203831d35Sstevel 				for (j = 0; j < S0_LPORT_COUNT; j++) {
152303831d35Sstevel 					DRMACH_PR("  MC %2d: "
152403831d35Sstevel 					    "MADR[%d] = 0x%lx, "
152503831d35Sstevel 					    "MADR[%d] = 0x%lx\n", j,
152603831d35Sstevel 					    0, DRMACH_MCREG_TO_U64(
152703831d35Sstevel 					    memregs->madr[j][0]),
152803831d35Sstevel 					    1, DRMACH_MCREG_TO_U64(
152903831d35Sstevel 					    memregs->madr[j][1]));
153003831d35Sstevel 					DRMACH_PR("       : "
153103831d35Sstevel 					    "MADR[%d] = 0x%lx, "
153203831d35Sstevel 					    "MADR[%d] = 0x%lx\n",
153303831d35Sstevel 					    2, DRMACH_MCREG_TO_U64(
153403831d35Sstevel 					    memregs->madr[j][2]),
153503831d35Sstevel 					    3, DRMACH_MCREG_TO_U64(
153603831d35Sstevel 					    memregs->madr[j][3]));
153703831d35Sstevel 				}
153803831d35Sstevel 			}
153903831d35Sstevel 			break;
154003831d35Sstevel 		case DRMSG_POWERON:
154103831d35Sstevel 			if (dir) {
154203831d35Sstevel 				DRMACH_PR("POWERON Request:\n");
154303831d35Sstevel 			} else {
154403831d35Sstevel 				DRMACH_PR("POWERON Reply:\n");
154503831d35Sstevel 			}
154603831d35Sstevel 			break;
154703831d35Sstevel 		case DRMSG_POWEROFF:
154803831d35Sstevel 			if (dir) {
154903831d35Sstevel 				DRMACH_PR("POWEROFF Request:\n");
155003831d35Sstevel 			} else {
155103831d35Sstevel 				DRMACH_PR("POWEROFF Reply:\n");
155203831d35Sstevel 			}
155303831d35Sstevel 			break;
155403831d35Sstevel 		case DRMSG_TESTBOARD:
155503831d35Sstevel 			if (dir) {
155603831d35Sstevel 				DRMACH_PR("TESTBOARD Request:\n");
155703831d35Sstevel 				DRMACH_PR("\tmemaddrhi=0x%x memaddrlo=0x%x ",
155803831d35Sstevel 				    mp->dm_tb.memaddrhi,
155903831d35Sstevel 				    mp->dm_tb.memaddrlo);
156003831d35Sstevel 				DRMACH_PR("memlen=0x%x cpu_portid=0x%x\n",
156103831d35Sstevel 				    mp->dm_tb.memlen, mp->dm_tb.cpu_portid);
156203831d35Sstevel 				DRMACH_PR("\tforce=0x%x imm=0x%x\n",
156303831d35Sstevel 				    mp->dm_tb.force, mp->dm_tb.immediate);
156403831d35Sstevel 			} else {
156503831d35Sstevel 				DRMACH_PR("TESTBOARD Reply:\n");
156603831d35Sstevel 				DRMACH_PR("\tmemaddrhi=0x%x memaddrlo=0x%x ",
156703831d35Sstevel 				    mp->dm_tr.memaddrhi,
156803831d35Sstevel 				    mp->dm_tr.memaddrlo);
156903831d35Sstevel 				DRMACH_PR("memlen=0x%x cpu_portid=0x%x\n",
157003831d35Sstevel 				    mp->dm_tr.memlen, mp->dm_tr.cpu_portid);
157103831d35Sstevel 				DRMACH_PR("\trecovered=0x%x test status=0x%x\n",
157203831d35Sstevel 				    mp->dm_tr.cpu_recovered,
157303831d35Sstevel 				    mp->dm_tr.test_status);
157403831d35Sstevel 
157503831d35Sstevel 			}
157603831d35Sstevel 			break;
157703831d35Sstevel 		case DRMSG_ABORT_TEST:
157803831d35Sstevel 			if (dir) {
157903831d35Sstevel 				DRMACH_PR("ABORT_TEST Request:\n");
158003831d35Sstevel 			} else {
158103831d35Sstevel 				DRMACH_PR("ABORT_TEST Reply:\n");
158203831d35Sstevel 			}
158303831d35Sstevel 
158403831d35Sstevel 			DRMACH_PR("\tmemaddrhi=0x%x memaddrlo=0x%x ",
158503831d35Sstevel 			    mp->dm_ta.memaddrhi,
158603831d35Sstevel 			    mp->dm_ta.memaddrlo);
158703831d35Sstevel 			DRMACH_PR("memlen=0x%x cpu_portid=0x%x\n",
158803831d35Sstevel 			    mp->dm_ta.memlen, mp->dm_ta.cpu_portid);
158903831d35Sstevel 			break;
159003831d35Sstevel 		case DRMSG_SHOWBOARD:
159103831d35Sstevel 			if (dir) {
159203831d35Sstevel 				DRMACH_PR("SHOWBOARD Request:\n");
159303831d35Sstevel 			} else {
159403831d35Sstevel 				DRMACH_PR("SHOWBOARD Reply:\n");
159503831d35Sstevel 
159603831d35Sstevel 				DRMACH_PR(": empty=%d power=%d assigned=%d",
159703831d35Sstevel 				    mp->dm_sb.slot_empty,
159803831d35Sstevel 				    mp->dm_sb.power_on,
159903831d35Sstevel 				    mp->dm_sb.bd_assigned);
160003831d35Sstevel 				DRMACH_PR(": active=%d t_status=%d t_level=%d ",
160103831d35Sstevel 				    mp->dm_sb.bd_active,
160203831d35Sstevel 				    mp->dm_sb.test_status,
160303831d35Sstevel 				    mp->dm_sb.test_level);
160403831d35Sstevel 				DRMACH_PR(": type=%s ", mp->dm_sb.board_type);
160503831d35Sstevel 			}
160603831d35Sstevel 			break;
160703831d35Sstevel 		default:
160803831d35Sstevel 			DRMACH_PR("Unknown message type\n");
160903831d35Sstevel 			break;
161003831d35Sstevel 	}
161103831d35Sstevel 
161203831d35Sstevel 	DRMACH_PR("dr hdr:\n\tid=0x%x vers=0x%x cmd=0x%x exp=0x%x slot=0x%x\n",
161303831d35Sstevel 	    php->message_id, php->drproto_version, php->command,
161403831d35Sstevel 	    php->expbrd, php->slot);
161503831d35Sstevel #endif
161603831d35Sstevel 	DRMACH_PR("\treply_status=0x%x error_code=0x%x\n", php->reply_status,
161703831d35Sstevel 	    php->error_code);
161803831d35Sstevel }
161903831d35Sstevel 
162003831d35Sstevel /*
162103831d35Sstevel  * Callback function passed to taskq_dispatch when a mailbox reinitialization
162203831d35Sstevel  * handshake needs to be scheduled.  The handshake can't be performed by the
162303831d35Sstevel  * thread that determines it is needed, in most cases, so this function is
162403831d35Sstevel  * dispatched on the system-wide taskq pool of threads.  Failure is reported but
162503831d35Sstevel  * otherwise ignored, since any situation that requires a mailbox initialization
162603831d35Sstevel  * handshake will continue to request the handshake until it succeeds.
162703831d35Sstevel  */
162803831d35Sstevel static void
drmach_mbox_reinit(void * unused)162903831d35Sstevel drmach_mbox_reinit(void *unused)
163003831d35Sstevel {
163103831d35Sstevel 	_NOTE(ARGUNUSED(unused))
163203831d35Sstevel 
163303831d35Sstevel 	caddr_t		obufp = NULL;
163403831d35Sstevel 	sbd_error_t	*serr = NULL;
163503831d35Sstevel 
163603831d35Sstevel 	DRMACH_PR("scheduled mailbox reinit running\n");
163703831d35Sstevel 
163803831d35Sstevel 	mutex_enter(&drmach_ri_mbox_mutex);
163903831d35Sstevel 	mutex_enter(&drmach_g_mbox_mutex);
164003831d35Sstevel 	if (drmach_mbox_iflag == 0) {
164103831d35Sstevel 		/* need to initialize the mailbox */
164203831d35Sstevel 		mutex_exit(&drmach_g_mbox_mutex);
164303831d35Sstevel 
164403831d35Sstevel 		cmn_err(CE_NOTE, "!reinitializing DR mailbox");
164503831d35Sstevel 		obufp = kmem_zalloc(sizeof (dr_proto_hdr_t), KM_SLEEP);
164603831d35Sstevel 		serr = drmach_mbox_trans(DRMSG_MBOX_INIT, 0, obufp,
164703831d35Sstevel 		    sizeof (dr_proto_hdr_t), (caddr_t)NULL, 0);
164803831d35Sstevel 		kmem_free(obufp, sizeof (dr_proto_hdr_t));
164903831d35Sstevel 
165003831d35Sstevel 		if (serr) {
165103831d35Sstevel 			cmn_err(CE_WARN,
165203831d35Sstevel 			    "mbox_init: MBOX_INIT failed ecode=0x%x",
165303831d35Sstevel 			    serr->e_code);
165403831d35Sstevel 			sbd_err_clear(&serr);
165503831d35Sstevel 		}
165603831d35Sstevel 		mutex_enter(&drmach_g_mbox_mutex);
165703831d35Sstevel 		if (!serr) {
165803831d35Sstevel 			drmach_mbox_iflag = 1;
165903831d35Sstevel 		}
166003831d35Sstevel 	}
166103831d35Sstevel 	drmach_mbox_ipending = 0;
166203831d35Sstevel 	mutex_exit(&drmach_g_mbox_mutex);
166303831d35Sstevel 	mutex_exit(&drmach_ri_mbox_mutex);
166403831d35Sstevel }
166503831d35Sstevel 
166603831d35Sstevel /*
166703831d35Sstevel  * To ensure sufficient compatibility with future versions of the DR mailbox
166803831d35Sstevel  * protocol, we use a buffer that is large enough to receive the largest message
166903831d35Sstevel  * that could possibly be sent to us.  However, since that ends up being fairly
167003831d35Sstevel  * large, allocating it on the stack is a bad idea.  Fortunately, this function
167103831d35Sstevel  * does not need to be MT-safe since it is only invoked by the mailbox
167203831d35Sstevel  * framework, which will never invoke it multiple times concurrently.  Since
167303831d35Sstevel  * that is the case, we can use a static buffer.
167403831d35Sstevel  */
167503831d35Sstevel void
drmach_mbox_event(void)167603831d35Sstevel drmach_mbox_event(void)
167703831d35Sstevel {
167803831d35Sstevel 	static uint8_t	buf[DRMACH_MAX_MBOX_MSG_SIZE];
167903831d35Sstevel 	dr_mbox_msg_t	*msg = (dr_mbox_msg_t *)buf;
168003831d35Sstevel 	int		err;
168103831d35Sstevel 	uint32_t	type = MBOXSC_MSG_EVENT;
168203831d35Sstevel 	uint32_t	command = DRMSG_BOARDEVENT;
168303831d35Sstevel 	uint64_t	transid = 0;
168403831d35Sstevel 	uint32_t	length = DRMACH_MAX_MBOX_MSG_SIZE;
168503831d35Sstevel 	char		*hint = "";
168603831d35Sstevel 	int		logsys = 0;
168703831d35Sstevel 
168803831d35Sstevel 	do {
1689d3d50737SRafael Vanoni 		err = mboxsc_getmsg(KEY_SCDR, &type, &command, &transid,
1690d3d50737SRafael Vanoni 		    &length, (void *)msg, 0);
169103831d35Sstevel 	} while (err == EAGAIN);
169203831d35Sstevel 
169303831d35Sstevel 	/* don't try to interpret anything with the wrong version number */
169403831d35Sstevel 	if ((err == 0) && (msg->p_hdr.drproto_version != DRMBX_VERSION)) {
169503831d35Sstevel 		cmn_err(CE_WARN, "mailbox version mismatch 0x%x vs 0x%x",
169603831d35Sstevel 		    msg->p_hdr.drproto_version, DRMBX_VERSION);
169703831d35Sstevel 		mutex_enter(&drmach_g_mbox_mutex);
169803831d35Sstevel 		drmach_mbox_iflag = 0;
169903831d35Sstevel 		/* schedule a reinit handshake if one isn't pending */
170003831d35Sstevel 		if (!drmach_mbox_ipending) {
170103831d35Sstevel 			if (taskq_dispatch(system_taskq, drmach_mbox_reinit,
170203831d35Sstevel 			    NULL, TQ_NOSLEEP) != NULL) {
170303831d35Sstevel 				drmach_mbox_ipending = 1;
170403831d35Sstevel 			} else {
170503831d35Sstevel 				cmn_err(CE_WARN,
170603831d35Sstevel 				    "failed to schedule mailbox reinit");
170703831d35Sstevel 			}
170803831d35Sstevel 		}
170903831d35Sstevel 		mutex_exit(&drmach_g_mbox_mutex);
171003831d35Sstevel 		return;
171103831d35Sstevel 	}
171203831d35Sstevel 
171303831d35Sstevel 	if ((err != 0) || (msg->p_hdr.reply_status != DRMSG_REPLY_OK)) {
171403831d35Sstevel 		cmn_err(CE_WARN,
171503831d35Sstevel 		    "Unsolicited mboxsc_getmsg failed: err=0x%x code=0x%x",
171603831d35Sstevel 		    err, msg->p_hdr.error_code);
171703831d35Sstevel 	} else {
171803831d35Sstevel 		dr_boardevent_t	*be;
171903831d35Sstevel 		be = (dr_boardevent_t *)&msg->msgdata;
172003831d35Sstevel 
172103831d35Sstevel 		/* check for initialization event */
172203831d35Sstevel 		if (be->initialized) {
172303831d35Sstevel 			mutex_enter(&drmach_g_mbox_mutex);
172403831d35Sstevel 			drmach_mbox_iflag = 0;
172503831d35Sstevel 			/* schedule a reinit handshake if one isn't pending */
172603831d35Sstevel 			if (!drmach_mbox_ipending) {
172703831d35Sstevel 				if (taskq_dispatch(system_taskq,
172803831d35Sstevel 				    drmach_mbox_reinit, NULL, TQ_NOSLEEP)
172903831d35Sstevel 				    != NULL) {
173003831d35Sstevel 					drmach_mbox_ipending = 1;
173103831d35Sstevel 				} else {
1732d3d50737SRafael Vanoni 					cmn_err(CE_WARN, "failed to schedule "
1733d3d50737SRafael Vanoni 					    "mailbox reinit");
173403831d35Sstevel 				}
173503831d35Sstevel 			}
173603831d35Sstevel 			mutex_exit(&drmach_g_mbox_mutex);
173703831d35Sstevel 			cmn_err(CE_NOTE, "!Mailbox Init event received");
173803831d35Sstevel 		}
173903831d35Sstevel 
174003831d35Sstevel 		/* anything else will be a log_sysevent call */
174103831d35Sstevel 
174203831d35Sstevel 		if (be->board_insertion) {
174303831d35Sstevel 			DRMACH_PR("Board Insertion event received");
174403831d35Sstevel 			hint = DR_HINT_INSERT;
174503831d35Sstevel 			logsys++;
174603831d35Sstevel 	}
174703831d35Sstevel 		if (be->board_removal) {
174803831d35Sstevel 			DRMACH_PR("Board Removal event received");
174903831d35Sstevel 			hint = DR_HINT_REMOVE;
175003831d35Sstevel 			logsys++;
175103831d35Sstevel 		}
175203831d35Sstevel 		if (be->slot_assign) {
175303831d35Sstevel 			DRMACH_PR("Slot Assign event received");
175403831d35Sstevel 			logsys++;
175503831d35Sstevel 		}
175603831d35Sstevel 		if (be->slot_unassign) {
175703831d35Sstevel 			DRMACH_PR("Slot Unassign event received");
175803831d35Sstevel 			logsys++;
175903831d35Sstevel 		}
176003831d35Sstevel 		if (be->slot_avail) {
176103831d35Sstevel 			DRMACH_PR("Slot Available event received");
176203831d35Sstevel 			logsys++;
176303831d35Sstevel 		}
176403831d35Sstevel 		if (be->slot_unavail) {
176503831d35Sstevel 			DRMACH_PR("Slot Unavailable event received");
176603831d35Sstevel 			logsys++;
176703831d35Sstevel 		}
176803831d35Sstevel 		if (be->power_on) {
176903831d35Sstevel 			DRMACH_PR("Power ON event received");
177003831d35Sstevel 			logsys++;
177103831d35Sstevel 		}
177203831d35Sstevel 		if (be->power_off) {
177303831d35Sstevel 			DRMACH_PR("Power OFF event received");
177403831d35Sstevel 			logsys++;
177503831d35Sstevel 		}
177603831d35Sstevel 
177703831d35Sstevel 		if (logsys)
177807d06da5SSurya Prakki 			(void) drmach_log_sysevent(
177903831d35Sstevel 			    DRMACH_EXPSLOT2BNUM(msg->p_hdr.expbrd,
1780d3d50737SRafael Vanoni 			    msg->p_hdr.slot), hint, SE_NOSLEEP, 1);
178103831d35Sstevel 	}
178203831d35Sstevel }
178303831d35Sstevel 
178403831d35Sstevel static uint32_t
drmach_get_msgid()178503831d35Sstevel drmach_get_msgid()
178603831d35Sstevel {
178703831d35Sstevel 	uint32_t	rv;
178803831d35Sstevel 	mutex_enter(&drmach_msglist_mutex);
178903831d35Sstevel 	if (!(++drmach_msgid))
179003831d35Sstevel 		++drmach_msgid;
179103831d35Sstevel 	rv = drmach_msgid;
179203831d35Sstevel 	mutex_exit(&drmach_msglist_mutex);
179303831d35Sstevel 	return (rv);
179403831d35Sstevel }
179503831d35Sstevel 
179603831d35Sstevel /*
179703831d35Sstevel  *	unlink an entry from the message transaction list
179803831d35Sstevel  *
179903831d35Sstevel  *	caller must hold drmach_msglist_mutex
180003831d35Sstevel  */
180103831d35Sstevel void
drmach_msglist_unlink(drmach_msglist_t * entry)180203831d35Sstevel drmach_msglist_unlink(drmach_msglist_t *entry)
180303831d35Sstevel {
180403831d35Sstevel 	ASSERT(mutex_owned(&drmach_msglist_mutex));
180503831d35Sstevel 	if (entry->prev) {
180603831d35Sstevel 		entry->prev->next = entry->next;
180703831d35Sstevel 		if (entry->next)
180803831d35Sstevel 			entry->next->prev = entry->prev;
180903831d35Sstevel 	} else {
181003831d35Sstevel 		drmach_msglist_first = entry->next;
181103831d35Sstevel 		if (entry->next)
181203831d35Sstevel 			entry->next->prev = NULL;
181303831d35Sstevel 	}
181403831d35Sstevel 	if (entry == drmach_msglist_last) {
181503831d35Sstevel 		drmach_msglist_last = entry->prev;
181603831d35Sstevel 	}
181703831d35Sstevel }
181803831d35Sstevel 
181903831d35Sstevel void
drmach_msglist_link(drmach_msglist_t * entry)182003831d35Sstevel drmach_msglist_link(drmach_msglist_t *entry)
182103831d35Sstevel {
182203831d35Sstevel 	mutex_enter(&drmach_msglist_mutex);
182303831d35Sstevel 	if (drmach_msglist_last) {
182403831d35Sstevel 		entry->prev = drmach_msglist_last;
182503831d35Sstevel 		drmach_msglist_last->next = entry;
182603831d35Sstevel 		drmach_msglist_last = entry;
182703831d35Sstevel 	} else {
182803831d35Sstevel 		drmach_msglist_last = drmach_msglist_first = entry;
182903831d35Sstevel 	}
183003831d35Sstevel 	mutex_exit(&drmach_msglist_mutex);
183103831d35Sstevel }
183203831d35Sstevel 
183303831d35Sstevel void
drmach_mbox_getmsg()183403831d35Sstevel drmach_mbox_getmsg()
183503831d35Sstevel {
183603831d35Sstevel 	int			err;
183703831d35Sstevel 	register int		msgid;
183803831d35Sstevel 	static uint8_t		buf[DRMACH_MAX_MBOX_MSG_SIZE];
183903831d35Sstevel 	dr_mbox_msg_t		*msg = (dr_mbox_msg_t *)buf;
184003831d35Sstevel 	dr_proto_hdr_t		*php;
184103831d35Sstevel 	drmach_msglist_t	*found, *entry;
184203831d35Sstevel 	uint32_t		type = MBOXSC_MSG_REPLY;
184303831d35Sstevel 	uint32_t		command;
184403831d35Sstevel 	uint64_t		transid;
184503831d35Sstevel 	uint32_t		length;
184603831d35Sstevel 
184703831d35Sstevel 	php = &msg->p_hdr;
184803831d35Sstevel 
184903831d35Sstevel 	while (drmach_getmsg_thread_run != 0) {
185003831d35Sstevel 		/* get a reply message */
185103831d35Sstevel 		command = 0;
185203831d35Sstevel 		transid = 0;
185303831d35Sstevel 		length = DRMACH_MAX_MBOX_MSG_SIZE;
1854d3d50737SRafael Vanoni 		err = mboxsc_getmsg(KEY_SCDR, &type, &command, &transid,
1855d3d50737SRafael Vanoni 		    &length, (void *)msg, drmach_to_getmsg);
185603831d35Sstevel 
185703831d35Sstevel 		if (err) {
185803831d35Sstevel 			/*
185903831d35Sstevel 			 * If mboxsc_getmsg returns ETIMEDOUT or EAGAIN, then
186003831d35Sstevel 			 * the "error" is really just a normal, transient
186103831d35Sstevel 			 * condition and we can retry the operation right away.
186203831d35Sstevel 			 * Any other error suggests a more serious problem,
186303831d35Sstevel 			 * ranging from a message being too big for our buffer
186403831d35Sstevel 			 * (EMSGSIZE) to total failure of the mailbox layer.
186503831d35Sstevel 			 * This second class of errors is much less "transient",
186603831d35Sstevel 			 * so rather than retrying over and over (and getting
186703831d35Sstevel 			 * the same error over and over) as fast as we can,
186803831d35Sstevel 			 * we'll sleep for a while before retrying.
186903831d35Sstevel 			 */
187003831d35Sstevel 			if ((err != ETIMEDOUT) && (err != EAGAIN)) {
187103831d35Sstevel 				cmn_err(CE_WARN,
187203831d35Sstevel 				    "mboxsc_getmsg failed, err=0x%x", err);
187303831d35Sstevel 				delay(drmach_mbxerr_delay * hz);
187403831d35Sstevel 			}
187503831d35Sstevel 			continue;
187603831d35Sstevel 		}
187703831d35Sstevel 
187803831d35Sstevel 		drmach_mbox_prmsg(msg, 0);
187903831d35Sstevel 
188003831d35Sstevel 		if (php->drproto_version != DRMBX_VERSION) {
188103831d35Sstevel 			cmn_err(CE_WARN,
188203831d35Sstevel 			    "mailbox version mismatch 0x%x vs 0x%x",
188303831d35Sstevel 			    php->drproto_version, DRMBX_VERSION);
188403831d35Sstevel 
188503831d35Sstevel 			mutex_enter(&drmach_g_mbox_mutex);
188603831d35Sstevel 			drmach_mbox_iflag = 0;
188703831d35Sstevel 			/* schedule a reinit handshake if one isn't pending */
188803831d35Sstevel 			if (!drmach_mbox_ipending) {
188903831d35Sstevel 				if (taskq_dispatch(system_taskq,
189003831d35Sstevel 				    drmach_mbox_reinit, NULL, TQ_NOSLEEP)
189103831d35Sstevel 				    != NULL) {
189203831d35Sstevel 					drmach_mbox_ipending = 1;
189303831d35Sstevel 				} else {
1894d3d50737SRafael Vanoni 					cmn_err(CE_WARN, "failed to schedule "
1895d3d50737SRafael Vanoni 					    "mailbox reinit");
189603831d35Sstevel 				}
189703831d35Sstevel 			}
189803831d35Sstevel 			mutex_exit(&drmach_g_mbox_mutex);
189903831d35Sstevel 
190003831d35Sstevel 			continue;
190103831d35Sstevel 		}
190203831d35Sstevel 
190303831d35Sstevel 		msgid = php->message_id;
190403831d35Sstevel 		found = NULL;
190503831d35Sstevel 		mutex_enter(&drmach_msglist_mutex);
190603831d35Sstevel 		entry = drmach_msglist_first;
190703831d35Sstevel 		while (entry != NULL) {
190803831d35Sstevel 			if (entry->msgid == msgid) {
190903831d35Sstevel 				found = entry;
191003831d35Sstevel 				drmach_msglist_unlink(entry);
191103831d35Sstevel 				entry = NULL;
191203831d35Sstevel 			} else
191303831d35Sstevel 				entry = entry->next;
191403831d35Sstevel 		}
191503831d35Sstevel 
191603831d35Sstevel 		if (found) {
191703831d35Sstevel 			mutex_enter(&found->g_lock);
191803831d35Sstevel 
191903831d35Sstevel 			found->e_code = php->error_code;
192003831d35Sstevel 			if (found->i_buflen > 0)
192103831d35Sstevel 				bcopy((caddr_t)&msg->msgdata, found->i_buf,
192203831d35Sstevel 				    found->i_buflen);
192303831d35Sstevel 			found->m_reply = 1;
192403831d35Sstevel 
192503831d35Sstevel 			cv_signal(&found->g_cv);
192603831d35Sstevel 			mutex_exit(&found->g_lock);
192703831d35Sstevel 		} else {
192803831d35Sstevel 			cmn_err(CE_WARN, "!mbox_getmsg: no match for id 0x%x",
192903831d35Sstevel 			    msgid);
193003831d35Sstevel 			cmn_err(CE_WARN, "!    cmd = 0x%x, exb = %d, slot = %d",
193103831d35Sstevel 			    php->command, php->expbrd, php->slot);
193203831d35Sstevel 		}
193303831d35Sstevel 
193403831d35Sstevel 		mutex_exit(&drmach_msglist_mutex);
193503831d35Sstevel 	}
193603831d35Sstevel 	cmn_err(CE_WARN, "mbox_getmsg: exiting");
193703831d35Sstevel 	mutex_enter(&drmach_msglist_mutex);
193803831d35Sstevel 	entry = drmach_msglist_first;
193903831d35Sstevel 	while (entry != NULL) {
194003831d35Sstevel 		if (entry->p_flag == 1) {
194103831d35Sstevel 			entry->f_error = -1;
194203831d35Sstevel 			mutex_enter(&entry->g_lock);
194303831d35Sstevel 			cv_signal(&entry->g_cv);
194403831d35Sstevel 			mutex_exit(&entry->g_lock);
194503831d35Sstevel 			drmach_msglist_unlink(entry);
194603831d35Sstevel 		}
194703831d35Sstevel 		entry = entry->next;
194803831d35Sstevel 	}
194903831d35Sstevel 	mutex_exit(&drmach_msglist_mutex);
195003831d35Sstevel 	drmach_getmsg_thread_run = -1;
195103831d35Sstevel 	thread_exit();
195203831d35Sstevel }
195303831d35Sstevel 
195403831d35Sstevel void
drmach_mbox_sendmsg()195503831d35Sstevel drmach_mbox_sendmsg()
195603831d35Sstevel {
195703831d35Sstevel 	int		err, retry;
195803831d35Sstevel 	drmach_msglist_t *entry;
195903831d35Sstevel 	dr_mbox_msg_t   *mp;
196003831d35Sstevel 	dr_proto_hdr_t  *php;
196103831d35Sstevel 
196203831d35Sstevel 	while (drmach_sendmsg_thread_run != 0) {
196303831d35Sstevel 		/*
196403831d35Sstevel 		 * Search through the list to find entries awaiting
196503831d35Sstevel 		 * transmission to the SC
196603831d35Sstevel 		 */
196703831d35Sstevel 		mutex_enter(&drmach_msglist_mutex);
196803831d35Sstevel 		entry = drmach_msglist_first;
196903831d35Sstevel 		retry = 0;
197003831d35Sstevel 		while (entry != NULL) {
197103831d35Sstevel 			if (entry->p_flag == 1) {
197203831d35Sstevel 				entry = entry->next;
197303831d35Sstevel 				continue;
197403831d35Sstevel 			}
197503831d35Sstevel 
197603831d35Sstevel 			mutex_exit(&drmach_msglist_mutex);
197703831d35Sstevel 
197803831d35Sstevel 			if (!retry)
197903831d35Sstevel 				mutex_enter(&entry->s_lock);
198003831d35Sstevel 			mp = (dr_mbox_msg_t *)entry->o_buf;
198103831d35Sstevel 			php = &mp->p_hdr;
198203831d35Sstevel 
198303831d35Sstevel 			drmach_mbox_prmsg(mp, 1);
198403831d35Sstevel 
198503831d35Sstevel 			err = mboxsc_putmsg(KEY_DRSC, MBOXSC_MSG_REQUEST,
198603831d35Sstevel 			    php->command, NULL, entry->o_buflen, (void *)mp,
198703831d35Sstevel 			    drmach_to_putmsg);
198803831d35Sstevel 
198903831d35Sstevel 			if (err) {
199003831d35Sstevel 				switch (err) {
199103831d35Sstevel 
199203831d35Sstevel 				case EAGAIN:
199303831d35Sstevel 				case EBUSY:
199403831d35Sstevel 					++retry;
199503831d35Sstevel 					mutex_enter(&drmach_msglist_mutex);
199603831d35Sstevel 					continue;
199703831d35Sstevel 
199803831d35Sstevel 				case ETIMEDOUT:
199903831d35Sstevel 					if (--entry->o_nretry <= 0) {
200003831d35Sstevel 						mutex_enter(
200103831d35Sstevel 						    &drmach_msglist_mutex);
200203831d35Sstevel 						drmach_msglist_unlink(entry);
200303831d35Sstevel 						mutex_exit(
200403831d35Sstevel 						    &drmach_msglist_mutex);
200503831d35Sstevel 						entry->f_error = err;
200603831d35Sstevel 						entry->p_flag = 1;
200703831d35Sstevel 						cv_signal(&entry->s_cv);
200803831d35Sstevel 					} else {
200903831d35Sstevel 						++retry;
201003831d35Sstevel 						mutex_enter(
201103831d35Sstevel 						    &drmach_msglist_mutex);
201203831d35Sstevel 						continue;
201303831d35Sstevel 					}
201403831d35Sstevel 					break;
201503831d35Sstevel 				default:
201603831d35Sstevel 					mutex_enter(&drmach_msglist_mutex);
201703831d35Sstevel 					drmach_msglist_unlink(entry);
201803831d35Sstevel 					mutex_exit(&drmach_msglist_mutex);
201903831d35Sstevel 					entry->f_error = err;
202003831d35Sstevel 					entry->p_flag = 1;
202103831d35Sstevel 					cv_signal(&entry->s_cv);
202203831d35Sstevel 					break;
202303831d35Sstevel 				}
202403831d35Sstevel 			} else {
202503831d35Sstevel 				entry->p_flag = 1;
202603831d35Sstevel 				cv_signal(&entry->s_cv);
202703831d35Sstevel 			}
202803831d35Sstevel 
202903831d35Sstevel 			mutex_exit(&entry->s_lock);
203003831d35Sstevel 			retry = 0;
203103831d35Sstevel 			mutex_enter(&drmach_msglist_mutex);
203203831d35Sstevel 			entry = drmach_msglist_first;
203303831d35Sstevel 		}
203403831d35Sstevel 		mutex_exit(&drmach_msglist_mutex);
203503831d35Sstevel 
203603831d35Sstevel 		mutex_enter(&drmach_sendmsg_mutex);
2037d3d50737SRafael Vanoni 		(void) cv_reltimedwait(&drmach_sendmsg_cv,
2038d3d50737SRafael Vanoni 		    &drmach_sendmsg_mutex, (5 * hz), TR_CLOCK_TICK);
203903831d35Sstevel 		mutex_exit(&drmach_sendmsg_mutex);
204003831d35Sstevel 	}
204103831d35Sstevel 	cmn_err(CE_WARN, "mbox_sendmsg: exiting");
204203831d35Sstevel 	mutex_enter(&drmach_msglist_mutex);
204303831d35Sstevel 	entry = drmach_msglist_first;
204403831d35Sstevel 	while (entry != NULL) {
204503831d35Sstevel 		if (entry->p_flag == 0) {
204603831d35Sstevel 			entry->f_error = -1;
204703831d35Sstevel 			mutex_enter(&entry->s_lock);
204803831d35Sstevel 			cv_signal(&entry->s_cv);
204903831d35Sstevel 			mutex_exit(&entry->s_lock);
205003831d35Sstevel 			drmach_msglist_unlink(entry);
205103831d35Sstevel 		}
205203831d35Sstevel 		entry = entry->next;
205303831d35Sstevel 	}
205403831d35Sstevel 	mutex_exit(&drmach_msglist_mutex);
205503831d35Sstevel 	cv_destroy(&drmach_sendmsg_cv);
205603831d35Sstevel 	mutex_destroy(&drmach_sendmsg_mutex);
205703831d35Sstevel 
205803831d35Sstevel 	drmach_sendmsg_thread_run = -1;
205903831d35Sstevel 	thread_exit();
206003831d35Sstevel }
206103831d35Sstevel 
206203831d35Sstevel void
drmach_msglist_destroy(drmach_msglist_t * listp)206303831d35Sstevel drmach_msglist_destroy(drmach_msglist_t *listp)
206403831d35Sstevel {
206503831d35Sstevel 	if (listp != NULL) {
206603831d35Sstevel 		drmach_msglist_t	*entry;
206703831d35Sstevel 
206803831d35Sstevel 		mutex_enter(&drmach_msglist_mutex);
206903831d35Sstevel 		entry = drmach_msglist_first;
207003831d35Sstevel 		while (entry) {
207103831d35Sstevel 			if (listp == entry) {
207203831d35Sstevel 				drmach_msglist_unlink(listp);
207303831d35Sstevel 				entry = NULL;
207403831d35Sstevel 			} else
207503831d35Sstevel 				entry = entry->next;
207603831d35Sstevel 		}
207703831d35Sstevel 
207803831d35Sstevel 		mutex_destroy(&listp->s_lock);
207903831d35Sstevel 		cv_destroy(&listp->s_cv);
208003831d35Sstevel 		mutex_destroy(&listp->g_lock);
208103831d35Sstevel 		cv_destroy(&listp->g_cv);
208203831d35Sstevel 		kmem_free(listp, sizeof (drmach_msglist_t));
208303831d35Sstevel 
208403831d35Sstevel 		mutex_exit(&drmach_msglist_mutex);
208503831d35Sstevel 	}
208603831d35Sstevel }
208703831d35Sstevel 
208803831d35Sstevel static drmach_msglist_t	*
drmach_msglist_new(caddr_t ibufp,uint32_t ilen,dr_proto_hdr_t * hdrp,uint32_t olen,int nrtry)208903831d35Sstevel drmach_msglist_new(caddr_t ibufp, uint32_t ilen, dr_proto_hdr_t *hdrp,
209003831d35Sstevel 	uint32_t olen, int nrtry)
209103831d35Sstevel {
209203831d35Sstevel 	drmach_msglist_t	*listp;
209303831d35Sstevel 
209403831d35Sstevel 	listp = kmem_zalloc(sizeof (drmach_msglist_t), KM_SLEEP);
209503831d35Sstevel 	mutex_init(&listp->s_lock, NULL, MUTEX_DRIVER, NULL);
209603831d35Sstevel 	cv_init(&listp->s_cv, NULL, CV_DRIVER, NULL);
209703831d35Sstevel 	mutex_init(&listp->g_lock, NULL, MUTEX_DRIVER, NULL);
209803831d35Sstevel 	cv_init(&listp->g_cv, NULL, CV_DRIVER, NULL);
209903831d35Sstevel 	listp->o_buf = (caddr_t)hdrp;
210003831d35Sstevel 	listp->o_buflen = olen;
210103831d35Sstevel 	listp->i_buf = ibufp;
210203831d35Sstevel 	listp->i_buflen = ilen;
210303831d35Sstevel 	listp->o_nretry = nrtry;
210403831d35Sstevel 	listp->msgid = hdrp->message_id;
210503831d35Sstevel 
210603831d35Sstevel 	return (listp);
210703831d35Sstevel }
210803831d35Sstevel 
210903831d35Sstevel static drmach_msglist_t *
drmach_mbox_req_rply(dr_proto_hdr_t * hdrp,uint32_t olen,caddr_t ibufp,uint32_t ilen,int timeout,int nrtry,int nosig,drmach_msglist_t * link)211003831d35Sstevel drmach_mbox_req_rply(dr_proto_hdr_t *hdrp, uint32_t olen, caddr_t ibufp,
211103831d35Sstevel 	uint32_t ilen, int timeout, int nrtry, int nosig,
211203831d35Sstevel 	drmach_msglist_t *link)
211303831d35Sstevel {
211403831d35Sstevel 	int		crv;
211503831d35Sstevel 	drmach_msglist_t *listp;
211603831d35Sstevel 	clock_t		to_val;
211703831d35Sstevel 	dr_proto_hdr_t	*php;
211803831d35Sstevel 
211903831d35Sstevel 	/* setup transaction list entry */
212003831d35Sstevel 	listp = drmach_msglist_new(ibufp, ilen, hdrp, olen, nrtry);
212103831d35Sstevel 
212203831d35Sstevel 	/* send mailbox message, await reply */
212303831d35Sstevel 	mutex_enter(&listp->s_lock);
212403831d35Sstevel 	mutex_enter(&listp->g_lock);
212503831d35Sstevel 
212603831d35Sstevel 	listp->link = link;
212703831d35Sstevel 	drmach_msglist_link(listp);
212803831d35Sstevel 
212903831d35Sstevel 	mutex_enter(&drmach_sendmsg_mutex);
213003831d35Sstevel 	cv_signal(&drmach_sendmsg_cv);
213103831d35Sstevel 	mutex_exit(&drmach_sendmsg_mutex);
213203831d35Sstevel 
213303831d35Sstevel 	while (listp->p_flag == 0) {
213403831d35Sstevel 		cv_wait(&listp->s_cv, &listp->s_lock);
213503831d35Sstevel 	}
213603831d35Sstevel 
213703831d35Sstevel 	to_val = ddi_get_lbolt() + (timeout * hz);
213803831d35Sstevel 
213903831d35Sstevel 	if (listp->f_error) {
214003831d35Sstevel 		listp->p_flag = 0;
2141d3d50737SRafael Vanoni 		cmn_err(CE_WARN, "!mboxsc_putmsg failed: 0x%x", listp->f_error);
214203831d35Sstevel 		php = (dr_proto_hdr_t *)listp->o_buf;
214303831d35Sstevel 		cmn_err(CE_WARN, "!    cmd = 0x%x, exb = %d, slot = %d",
214403831d35Sstevel 		    php->command, php->expbrd, php->slot);
214503831d35Sstevel 	} else {
214603831d35Sstevel 		while (listp->m_reply == 0 && listp->f_error == 0) {
214703831d35Sstevel 			if (nosig)
214803831d35Sstevel 				crv = cv_timedwait(&listp->g_cv, &listp->g_lock,
214903831d35Sstevel 				    to_val);
215003831d35Sstevel 			else
215103831d35Sstevel 				crv = cv_timedwait_sig(&listp->g_cv,
215203831d35Sstevel 				    &listp->g_lock, to_val);
215303831d35Sstevel 			switch (crv) {
215403831d35Sstevel 				case -1: /* timed out */
215503831d35Sstevel 					cmn_err(CE_WARN,
215603831d35Sstevel 					    "!msgid=0x%x reply timed out",
215703831d35Sstevel 					    hdrp->message_id);
215803831d35Sstevel 					php = (dr_proto_hdr_t *)listp->o_buf;
215903831d35Sstevel 					cmn_err(CE_WARN, "!    cmd = 0x%x, "
216003831d35Sstevel 					    "exb = %d, slot = %d", php->command,
216103831d35Sstevel 					    php->expbrd, php->slot);
216203831d35Sstevel 					listp->f_error = ETIMEDOUT;
216303831d35Sstevel 					break;
216403831d35Sstevel 				case 0: /* signal received */
216503831d35Sstevel 					cmn_err(CE_WARN,
216603831d35Sstevel 					    "operation interrupted by signal");
216703831d35Sstevel 					listp->f_error = EINTR;
216803831d35Sstevel 					break;
216903831d35Sstevel 				default:
217003831d35Sstevel 					break;
217103831d35Sstevel 				}
217203831d35Sstevel 		}
217303831d35Sstevel 
217403831d35Sstevel 		/*
217503831d35Sstevel 		 * If link is set for this entry, check to see if
217603831d35Sstevel 		 * the linked entry has been replied to.  If not,
217703831d35Sstevel 		 * wait for the response.
217803831d35Sstevel 		 * Currently, this is only used for ABORT_TEST functionality,
217903831d35Sstevel 		 * wherein a check is made for the TESTBOARD reply when
218003831d35Sstevel 		 * the ABORT_TEST reply is received.
218103831d35Sstevel 		 */
218203831d35Sstevel 
218303831d35Sstevel 		if (link) {
218403831d35Sstevel 			mutex_enter(&link->g_lock);
218503831d35Sstevel 			/*
218603831d35Sstevel 			 * If the reply to the linked entry hasn't been
218703831d35Sstevel 			 * received, clear the existing link->f_error,
218803831d35Sstevel 			 * and await the reply.
218903831d35Sstevel 			 */
219003831d35Sstevel 			if (link->m_reply == 0) {
219103831d35Sstevel 				link->f_error = 0;
219203831d35Sstevel 			}
219303831d35Sstevel 			to_val =  ddi_get_lbolt() + (timeout * hz);
219403831d35Sstevel 			while (link->m_reply == 0 && link->f_error == 0) {
219503831d35Sstevel 				crv = cv_timedwait(&link->g_cv, &link->g_lock,
219603831d35Sstevel 				    to_val);
219703831d35Sstevel 				switch (crv) {
219803831d35Sstevel 				case -1: /* timed out */
219903831d35Sstevel 					cmn_err(CE_NOTE,
220003831d35Sstevel 					    "!link msgid=0x%x reply timed out",
220103831d35Sstevel 					    link->msgid);
220203831d35Sstevel 					link->f_error = ETIMEDOUT;
220303831d35Sstevel 					break;
220403831d35Sstevel 				default:
220503831d35Sstevel 					break;
220603831d35Sstevel 				}
220703831d35Sstevel 			}
220803831d35Sstevel 			mutex_exit(&link->g_lock);
220903831d35Sstevel 		}
221003831d35Sstevel 	}
221103831d35Sstevel 	mutex_exit(&listp->g_lock);
221203831d35Sstevel 	mutex_exit(&listp->s_lock);
221303831d35Sstevel 	return (listp);
221403831d35Sstevel }
221503831d35Sstevel 
221603831d35Sstevel static sbd_error_t *
drmach_mbx2sbderr(drmach_msglist_t * mlp)221703831d35Sstevel drmach_mbx2sbderr(drmach_msglist_t *mlp)
221803831d35Sstevel {
221903831d35Sstevel 	char		a_pnt[MAXNAMELEN];
222003831d35Sstevel 	dr_proto_hdr_t	*php;
222103831d35Sstevel 	int		bnum;
222203831d35Sstevel 
222303831d35Sstevel 	if (mlp->f_error) {
222403831d35Sstevel 		/*
222503831d35Sstevel 		 * If framework failure is due to signal, return "no error"
222603831d35Sstevel 		 * error.
222703831d35Sstevel 		 */
222803831d35Sstevel 		if (mlp->f_error == EINTR)
222903831d35Sstevel 			return (drerr_new(0, ESTC_NONE, NULL));
223003831d35Sstevel 
223103831d35Sstevel 		mutex_enter(&drmach_g_mbox_mutex);
223203831d35Sstevel 		drmach_mbox_iflag = 0;
223303831d35Sstevel 		mutex_exit(&drmach_g_mbox_mutex);
223403831d35Sstevel 		if (!mlp->p_flag)
223503831d35Sstevel 			return (drerr_new(1, ESTC_MBXRQST, NULL));
223603831d35Sstevel 		else
223703831d35Sstevel 			return (drerr_new(1, ESTC_MBXRPLY, NULL));
223803831d35Sstevel 	}
223903831d35Sstevel 	php = (dr_proto_hdr_t *)mlp->o_buf;
224003831d35Sstevel 	bnum = 2 * php->expbrd + php->slot;
224103831d35Sstevel 	a_pnt[0] = '\0';
224203831d35Sstevel 	(void) drmach_board_name(bnum, a_pnt, MAXNAMELEN);
224303831d35Sstevel 
224403831d35Sstevel 	switch (mlp->e_code) {
224503831d35Sstevel 		case 0:
224603831d35Sstevel 			return (NULL);
224703831d35Sstevel 		case DRERR_NOACL:
224803831d35Sstevel 			return (drerr_new(0, ESTC_NOACL, "%s", a_pnt));
224903831d35Sstevel 		case DRERR_NOT_ASSIGNED:
225003831d35Sstevel 			return (drerr_new(0, ESTC_NOT_ASSIGNED, "%s", a_pnt));
225103831d35Sstevel 		case DRERR_NOT_ACTIVE:
225203831d35Sstevel 			return (drerr_new(0, ESTC_NOT_ACTIVE, "%s", a_pnt));
225303831d35Sstevel 		case DRERR_EMPTY_SLOT:
225403831d35Sstevel 			return (drerr_new(0, ESTC_EMPTY_SLOT, "%s", a_pnt));
225503831d35Sstevel 		case DRERR_POWER_OFF:
225603831d35Sstevel 			return (drerr_new(0, ESTC_POWER_OFF, "%s", a_pnt));
225703831d35Sstevel 		case DRERR_TEST_IN_PROGRESS:
2258d3d50737SRafael Vanoni 			return (drerr_new(0, ESTC_TEST_IN_PROGRESS, "%s",
2259d3d50737SRafael Vanoni 			    a_pnt));
226003831d35Sstevel 		case DRERR_TESTING_BUSY:
226103831d35Sstevel 			return (drerr_new(0, ESTC_TESTING_BUSY, "%s", a_pnt));
226203831d35Sstevel 		case DRERR_TEST_REQUIRED:
226303831d35Sstevel 			return (drerr_new(0, ESTC_TEST_REQUIRED, "%s", a_pnt));
226403831d35Sstevel 		case DRERR_UNAVAILABLE:
226503831d35Sstevel 			return (drerr_new(0, ESTC_UNAVAILABLE, "%s", a_pnt));
226603831d35Sstevel 		case DRERR_RECOVERABLE:
2267d3d50737SRafael Vanoni 			return (drerr_new(0, ESTC_SMS_ERR_RECOVERABLE, "%s",
2268d3d50737SRafael Vanoni 			    a_pnt));
226903831d35Sstevel 		case DRERR_UNRECOVERABLE:
2270d3d50737SRafael Vanoni 			return (drerr_new(1, ESTC_SMS_ERR_UNRECOVERABLE, "%s",
2271d3d50737SRafael Vanoni 			    a_pnt));
227203831d35Sstevel 		default:
227303831d35Sstevel 			return (drerr_new(1, ESTC_MBOX_UNKNOWN, NULL));
227403831d35Sstevel 	}
227503831d35Sstevel }
227603831d35Sstevel 
227703831d35Sstevel static sbd_error_t *
drmach_mbox_trans(uint8_t msgtype,int bnum,caddr_t obufp,int olen,caddr_t ibufp,int ilen)227803831d35Sstevel drmach_mbox_trans(uint8_t msgtype, int bnum, caddr_t obufp, int olen,
227903831d35Sstevel 	caddr_t ibufp, int ilen)
228003831d35Sstevel {
228103831d35Sstevel 	int			timeout = 0;
228203831d35Sstevel 	int			ntries = 0;
228303831d35Sstevel 	int			nosignals = 0;
228403831d35Sstevel 	dr_proto_hdr_t 		*hdrp;
228503831d35Sstevel 	drmach_msglist_t 	*mlp;
228603831d35Sstevel 	sbd_error_t		*err = NULL;
228703831d35Sstevel 
228803831d35Sstevel 	if (msgtype != DRMSG_MBOX_INIT) {
228903831d35Sstevel 		mutex_enter(&drmach_ri_mbox_mutex);
229003831d35Sstevel 		mutex_enter(&drmach_g_mbox_mutex);
229103831d35Sstevel 		if (drmach_mbox_iflag == 0) {
229203831d35Sstevel 			/* need to initialize the mailbox */
229303831d35Sstevel 			dr_proto_hdr_t	imsg;
229403831d35Sstevel 
229503831d35Sstevel 			mutex_exit(&drmach_g_mbox_mutex);
229603831d35Sstevel 
229703831d35Sstevel 			imsg.command = DRMSG_MBOX_INIT;
229803831d35Sstevel 
229903831d35Sstevel 			imsg.message_id = drmach_get_msgid();
230003831d35Sstevel 			imsg.drproto_version = DRMBX_VERSION;
230103831d35Sstevel 			imsg.expbrd = 0;
230203831d35Sstevel 			imsg.slot = 0;
230303831d35Sstevel 
2304d3d50737SRafael Vanoni 			cmn_err(CE_WARN, "!reinitializing DR mailbox");
230503831d35Sstevel 			mlp = drmach_mbox_req_rply(&imsg, sizeof (imsg), 0, 0,
230603831d35Sstevel 			    10, 5, 0, NULL);
230703831d35Sstevel 			err = drmach_mbx2sbderr(mlp);
230803831d35Sstevel 			/*
230903831d35Sstevel 			 * If framework failure incoming is encountered on
231003831d35Sstevel 			 * the MBOX_INIT [timeout on SMS reply], the error
231103831d35Sstevel 			 * type must be changed before returning to caller.
231203831d35Sstevel 			 * This is to prevent drmach_board_connect() and
231303831d35Sstevel 			 * drmach_board_disconnect() from marking boards
231403831d35Sstevel 			 * UNUSABLE based on MBOX_INIT failures.
231503831d35Sstevel 			 */
231603831d35Sstevel 			if ((err != NULL) && (err->e_code == ESTC_MBXRPLY)) {
231703831d35Sstevel 				cmn_err(CE_WARN,
231803831d35Sstevel 				    "!Changed mbox incoming to outgoing"
231903831d35Sstevel 				    " failure on reinit");
232003831d35Sstevel 				sbd_err_clear(&err);
232103831d35Sstevel 				err = drerr_new(0, ESTC_MBXRQST, NULL);
232203831d35Sstevel 			}
232303831d35Sstevel 			drmach_msglist_destroy(mlp);
232403831d35Sstevel 			if (err) {
232503831d35Sstevel 				mutex_exit(&drmach_ri_mbox_mutex);
232603831d35Sstevel 				return (err);
232703831d35Sstevel 			}
232803831d35Sstevel 			mutex_enter(&drmach_g_mbox_mutex);
232903831d35Sstevel 			drmach_mbox_iflag = 1;
233003831d35Sstevel 		}
233103831d35Sstevel 		mutex_exit(&drmach_g_mbox_mutex);
233203831d35Sstevel 		mutex_exit(&drmach_ri_mbox_mutex);
233303831d35Sstevel 	}
233403831d35Sstevel 
233503831d35Sstevel 	hdrp = (dr_proto_hdr_t *)obufp;
233603831d35Sstevel 
233703831d35Sstevel 	/* setup outgoing mailbox header */
233803831d35Sstevel 	hdrp->command = msgtype;
233903831d35Sstevel 	hdrp->message_id = drmach_get_msgid();
234003831d35Sstevel 	hdrp->drproto_version = DRMBX_VERSION;
234103831d35Sstevel 	hdrp->expbrd = DRMACH_BNUM2EXP(bnum);
234203831d35Sstevel 	hdrp->slot = DRMACH_BNUM2SLOT(bnum);
234303831d35Sstevel 
234403831d35Sstevel 	switch (msgtype) {
234503831d35Sstevel 
234603831d35Sstevel 		case DRMSG_MBOX_INIT:
234703831d35Sstevel 			timeout = drmach_to_mbxinit;
234803831d35Sstevel 			ntries = 1;
234903831d35Sstevel 			nosignals = 0;
235003831d35Sstevel 			break;
235103831d35Sstevel 
235203831d35Sstevel 		case DRMSG_ASSIGN:
235303831d35Sstevel 			timeout = drmach_to_assign;
235403831d35Sstevel 			ntries = 1;
235503831d35Sstevel 			nosignals = 0;
235603831d35Sstevel 			break;
235703831d35Sstevel 
235803831d35Sstevel 		case DRMSG_UNASSIGN:
235903831d35Sstevel 			timeout = drmach_to_unassign;
236003831d35Sstevel 			ntries = 1;
236103831d35Sstevel 			nosignals = 0;
236203831d35Sstevel 			break;
236303831d35Sstevel 
236403831d35Sstevel 		case DRMSG_POWERON:
236503831d35Sstevel 			timeout = drmach_to_poweron;
236603831d35Sstevel 			ntries = 1;
236703831d35Sstevel 			nosignals = 0;
236803831d35Sstevel 			break;
236903831d35Sstevel 
237003831d35Sstevel 		case DRMSG_POWEROFF:
237103831d35Sstevel 			timeout = drmach_to_poweroff;
237203831d35Sstevel 			ntries = 1;
237303831d35Sstevel 			nosignals = 0;
237403831d35Sstevel 			break;
237503831d35Sstevel 
237603831d35Sstevel 		case DRMSG_SHOWBOARD:
237703831d35Sstevel 			timeout = drmach_to_showboard;
237803831d35Sstevel 			ntries = 1;
237903831d35Sstevel 			nosignals = 0;
238003831d35Sstevel 			break;
238103831d35Sstevel 
238203831d35Sstevel 		case DRMSG_CLAIM:
238303831d35Sstevel 			timeout = drmach_to_claim;
238403831d35Sstevel 			ntries = 1;
238503831d35Sstevel 			nosignals = 1;
238603831d35Sstevel 			break;
238703831d35Sstevel 
238803831d35Sstevel 		case DRMSG_UNCLAIM:
238903831d35Sstevel 			timeout = drmach_to_unclaim;
239003831d35Sstevel 			ntries = 1;
239103831d35Sstevel 			nosignals = 1;
239203831d35Sstevel 			break;
239303831d35Sstevel 
239403831d35Sstevel 		case DRMSG_UNCONFIG:
239503831d35Sstevel 			timeout = drmach_to_unconfig;
239603831d35Sstevel 			ntries = 1;
239703831d35Sstevel 			nosignals = 0;
239803831d35Sstevel 			break;
239903831d35Sstevel 
240003831d35Sstevel 		case DRMSG_TESTBOARD:
240103831d35Sstevel 			timeout = drmach_to_testboard;
240203831d35Sstevel 			ntries = 1;
240303831d35Sstevel 			nosignals = 0;
240403831d35Sstevel 			break;
240503831d35Sstevel 
240603831d35Sstevel 		default:
2407d3d50737SRafael Vanoni 			cmn_err(CE_WARN, "Unknown outgoing message type 0x%x",
2408d3d50737SRafael Vanoni 			    msgtype);
240903831d35Sstevel 			err = DRMACH_INTERNAL_ERROR();
241003831d35Sstevel 			break;
241103831d35Sstevel 	}
241203831d35Sstevel 
241303831d35Sstevel 	if (err == NULL) {
2414d3d50737SRafael Vanoni 		mlp = drmach_mbox_req_rply(hdrp, olen, ibufp, ilen, timeout,
2415d3d50737SRafael Vanoni 		    ntries, nosignals, NULL);
241603831d35Sstevel 		err = drmach_mbx2sbderr(mlp);
241703831d35Sstevel 
241803831d35Sstevel 		/*
241903831d35Sstevel 		 * For DRMSG_TESTBOARD attempts which have timed out, or
242003831d35Sstevel 		 * been aborted due to a signal received after mboxsc_putmsg()
242103831d35Sstevel 		 * has succeeded in sending the message, a DRMSG_ABORT_TEST
242203831d35Sstevel 		 * must be sent.
242303831d35Sstevel 		 */
242403831d35Sstevel 		if ((msgtype == DRMSG_TESTBOARD) && (err != NULL) &&
242503831d35Sstevel 		    ((mlp->f_error == EINTR) || ((mlp->f_error == ETIMEDOUT) &&
242603831d35Sstevel 		    (mlp->p_flag != 0)))) {
242703831d35Sstevel 			drmach_msglist_t	*abmlp;
242803831d35Sstevel 			dr_abort_test_t		abibuf;
242903831d35Sstevel 
243003831d35Sstevel 			hdrp->command = DRMSG_ABORT_TEST;
243103831d35Sstevel 			hdrp->message_id = drmach_get_msgid();
243203831d35Sstevel 			abmlp = drmach_mbox_req_rply(hdrp,
243303831d35Sstevel 			    sizeof (dr_abort_test_t), (caddr_t)&abibuf,
243403831d35Sstevel 			    sizeof (abibuf), drmach_to_aborttest, 5, 1, mlp);
243503831d35Sstevel 			cmn_err(CE_WARN, "test aborted");
243603831d35Sstevel 			drmach_msglist_destroy(abmlp);
243703831d35Sstevel 		}
243803831d35Sstevel 
243903831d35Sstevel 		drmach_msglist_destroy(mlp);
244003831d35Sstevel 	}
244103831d35Sstevel 
244203831d35Sstevel 	return (err);
244303831d35Sstevel }
244403831d35Sstevel 
244503831d35Sstevel static int
drmach_mbox_init()244603831d35Sstevel drmach_mbox_init()
244703831d35Sstevel {
244803831d35Sstevel 	int			err;
244903831d35Sstevel 	caddr_t			obufp;
245003831d35Sstevel 	sbd_error_t		*serr = NULL;
245103831d35Sstevel 	mboxsc_timeout_range_t	mbxtoz;
245203831d35Sstevel 
245303831d35Sstevel 	drmach_mbox_istate = 0;
245403831d35Sstevel 	/* register the outgoing mailbox */
245503831d35Sstevel 	if ((err = mboxsc_init(KEY_DRSC, MBOXSC_MBOX_OUT,
245603831d35Sstevel 	    NULL)) != 0) {
245703831d35Sstevel 		cmn_err(CE_WARN, "DR - SC mboxsc_init failed: 0x%x", err);
245803831d35Sstevel 		return (-1);
245903831d35Sstevel 	}
246003831d35Sstevel 	drmach_mbox_istate = 1;
246103831d35Sstevel 
246203831d35Sstevel 	/* setup the mboxsc_putmsg timeout value */
246303831d35Sstevel 	if (drmach_use_tuned_putmsg_to) {
246403831d35Sstevel 		cmn_err(CE_NOTE, "!using tuned drmach_to_putmsg = 0x%lx\n",
246503831d35Sstevel 		    drmach_to_putmsg);
246603831d35Sstevel 	} else {
246703831d35Sstevel 		if ((err = mboxsc_ctrl(KEY_DRSC,
246803831d35Sstevel 		    MBOXSC_CMD_PUTMSG_TIMEOUT_RANGE, &mbxtoz)) != 0) {
246903831d35Sstevel 			cmn_err(CE_WARN, "mboxsc_ctrl failed: 0x%x", err);
247003831d35Sstevel 			drmach_to_putmsg = 60000;
247103831d35Sstevel 		} else {
247203831d35Sstevel 			drmach_to_putmsg = mboxsc_putmsg_def_timeout() * 6;
247303831d35Sstevel 			DRMACH_PR("putmsg range is 0x%lx - 0x%lx value"
247403831d35Sstevel 			    " is 0x%lx\n", mbxtoz.min_timeout,
247503831d35Sstevel 			    mbxtoz.max_timeout, drmach_to_putmsg);
247603831d35Sstevel 		}
247703831d35Sstevel 	}
247803831d35Sstevel 
247903831d35Sstevel 	/* register the incoming mailbox */
248003831d35Sstevel 	if ((err = mboxsc_init(KEY_SCDR, MBOXSC_MBOX_IN,
248103831d35Sstevel 	    drmach_mbox_event)) != 0) {
248203831d35Sstevel 		cmn_err(CE_WARN, "SC - DR mboxsc_init failed: 0x%x", err);
248303831d35Sstevel 		return (-1);
248403831d35Sstevel 	}
248503831d35Sstevel 	drmach_mbox_istate = 2;
248603831d35Sstevel 
248703831d35Sstevel 	/* initialize mutex for mailbox globals */
248803831d35Sstevel 	mutex_init(&drmach_g_mbox_mutex, NULL, MUTEX_DRIVER, NULL);
248903831d35Sstevel 
249003831d35Sstevel 	/* initialize mutex for mailbox re-init */
249103831d35Sstevel 	mutex_init(&drmach_ri_mbox_mutex, NULL, MUTEX_DRIVER, NULL);
249203831d35Sstevel 
249303831d35Sstevel 	/* initialize mailbox message list elements */
249403831d35Sstevel 	drmach_msglist_first = drmach_msglist_last = NULL;
249503831d35Sstevel 	mutex_init(&drmach_msglist_mutex, NULL, MUTEX_DRIVER, NULL);
249603831d35Sstevel 
249703831d35Sstevel 	mutex_init(&drmach_sendmsg_mutex, NULL, MUTEX_DRIVER, NULL);
249803831d35Sstevel 	cv_init(&drmach_sendmsg_cv, NULL, CV_DRIVER, NULL);
249903831d35Sstevel 
250003831d35Sstevel 	drmach_mbox_istate = 3;
250103831d35Sstevel 
250203831d35Sstevel 	/* start mailbox sendmsg thread */
250303831d35Sstevel 	drmach_sendmsg_thread_run = 1;
250403831d35Sstevel 	if (drmach_sendmsg_thread == NULL)
250503831d35Sstevel 		drmach_sendmsg_thread = thread_create(NULL, 0,
250603831d35Sstevel 		    (void (*)())drmach_mbox_sendmsg, NULL, 0, &p0,
250703831d35Sstevel 		    TS_RUN, minclsyspri);
250803831d35Sstevel 
250903831d35Sstevel 	/* start mailbox getmsg thread */
251003831d35Sstevel 	drmach_getmsg_thread_run = 1;
251103831d35Sstevel 	if (drmach_getmsg_thread == NULL)
251203831d35Sstevel 		drmach_getmsg_thread = thread_create(NULL, 0,
251303831d35Sstevel 		    (void (*)())drmach_mbox_getmsg, NULL, 0, &p0,
251403831d35Sstevel 		    TS_RUN, minclsyspri);
251503831d35Sstevel 
251603831d35Sstevel 	obufp = kmem_zalloc(sizeof (dr_proto_hdr_t), KM_SLEEP);
251703831d35Sstevel 	serr = drmach_mbox_trans(DRMSG_MBOX_INIT, 0, obufp,
251803831d35Sstevel 	    sizeof (dr_proto_hdr_t), (caddr_t)NULL, 0);
251903831d35Sstevel 	kmem_free(obufp, sizeof (dr_proto_hdr_t));
252003831d35Sstevel 	if (serr) {
252103831d35Sstevel 		cmn_err(CE_WARN, "mbox_init: MBOX_INIT failed ecode=0x%x",
252203831d35Sstevel 		    serr->e_code);
252303831d35Sstevel 		sbd_err_clear(&serr);
252403831d35Sstevel 		return (-1);
252503831d35Sstevel 	}
252603831d35Sstevel 	mutex_enter(&drmach_g_mbox_mutex);
252703831d35Sstevel 	drmach_mbox_iflag = 1;
252803831d35Sstevel 	drmach_mbox_ipending = 0;
252903831d35Sstevel 	mutex_exit(&drmach_g_mbox_mutex);
253003831d35Sstevel 
253103831d35Sstevel 	return (0);
253203831d35Sstevel }
253303831d35Sstevel 
253403831d35Sstevel static int
drmach_mbox_fini()253503831d35Sstevel drmach_mbox_fini()
253603831d35Sstevel {
253703831d35Sstevel 	int err, rv = 0;
253803831d35Sstevel 
253903831d35Sstevel 	if (drmach_mbox_istate > 2) {
254003831d35Sstevel 		drmach_getmsg_thread_run = 0;
254103831d35Sstevel 		drmach_sendmsg_thread_run = 0;
254203831d35Sstevel 		cmn_err(CE_WARN,
254303831d35Sstevel 		    "drmach_mbox_fini: waiting for mbox threads...");
254403831d35Sstevel 		while ((drmach_getmsg_thread_run == 0) ||
254503831d35Sstevel 		    (drmach_sendmsg_thread_run == 0)) {
254603831d35Sstevel 			continue;
254703831d35Sstevel 		}
2548d3d50737SRafael Vanoni 		cmn_err(CE_WARN, "drmach_mbox_fini: mbox threads done.");
254903831d35Sstevel 		mutex_destroy(&drmach_msglist_mutex);
255003831d35Sstevel 
255103831d35Sstevel 	}
255203831d35Sstevel 	if (drmach_mbox_istate) {
255303831d35Sstevel 		/* de-register the outgoing mailbox */
255403831d35Sstevel 		if ((err = mboxsc_fini(KEY_DRSC)) != 0) {
255503831d35Sstevel 			cmn_err(CE_WARN, "DR - SC mboxsc_fini failed: 0x%x",
255603831d35Sstevel 			    err);
255703831d35Sstevel 			rv = -1;
255803831d35Sstevel 		}
255903831d35Sstevel 	}
256003831d35Sstevel 	if (drmach_mbox_istate > 1) {
256103831d35Sstevel 		/* de-register the incoming mailbox */
256203831d35Sstevel 		if ((err = mboxsc_fini(KEY_SCDR)) != 0) {
256303831d35Sstevel 			cmn_err(CE_WARN, "SC - DR mboxsc_fini failed: 0x%x",
256403831d35Sstevel 			    err);
256503831d35Sstevel 			rv = -1;
256603831d35Sstevel 		}
256703831d35Sstevel 	}
256803831d35Sstevel 	mutex_destroy(&drmach_g_mbox_mutex);
256903831d35Sstevel 	mutex_destroy(&drmach_ri_mbox_mutex);
257003831d35Sstevel 	return (rv);
257103831d35Sstevel }
257203831d35Sstevel 
257303831d35Sstevel static int
drmach_portid2bnum(int portid)257403831d35Sstevel drmach_portid2bnum(int portid)
257503831d35Sstevel {
257603831d35Sstevel 	int slot;
257703831d35Sstevel 
257803831d35Sstevel 	switch (portid & 0x1f) {
257903831d35Sstevel 	case 0: case 1: case 2: case 3:	/* cpu/wci devices */
258003831d35Sstevel 	case 0x1e:			/* slot 0 axq registers */
258103831d35Sstevel 		slot = 0;
258203831d35Sstevel 		break;
258303831d35Sstevel 
258403831d35Sstevel 	case 8: case 9:			/* cpu devices */
258503831d35Sstevel 	case 0x1c: case 0x1d:		/* schizo/wci devices */
258603831d35Sstevel 	case 0x1f:			/* slot 1 axq registers */
258703831d35Sstevel 		slot = 1;
258803831d35Sstevel 		break;
258903831d35Sstevel 
259003831d35Sstevel 	default:
259103831d35Sstevel 		ASSERT(0);		/* catch in debug kernels */
259203831d35Sstevel 	}
259303831d35Sstevel 
259403831d35Sstevel 	return (((portid >> 4) & 0x7e) | slot);
259503831d35Sstevel }
259603831d35Sstevel 
259703831d35Sstevel extern int axq_suspend_iopause;
259803831d35Sstevel 
259903831d35Sstevel static int
hold_rele_branch(dev_info_t * rdip,void * arg)260003831d35Sstevel hold_rele_branch(dev_info_t *rdip, void *arg)
260103831d35Sstevel {
260203831d35Sstevel 	int	i;
260303831d35Sstevel 	int	*holdp	= (int *)arg;
260403831d35Sstevel 	char	*name = ddi_node_name(rdip);
260503831d35Sstevel 
260603831d35Sstevel 	/*
260703831d35Sstevel 	 * For Starcat, we must be children of the root devinfo node
260803831d35Sstevel 	 */
260903831d35Sstevel 	ASSERT(ddi_get_parent(rdip) == ddi_root_node());
261003831d35Sstevel 
261103831d35Sstevel 	i = drmach_name2type_idx(name);
261203831d35Sstevel 
261303831d35Sstevel 	/*
261403831d35Sstevel 	 * Only children of the root devinfo node need to be
261503831d35Sstevel 	 * held/released since they are the only valid targets
261603831d35Sstevel 	 * of tree operations. This corresponds to the node types
261703831d35Sstevel 	 * listed in the drmach_name2type array.
261803831d35Sstevel 	 */
261903831d35Sstevel 	if (i < 0) {
262003831d35Sstevel 		/* Not of interest to us */
262103831d35Sstevel 		return (DDI_WALK_PRUNECHILD);
262203831d35Sstevel 	}
262303831d35Sstevel 
262403831d35Sstevel 	if (*holdp) {
262503831d35Sstevel 		ASSERT(!e_ddi_branch_held(rdip));
262603831d35Sstevel 		e_ddi_branch_hold(rdip);
262703831d35Sstevel 	} else {
262803831d35Sstevel 		ASSERT(e_ddi_branch_held(rdip));
262903831d35Sstevel 		e_ddi_branch_rele(rdip);
263003831d35Sstevel 	}
263103831d35Sstevel 
263203831d35Sstevel 	return (DDI_WALK_PRUNECHILD);
263303831d35Sstevel }
263403831d35Sstevel 
263503831d35Sstevel static int
drmach_init(void)263603831d35Sstevel drmach_init(void)
263703831d35Sstevel {
263803831d35Sstevel 	pnode_t 	nodeid;
263903831d35Sstevel 	gdcd_t		*gdcd;
264003831d35Sstevel 	int		bnum;
264103831d35Sstevel 	dev_info_t	*rdip;
264203831d35Sstevel 	int		hold, circ;
264303831d35Sstevel 
264403831d35Sstevel 	mutex_enter(&drmach_i_lock);
264503831d35Sstevel 	if (drmach_initialized) {
264603831d35Sstevel 		mutex_exit(&drmach_i_lock);
264703831d35Sstevel 		return (0);
264803831d35Sstevel 	}
264903831d35Sstevel 
265003831d35Sstevel 	gdcd = drmach_gdcd_new();
265103831d35Sstevel 	if (gdcd == NULL) {
265203831d35Sstevel 		mutex_exit(&drmach_i_lock);
265303831d35Sstevel 		cmn_err(CE_WARN, "drmach_init: failed to access GDCD\n");
265403831d35Sstevel 		return (-1);
265503831d35Sstevel 	}
265603831d35Sstevel 
265703831d35Sstevel 	drmach_boards = drmach_array_new(0, MAX_BOARDS - 1);
265803831d35Sstevel 
265903831d35Sstevel 	nodeid = prom_childnode(prom_rootnode());
266003831d35Sstevel 	do {
266103831d35Sstevel 		int		 len;
266203831d35Sstevel 		int		 portid;
266303831d35Sstevel 		drmachid_t	 id;
266403831d35Sstevel 
266503831d35Sstevel 		len = prom_getproplen(nodeid, "portid");
266603831d35Sstevel 		if (len != sizeof (portid))
266703831d35Sstevel 			continue;
266803831d35Sstevel 
266903831d35Sstevel 		portid = -1;
267003831d35Sstevel 		(void) prom_getprop(nodeid, "portid", (caddr_t)&portid);
267103831d35Sstevel 		if (portid == -1)
267203831d35Sstevel 			continue;
267303831d35Sstevel 
267403831d35Sstevel 		bnum = drmach_portid2bnum(portid);
267503831d35Sstevel 
267603831d35Sstevel 		if (drmach_array_get(drmach_boards, bnum, &id) == -1) {
267703831d35Sstevel 			/* portid translated to an invalid board number */
267803831d35Sstevel 			cmn_err(CE_WARN, "OBP node 0x%x has"
267903831d35Sstevel 			    " invalid property value, %s=%u",
268003831d35Sstevel 			    nodeid, "portid", portid);
268103831d35Sstevel 
268203831d35Sstevel 			/* clean up */
268303831d35Sstevel 			drmach_array_dispose(drmach_boards,
268403831d35Sstevel 			    drmach_board_dispose);
268503831d35Sstevel 			drmach_gdcd_dispose(gdcd);
268603831d35Sstevel 			mutex_exit(&drmach_i_lock);
268703831d35Sstevel 			return (-1);
268803831d35Sstevel 		} else if (id == NULL) {
268903831d35Sstevel 			drmach_board_t	*bp;
269003831d35Sstevel 			l1_slot_stat_t	*dcd;
269103831d35Sstevel 			int		exp, slot;
269203831d35Sstevel 
269303831d35Sstevel 			bp = drmach_board_new(bnum);
269403831d35Sstevel 			bp->assigned = !drmach_initialized;
269503831d35Sstevel 			bp->powered = !drmach_initialized;
269603831d35Sstevel 
269703831d35Sstevel 			exp = DRMACH_BNUM2EXP(bnum);
269803831d35Sstevel 			slot = DRMACH_BNUM2SLOT(bnum);
269903831d35Sstevel 			dcd = &gdcd->dcd_slot[exp][slot];
270003831d35Sstevel 			bp->stardrb_offset =
270103831d35Sstevel 			    dcd->l1ss_cpu_drblock_xwd_offset << 3;
270203831d35Sstevel 			DRMACH_PR("%s: stardrb_offset=0x%lx\n", bp->cm.name,
270303831d35Sstevel 			    bp->stardrb_offset);
270403831d35Sstevel 
270503831d35Sstevel 			if (gdcd->dcd_slot[exp][slot].l1ss_flags &
270603831d35Sstevel 			    L1SSFLG_THIS_L1_NULL_PROC_LPA) {
270703831d35Sstevel 				bp->flags |= DRMACH_NULL_PROC_LPA;
270803831d35Sstevel 				DRMACH_PR("%s: NULL proc LPA\n", bp->cm.name);
270903831d35Sstevel 			}
271003831d35Sstevel 		}
271103831d35Sstevel 	} while ((nodeid = prom_nextnode(nodeid)) != OBP_NONODE);
271203831d35Sstevel 
271303831d35Sstevel 	drmach_cpu_sram_va = vmem_alloc(heap_arena, PAGESIZE, VM_SLEEP);
271403831d35Sstevel 
271503831d35Sstevel 	if (gdcd->dcd_testcage_log2_mbytes_size != DCD_DR_TESTCAGE_DISABLED) {
271603831d35Sstevel 		ASSERT(gdcd->dcd_testcage_log2_mbytes_size ==
271703831d35Sstevel 		    gdcd->dcd_testcage_log2_mbytes_align);
271803831d35Sstevel 		drmach_iocage_paddr =
271903831d35Sstevel 		    (uint64_t)gdcd->dcd_testcage_mbyte_PA << 20;
272003831d35Sstevel 		drmach_iocage_size =
272103831d35Sstevel 		    1 << (gdcd->dcd_testcage_log2_mbytes_size + 20);
272203831d35Sstevel 
272303831d35Sstevel 		drmach_iocage_vaddr = (caddr_t)vmem_alloc(heap_arena,
272403831d35Sstevel 		    drmach_iocage_size, VM_SLEEP);
272503831d35Sstevel 		hat_devload(kas.a_hat, drmach_iocage_vaddr, drmach_iocage_size,
272603831d35Sstevel 		    mmu_btop(drmach_iocage_paddr),
272703831d35Sstevel 		    PROT_READ | PROT_WRITE,
272803831d35Sstevel 		    HAT_LOAD_LOCK | HAT_LOAD_NOCONSIST);
272903831d35Sstevel 
273003831d35Sstevel 		DRMACH_PR("gdcd size=0x%x align=0x%x PA=0x%x\n",
273103831d35Sstevel 		    gdcd->dcd_testcage_log2_mbytes_size,
273203831d35Sstevel 		    gdcd->dcd_testcage_log2_mbytes_align,
273303831d35Sstevel 		    gdcd->dcd_testcage_mbyte_PA);
273403831d35Sstevel 		DRMACH_PR("drmach size=0x%x PA=0x%lx VA=0x%p\n",
273503831d35Sstevel 		    drmach_iocage_size, drmach_iocage_paddr,
273607d06da5SSurya Prakki 		    (void *)drmach_iocage_vaddr);
273703831d35Sstevel 	}
273803831d35Sstevel 
273903831d35Sstevel 	if (drmach_iocage_size == 0) {
274003831d35Sstevel 		drmach_array_dispose(drmach_boards, drmach_board_dispose);
274103831d35Sstevel 		drmach_boards = NULL;
274203831d35Sstevel 		vmem_free(heap_arena, drmach_cpu_sram_va, PAGESIZE);
274303831d35Sstevel 		drmach_gdcd_dispose(gdcd);
274403831d35Sstevel 		mutex_exit(&drmach_i_lock);
274503831d35Sstevel 		cmn_err(CE_WARN, "drmach_init: iocage not available\n");
274603831d35Sstevel 		return (-1);
274703831d35Sstevel 	}
274803831d35Sstevel 
274903831d35Sstevel 	drmach_gdcd_dispose(gdcd);
275003831d35Sstevel 
275103831d35Sstevel 	mutex_init(&drmach_iocage_lock, NULL, MUTEX_DRIVER, NULL);
275203831d35Sstevel 	cv_init(&drmach_iocage_cv, NULL, CV_DRIVER, NULL);
275303831d35Sstevel 	mutex_init(&drmach_xt_mb_lock, NULL, MUTEX_DRIVER, NULL);
275403831d35Sstevel 	mutex_init(&drmach_bus_sync_lock, NULL, MUTEX_DRIVER, NULL);
275503831d35Sstevel 	mutex_init(&drmach_slice_table_lock, NULL, MUTEX_DRIVER, NULL);
275603831d35Sstevel 
275703831d35Sstevel 	mutex_enter(&cpu_lock);
275803831d35Sstevel 	mutex_enter(&drmach_iocage_lock);
275903831d35Sstevel 	ASSERT(drmach_iocage_is_busy == 0);
276003831d35Sstevel 	drmach_iocage_is_busy = 1;
276103831d35Sstevel 	drmach_iocage_mem_scrub(drmach_iocage_size);
276203831d35Sstevel 	drmach_iocage_is_busy = 0;
276303831d35Sstevel 	cv_signal(&drmach_iocage_cv);
276403831d35Sstevel 	mutex_exit(&drmach_iocage_lock);
276503831d35Sstevel 	mutex_exit(&cpu_lock);
276603831d35Sstevel 
276703831d35Sstevel 
276803831d35Sstevel 	if (drmach_mbox_init() == -1) {
276903831d35Sstevel 		cmn_err(CE_WARN, "DR - SC mailbox initialization Failed");
277003831d35Sstevel 	}
277103831d35Sstevel 
277203831d35Sstevel 	/*
277303831d35Sstevel 	 * Walk immediate children of devinfo root node and hold
277403831d35Sstevel 	 * all devinfo branches of interest.
277503831d35Sstevel 	 */
277603831d35Sstevel 	hold = 1;
277703831d35Sstevel 	rdip = ddi_root_node();
277803831d35Sstevel 
277903831d35Sstevel 	ndi_devi_enter(rdip, &circ);
278003831d35Sstevel 	ddi_walk_devs(ddi_get_child(rdip), hold_rele_branch, &hold);
278103831d35Sstevel 	ndi_devi_exit(rdip, circ);
278203831d35Sstevel 
278303831d35Sstevel 	drmach_initialized = 1;
278403831d35Sstevel 
278503831d35Sstevel 	/*
278603831d35Sstevel 	 * To avoid a circular patch dependency between DR and AXQ, the AXQ
278703831d35Sstevel 	 * rev introducing the axq_iopause_*_all interfaces should not regress
278803831d35Sstevel 	 * when installed without the DR rev using those interfaces. The default
278903831d35Sstevel 	 * is for iopause to be enabled/disabled during axq suspend/resume. By
279003831d35Sstevel 	 * setting the following axq flag to zero, axq will not enable iopause
279103831d35Sstevel 	 * during suspend/resume, instead DR will call the axq_iopause_*_all
279203831d35Sstevel 	 * interfaces during drmach_copy_rename.
279303831d35Sstevel 	 */
279403831d35Sstevel 	axq_suspend_iopause = 0;
279503831d35Sstevel 
279603831d35Sstevel 	mutex_exit(&drmach_i_lock);
279703831d35Sstevel 
279803831d35Sstevel 	return (0);
279903831d35Sstevel }
280003831d35Sstevel 
280103831d35Sstevel static void
drmach_fini(void)280203831d35Sstevel drmach_fini(void)
280303831d35Sstevel {
280403831d35Sstevel 	dev_info_t	*rdip;
280503831d35Sstevel 	int		hold, circ;
280603831d35Sstevel 
280703831d35Sstevel 	if (drmach_initialized) {
280803831d35Sstevel 		rw_enter(&drmach_boards_rwlock, RW_WRITER);
280903831d35Sstevel 		drmach_array_dispose(drmach_boards, drmach_board_dispose);
281003831d35Sstevel 		drmach_boards = NULL;
281103831d35Sstevel 		rw_exit(&drmach_boards_rwlock);
281203831d35Sstevel 
281303831d35Sstevel 		mutex_destroy(&drmach_slice_table_lock);
281403831d35Sstevel 		mutex_destroy(&drmach_xt_mb_lock);
281503831d35Sstevel 		mutex_destroy(&drmach_bus_sync_lock);
281603831d35Sstevel 		cv_destroy(&drmach_iocage_cv);
281703831d35Sstevel 		mutex_destroy(&drmach_iocage_lock);
281803831d35Sstevel 
281903831d35Sstevel 		vmem_free(heap_arena, drmach_cpu_sram_va, PAGESIZE);
282003831d35Sstevel 
282103831d35Sstevel 		/*
282203831d35Sstevel 		 * Walk immediate children of the root devinfo node
282303831d35Sstevel 		 * releasing holds acquired on branches in drmach_init()
282403831d35Sstevel 		 */
282503831d35Sstevel 		hold = 0;
282603831d35Sstevel 		rdip = ddi_root_node();
282703831d35Sstevel 
282803831d35Sstevel 		ndi_devi_enter(rdip, &circ);
282903831d35Sstevel 		ddi_walk_devs(ddi_get_child(rdip), hold_rele_branch, &hold);
283003831d35Sstevel 		ndi_devi_exit(rdip, circ);
283103831d35Sstevel 
283203831d35Sstevel 		drmach_initialized = 0;
283303831d35Sstevel 	}
283403831d35Sstevel 
283507d06da5SSurya Prakki 	(void) drmach_mbox_fini();
283603831d35Sstevel 	if (drmach_xt_mb != NULL) {
283703831d35Sstevel 		vmem_free(static_alloc_arena, (void *)drmach_xt_mb,
283803831d35Sstevel 		    drmach_xt_mb_size);
283903831d35Sstevel 	}
284003831d35Sstevel 	rw_destroy(&drmach_boards_rwlock);
284103831d35Sstevel 	mutex_destroy(&drmach_i_lock);
284203831d35Sstevel }
284303831d35Sstevel 
284403831d35Sstevel static void
drmach_mem_read_madr(drmach_mem_t * mp,int bank,uint64_t * madr)284503831d35Sstevel drmach_mem_read_madr(drmach_mem_t *mp, int bank, uint64_t *madr)
284603831d35Sstevel {
284703831d35Sstevel 	kpreempt_disable();
284803831d35Sstevel 
284903831d35Sstevel 	/* get register address, read madr value */
285003831d35Sstevel 	if (STARCAT_CPUID_TO_PORTID(CPU->cpu_id) == mp->dev.portid) {
285103831d35Sstevel 		*madr = lddmcdecode(DRMACH_MC_ASI_ADDR(mp, bank));
285203831d35Sstevel 	} else {
285303831d35Sstevel 		*madr = lddphysio(DRMACH_MC_ADDR(mp, bank));
285403831d35Sstevel 	}
285503831d35Sstevel 
285603831d35Sstevel 	kpreempt_enable();
285703831d35Sstevel }
285803831d35Sstevel 
285903831d35Sstevel 
286003831d35Sstevel static uint64_t *
drmach_prep_mc_rename(uint64_t * p,int local,drmach_mem_t * mp,uint64_t current_basepa,uint64_t new_basepa)286103831d35Sstevel drmach_prep_mc_rename(uint64_t *p, int local,
286203831d35Sstevel 	drmach_mem_t *mp, uint64_t current_basepa, uint64_t new_basepa)
286303831d35Sstevel {
286403831d35Sstevel 	int bank;
286503831d35Sstevel 
286603831d35Sstevel 	for (bank = 0; bank < DRMACH_MC_NBANKS; bank++) {
286703831d35Sstevel 		uint64_t madr, bank_offset;
286803831d35Sstevel 
286903831d35Sstevel 		/* fetch mc's bank madr register value */
287003831d35Sstevel 		drmach_mem_read_madr(mp, bank, &madr);
287103831d35Sstevel 		if (madr & DRMACH_MC_VALID_MASK) {
287203831d35Sstevel 			uint64_t bankpa;
287303831d35Sstevel 
287403831d35Sstevel 			bank_offset = (DRMACH_MC_UM_TO_PA(madr) |
287503831d35Sstevel 			    DRMACH_MC_LM_TO_PA(madr)) - current_basepa;
287603831d35Sstevel 			bankpa = new_basepa + bank_offset;
287703831d35Sstevel 
287803831d35Sstevel 			/* encode new base pa into madr */
287903831d35Sstevel 			madr &= ~DRMACH_MC_UM_MASK;
288003831d35Sstevel 			madr |= DRMACH_MC_PA_TO_UM(bankpa);
288103831d35Sstevel 			madr &= ~DRMACH_MC_LM_MASK;
288203831d35Sstevel 			madr |= DRMACH_MC_PA_TO_LM(bankpa);
288303831d35Sstevel 
288403831d35Sstevel 			if (local)
288503831d35Sstevel 				*p++ = DRMACH_MC_ASI_ADDR(mp, bank);
288603831d35Sstevel 			else
288703831d35Sstevel 				*p++ = DRMACH_MC_ADDR(mp, bank);
288803831d35Sstevel 
288903831d35Sstevel 			*p++ = madr;
289003831d35Sstevel 		}
289103831d35Sstevel 	}
289203831d35Sstevel 
289303831d35Sstevel 	return (p);
289403831d35Sstevel }
289503831d35Sstevel 
289603831d35Sstevel static uint64_t *
drmach_prep_schizo_script(uint64_t * p,drmach_mem_t * mp,uint64_t new_basepa)289703831d35Sstevel drmach_prep_schizo_script(uint64_t *p, drmach_mem_t *mp, uint64_t new_basepa)
289803831d35Sstevel {
289903831d35Sstevel 	drmach_board_t	*bp;
290003831d35Sstevel 	int		 rv;
290103831d35Sstevel 	int		 idx;
290203831d35Sstevel 	drmachid_t	 id;
290303831d35Sstevel 	uint64_t	 last_scsr_pa = 0;
290403831d35Sstevel 
290503831d35Sstevel 	/* memory is always in slot 0 */
290603831d35Sstevel 	ASSERT(DRMACH_BNUM2SLOT(mp->dev.bp->bnum) == 0);
290703831d35Sstevel 
290803831d35Sstevel 	/* look up slot 1 board on same expander */
290903831d35Sstevel 	idx = DRMACH_EXPSLOT2BNUM(DRMACH_BNUM2EXP(mp->dev.bp->bnum), 1);
291003831d35Sstevel 	rv = drmach_array_get(drmach_boards, idx, &id);
291103831d35Sstevel 	bp = id; /* bp will be NULL if board not found */
291203831d35Sstevel 
291303831d35Sstevel 	/* look up should never be out of bounds */
291403831d35Sstevel 	ASSERT(rv == 0);
291503831d35Sstevel 
291603831d35Sstevel 	/* nothing to do when board is not found or has no devices */
291703831d35Sstevel 	if (rv == -1 || bp == NULL || bp->devices == NULL)
291803831d35Sstevel 		return (p);
291903831d35Sstevel 
292003831d35Sstevel 	rv = drmach_array_first(bp->devices, &idx, &id);
292103831d35Sstevel 	while (rv == 0) {
292203831d35Sstevel 		if (DRMACH_IS_IO_ID(id)) {
292303831d35Sstevel 			drmach_io_t *io = id;
292403831d35Sstevel 
292503831d35Sstevel 			/*
292603831d35Sstevel 			 * Skip all non-Schizo IO devices (only IO nodes
292703831d35Sstevel 			 * that are Schizo devices have non-zero scsr_pa).
292803831d35Sstevel 			 * Filter out "other" leaf to avoid writing to the
292903831d35Sstevel 			 * same Schizo Control/Status Register twice.
293003831d35Sstevel 			 */
293103831d35Sstevel 			if (io->scsr_pa && io->scsr_pa != last_scsr_pa) {
293203831d35Sstevel 				uint64_t scsr;
293303831d35Sstevel 
293403831d35Sstevel 				scsr  = lddphysio(io->scsr_pa);
293503831d35Sstevel 				scsr &= ~(DRMACH_LPA_BASE_MASK |
293603831d35Sstevel 				    DRMACH_LPA_BND_MASK);
293703831d35Sstevel 				scsr |= DRMACH_PA_TO_LPA_BASE(new_basepa);
293803831d35Sstevel 				scsr |= DRMACH_PA_TO_LPA_BND(
293903831d35Sstevel 				    new_basepa + DRMACH_MEM_SLICE_SIZE);
294003831d35Sstevel 
294103831d35Sstevel 				*p++ = io->scsr_pa;
294203831d35Sstevel 				*p++ = scsr;
294303831d35Sstevel 
294403831d35Sstevel 				last_scsr_pa = io->scsr_pa;
294503831d35Sstevel 			}
294603831d35Sstevel 		}
294703831d35Sstevel 		rv = drmach_array_next(bp->devices, &idx, &id);
294803831d35Sstevel 	}
294903831d35Sstevel 
295003831d35Sstevel 	return (p);
295103831d35Sstevel }
295203831d35Sstevel 
295303831d35Sstevel /*
295403831d35Sstevel  * For Panther MCs, append the MC idle reg address and drmach_mem_t pointer.
295503831d35Sstevel  * The latter is returned when drmach_rename fails to idle a Panther MC and
295603831d35Sstevel  * is used to identify the MC for error reporting.
295703831d35Sstevel  */
295803831d35Sstevel static uint64_t *
drmach_prep_pn_mc_idle(uint64_t * p,drmach_mem_t * mp,int local)295903831d35Sstevel drmach_prep_pn_mc_idle(uint64_t *p, drmach_mem_t *mp, int local)
296003831d35Sstevel {
296103831d35Sstevel 	/* only slot 0 has memory */
296203831d35Sstevel 	ASSERT(DRMACH_BNUM2SLOT(mp->dev.bp->bnum) == 0);
296303831d35Sstevel 	ASSERT(IS_PANTHER(mp->dev.bp->cpu_impl));
296403831d35Sstevel 
296503831d35Sstevel 	for (mp = mp->dev.bp->mem; mp != NULL; mp = mp->next) {
296603831d35Sstevel 		ASSERT(DRMACH_IS_MEM_ID(mp));
296703831d35Sstevel 
296803831d35Sstevel 		if (mp->dev.portid == STARCAT_CPUID_TO_PORTID(CPU->cpu_id)) {
296903831d35Sstevel 			if (local) {
297003831d35Sstevel 				*p++ = ASI_EMU_ACT_STATUS_VA;	/* local ASI */
297103831d35Sstevel 				*p++ = (uintptr_t)mp;
297203831d35Sstevel 			}
297303831d35Sstevel 		} else if (!local) {
297403831d35Sstevel 			*p++ = DRMACH_EMU_ACT_STATUS_ADDR(mp);	/* PIO */
297503831d35Sstevel 			*p++ = (uintptr_t)mp;
297603831d35Sstevel 		}
297703831d35Sstevel 	}
297803831d35Sstevel 
297903831d35Sstevel 	return (p);
298003831d35Sstevel }
298103831d35Sstevel 
298203831d35Sstevel static sbd_error_t *
drmach_prep_rename_script(drmach_mem_t * s_mp,drmach_mem_t * t_mp,uint64_t t_slice_offset,caddr_t buf,int buflen)298303831d35Sstevel drmach_prep_rename_script(drmach_mem_t *s_mp, drmach_mem_t *t_mp,
298403831d35Sstevel 	uint64_t t_slice_offset, caddr_t buf, int buflen)
298503831d35Sstevel {
298603831d35Sstevel 	_NOTE(ARGUNUSED(buflen))
298703831d35Sstevel 
298803831d35Sstevel 	uint64_t		*p = (uint64_t *)buf, *q;
298903831d35Sstevel 	sbd_error_t		*err;
299003831d35Sstevel 	int			 rv;
299103831d35Sstevel 	drmach_mem_t		*mp, *skip_mp;
299203831d35Sstevel 	uint64_t		 s_basepa, t_basepa;
299303831d35Sstevel 	uint64_t		 s_new_basepa, t_new_basepa;
299403831d35Sstevel 
299503831d35Sstevel 	/* verify supplied buffer space is adequate */
299603831d35Sstevel 	ASSERT(buflen >=
299703831d35Sstevel 	    /* addr for all possible MC banks */
299803831d35Sstevel 	    (sizeof (uint64_t) * 4 * 4 * 18) +
299903831d35Sstevel 	    /* list section terminator */
300003831d35Sstevel 	    (sizeof (uint64_t) * 1) +
300103831d35Sstevel 	    /* addr/id tuple for local Panther MC idle reg */
300203831d35Sstevel 	    (sizeof (uint64_t) * 2) +
300303831d35Sstevel 	    /* list section terminator */
300403831d35Sstevel 	    (sizeof (uint64_t) * 1) +
300503831d35Sstevel 	    /* addr/id tuple for 2 boards with 4 Panther MC idle regs */
300603831d35Sstevel 	    (sizeof (uint64_t) * 2 * 2 * 4) +
300703831d35Sstevel 	    /* list section terminator */
300803831d35Sstevel 	    (sizeof (uint64_t) * 1) +
300903831d35Sstevel 	    /* addr/val tuple for 1 proc with 4 MC banks */
301003831d35Sstevel 	    (sizeof (uint64_t) * 2 * 4) +
301103831d35Sstevel 	    /* list section terminator */
301203831d35Sstevel 	    (sizeof (uint64_t) * 1) +
301303831d35Sstevel 	    /* addr/val tuple for 2 boards w/ 2 schizos each */
301403831d35Sstevel 	    (sizeof (uint64_t) * 2 * 2 * 2) +
301503831d35Sstevel 	    /* addr/val tuple for 2 boards w/ 16 MC banks each */
301603831d35Sstevel 	    (sizeof (uint64_t) * 2 * 2 * 16) +
301703831d35Sstevel 	    /* list section terminator */
301803831d35Sstevel 	    (sizeof (uint64_t) * 1) +
301903831d35Sstevel 	    /* addr/val tuple for 18 AXQs w/ two slots each */
302003831d35Sstevel 	    (sizeof (uint64_t) * 2 * 2 * 18) +
302103831d35Sstevel 	    /* list section terminator */
302203831d35Sstevel 	    (sizeof (uint64_t) * 1) +
302303831d35Sstevel 	    /* list terminator */
302403831d35Sstevel 	    (sizeof (uint64_t) * 1));
302503831d35Sstevel 
302603831d35Sstevel 	/* copy bank list to rename script */
302703831d35Sstevel 	mutex_enter(&drmach_bus_sync_lock);
302803831d35Sstevel 	for (q = drmach_bus_sync_list; *q; q++, p++)
302903831d35Sstevel 		*p = *q;
303003831d35Sstevel 	mutex_exit(&drmach_bus_sync_lock);
303103831d35Sstevel 
303203831d35Sstevel 	/* list section terminator */
303303831d35Sstevel 	*p++ = 0;
303403831d35Sstevel 
303503831d35Sstevel 	/*
303603831d35Sstevel 	 * Write idle script for MC on this processor.  A script will be
303703831d35Sstevel 	 * produced only if this is a Panther processor on the source or
303803831d35Sstevel 	 * target board.
303903831d35Sstevel 	 */
304003831d35Sstevel 	if (IS_PANTHER(s_mp->dev.bp->cpu_impl))
304103831d35Sstevel 		p = drmach_prep_pn_mc_idle(p, s_mp, 1);
304203831d35Sstevel 
304303831d35Sstevel 	if (IS_PANTHER(t_mp->dev.bp->cpu_impl))
304403831d35Sstevel 		p = drmach_prep_pn_mc_idle(p, t_mp, 1);
304503831d35Sstevel 
304603831d35Sstevel 	/* list section terminator */
304703831d35Sstevel 	*p++ = 0;
304803831d35Sstevel 
304903831d35Sstevel 	/*
305003831d35Sstevel 	 * Write idle script for all other MCs on source and target
305103831d35Sstevel 	 * Panther boards.
305203831d35Sstevel 	 */
305303831d35Sstevel 	if (IS_PANTHER(s_mp->dev.bp->cpu_impl))
305403831d35Sstevel 		p = drmach_prep_pn_mc_idle(p, s_mp, 0);
305503831d35Sstevel 
305603831d35Sstevel 	if (IS_PANTHER(t_mp->dev.bp->cpu_impl))
305703831d35Sstevel 		p = drmach_prep_pn_mc_idle(p, t_mp, 0);
305803831d35Sstevel 
305903831d35Sstevel 	/* list section terminator */
306003831d35Sstevel 	*p++ = 0;
306103831d35Sstevel 
306203831d35Sstevel 	/*
306303831d35Sstevel 	 * Step 1:	Write source base address to target MC
306403831d35Sstevel 	 *		with present bit off.
306503831d35Sstevel 	 * Step 2:	Now rewrite target reg with present bit on.
306603831d35Sstevel 	 */
306703831d35Sstevel 	err = drmach_mem_get_base_physaddr(s_mp, &s_basepa);
306803831d35Sstevel 	ASSERT(err == NULL);
306903831d35Sstevel 	err = drmach_mem_get_base_physaddr(t_mp, &t_basepa);
307003831d35Sstevel 	ASSERT(err == NULL);
307103831d35Sstevel 
307203831d35Sstevel 	/* exchange base pa. include slice offset in new target base pa */
307303831d35Sstevel 	s_new_basepa = t_basepa & ~ (DRMACH_MEM_SLICE_SIZE - 1);
307403831d35Sstevel 	t_new_basepa = (s_basepa & ~ (DRMACH_MEM_SLICE_SIZE - 1)) +
307503831d35Sstevel 	    t_slice_offset;
307603831d35Sstevel 
307703831d35Sstevel 	DRMACH_PR("s_new_basepa 0x%lx\n", s_new_basepa);
307803831d35Sstevel 	DRMACH_PR("t_new_basepa 0x%lx\n", t_new_basepa);
307903831d35Sstevel 
308003831d35Sstevel 	DRMACH_PR("preparing MC MADR rename script (master is CPU%d):\n",
308103831d35Sstevel 	    CPU->cpu_id);
308203831d35Sstevel 
308303831d35Sstevel 	/*
308403831d35Sstevel 	 * Write rename script for MC on this processor.  A script will
308503831d35Sstevel 	 * be produced only if this processor is on the source or target
308603831d35Sstevel 	 * board.
308703831d35Sstevel 	 */
308803831d35Sstevel 
308903831d35Sstevel 	skip_mp = NULL;
309003831d35Sstevel 	mp = s_mp->dev.bp->mem;
309103831d35Sstevel 	while (mp != NULL && skip_mp == NULL) {
309203831d35Sstevel 		if (mp->dev.portid == STARCAT_CPUID_TO_PORTID(CPU->cpu_id)) {
309303831d35Sstevel 			skip_mp = mp;
309403831d35Sstevel 			p = drmach_prep_mc_rename(p, 1, mp, s_basepa,
309503831d35Sstevel 			    s_new_basepa);
309603831d35Sstevel 		}
309703831d35Sstevel 
309803831d35Sstevel 		mp = mp->next;
309903831d35Sstevel 	}
310003831d35Sstevel 
310103831d35Sstevel 	mp = t_mp->dev.bp->mem;
310203831d35Sstevel 	while (mp != NULL && skip_mp == NULL) {
310303831d35Sstevel 		if (mp->dev.portid == STARCAT_CPUID_TO_PORTID(CPU->cpu_id)) {
310403831d35Sstevel 			skip_mp = mp;
310503831d35Sstevel 			p = drmach_prep_mc_rename(p, 1, mp, t_basepa,
310603831d35Sstevel 			    t_new_basepa);
310703831d35Sstevel 		}
310803831d35Sstevel 
310903831d35Sstevel 		mp = mp->next;
311003831d35Sstevel 	}
311103831d35Sstevel 
311203831d35Sstevel 	/* list section terminator */
311303831d35Sstevel 	*p++ = 0;
311403831d35Sstevel 
311503831d35Sstevel 	/*
311603831d35Sstevel 	 * Write rename script for all other MCs on source and target
311703831d35Sstevel 	 * boards.
311803831d35Sstevel 	 */
311903831d35Sstevel 
312003831d35Sstevel 	for (mp = s_mp->dev.bp->mem; mp; mp = mp->next) {
312103831d35Sstevel 		if (mp == skip_mp)
312203831d35Sstevel 			continue;
312303831d35Sstevel 		p = drmach_prep_mc_rename(p, 0, mp, s_basepa, s_new_basepa);
312403831d35Sstevel 	}
312503831d35Sstevel 
312603831d35Sstevel 	for (mp = t_mp->dev.bp->mem; mp; mp = mp->next) {
312703831d35Sstevel 		if (mp == skip_mp)
312803831d35Sstevel 			continue;
312903831d35Sstevel 		p = drmach_prep_mc_rename(p, 0, mp, t_basepa, t_new_basepa);
313003831d35Sstevel 	}
313103831d35Sstevel 
313203831d35Sstevel 	/* Write rename script for Schizo LPA_BASE/LPA_BND */
313303831d35Sstevel 	p = drmach_prep_schizo_script(p, s_mp, s_new_basepa);
313403831d35Sstevel 	p = drmach_prep_schizo_script(p, t_mp, t_new_basepa);
313503831d35Sstevel 
313603831d35Sstevel 	/* list section terminator */
313703831d35Sstevel 	*p++ = 0;
313803831d35Sstevel 
313903831d35Sstevel 	DRMACH_PR("preparing AXQ CASM rename script (EXP%d <> EXP%d):\n",
314003831d35Sstevel 	    DRMACH_BNUM2EXP(s_mp->dev.bp->bnum),
314103831d35Sstevel 	    DRMACH_BNUM2EXP(t_mp->dev.bp->bnum));
314203831d35Sstevel 
314303831d35Sstevel 	rv = axq_do_casm_rename_script(&p,
314403831d35Sstevel 	    DRMACH_PA_TO_SLICE(s_new_basepa),
314503831d35Sstevel 	    DRMACH_PA_TO_SLICE(t_new_basepa));
314603831d35Sstevel 	if (rv == DDI_FAILURE)
314703831d35Sstevel 		return (DRMACH_INTERNAL_ERROR());
314803831d35Sstevel 
314903831d35Sstevel 	/* list section & final terminator */
315003831d35Sstevel 	*p++ = 0;
315103831d35Sstevel 	*p++ = 0;
315203831d35Sstevel 
315303831d35Sstevel #ifdef DEBUG
315403831d35Sstevel 	{
315503831d35Sstevel 		uint64_t *q = (uint64_t *)buf;
315603831d35Sstevel 
315703831d35Sstevel 		/* paranoia */
315803831d35Sstevel 		ASSERT((caddr_t)p <= buf + buflen);
315903831d35Sstevel 
316003831d35Sstevel 		DRMACH_PR("MC bank base pa list:\n");
316103831d35Sstevel 		while (*q) {
316203831d35Sstevel 			uint64_t a = *q++;
316303831d35Sstevel 
316403831d35Sstevel 			DRMACH_PR("0x%lx\n", a);
316503831d35Sstevel 		}
316603831d35Sstevel 
316703831d35Sstevel 		/* skip terminator */
316803831d35Sstevel 		q += 1;
316903831d35Sstevel 
317003831d35Sstevel 		DRMACH_PR("local Panther MC idle reg (via ASI 0x4a):\n");
317103831d35Sstevel 		while (*q) {
317203831d35Sstevel 			DRMACH_PR("addr=0x%lx, mp=0x%lx\n", *q, *(q + 1));
317303831d35Sstevel 			q += 2;
317403831d35Sstevel 		}
317503831d35Sstevel 
317603831d35Sstevel 		/* skip terminator */
317703831d35Sstevel 		q += 1;
317803831d35Sstevel 
317903831d35Sstevel 		DRMACH_PR("non-local Panther MC idle reg (via ASI 0x15):\n");
318003831d35Sstevel 		while (*q) {
318103831d35Sstevel 			DRMACH_PR("addr=0x%lx, mp=0x%lx\n", *q, *(q + 1));
318203831d35Sstevel 			q += 2;
318303831d35Sstevel 		}
318403831d35Sstevel 
318503831d35Sstevel 		/* skip terminator */
318603831d35Sstevel 		q += 1;
318703831d35Sstevel 
318803831d35Sstevel 		DRMACH_PR("MC reprogramming script (via ASI 0x72):\n");
318903831d35Sstevel 		while (*q) {
319003831d35Sstevel 			uint64_t r = *q++;	/* register address */
319103831d35Sstevel 			uint64_t v = *q++;	/* new register value */
319203831d35Sstevel 
319303831d35Sstevel 			DRMACH_PR("0x%lx = 0x%lx, basepa 0x%lx\n",
319407d06da5SSurya Prakki 			    r, v, (long)(DRMACH_MC_UM_TO_PA(v)|
319507d06da5SSurya Prakki 			    DRMACH_MC_LM_TO_PA(v)));
319603831d35Sstevel 		}
319703831d35Sstevel 
319803831d35Sstevel 		/* skip terminator */
319903831d35Sstevel 		q += 1;
320003831d35Sstevel 
320103831d35Sstevel 		DRMACH_PR("MC/SCHIZO reprogramming script:\n");
320203831d35Sstevel 		while (*q) {
320303831d35Sstevel 			DRMACH_PR("0x%lx = 0x%lx\n", *q, *(q + 1));
320403831d35Sstevel 			q += 2;
320503831d35Sstevel 		}
320603831d35Sstevel 
320703831d35Sstevel 		/* skip terminator */
320803831d35Sstevel 		q += 1;
320903831d35Sstevel 
321003831d35Sstevel 		DRMACH_PR("AXQ reprogramming script:\n");
321103831d35Sstevel 		while (*q) {
321203831d35Sstevel 			DRMACH_PR("0x%lx = 0x%lx\n", *q, *(q + 1));
321303831d35Sstevel 			q += 2;
321403831d35Sstevel 		}
321503831d35Sstevel 
321603831d35Sstevel 		/* verify final terminator is present */
321703831d35Sstevel 		ASSERT(*(q + 1) == 0);
321803831d35Sstevel 
321907d06da5SSurya Prakki 		DRMACH_PR("copy-rename script 0x%p, len %d\n",
322007d06da5SSurya Prakki 		    (void *)buf, (int)((intptr_t)p - (intptr_t)buf));
322103831d35Sstevel 
322203831d35Sstevel 		if (drmach_debug)
322303831d35Sstevel 			DELAY(10000000);
322403831d35Sstevel 	}
322503831d35Sstevel #endif
322603831d35Sstevel 
322703831d35Sstevel 	return (NULL);
322803831d35Sstevel }
322903831d35Sstevel 
323003831d35Sstevel static void
drmach_prep_xt_mb_for_slice_update(drmach_board_t * bp,uchar_t slice)323103831d35Sstevel drmach_prep_xt_mb_for_slice_update(drmach_board_t *bp, uchar_t slice)
323203831d35Sstevel {
323303831d35Sstevel 	int		 rv;
323403831d35Sstevel 
323503831d35Sstevel 	ASSERT(MUTEX_HELD(&drmach_xt_mb_lock));
323603831d35Sstevel 
323703831d35Sstevel 	if (bp->devices) {
323803831d35Sstevel 		int		 d_idx;
323903831d35Sstevel 		drmachid_t	 d_id;
324003831d35Sstevel 
324103831d35Sstevel 		rv = drmach_array_first(bp->devices, &d_idx, &d_id);
324203831d35Sstevel 		while (rv == 0) {
324303831d35Sstevel 			if (DRMACH_IS_CPU_ID(d_id)) {
324403831d35Sstevel 				drmach_cpu_t	*cp = d_id;
324503831d35Sstevel 				processorid_t	 cpuid = cp->cpuid;
324603831d35Sstevel 
324703831d35Sstevel 				mutex_enter(&cpu_lock);
324803831d35Sstevel 				if (cpu[cpuid] && cpu[cpuid]->cpu_flags)
324903831d35Sstevel 					drmach_xt_mb[cpuid] = 0x80 | slice;
325003831d35Sstevel 				mutex_exit(&cpu_lock);
325103831d35Sstevel 			}
325203831d35Sstevel 			rv = drmach_array_next(bp->devices, &d_idx, &d_id);
325303831d35Sstevel 		}
325403831d35Sstevel 	}
325503831d35Sstevel 	if (DRMACH_BNUM2SLOT(bp->bnum) == 0) {
325603831d35Sstevel 		drmach_board_t	*s1bp = NULL;
325703831d35Sstevel 
325803831d35Sstevel 		rv = drmach_array_get(drmach_boards, bp->bnum + 1,
325903831d35Sstevel 		    (void *) &s1bp);
326003831d35Sstevel 		if (rv == 0 && s1bp != NULL) {
326103831d35Sstevel 			ASSERT(DRMACH_IS_BOARD_ID(s1bp));
326203831d35Sstevel 			ASSERT(DRMACH_BNUM2SLOT(s1bp->bnum) == 1);
326303831d35Sstevel 			drmach_prep_xt_mb_for_slice_update(s1bp, slice);
326403831d35Sstevel 		}
326503831d35Sstevel 	}
326603831d35Sstevel }
326703831d35Sstevel 
326803831d35Sstevel sbd_error_t *
drmach_copy_rename_init(drmachid_t t_id,uint64_t t_slice_offset,drmachid_t s_id,struct memlist * c_ml,drmachid_t * cr_id)326903831d35Sstevel drmach_copy_rename_init(drmachid_t t_id, uint64_t t_slice_offset,
327003831d35Sstevel 	drmachid_t s_id, struct memlist *c_ml, drmachid_t *cr_id)
327103831d35Sstevel {
327203831d35Sstevel 	extern void drmach_rename(uint64_t *, uint_t *, uint64_t *);
327303831d35Sstevel 	extern void drmach_rename_end(void);
327403831d35Sstevel 
327503831d35Sstevel 	drmach_mem_t	*s_mp, *t_mp;
327603831d35Sstevel 	struct memlist	*x_ml;
327703831d35Sstevel 	uint64_t	 off_mask, s_copybasepa, t_copybasepa, t_basepa;
327803831d35Sstevel 	int		 len;
327903831d35Sstevel 	caddr_t		 bp, wp;
328003831d35Sstevel 	uint_t		*p, *q;
328103831d35Sstevel 	sbd_error_t	*err;
328203831d35Sstevel 	tte_t		*tte;
328303831d35Sstevel 	drmach_copy_rename_t *cr;
328403831d35Sstevel 
328503831d35Sstevel 	if (!DRMACH_IS_MEM_ID(s_id))
328603831d35Sstevel 		return (drerr_new(0, ESTC_INAPPROP, NULL));
328703831d35Sstevel 	if (!DRMACH_IS_MEM_ID(t_id))
328803831d35Sstevel 		return (drerr_new(0, ESTC_INAPPROP, NULL));
328903831d35Sstevel 	s_mp = s_id;
329003831d35Sstevel 	t_mp = t_id;
329103831d35Sstevel 
329203831d35Sstevel 	/* get starting physical address of target memory */
329303831d35Sstevel 	err = drmach_mem_get_base_physaddr(t_id, &t_basepa);
329403831d35Sstevel 	if (err)
329503831d35Sstevel 		return (err);
329603831d35Sstevel 
329703831d35Sstevel 	/* calculate slice offset mask from slice size */
329803831d35Sstevel 	off_mask = DRMACH_MEM_SLICE_SIZE - 1;
329903831d35Sstevel 
330003831d35Sstevel 	/* calculate source and target base pa */
330156f33205SJonathan Adams 	s_copybasepa = c_ml->ml_address;
330256f33205SJonathan Adams 	t_copybasepa =
330356f33205SJonathan Adams 	    t_basepa + ((c_ml->ml_address & off_mask) - t_slice_offset);
330403831d35Sstevel 
330503831d35Sstevel 	/* paranoia */
330656f33205SJonathan Adams 	ASSERT((c_ml->ml_address & off_mask) >= t_slice_offset);
330703831d35Sstevel 
330803831d35Sstevel 	/* adjust copy memlist addresses to be relative to copy base pa */
330903831d35Sstevel 	x_ml = c_ml;
331003831d35Sstevel 	while (x_ml != NULL) {
331156f33205SJonathan Adams 		x_ml->ml_address -= s_copybasepa;
331256f33205SJonathan Adams 		x_ml = x_ml->ml_next;
331303831d35Sstevel 	}
331403831d35Sstevel 
331503831d35Sstevel #ifdef DEBUG
331603831d35Sstevel 	{
331703831d35Sstevel 	uint64_t s_basepa, s_size, t_size;
331803831d35Sstevel 
331903831d35Sstevel 	x_ml = c_ml;
332056f33205SJonathan Adams 	while (x_ml->ml_next != NULL)
332156f33205SJonathan Adams 		x_ml = x_ml->ml_next;
332203831d35Sstevel 
332303831d35Sstevel 	DRMACH_PR("source copy span: base pa 0x%lx, end pa 0x%lx\n",
332403831d35Sstevel 	    s_copybasepa,
332556f33205SJonathan Adams 	    s_copybasepa + x_ml->ml_address + x_ml->ml_size);
332603831d35Sstevel 
332703831d35Sstevel 	DRMACH_PR("target copy span: base pa 0x%lx, end pa 0x%lx\n",
332803831d35Sstevel 	    t_copybasepa,
332956f33205SJonathan Adams 	    t_copybasepa + x_ml->ml_address + x_ml->ml_size);
333003831d35Sstevel 
333103831d35Sstevel 	DRMACH_PR("copy memlist (relative to copy base pa):\n");
333203831d35Sstevel 	DRMACH_MEMLIST_DUMP(c_ml);
333303831d35Sstevel 
333403831d35Sstevel 	err = drmach_mem_get_base_physaddr(s_id, &s_basepa);
333503831d35Sstevel 	ASSERT(err == NULL);
333603831d35Sstevel 
333703831d35Sstevel 	err = drmach_mem_get_size(s_id, &s_size);
333803831d35Sstevel 	ASSERT(err == NULL);
333903831d35Sstevel 
334003831d35Sstevel 	err = drmach_mem_get_size(t_id, &t_size);
334103831d35Sstevel 	ASSERT(err == NULL);
334203831d35Sstevel 
334303831d35Sstevel 	DRMACH_PR("current source base pa 0x%lx, size 0x%lx\n",
334403831d35Sstevel 	    s_basepa, s_size);
334503831d35Sstevel 	DRMACH_PR("current target base pa 0x%lx, size 0x%lx\n",
334603831d35Sstevel 	    t_basepa, t_size);
334703831d35Sstevel 	}
334803831d35Sstevel #endif /* DEBUG */
334903831d35Sstevel 
335003831d35Sstevel 	/* Map in appropriate cpu sram page */
335103831d35Sstevel 	tte = &drmach_cpu_sram_tte[CPU->cpu_id];
335203831d35Sstevel 	ASSERT(TTE_IS_VALID(tte) && TTE_IS_8K(tte) &&
335303831d35Sstevel 	    TTE_IS_PRIVILEGED(tte) && TTE_IS_LOCKED(tte));
33541e2e7a75Shuah 	sfmmu_dtlb_ld_kva(drmach_cpu_sram_va, tte);
33551e2e7a75Shuah 	sfmmu_itlb_ld_kva(drmach_cpu_sram_va, tte);
335603831d35Sstevel 
335703831d35Sstevel 	bp = wp = drmach_cpu_sram_va;
335803831d35Sstevel 
335903831d35Sstevel 	/* Make sure the rename routine will fit */
336003831d35Sstevel 	len = (ptrdiff_t)drmach_rename_end - (ptrdiff_t)drmach_rename;
336103831d35Sstevel 	ASSERT(wp + len < bp + PAGESIZE);
336203831d35Sstevel 
336303831d35Sstevel 	/* copy text. standard bcopy not designed to work in nc space */
336403831d35Sstevel 	p = (uint_t *)wp;
336503831d35Sstevel 	q = (uint_t *)drmach_rename;
336603831d35Sstevel 	while (q < (uint_t *)drmach_rename_end)
336703831d35Sstevel 		*p++ = *q++;
336803831d35Sstevel 
336903831d35Sstevel 	/* zero remainder. standard bzero not designed to work in nc space */
337003831d35Sstevel 	while (p < (uint_t *)(bp + PAGESIZE))
337103831d35Sstevel 		*p++ = 0;
337203831d35Sstevel 
337307d06da5SSurya Prakki 	DRMACH_PR("drmach_rename function 0x%p, len %d\n", (void *)wp, len);
337403831d35Sstevel 	wp += (len + 15) & ~15;
337503831d35Sstevel 
3376d3d50737SRafael Vanoni 	err = drmach_prep_rename_script(s_mp, t_mp, t_slice_offset, wp,
3377d3d50737SRafael Vanoni 	    PAGESIZE - (wp - bp));
337803831d35Sstevel 	if (err) {
337903831d35Sstevel cleanup:
338003831d35Sstevel 		xt_one(CPU->cpu_id, vtag_flushpage_tl1,
33811e2e7a75Shuah 		    (uint64_t)drmach_cpu_sram_va, (uint64_t)ksfmmup);
338203831d35Sstevel 		return (err);
338303831d35Sstevel 	}
338403831d35Sstevel 
338503831d35Sstevel 	/* disable and flush CDC */
338603831d35Sstevel 	if (axq_cdc_disable_flush_all() != DDI_SUCCESS) {
338703831d35Sstevel 		axq_cdc_enable_all();	/* paranoia */
338803831d35Sstevel 		err = DRMACH_INTERNAL_ERROR();
338903831d35Sstevel 		goto cleanup;
339003831d35Sstevel 	}
339103831d35Sstevel 
339203831d35Sstevel 	/* mark both memory units busy */
339303831d35Sstevel 	t_mp->dev.busy++;
339403831d35Sstevel 	s_mp->dev.busy++;
339503831d35Sstevel 
339603831d35Sstevel 	cr = vmem_alloc(static_alloc_arena, sizeof (drmach_copy_rename_t),
339703831d35Sstevel 	    VM_SLEEP);
339803831d35Sstevel 	cr->isa = (void *)drmach_copy_rename_init;
339903831d35Sstevel 	cr->data = wp;
340003831d35Sstevel 	cr->c_ml = c_ml;
340103831d35Sstevel 	cr->s_mp = s_mp;
340203831d35Sstevel 	cr->t_mp = t_mp;
340303831d35Sstevel 	cr->s_copybasepa = s_copybasepa;
340403831d35Sstevel 	cr->t_copybasepa = t_copybasepa;
340503831d35Sstevel 	cr->ecode = DRMACH_CR_OK;
340603831d35Sstevel 
340703831d35Sstevel 	mutex_enter(&drmach_slice_table_lock);
340803831d35Sstevel 
340903831d35Sstevel 	mutex_enter(&drmach_xt_mb_lock);
341003831d35Sstevel 	bzero((void *)drmach_xt_mb, drmach_xt_mb_size);
341103831d35Sstevel 
341203831d35Sstevel 	if (DRMACH_L1_SET_LPA(s_mp->dev.bp) && drmach_reprogram_lpa) {
341303831d35Sstevel 		drmach_prep_xt_mb_for_slice_update(s_mp->dev.bp,
341403831d35Sstevel 		    DRMACH_PA_TO_SLICE(t_copybasepa));
341503831d35Sstevel 	}
341603831d35Sstevel 	if (DRMACH_L1_SET_LPA(t_mp->dev.bp) && drmach_reprogram_lpa) {
341703831d35Sstevel 		drmach_prep_xt_mb_for_slice_update(t_mp->dev.bp,
341803831d35Sstevel 		    DRMACH_PA_TO_SLICE(s_copybasepa));
341903831d35Sstevel 	}
342003831d35Sstevel 
342103831d35Sstevel 	*cr_id = cr;
342203831d35Sstevel 	return (NULL);
342303831d35Sstevel }
342403831d35Sstevel 
342503831d35Sstevel int drmach_rename_count;
342603831d35Sstevel int drmach_rename_ntries;
342703831d35Sstevel 
342803831d35Sstevel sbd_error_t *
drmach_copy_rename_fini(drmachid_t id)342903831d35Sstevel drmach_copy_rename_fini(drmachid_t id)
343003831d35Sstevel {
343103831d35Sstevel 	drmach_copy_rename_t	*cr = id;
343203831d35Sstevel 	sbd_error_t		*err = NULL;
343303831d35Sstevel 	dr_mbox_msg_t		*obufp;
343403831d35Sstevel 
343503831d35Sstevel 	ASSERT(cr->isa == (void *)drmach_copy_rename_init);
343603831d35Sstevel 
343703831d35Sstevel 	axq_cdc_enable_all();
343803831d35Sstevel 
343903831d35Sstevel 	xt_one(CPU->cpu_id, vtag_flushpage_tl1,
34401e2e7a75Shuah 	    (uint64_t)drmach_cpu_sram_va, (uint64_t)ksfmmup);
344103831d35Sstevel 
344203831d35Sstevel 	switch (cr->ecode) {
344303831d35Sstevel 	case DRMACH_CR_OK:
344403831d35Sstevel 		break;
344503831d35Sstevel 	case DRMACH_CR_MC_IDLE_ERR: {
344603831d35Sstevel 		dev_info_t	*dip = NULL;
344703831d35Sstevel 		drmach_mem_t	*mp = (drmach_mem_t *)cr->earg;
344803831d35Sstevel 		char *path = kmem_alloc(MAXPATHLEN, KM_SLEEP);
344903831d35Sstevel 
345003831d35Sstevel 		ASSERT(DRMACH_IS_MEM_ID(mp));
345103831d35Sstevel 
345203831d35Sstevel 		err = drmach_get_dip(mp, &dip);
345303831d35Sstevel 
345403831d35Sstevel 		ASSERT(err == NULL);
345503831d35Sstevel 		ASSERT(dip != NULL);
345603831d35Sstevel 
345703831d35Sstevel 		err = drerr_new(0, ESBD_MEMFAIL, NULL);
345803831d35Sstevel 		(void) ddi_pathname(dip, path);
345903831d35Sstevel 		cmn_err(CE_WARN, "failed to idle memory controller %s on %s: "
346003831d35Sstevel 		    "copy-rename aborted", path, mp->dev.bp->cm.name);
346103831d35Sstevel 		kmem_free(path, MAXPATHLEN);
346203831d35Sstevel 		break;
346303831d35Sstevel 	}
346403831d35Sstevel 	case DRMACH_CR_IOPAUSE_ERR:
346503831d35Sstevel 		ASSERT((uintptr_t)cr->earg >= 0 &&
346603831d35Sstevel 		    (uintptr_t)cr->earg < AXQ_MAX_EXP);
346703831d35Sstevel 
346803831d35Sstevel 		err = drerr_new(0,  ESBD_SUSPEND, "EX%d", (uintptr_t)cr->earg);
346903831d35Sstevel 		cmn_err(CE_WARN, "failed to idle EX%ld AXQ slot1 activity prior"
347003831d35Sstevel 		    " to copy-rename", (uintptr_t)cr->earg);
347103831d35Sstevel 		break;
347203831d35Sstevel 	case DRMACH_CR_ONTRAP_ERR:
347303831d35Sstevel 		err = drerr_new(0, ESBD_MEMFAIL, NULL);
347403831d35Sstevel 		cmn_err(CE_WARN, "copy-rename aborted due to uncorrectable "
347503831d35Sstevel 		    "memory error");
347603831d35Sstevel 		break;
347703831d35Sstevel 	default:
347803831d35Sstevel 		err = DRMACH_INTERNAL_ERROR();
347903831d35Sstevel 		cmn_err(CE_WARN, "unknown copy-rename error code (%d)\n",
348003831d35Sstevel 		    cr->ecode);
348103831d35Sstevel 		break;
348203831d35Sstevel 	}
348303831d35Sstevel 
348403831d35Sstevel #ifdef DEBUG
348503831d35Sstevel 	if ((DRMACH_L1_SET_LPA(cr->s_mp->dev.bp) ||
348603831d35Sstevel 	    DRMACH_L1_SET_LPA(cr->t_mp->dev.bp)) && drmach_reprogram_lpa) {
348703831d35Sstevel 		int	i;
348803831d35Sstevel 		for (i = 0; i < NCPU; i++) {
348903831d35Sstevel 			if (drmach_xt_mb[i])
349003831d35Sstevel 				DRMACH_PR("cpu%d ignored drmach_xt_mb", i);
349103831d35Sstevel 		}
349203831d35Sstevel 	}
349303831d35Sstevel #endif
349403831d35Sstevel 	mutex_exit(&drmach_xt_mb_lock);
349503831d35Sstevel 
349603831d35Sstevel 	if (cr->c_ml != NULL)
349703831d35Sstevel 		memlist_delete(cr->c_ml);
349803831d35Sstevel 
349903831d35Sstevel 	cr->t_mp->dev.busy--;
350003831d35Sstevel 	cr->s_mp->dev.busy--;
350103831d35Sstevel 
350203831d35Sstevel 	if (err) {
350303831d35Sstevel 		mutex_exit(&drmach_slice_table_lock);
350403831d35Sstevel 		goto done;
350503831d35Sstevel 	}
350603831d35Sstevel 
350703831d35Sstevel 	/* update casm shadow for target and source board */
350803831d35Sstevel 	drmach_slice_table_update(cr->t_mp->dev.bp, 0);
350903831d35Sstevel 	drmach_slice_table_update(cr->s_mp->dev.bp, 0);
351003831d35Sstevel 	mutex_exit(&drmach_slice_table_lock);
351103831d35Sstevel 
351203831d35Sstevel 	mutex_enter(&drmach_bus_sync_lock);
351303831d35Sstevel 	drmach_bus_sync_list_update();
351403831d35Sstevel 	mutex_exit(&drmach_bus_sync_lock);
351503831d35Sstevel 
351603831d35Sstevel 	/*
351703831d35Sstevel 	 * Make a good-faith effort to notify the SC about the copy-rename, but
351803831d35Sstevel 	 * don't worry if it fails, since a subsequent claim/unconfig/unclaim
351903831d35Sstevel 	 * will duplicate the update.
352003831d35Sstevel 	 */
352103831d35Sstevel 	obufp = kmem_zalloc(sizeof (dr_mbox_msg_t), KM_SLEEP);
352203831d35Sstevel 	mutex_enter(&drmach_slice_table_lock);
352303831d35Sstevel 	drmach_msg_memslice_init(obufp->msgdata.dm_uc.mem_slice);
352403831d35Sstevel 	drmach_msg_memregs_init(obufp->msgdata.dm_uc.mem_regs);
352503831d35Sstevel 	mutex_exit(&drmach_slice_table_lock);
352603831d35Sstevel 	(void) drmach_mbox_trans(DRMSG_UNCONFIG, cr->s_mp->dev.bp->bnum,
352703831d35Sstevel 	    (caddr_t)obufp, sizeof (dr_mbox_msg_t), (caddr_t)NULL, 0);
352803831d35Sstevel 	kmem_free(obufp, sizeof (dr_mbox_msg_t));
352903831d35Sstevel 
353003831d35Sstevel done:
353103831d35Sstevel 	vmem_free(static_alloc_arena, cr, sizeof (drmach_copy_rename_t));
353203831d35Sstevel 
353303831d35Sstevel 	DRMACH_PR("waited %d out of %d tries for drmach_rename_wait on %d cpus",
353403831d35Sstevel 	    drmach_rename_ntries, drmach_cpu_ntries, drmach_rename_count);
353503831d35Sstevel 
353603831d35Sstevel 	return (err);
353703831d35Sstevel }
353803831d35Sstevel 
353903831d35Sstevel int drmach_slow_copy = 0;
354003831d35Sstevel 
354103831d35Sstevel void
drmach_copy_rename(drmachid_t id)354203831d35Sstevel drmach_copy_rename(drmachid_t id)
354303831d35Sstevel {
354403831d35Sstevel 	extern uint_t		 getpstate(void);
354503831d35Sstevel 	extern void		 setpstate(uint_t);
354603831d35Sstevel 
354703831d35Sstevel 	extern xcfunc_t		 drmach_rename_wait;
354803831d35Sstevel 	extern xcfunc_t		 drmach_rename_done;
354903831d35Sstevel 	extern xcfunc_t		 drmach_rename_abort;
355003831d35Sstevel 
355103831d35Sstevel 	drmach_copy_rename_t	*cr = id;
355203831d35Sstevel 	uint64_t		 neer;
355303831d35Sstevel 	struct memlist		*ml;
355403831d35Sstevel 	int			 i, count;
355503831d35Sstevel 	int			 csize, lnsize;
355603831d35Sstevel 	uint64_t		 caddr;
355703831d35Sstevel 	cpuset_t		 cpuset;
355803831d35Sstevel 	uint_t			 pstate;
355903831d35Sstevel 	uint32_t		 exp = 0;
356003831d35Sstevel 	on_trap_data_t		 otd;
356103831d35Sstevel 	xcfunc_t		*drmach_end_wait_xcall = drmach_rename_done;
356203831d35Sstevel 
356303831d35Sstevel 	ASSERT(cr->isa == (void *)drmach_copy_rename_init);
356403831d35Sstevel 	ASSERT(MUTEX_HELD(&cpu_lock));
356503831d35Sstevel 	ASSERT(cr->ecode == DRMACH_CR_OK);
356603831d35Sstevel 
356703831d35Sstevel 	/*
356803831d35Sstevel 	 * Prevent slot1 IO from accessing Safari memory bus.
356903831d35Sstevel 	 */
357003831d35Sstevel 	if (axq_iopause_enable_all(&exp) != DDI_SUCCESS) {
357103831d35Sstevel 		ASSERT(exp >= 0 && exp < AXQ_MAX_EXP);
357203831d35Sstevel 		cr->ecode = DRMACH_CR_IOPAUSE_ERR;
357303831d35Sstevel 		cr->earg = (void *)(uintptr_t)exp;
357403831d35Sstevel 		return;
357503831d35Sstevel 	}
357603831d35Sstevel 
357703831d35Sstevel 	cpuset = cpu_ready_set;
357803831d35Sstevel 	CPUSET_DEL(cpuset, CPU->cpu_id);
357903831d35Sstevel 	count = ncpus - 1;
358003831d35Sstevel 	drmach_rename_count = count;	/* for debug */
358103831d35Sstevel 
358203831d35Sstevel 	drmach_xt_ready = 0;
358303831d35Sstevel 	xt_some(cpuset, drmach_rename_wait, NULL, NULL);
358403831d35Sstevel 
358503831d35Sstevel 	for (i = 0; i < drmach_cpu_ntries; i++) {
358603831d35Sstevel 		if (drmach_xt_ready == count)
358703831d35Sstevel 			break;
358803831d35Sstevel 		DELAY(drmach_cpu_delay);
358903831d35Sstevel 	}
359003831d35Sstevel 
359103831d35Sstevel 	drmach_rename_ntries = i;	/* for debug */
359203831d35Sstevel 
359303831d35Sstevel 	drmach_xt_ready = 0;		/* steal the line back */
359403831d35Sstevel 	for (i = 0; i < NCPU; i++)	/* steal the line back, preserve data */
359503831d35Sstevel 		drmach_xt_mb[i] = drmach_xt_mb[i];
359603831d35Sstevel 
359703831d35Sstevel 	caddr = drmach_iocage_paddr;
359803831d35Sstevel 	csize = cpunodes[CPU->cpu_id].ecache_size;
359903831d35Sstevel 	lnsize = cpunodes[CPU->cpu_id].ecache_linesize;
360003831d35Sstevel 
360103831d35Sstevel 	/* disable CE reporting */
360203831d35Sstevel 	neer = get_error_enable();
360303831d35Sstevel 	set_error_enable(neer & ~EN_REG_CEEN);
360403831d35Sstevel 
360503831d35Sstevel 	/* disable interrupts (paranoia) */
360603831d35Sstevel 	pstate = getpstate();
360703831d35Sstevel 	setpstate(pstate & ~PSTATE_IE);
360803831d35Sstevel 
360903831d35Sstevel 	/*
361003831d35Sstevel 	 * Execute copy-rename under on_trap to protect against a panic due
361103831d35Sstevel 	 * to an uncorrectable error. Instead, DR will abort the copy-rename
361203831d35Sstevel 	 * operation and rely on the OS to do the error reporting.
361303831d35Sstevel 	 *
361403831d35Sstevel 	 * In general, trap handling on any cpu once the copy begins
361503831d35Sstevel 	 * can result in an inconsistent memory image on the target.
361603831d35Sstevel 	 */
361703831d35Sstevel 	if (on_trap(&otd, OT_DATA_EC)) {
361803831d35Sstevel 		cr->ecode = DRMACH_CR_ONTRAP_ERR;
361903831d35Sstevel 		goto copy_rename_end;
362003831d35Sstevel 	}
362103831d35Sstevel 
362203831d35Sstevel 	/*
362303831d35Sstevel 	 * DO COPY.
362403831d35Sstevel 	 */
362556f33205SJonathan Adams 	for (ml = cr->c_ml; ml; ml = ml->ml_next) {
362603831d35Sstevel 		uint64_t	s_pa, t_pa;
362703831d35Sstevel 		uint64_t	nbytes;
362803831d35Sstevel 
362956f33205SJonathan Adams 		s_pa = cr->s_copybasepa + ml->ml_address;
363056f33205SJonathan Adams 		t_pa = cr->t_copybasepa + ml->ml_address;
363156f33205SJonathan Adams 		nbytes = ml->ml_size;
363203831d35Sstevel 
363303831d35Sstevel 		while (nbytes != 0ull) {
363403831d35Sstevel 			/* copy 32 bytes at src_pa to dst_pa */
363503831d35Sstevel 			bcopy32_il(s_pa, t_pa);
363603831d35Sstevel 
363703831d35Sstevel 			/* increment by 32 bytes */
363803831d35Sstevel 			s_pa += (4 * sizeof (uint64_t));
363903831d35Sstevel 			t_pa += (4 * sizeof (uint64_t));
364003831d35Sstevel 
364103831d35Sstevel 			/* decrement by 32 bytes */
364203831d35Sstevel 			nbytes -= (4 * sizeof (uint64_t));
364303831d35Sstevel 
364403831d35Sstevel 			if (drmach_slow_copy) {	/* for debug */
364503831d35Sstevel 				uint64_t i = 13 * 50;
3646f500b196SRichard Bean 				while (i--)
3647f500b196SRichard Bean 					;
364803831d35Sstevel 			}
364903831d35Sstevel 		}
365003831d35Sstevel 	}
365103831d35Sstevel 
365203831d35Sstevel 	/*
365303831d35Sstevel 	 * XXX CHEETAH SUPPORT
365403831d35Sstevel 	 * For cheetah, we need to grab the iocage lock since iocage
365503831d35Sstevel 	 * memory is used for e$ flush.
365603831d35Sstevel 	 *
365703831d35Sstevel 	 * NOTE: This code block is dangerous at this point in the
365803831d35Sstevel 	 * copy-rename operation. It modifies memory after the copy
365903831d35Sstevel 	 * has taken place which means that any persistent state will
366003831d35Sstevel 	 * be abandoned after the rename operation. The code is also
366103831d35Sstevel 	 * performing thread synchronization at a time when all but
366203831d35Sstevel 	 * one processors are paused. This is a potential deadlock
366303831d35Sstevel 	 * situation.
366403831d35Sstevel 	 *
366503831d35Sstevel 	 * This code block must be moved to drmach_copy_rename_init.
366603831d35Sstevel 	 */
366703831d35Sstevel 	if (drmach_is_cheetah) {
366803831d35Sstevel 		mutex_enter(&drmach_iocage_lock);
366903831d35Sstevel 		while (drmach_iocage_is_busy)
367003831d35Sstevel 			cv_wait(&drmach_iocage_cv, &drmach_iocage_lock);
367103831d35Sstevel 		drmach_iocage_is_busy = 1;
367203831d35Sstevel 		drmach_iocage_mem_scrub(ecache_size * 2);
367303831d35Sstevel 		mutex_exit(&drmach_iocage_lock);
367403831d35Sstevel 	}
367503831d35Sstevel 
367603831d35Sstevel 	/*
367703831d35Sstevel 	 * bcopy32_il is implemented as a series of ldxa/stxa via
367803831d35Sstevel 	 * ASI_MEM instructions. Following the copy loop, the E$
367903831d35Sstevel 	 * of the master (this) processor will have lines in state
368003831d35Sstevel 	 * O that correspond to lines of home memory in state gI.
368103831d35Sstevel 	 * An E$ flush is necessary to commit these lines before
368203831d35Sstevel 	 * proceeding with the rename operation.
368303831d35Sstevel 	 *
368403831d35Sstevel 	 * Flushing the E$ will automatically flush the W$, but
368503831d35Sstevel 	 * the D$ and I$ must be flushed separately and explicitly.
368603831d35Sstevel 	 */
368703831d35Sstevel 	flush_ecache_il(caddr, csize, lnsize);	/* inline version */
368803831d35Sstevel 
368903831d35Sstevel 	/*
369003831d35Sstevel 	 * Each line of home memory is now in state gM, except in
369103831d35Sstevel 	 * the case of a cheetah processor when the E$ flush area
369203831d35Sstevel 	 * is included within the copied region. In such a case,
369303831d35Sstevel 	 * the lines of home memory for the upper half of the
369403831d35Sstevel 	 * flush area are in state gS.
369503831d35Sstevel 	 *
369603831d35Sstevel 	 * Each line of target memory is in state gM.
369703831d35Sstevel 	 *
369803831d35Sstevel 	 * Each line of this processor's E$ is in state I, except
369903831d35Sstevel 	 * those of a cheetah processor. All lines of a cheetah
370003831d35Sstevel 	 * processor's E$ are in state S and correspond to the lines
370103831d35Sstevel 	 * in upper half of the E$ flush area.
370203831d35Sstevel 	 *
370303831d35Sstevel 	 * It is vital at this point that none of the lines in the
370403831d35Sstevel 	 * home or target memories are in state gI and that none
370503831d35Sstevel 	 * of the lines in this processor's E$ are in state O or Os.
370603831d35Sstevel 	 * A single instance of such a condition will cause loss of
370703831d35Sstevel 	 * coherency following the rename operation.
370803831d35Sstevel 	 */
370903831d35Sstevel 
371003831d35Sstevel 	/*
371103831d35Sstevel 	 * Rename
371203831d35Sstevel 	 */
371303831d35Sstevel 	(*(void(*)())drmach_cpu_sram_va)(cr->data, &cr->ecode, &cr->earg);
371403831d35Sstevel 
371503831d35Sstevel 	/*
371603831d35Sstevel 	 * Rename operation complete. The physical address space
371703831d35Sstevel 	 * of the home and target memories have been swapped, the
371803831d35Sstevel 	 * routing data in the respective CASM entries have been
371903831d35Sstevel 	 * swapped, and LPA settings in the processor and schizo
372003831d35Sstevel 	 * devices have been reprogrammed accordingly.
372103831d35Sstevel 	 *
372203831d35Sstevel 	 * In the case of a cheetah processor, the E$ remains
372303831d35Sstevel 	 * populated with lines in state S that correspond to the
372403831d35Sstevel 	 * lines in the former home memory. Now that the physical
372503831d35Sstevel 	 * addresses have been swapped, these E$ lines correspond
372603831d35Sstevel 	 * to lines in the new home memory which are in state gM.
372703831d35Sstevel 	 * This combination is invalid. An additional E$ flush is
372803831d35Sstevel 	 * necessary to restore coherency. The E$ flush will cause
372903831d35Sstevel 	 * the lines of the new home memory for the flush region
373003831d35Sstevel 	 * to transition from state gM to gS. The former home memory
373103831d35Sstevel 	 * remains unmodified. This additional E$ flush has no effect
373203831d35Sstevel 	 * on a cheetah+ processor.
373303831d35Sstevel 	 */
373403831d35Sstevel 	flush_ecache_il(caddr, csize, lnsize);	/* inline version */
373503831d35Sstevel 
373603831d35Sstevel 	/*
373703831d35Sstevel 	 * The D$ and I$ must be flushed to ensure that coherency is
373803831d35Sstevel 	 * maintained. Any line in a cache that is in the valid
373903831d35Sstevel 	 * state has its corresponding line of the new home memory
374003831d35Sstevel 	 * in the gM state. This is an invalid condition. When the
374103831d35Sstevel 	 * flushes are complete the cache line states will be
374203831d35Sstevel 	 * resynchronized with those in the new home memory.
374303831d35Sstevel 	 */
374403831d35Sstevel 	flush_icache_il();			/* inline version */
374503831d35Sstevel 	flush_dcache_il();			/* inline version */
374603831d35Sstevel 	flush_pcache_il();			/* inline version */
374703831d35Sstevel 
374803831d35Sstevel copy_rename_end:
374903831d35Sstevel 
375003831d35Sstevel 	no_trap();
375103831d35Sstevel 
375203831d35Sstevel 	/* enable interrupts */
375303831d35Sstevel 	setpstate(pstate);
375403831d35Sstevel 
375503831d35Sstevel 	/* enable CE reporting */
375603831d35Sstevel 	set_error_enable(neer);
375703831d35Sstevel 
375803831d35Sstevel 	if (cr->ecode != DRMACH_CR_OK)
375903831d35Sstevel 		drmach_end_wait_xcall = drmach_rename_abort;
376003831d35Sstevel 
376103831d35Sstevel 	/*
376203831d35Sstevel 	 * XXX CHEETAH SUPPORT
376303831d35Sstevel 	 */
376403831d35Sstevel 	if (drmach_is_cheetah) {
376503831d35Sstevel 		mutex_enter(&drmach_iocage_lock);
376603831d35Sstevel 		drmach_iocage_mem_scrub(ecache_size * 2);
376703831d35Sstevel 		drmach_iocage_is_busy = 0;
376803831d35Sstevel 		cv_signal(&drmach_iocage_cv);
376903831d35Sstevel 		mutex_exit(&drmach_iocage_lock);
377003831d35Sstevel 	}
377103831d35Sstevel 
377203831d35Sstevel 	axq_iopause_disable_all();
377303831d35Sstevel 
377403831d35Sstevel 	xt_some(cpuset, drmach_end_wait_xcall, NULL, NULL);
377503831d35Sstevel }
377603831d35Sstevel 
377703831d35Sstevel static void drmach_io_dispose(drmachid_t);
377803831d35Sstevel static sbd_error_t *drmach_io_release(drmachid_t);
377903831d35Sstevel static sbd_error_t *drmach_io_status(drmachid_t, drmach_status_t *);
378003831d35Sstevel 
378103831d35Sstevel static sbd_error_t *
drmach_pci_new(drmach_device_t * proto,drmachid_t * idp)378203831d35Sstevel drmach_pci_new(drmach_device_t *proto, drmachid_t *idp)
378303831d35Sstevel {
378403831d35Sstevel 	drmach_node_t	*node = proto->node;
378503831d35Sstevel 	sbd_error_t	*err;
378603831d35Sstevel 	drmach_reg_t	 regs[3];
378703831d35Sstevel 	int		 rv;
378803831d35Sstevel 	int		 len = 0;
378903831d35Sstevel 
379003831d35Sstevel 	rv = node->n_getproplen(node, "reg", &len);
379103831d35Sstevel 	if (rv != 0 || len != sizeof (regs)) {
379203831d35Sstevel 		sbd_error_t *err;
379303831d35Sstevel 
379403831d35Sstevel 		/* pci nodes are expected to have regs */
379503831d35Sstevel 		err = drerr_new(1, ESTC_GETPROP,
379603831d35Sstevel 		    "Device Node 0x%x: property %s",
379703831d35Sstevel 		    (uint_t)node->get_dnode(node), "reg");
379803831d35Sstevel 		return (err);
379903831d35Sstevel 	}
380003831d35Sstevel 
380103831d35Sstevel 	rv = node->n_getprop(node, "reg", (void *)regs, sizeof (regs));
380203831d35Sstevel 	if (rv) {
380303831d35Sstevel 		sbd_error_t *err;
380403831d35Sstevel 
380503831d35Sstevel 		err = drerr_new(1, ESTC_GETPROP,
380603831d35Sstevel 		    "Device Node 0x%x: property %s",
380703831d35Sstevel 		    (uint_t)node->get_dnode(node), "reg");
380803831d35Sstevel 
380903831d35Sstevel 		return (err);
381003831d35Sstevel 	}
381103831d35Sstevel 
381203831d35Sstevel 	/*
381303831d35Sstevel 	 * Fix up unit number so that Leaf A has a lower unit number
381403831d35Sstevel 	 * than Leaf B.
381503831d35Sstevel 	 */
381603831d35Sstevel 	if ((proto->portid % 2) != 0) {
381703831d35Sstevel 		if ((regs[0].reg_addr_lo & 0x700000) == 0x700000)
381803831d35Sstevel 			proto->unum = 0;
381903831d35Sstevel 		else
382003831d35Sstevel 			proto->unum = 1;
382103831d35Sstevel 	} else {
382203831d35Sstevel 		if ((regs[0].reg_addr_lo & 0x700000) == 0x700000)
382303831d35Sstevel 			proto->unum = 2;
382403831d35Sstevel 		else
382503831d35Sstevel 			proto->unum = 3;
382603831d35Sstevel 	}
382703831d35Sstevel 
382803831d35Sstevel 	err = drmach_io_new(proto, idp);
382903831d35Sstevel 	if (err == NULL) {
383003831d35Sstevel 		drmach_io_t *self = *idp;
383103831d35Sstevel 
383203831d35Sstevel 		/* reassemble 64-bit base address */
383303831d35Sstevel 		self->scsr_pa  = (uint64_t)regs[1].reg_addr_hi << 32;
383403831d35Sstevel 		self->scsr_pa |= (uint64_t)regs[1].reg_addr_lo;
383503831d35Sstevel 	}
383603831d35Sstevel 
383703831d35Sstevel 	return (err);
383803831d35Sstevel }
383903831d35Sstevel 
384003831d35Sstevel static sbd_error_t *
drmach_io_new(drmach_device_t * proto,drmachid_t * idp)384103831d35Sstevel drmach_io_new(drmach_device_t *proto, drmachid_t *idp)
384203831d35Sstevel {
384303831d35Sstevel 	drmach_io_t	*ip;
384403831d35Sstevel 
384503831d35Sstevel 	ip = kmem_zalloc(sizeof (drmach_io_t), KM_SLEEP);
384603831d35Sstevel 	bcopy(proto, &ip->dev, sizeof (ip->dev));
384703831d35Sstevel 	ip->dev.node = drmach_node_dup(proto->node);
384803831d35Sstevel 	ip->dev.cm.isa = (void *)drmach_io_new;
384903831d35Sstevel 	ip->dev.cm.dispose = drmach_io_dispose;
385003831d35Sstevel 	ip->dev.cm.release = drmach_io_release;
385103831d35Sstevel 	ip->dev.cm.status = drmach_io_status;
385203831d35Sstevel 
385307d06da5SSurya Prakki 	(void) snprintf(ip->dev.cm.name, sizeof (ip->dev.cm.name), "%s%d",
385403831d35Sstevel 	    ip->dev.type, ip->dev.unum);
385503831d35Sstevel 
385603831d35Sstevel 	*idp = (drmachid_t)ip;
385703831d35Sstevel 	return (NULL);
385803831d35Sstevel }
385903831d35Sstevel 
386003831d35Sstevel static void
drmach_io_dispose(drmachid_t id)386103831d35Sstevel drmach_io_dispose(drmachid_t id)
386203831d35Sstevel {
386303831d35Sstevel 	drmach_io_t *self;
386403831d35Sstevel 
386503831d35Sstevel 	ASSERT(DRMACH_IS_IO_ID(id));
386603831d35Sstevel 
386703831d35Sstevel 	self = id;
386803831d35Sstevel 	if (self->dev.node)
386903831d35Sstevel 		drmach_node_dispose(self->dev.node);
387003831d35Sstevel 
387103831d35Sstevel 	kmem_free(self, sizeof (*self));
387203831d35Sstevel }
387303831d35Sstevel 
387403831d35Sstevel /*ARGSUSED*/
387503831d35Sstevel sbd_error_t *
drmach_pre_op(int cmd,drmachid_t id,drmach_opts_t * opts)387603831d35Sstevel drmach_pre_op(int cmd, drmachid_t id, drmach_opts_t *opts)
387703831d35Sstevel {
387803831d35Sstevel 	drmach_board_t	*bp = (drmach_board_t *)id;
387903831d35Sstevel 	sbd_error_t	*err = NULL;
388003831d35Sstevel 
388103831d35Sstevel 	if (id && DRMACH_IS_BOARD_ID(id)) {
388203831d35Sstevel 		switch (cmd) {
388303831d35Sstevel 			case SBD_CMD_TEST:
388403831d35Sstevel 			case SBD_CMD_STATUS:
388503831d35Sstevel 			case SBD_CMD_GETNCM:
388603831d35Sstevel 				break;
388703831d35Sstevel 			case SBD_CMD_CONNECT:
388803831d35Sstevel 				if (bp->connected)
388903831d35Sstevel 					err = drerr_new(0, ESBD_STATE, NULL);
389003831d35Sstevel 
389103831d35Sstevel 				if (bp->cond == SBD_COND_UNUSABLE)
389203831d35Sstevel 					err = drerr_new(0,
389303831d35Sstevel 					    ESBD_FATAL_STATE, NULL);
389403831d35Sstevel 				break;
389503831d35Sstevel 			case SBD_CMD_DISCONNECT:
389603831d35Sstevel 				if (!bp->connected)
389703831d35Sstevel 					err = drerr_new(0, ESBD_STATE, NULL);
389803831d35Sstevel 
389903831d35Sstevel 				if (bp->cond == SBD_COND_UNUSABLE)
390003831d35Sstevel 					err = drerr_new(0,
390103831d35Sstevel 					    ESBD_FATAL_STATE, NULL);
390203831d35Sstevel 				break;
390303831d35Sstevel 			default:
390403831d35Sstevel 				if (bp->cond == SBD_COND_UNUSABLE)
390503831d35Sstevel 					err = drerr_new(0,
390603831d35Sstevel 					    ESBD_FATAL_STATE, NULL);
390703831d35Sstevel 				break;
390803831d35Sstevel 
390903831d35Sstevel 		}
391003831d35Sstevel 	}
391103831d35Sstevel 
391203831d35Sstevel 	return (err);
391303831d35Sstevel }
391403831d35Sstevel 
391503831d35Sstevel /*ARGSUSED*/
391603831d35Sstevel sbd_error_t *
drmach_post_op(int cmd,drmachid_t id,drmach_opts_t * opts)391703831d35Sstevel drmach_post_op(int cmd, drmachid_t id, drmach_opts_t *opts)
391803831d35Sstevel {
391903831d35Sstevel 	return (NULL);
392003831d35Sstevel }
392103831d35Sstevel 
392203831d35Sstevel sbd_error_t *
drmach_board_assign(int bnum,drmachid_t * id)392303831d35Sstevel drmach_board_assign(int bnum, drmachid_t *id)
392403831d35Sstevel {
392503831d35Sstevel 	sbd_error_t	*err = NULL;
392603831d35Sstevel 	caddr_t		obufp;
392703831d35Sstevel 
392803831d35Sstevel 	if (!drmach_initialized && drmach_init() == -1) {
392903831d35Sstevel 		err = DRMACH_INTERNAL_ERROR();
393003831d35Sstevel 	}
393103831d35Sstevel 
393203831d35Sstevel 	rw_enter(&drmach_boards_rwlock, RW_WRITER);
393303831d35Sstevel 
393403831d35Sstevel 	if (!err) {
393503831d35Sstevel 		if (drmach_array_get(drmach_boards, bnum, id) == -1) {
393603831d35Sstevel 			err = drerr_new(0, ESTC_BNUM, "%d", bnum);
393703831d35Sstevel 		} else {
393803831d35Sstevel 			drmach_board_t	*bp;
393903831d35Sstevel 
394003831d35Sstevel 			if (*id)
394103831d35Sstevel 				rw_downgrade(&drmach_boards_rwlock);
394203831d35Sstevel 
394303831d35Sstevel 			obufp = kmem_zalloc(sizeof (dr_proto_hdr_t), KM_SLEEP);
394403831d35Sstevel 			err = drmach_mbox_trans(DRMSG_ASSIGN, bnum, obufp,
394503831d35Sstevel 			    sizeof (dr_proto_hdr_t), (caddr_t)NULL, 0);
394603831d35Sstevel 			kmem_free(obufp, sizeof (dr_proto_hdr_t));
394703831d35Sstevel 
394803831d35Sstevel 			if (!err) {
394903831d35Sstevel 				bp = *id;
395003831d35Sstevel 				if (!*id)
395103831d35Sstevel 					bp = *id  =
395203831d35Sstevel 					    (drmachid_t)drmach_board_new(bnum);
395303831d35Sstevel 				bp->assigned = 1;
395403831d35Sstevel 			}
395503831d35Sstevel 		}
395603831d35Sstevel 	}
395703831d35Sstevel 	rw_exit(&drmach_boards_rwlock);
395803831d35Sstevel 	return (err);
395903831d35Sstevel }
396003831d35Sstevel 
396103831d35Sstevel static uint_t
drmach_board_non_panther_cpus(gdcd_t * gdcd,uint_t exp,uint_t slot)396203831d35Sstevel drmach_board_non_panther_cpus(gdcd_t *gdcd, uint_t exp, uint_t slot)
396303831d35Sstevel {
396403831d35Sstevel 	uint_t	port, port_start, port_end;
396503831d35Sstevel 	uint_t	non_panther_cpus = 0;
396603831d35Sstevel 	uint_t	impl;
396703831d35Sstevel 
396803831d35Sstevel 	ASSERT(gdcd != NULL);
396903831d35Sstevel 
397003831d35Sstevel 	/*
397103831d35Sstevel 	 * Determine PRD port indices based on slot location.
397203831d35Sstevel 	 */
397303831d35Sstevel 	switch (slot) {
397403831d35Sstevel 	case 0:
397503831d35Sstevel 		port_start = 0;
397603831d35Sstevel 		port_end = 3;
397703831d35Sstevel 		break;
397803831d35Sstevel 	case 1:
397903831d35Sstevel 		port_start = 4;
398003831d35Sstevel 		port_end = 5;
398103831d35Sstevel 		break;
398203831d35Sstevel 	default:
398303831d35Sstevel 		ASSERT(0);
398403831d35Sstevel 		/* check all */
398503831d35Sstevel 		port_start = 0;
398603831d35Sstevel 		port_end = 5;
398703831d35Sstevel 		break;
398803831d35Sstevel 	}
398903831d35Sstevel 
399003831d35Sstevel 	for (port = port_start; port <= port_end; port++) {
399103831d35Sstevel 		if (gdcd->dcd_prd[exp][port].prd_ptype == SAFPTYPE_CPU &&
399203831d35Sstevel 		    RSV_GOOD(gdcd->dcd_prd[exp][port].prd_prsv)) {
399303831d35Sstevel 			/*
399403831d35Sstevel 			 * This Safari port passed POST and represents a
399503831d35Sstevel 			 * cpu, so check the implementation.
399603831d35Sstevel 			 */
399703831d35Sstevel 			impl = (gdcd->dcd_prd[exp][port].prd_ver_reg >> 32)
399803831d35Sstevel 			    & 0xffff;
399903831d35Sstevel 
400003831d35Sstevel 			switch (impl) {
400103831d35Sstevel 			case CHEETAH_IMPL:
400203831d35Sstevel 			case CHEETAH_PLUS_IMPL:
400303831d35Sstevel 			case JAGUAR_IMPL:
400403831d35Sstevel 				non_panther_cpus++;
400503831d35Sstevel 				break;
400603831d35Sstevel 			case PANTHER_IMPL:
400703831d35Sstevel 				break;
400803831d35Sstevel 			default:
400903831d35Sstevel 				ASSERT(0);
401003831d35Sstevel 				non_panther_cpus++;
401103831d35Sstevel 				break;
401203831d35Sstevel 			}
401303831d35Sstevel 		}
401403831d35Sstevel 	}
401503831d35Sstevel 
401603831d35Sstevel 	DRMACH_PR("drmach_board_non_panther_cpus: exp=%d, slot=%d, "
401703831d35Sstevel 	    "non_panther_cpus=%d", exp, slot, non_panther_cpus);
401803831d35Sstevel 
401903831d35Sstevel 	return (non_panther_cpus);
402003831d35Sstevel }
402103831d35Sstevel 
402203831d35Sstevel sbd_error_t *
drmach_board_connect(drmachid_t id,drmach_opts_t * opts)402303831d35Sstevel drmach_board_connect(drmachid_t id, drmach_opts_t *opts)
402403831d35Sstevel {
402503831d35Sstevel 	_NOTE(ARGUNUSED(opts))
402603831d35Sstevel 
402703831d35Sstevel 	drmach_board_t		*bp = (drmach_board_t *)id;
402803831d35Sstevel 	sbd_error_t		*err;
402903831d35Sstevel 	dr_mbox_msg_t		*obufp;
403003831d35Sstevel 	gdcd_t			*gdcd = NULL;
403103831d35Sstevel 	uint_t			exp, slot;
403203831d35Sstevel 	sc_gptwocfg_cookie_t	scc;
403303831d35Sstevel 	int			panther_pages_enabled;
403403831d35Sstevel 
403503831d35Sstevel 	if (!DRMACH_IS_BOARD_ID(id))
403603831d35Sstevel 		return (drerr_new(0, ESTC_INAPPROP, NULL));
403703831d35Sstevel 
403803831d35Sstevel 	/*
403903831d35Sstevel 	 * Build the casm info portion of the CLAIM message.
404003831d35Sstevel 	 */
404103831d35Sstevel 	obufp = kmem_zalloc(sizeof (dr_mbox_msg_t), KM_SLEEP);
404203831d35Sstevel 	mutex_enter(&drmach_slice_table_lock);
404303831d35Sstevel 	drmach_msg_memslice_init(obufp->msgdata.dm_cr.mem_slice);
404403831d35Sstevel 	drmach_msg_memregs_init(obufp->msgdata.dm_cr.mem_regs);
404503831d35Sstevel 	mutex_exit(&drmach_slice_table_lock);
404603831d35Sstevel 	err = drmach_mbox_trans(DRMSG_CLAIM, bp->bnum, (caddr_t)obufp,
404703831d35Sstevel 	    sizeof (dr_mbox_msg_t), (caddr_t)NULL, 0);
404803831d35Sstevel 	kmem_free(obufp, sizeof (dr_mbox_msg_t));
404903831d35Sstevel 
405003831d35Sstevel 	if (err) {
405103831d35Sstevel 		/*
405203831d35Sstevel 		 * if mailbox timeout or unrecoverable error from SC,
405303831d35Sstevel 		 * board cannot be touched.  Mark the status as
405403831d35Sstevel 		 * unusable.
405503831d35Sstevel 		 */
405603831d35Sstevel 		if ((err->e_code == ESTC_SMS_ERR_UNRECOVERABLE) ||
405703831d35Sstevel 		    (err->e_code == ESTC_MBXRPLY))
405803831d35Sstevel 			bp->cond = SBD_COND_UNUSABLE;
405903831d35Sstevel 		return (err);
406003831d35Sstevel 	}
406103831d35Sstevel 
406203831d35Sstevel 	gdcd = drmach_gdcd_new();
406303831d35Sstevel 	if (gdcd == NULL) {
406403831d35Sstevel 		cmn_err(CE_WARN, "failed to read GDCD info for %s\n",
406503831d35Sstevel 		    bp->cm.name);
406603831d35Sstevel 		return (DRMACH_INTERNAL_ERROR());
406703831d35Sstevel 	}
406803831d35Sstevel 
406903831d35Sstevel 	/*
407003831d35Sstevel 	 * Read CPU SRAM DR buffer offset from GDCD.
407103831d35Sstevel 	 */
407203831d35Sstevel 	exp = DRMACH_BNUM2EXP(bp->bnum);
407303831d35Sstevel 	slot = DRMACH_BNUM2SLOT(bp->bnum);
407403831d35Sstevel 	bp->stardrb_offset =
407503831d35Sstevel 	    gdcd->dcd_slot[exp][slot].l1ss_cpu_drblock_xwd_offset << 3;
407603831d35Sstevel 	DRMACH_PR("%s: stardrb_offset=0x%lx\n", bp->cm.name,
407703831d35Sstevel 	    bp->stardrb_offset);
407803831d35Sstevel 
407903831d35Sstevel 	/*
408003831d35Sstevel 	 * Read board LPA setting from GDCD.
408103831d35Sstevel 	 */
408203831d35Sstevel 	bp->flags &= ~DRMACH_NULL_PROC_LPA;
408303831d35Sstevel 	if (gdcd->dcd_slot[exp][slot].l1ss_flags &
408403831d35Sstevel 	    L1SSFLG_THIS_L1_NULL_PROC_LPA) {
408503831d35Sstevel 		bp->flags |= DRMACH_NULL_PROC_LPA;
408603831d35Sstevel 		DRMACH_PR("%s: NULL proc LPA\n", bp->cm.name);
408703831d35Sstevel 	}
408803831d35Sstevel 
408903831d35Sstevel 	/*
409003831d35Sstevel 	 * XXX Until the Solaris large pages support heterogeneous cpu
409103831d35Sstevel 	 * domains, DR needs to prevent the addition of non-Panther cpus
409203831d35Sstevel 	 * to an all-Panther domain with large pages enabled.
409303831d35Sstevel 	 */
409403831d35Sstevel 	panther_pages_enabled = (page_num_pagesizes() > DEFAULT_MMU_PAGE_SIZES);
409503831d35Sstevel 	if (drmach_board_non_panther_cpus(gdcd, exp, slot) > 0 &&
409603831d35Sstevel 	    panther_pages_enabled && drmach_large_page_restriction) {
409703831d35Sstevel 		cmn_err(CE_WARN, "Domain shutdown is required to add a non-"
409803831d35Sstevel 		    "UltraSPARC-IV+ board into an all UltraSPARC-IV+ domain");
409903831d35Sstevel 		err = drerr_new(0, ESTC_SUPPORT, NULL);
410003831d35Sstevel 	}
410103831d35Sstevel 
410203831d35Sstevel 	if (err == NULL) {
410303831d35Sstevel 		/* do saf configurator stuff */
410403831d35Sstevel 		DRMACH_PR("calling sc_probe_board for bnum=%d\n", bp->bnum);
410503831d35Sstevel 		scc = sc_probe_board(bp->bnum);
410603831d35Sstevel 		if (scc == NULL)
410703831d35Sstevel 			err = drerr_new(0, ESTC_PROBE, bp->cm.name);
410803831d35Sstevel 	}
410903831d35Sstevel 
411003831d35Sstevel 	if (err) {
411103831d35Sstevel 		/* flush CDC srams */
411203831d35Sstevel 		if (axq_cdc_flush_all() != DDI_SUCCESS) {
411303831d35Sstevel 			goto out;
411403831d35Sstevel 		}
411503831d35Sstevel 
411603831d35Sstevel 		/*
411703831d35Sstevel 		 * Build the casm info portion of the UNCLAIM message.
411803831d35Sstevel 		 */
411903831d35Sstevel 		obufp = kmem_zalloc(sizeof (dr_mbox_msg_t), KM_SLEEP);
412003831d35Sstevel 		mutex_enter(&drmach_slice_table_lock);
412103831d35Sstevel 		drmach_msg_memslice_init(obufp->msgdata.dm_ur.mem_slice);
412203831d35Sstevel 		drmach_msg_memregs_init(obufp->msgdata.dm_ur.mem_regs);
412303831d35Sstevel 		mutex_exit(&drmach_slice_table_lock);
412403831d35Sstevel 		(void) drmach_mbox_trans(DRMSG_UNCLAIM, bp->bnum,
412503831d35Sstevel 		    (caddr_t)obufp, sizeof (dr_mbox_msg_t),
412603831d35Sstevel 		    (caddr_t)NULL, 0);
412703831d35Sstevel 
412803831d35Sstevel 		kmem_free(obufp, sizeof (dr_mbox_msg_t));
412903831d35Sstevel 
413003831d35Sstevel 		/*
413103831d35Sstevel 		 * we clear the connected flag just in case it would have
413203831d35Sstevel 		 * been set by a concurrent drmach_board_status() thread
413303831d35Sstevel 		 * before the UNCLAIM completed.
413403831d35Sstevel 		 */
413503831d35Sstevel 		bp->connected = 0;
413603831d35Sstevel 		goto out;
413703831d35Sstevel 	}
413803831d35Sstevel 
413903831d35Sstevel 	/*
414003831d35Sstevel 	 * Now that the board has been successfully attached, obtain
414103831d35Sstevel 	 * platform-specific DIMM serial id information for the board.
414203831d35Sstevel 	 */
414303831d35Sstevel 	if ((DRMACH_BNUM2SLOT(bp->bnum) == 0) &&
414403831d35Sstevel 	    plat_ecc_capability_sc_get(PLAT_ECC_DIMM_SID_MESSAGE)) {
414503831d35Sstevel 		(void) plat_request_mem_sids(DRMACH_BNUM2EXP(bp->bnum));
414603831d35Sstevel 	}
414703831d35Sstevel 
414803831d35Sstevel out:
414903831d35Sstevel 	if (gdcd != NULL)
415003831d35Sstevel 		drmach_gdcd_dispose(gdcd);
415103831d35Sstevel 
415203831d35Sstevel 	return (err);
415303831d35Sstevel }
415403831d35Sstevel 
415503831d35Sstevel static void
drmach_slice_table_update(drmach_board_t * bp,int invalidate)415603831d35Sstevel drmach_slice_table_update(drmach_board_t *bp, int invalidate)
415703831d35Sstevel {
415803831d35Sstevel 	static char		*axq_name = "address-extender-queue";
415903831d35Sstevel 	static dev_info_t	*axq_dip = NULL;
416003831d35Sstevel 	static int		 axq_exp = -1;
416103831d35Sstevel 	static int		 axq_slot;
416203831d35Sstevel 	int			 e, s, slice;
416303831d35Sstevel 
416403831d35Sstevel 	ASSERT(MUTEX_HELD(&drmach_slice_table_lock));
416503831d35Sstevel 
416603831d35Sstevel 	e = DRMACH_BNUM2EXP(bp->bnum);
416703831d35Sstevel 	if (invalidate) {
416803831d35Sstevel 		ASSERT(DRMACH_BNUM2SLOT(bp->bnum) == 0);
416903831d35Sstevel 
417003831d35Sstevel 		/* invalidate cached casm value */
417103831d35Sstevel 		drmach_slice_table[e] = 0;
417203831d35Sstevel 
417303831d35Sstevel 		/* invalidate cached axq info if for same exp */
417403831d35Sstevel 		if (e == axq_exp && axq_dip) {
417503831d35Sstevel 			ndi_rele_devi(axq_dip);
417603831d35Sstevel 			axq_dip = NULL;
417703831d35Sstevel 		}
417803831d35Sstevel 	}
417903831d35Sstevel 
418003831d35Sstevel 	if (axq_dip == NULL || !i_ddi_devi_attached(axq_dip)) {
418103831d35Sstevel 		int i, portid;
418203831d35Sstevel 
418303831d35Sstevel 		/* search for an attached slot0 axq instance */
418403831d35Sstevel 		for (i = 0; i < AXQ_MAX_EXP * AXQ_MAX_SLOT_PER_EXP; i++) {
418503831d35Sstevel 			if (axq_dip)
418603831d35Sstevel 				ndi_rele_devi(axq_dip);
418703831d35Sstevel 			axq_dip = ddi_find_devinfo(axq_name, i, 0);
418803831d35Sstevel 			if (axq_dip && DDI_CF2(axq_dip)) {
418903831d35Sstevel 				portid = ddi_getprop(DDI_DEV_T_ANY, axq_dip,
419003831d35Sstevel 				    DDI_PROP_DONTPASS, "portid", -1);
419103831d35Sstevel 				if (portid == -1) {
419203831d35Sstevel 					DRMACH_PR("cant get portid of axq "
419303831d35Sstevel 					    "instance %d\n", i);
419403831d35Sstevel 					continue;
419503831d35Sstevel 				}
419603831d35Sstevel 
419703831d35Sstevel 				axq_exp = (portid >> 5) & 0x1f;
419803831d35Sstevel 				axq_slot = portid & 1;
419903831d35Sstevel 
420003831d35Sstevel 				if (invalidate && axq_exp == e)
420103831d35Sstevel 					continue;
420203831d35Sstevel 
420303831d35Sstevel 				if (axq_slot == 0)
420403831d35Sstevel 					break;	/* found */
420503831d35Sstevel 			}
420603831d35Sstevel 		}
420703831d35Sstevel 
420803831d35Sstevel 		if (i == AXQ_MAX_EXP * AXQ_MAX_SLOT_PER_EXP) {
420903831d35Sstevel 			if (axq_dip) {
421003831d35Sstevel 				ndi_rele_devi(axq_dip);
421103831d35Sstevel 				axq_dip = NULL;
421203831d35Sstevel 			}
421303831d35Sstevel 			DRMACH_PR("drmach_slice_table_update: failed to "
421403831d35Sstevel 			    "update axq dip\n");
421503831d35Sstevel 			return;
421603831d35Sstevel 		}
421703831d35Sstevel 
421803831d35Sstevel 	}
421903831d35Sstevel 
422003831d35Sstevel 	ASSERT(axq_dip);
422103831d35Sstevel 	ASSERT(axq_slot == 0);
422203831d35Sstevel 
422303831d35Sstevel 	if (invalidate)
422403831d35Sstevel 		return;
422503831d35Sstevel 
422603831d35Sstevel 	s = DRMACH_BNUM2SLOT(bp->bnum);
4227d3d50737SRafael Vanoni 	DRMACH_PR("using AXQ casm %d.%d for slot%d.%d\n", axq_exp, axq_slot,
4228d3d50737SRafael Vanoni 	    e, s);
422903831d35Sstevel 
423003831d35Sstevel 	/* invalidate entry */
423103831d35Sstevel 	drmach_slice_table[e] &= ~0x20;
423203831d35Sstevel 
423303831d35Sstevel 	/*
423403831d35Sstevel 	 * find a slice that routes to expander e. If no match
423503831d35Sstevel 	 * is found, drmach_slice_table[e] will remain invalid.
423603831d35Sstevel 	 *
423703831d35Sstevel 	 * The CASM is a routing table indexed by slice number.
423803831d35Sstevel 	 * Each element in the table contains permission bits,
423903831d35Sstevel 	 * a destination expander number and a valid bit. The
424003831d35Sstevel 	 * valid bit must true for the element to be meaningful.
424103831d35Sstevel 	 *
424203831d35Sstevel 	 * CASM entry structure
424303831d35Sstevel 	 *   Bits 15..6 ignored
424403831d35Sstevel 	 *   Bit  5	valid
424503831d35Sstevel 	 *   Bits 0..4	expander number
424603831d35Sstevel 	 *
424703831d35Sstevel 	 * NOTE: the for loop is really enumerating the range of slices,
424803831d35Sstevel 	 * which is ALWAYS equal to the range of expanders. Hence,
424903831d35Sstevel 	 * AXQ_MAX_EXP is okay to use in this loop.
425003831d35Sstevel 	 */
425103831d35Sstevel 	for (slice = 0; slice < AXQ_MAX_EXP; slice++) {
425203831d35Sstevel 		uint32_t casm = axq_casm_read(axq_exp, axq_slot, slice);
425303831d35Sstevel 
425403831d35Sstevel 		if ((casm & 0x20) && (casm & 0x1f) == e)
425503831d35Sstevel 			drmach_slice_table[e] = 0x20 | slice;
425603831d35Sstevel 	}
425703831d35Sstevel }
425803831d35Sstevel 
425903831d35Sstevel /*
426003831d35Sstevel  * Get base and bound PAs for slot 1 board lpa programming
426103831d35Sstevel  * If a cpu/mem board is present in the same expander, use slice
426203831d35Sstevel  * information corresponding to the CASM.  Otherwise, set base and
426303831d35Sstevel  * bound PAs to 0.
426403831d35Sstevel  */
426503831d35Sstevel static void
drmach_lpa_bb_get(drmach_board_t * s1bp,uint64_t * basep,uint64_t * boundp)426603831d35Sstevel drmach_lpa_bb_get(drmach_board_t *s1bp, uint64_t *basep, uint64_t *boundp)
426703831d35Sstevel {
426803831d35Sstevel 	drmachid_t s0id;
426903831d35Sstevel 
427003831d35Sstevel 	ASSERT(mutex_owned(&drmach_slice_table_lock));
427103831d35Sstevel 	ASSERT(DRMACH_BNUM2SLOT(s1bp->bnum) == 1);
427203831d35Sstevel 
427303831d35Sstevel 	*basep = *boundp = 0;
427403831d35Sstevel 	if (drmach_array_get(drmach_boards, s1bp->bnum - 1, &s0id) == 0 &&
427503831d35Sstevel 	    s0id != 0) {
427603831d35Sstevel 
427703831d35Sstevel 		uint32_t slice;
4278d3d50737SRafael Vanoni 		if ((slice = drmach_slice_table[DRMACH_BNUM2EXP(s1bp->bnum)])
427903831d35Sstevel 		    & 0x20) {
428003831d35Sstevel 			*basep = DRMACH_SLICE_TO_PA(slice & DRMACH_SLICE_MASK);
428103831d35Sstevel 			*boundp = *basep + DRMACH_MEM_SLICE_SIZE;
428203831d35Sstevel 		}
428303831d35Sstevel 	}
428403831d35Sstevel }
428503831d35Sstevel 
428603831d35Sstevel 
428703831d35Sstevel /*
428803831d35Sstevel  * Reprogram slot 1 lpa's as required.
428903831d35Sstevel  * The purpose of this routine is maintain the LPA settings of the devices
429003831d35Sstevel  * in slot 1. To date we know Schizo and Cheetah are the only devices that
429103831d35Sstevel  * require this attention. The LPA setting must match the slice field in the
429203831d35Sstevel  * CASM element for the local expander. This field is guaranteed to be
429303831d35Sstevel  * programmed in accordance with the cacheable address space on the slot 0
429403831d35Sstevel  * board of the local expander. If no memory is present on the slot 0 board,
429503831d35Sstevel  * there is no cacheable address space and, hence, the CASM slice field will
429603831d35Sstevel  * be zero or its valid bit will be false (or both).
429703831d35Sstevel  */
429803831d35Sstevel 
429903831d35Sstevel static void
drmach_slot1_lpa_set(drmach_board_t * bp)430003831d35Sstevel drmach_slot1_lpa_set(drmach_board_t *bp)
430103831d35Sstevel {
430203831d35Sstevel 	drmachid_t	id;
430303831d35Sstevel 	drmach_board_t	*s1bp = NULL;
430403831d35Sstevel 	int		rv, idx, is_maxcat = 1;
430503831d35Sstevel 	uint64_t	last_scsr_pa = 0;
430603831d35Sstevel 	uint64_t	new_basepa, new_boundpa;
430703831d35Sstevel 
430803831d35Sstevel 	if (DRMACH_BNUM2SLOT(bp->bnum)) {
430903831d35Sstevel 		s1bp = bp;
431003831d35Sstevel 		if (s1bp->devices == NULL) {
431103831d35Sstevel 			DRMACH_PR("drmach...lpa_set: slot1=%d not present",
431203831d35Sstevel 			    bp->bnum);
431303831d35Sstevel 			return;
431403831d35Sstevel 		}
431503831d35Sstevel 	} else {
431603831d35Sstevel 		rv = drmach_array_get(drmach_boards, bp->bnum + 1, &id);
431703831d35Sstevel 		/* nothing to do when board is not found or has no devices */
431803831d35Sstevel 		s1bp = id;
431903831d35Sstevel 		if (rv == -1 || s1bp == NULL || s1bp->devices == NULL) {
432003831d35Sstevel 			DRMACH_PR("drmach...lpa_set: slot1=%d not present",
432103831d35Sstevel 			    bp->bnum + 1);
432203831d35Sstevel 			return;
432303831d35Sstevel 		}
432403831d35Sstevel 		ASSERT(DRMACH_IS_BOARD_ID(id));
432503831d35Sstevel 	}
432603831d35Sstevel 	mutex_enter(&drmach_slice_table_lock);
432703831d35Sstevel 	drmach_lpa_bb_get(s1bp, &new_basepa, &new_boundpa);
432803831d35Sstevel 	DRMACH_PR("drmach_...lpa_set: bnum=%d base=0x%lx bound=0x%lx\n",
432903831d35Sstevel 	    s1bp->bnum, new_basepa, new_boundpa);
433003831d35Sstevel 
433103831d35Sstevel 	rv = drmach_array_first(s1bp->devices, &idx, &id);
433203831d35Sstevel 	while (rv == 0) {
433303831d35Sstevel 		if (DRMACH_IS_IO_ID(id)) {
433403831d35Sstevel 			drmach_io_t *io = id;
433503831d35Sstevel 
433603831d35Sstevel 			is_maxcat = 0;
433703831d35Sstevel 
433803831d35Sstevel 			/*
433903831d35Sstevel 			 * Skip all non-Schizo IO devices (only IO nodes
434003831d35Sstevel 			 * that are Schizo devices have non-zero scsr_pa).
434103831d35Sstevel 			 * Filter out "other" leaf to avoid writing to the
434203831d35Sstevel 			 * same Schizo Control/Status Register twice.
434303831d35Sstevel 			 */
434403831d35Sstevel 			if (io->scsr_pa && io->scsr_pa != last_scsr_pa) {
434503831d35Sstevel 				uint64_t scsr;
434603831d35Sstevel 
434703831d35Sstevel 				scsr  = lddphysio(io->scsr_pa);
434803831d35Sstevel 				DRMACH_PR("drmach...lpa_set: old scsr=0x%lx\n",
434903831d35Sstevel 				    scsr);
435003831d35Sstevel 				scsr &= ~(DRMACH_LPA_BASE_MASK |
435103831d35Sstevel 				    DRMACH_LPA_BND_MASK);
435203831d35Sstevel 				scsr |= DRMACH_PA_TO_LPA_BASE(new_basepa);
435303831d35Sstevel 				scsr |= DRMACH_PA_TO_LPA_BND(new_boundpa);
435403831d35Sstevel 
435503831d35Sstevel 				stdphysio(io->scsr_pa, scsr);
435603831d35Sstevel 				DRMACH_PR("drmach...lpa_set: new scsr=0x%lx\n",
435703831d35Sstevel 				    scsr);
435803831d35Sstevel 
435903831d35Sstevel 				last_scsr_pa = io->scsr_pa;
436003831d35Sstevel 			}
436103831d35Sstevel 		}
436203831d35Sstevel 		rv = drmach_array_next(s1bp->devices, &idx, &id);
436303831d35Sstevel 	}
436403831d35Sstevel 
436503831d35Sstevel 	if (is_maxcat && DRMACH_L1_SET_LPA(s1bp) && drmach_reprogram_lpa) {
436603831d35Sstevel 		extern xcfunc_t	drmach_set_lpa;
436703831d35Sstevel 
436803831d35Sstevel 		DRMACH_PR("reprogramming maxcat lpa's");
436903831d35Sstevel 
437003831d35Sstevel 		mutex_enter(&cpu_lock);
437103831d35Sstevel 		rv = drmach_array_first(s1bp->devices, &idx, &id);
437203831d35Sstevel 		while (rv == 0 && id != NULL) {
437303831d35Sstevel 			if (DRMACH_IS_CPU_ID(id)) {
437403831d35Sstevel 				int ntries;
437503831d35Sstevel 				processorid_t cpuid;
437603831d35Sstevel 
437703831d35Sstevel 				cpuid = ((drmach_cpu_t *)id)->cpuid;
437803831d35Sstevel 
437903831d35Sstevel 				/*
438003831d35Sstevel 				 * Check for unconfigured or powered-off
438103831d35Sstevel 				 * MCPUs.  If CPU_READY flag is clear, the
438203831d35Sstevel 				 * MCPU cannot be xcalled.
438303831d35Sstevel 				 */
438403831d35Sstevel 				if ((cpu[cpuid] == NULL) ||
438503831d35Sstevel 				    (cpu[cpuid]->cpu_flags &
438603831d35Sstevel 				    CPU_READY) == 0) {
438703831d35Sstevel 
438803831d35Sstevel 					rv = drmach_array_next(s1bp->devices,
438903831d35Sstevel 					    &idx, &id);
439003831d35Sstevel 					continue;
439103831d35Sstevel 				}
439203831d35Sstevel 
439303831d35Sstevel 				/*
439403831d35Sstevel 				 * XXX CHEETAH SUPPORT
439503831d35Sstevel 				 * for cheetah, we need to clear iocage
439603831d35Sstevel 				 * memory since it will be used for e$ flush
439703831d35Sstevel 				 * in drmach_set_lpa.
439803831d35Sstevel 				 */
439903831d35Sstevel 				if (drmach_is_cheetah) {
440003831d35Sstevel 					mutex_enter(&drmach_iocage_lock);
440103831d35Sstevel 					while (drmach_iocage_is_busy)
440203831d35Sstevel 						cv_wait(&drmach_iocage_cv,
440303831d35Sstevel 						    &drmach_iocage_lock);
440403831d35Sstevel 					drmach_iocage_is_busy = 1;
4405d3d50737SRafael Vanoni 					drmach_iocage_mem_scrub(ecache_size *
4406d3d50737SRafael Vanoni 					    2);
440703831d35Sstevel 					mutex_exit(&drmach_iocage_lock);
440803831d35Sstevel 				}
440903831d35Sstevel 
441003831d35Sstevel 				/*
441103831d35Sstevel 				 * drmach_slice_table[*]
441203831d35Sstevel 				 *	bit 5	valid
441303831d35Sstevel 				 *	bit 0:4	slice number
441403831d35Sstevel 				 *
441503831d35Sstevel 				 * drmach_xt_mb[*] format for drmach_set_lpa
441603831d35Sstevel 				 *	bit 7	valid
441703831d35Sstevel 				 *	bit 6	set null LPA
441803831d35Sstevel 				 *			(overrides bits 0:4)
441903831d35Sstevel 				 *	bit 0:4	slice number
442003831d35Sstevel 				 *
442103831d35Sstevel 				 * drmach_set_lpa derives processor CBASE and
442203831d35Sstevel 				 * CBND from bits 6 and 0:4 of drmach_xt_mb.
442303831d35Sstevel 				 * If bit 6 is set, then CBASE = CBND = 0.
442403831d35Sstevel 				 * Otherwise, CBASE = slice number;
442503831d35Sstevel 				 * CBND = slice number + 1.
442603831d35Sstevel 				 * No action is taken if bit 7 is zero.
442703831d35Sstevel 				 */
442803831d35Sstevel 
442903831d35Sstevel 				mutex_enter(&drmach_xt_mb_lock);
443003831d35Sstevel 				bzero((void *)drmach_xt_mb,
443103831d35Sstevel 				    drmach_xt_mb_size);
443203831d35Sstevel 
443303831d35Sstevel 				if (new_basepa == 0 && new_boundpa == 0)
443403831d35Sstevel 					drmach_xt_mb[cpuid] = 0x80 | 0x40;
443503831d35Sstevel 				else
443603831d35Sstevel 					drmach_xt_mb[cpuid] = 0x80 |
443703831d35Sstevel 					    DRMACH_PA_TO_SLICE(new_basepa);
443803831d35Sstevel 
443903831d35Sstevel 				drmach_xt_ready = 0;
444003831d35Sstevel 
444103831d35Sstevel 				xt_one(cpuid, drmach_set_lpa, NULL, NULL);
444203831d35Sstevel 
444303831d35Sstevel 				ntries = drmach_cpu_ntries;
444403831d35Sstevel 				while (!drmach_xt_ready && ntries) {
444503831d35Sstevel 					DELAY(drmach_cpu_delay);
444603831d35Sstevel 					ntries--;
444703831d35Sstevel 				}
444803831d35Sstevel 				mutex_exit(&drmach_xt_mb_lock);
444903831d35Sstevel 				drmach_xt_ready = 0;
445003831d35Sstevel 
445103831d35Sstevel 				/*
445203831d35Sstevel 				 * XXX CHEETAH SUPPORT
445303831d35Sstevel 				 * for cheetah, we need to clear iocage
445403831d35Sstevel 				 * memory since it was used for e$ flush
445503831d35Sstevel 				 * in performed drmach_set_lpa.
445603831d35Sstevel 				 */
445703831d35Sstevel 				if (drmach_is_cheetah) {
445803831d35Sstevel 					mutex_enter(&drmach_iocage_lock);
4459d3d50737SRafael Vanoni 					drmach_iocage_mem_scrub(ecache_size *
4460d3d50737SRafael Vanoni 					    2);
446103831d35Sstevel 					drmach_iocage_is_busy = 0;
446203831d35Sstevel 					cv_signal(&drmach_iocage_cv);
446303831d35Sstevel 					mutex_exit(&drmach_iocage_lock);
446403831d35Sstevel 				}
446503831d35Sstevel 			}
446603831d35Sstevel 			rv = drmach_array_next(s1bp->devices, &idx, &id);
446703831d35Sstevel 		}
446803831d35Sstevel 		mutex_exit(&cpu_lock);
446903831d35Sstevel 	}
447003831d35Sstevel 	mutex_exit(&drmach_slice_table_lock);
447103831d35Sstevel }
447203831d35Sstevel 
447303831d35Sstevel /*
447403831d35Sstevel  * Return the number of connected Panther boards in the domain.
447503831d35Sstevel  */
447603831d35Sstevel static int
drmach_panther_boards(void)447703831d35Sstevel drmach_panther_boards(void)
447803831d35Sstevel {
447903831d35Sstevel 	int		rv;
448003831d35Sstevel 	int		b_idx;
448103831d35Sstevel 	drmachid_t	b_id;
448203831d35Sstevel 	drmach_board_t	*bp;
448303831d35Sstevel 	int		npanther = 0;
448403831d35Sstevel 
448503831d35Sstevel 	rv = drmach_array_first(drmach_boards, &b_idx, &b_id);
448603831d35Sstevel 	while (rv == 0) {
448703831d35Sstevel 		ASSERT(DRMACH_IS_BOARD_ID(b_id));
448803831d35Sstevel 		bp = b_id;
448903831d35Sstevel 
449003831d35Sstevel 		if (IS_PANTHER(bp->cpu_impl))
449103831d35Sstevel 			npanther++;
449203831d35Sstevel 
449303831d35Sstevel 		rv = drmach_array_next(drmach_boards, &b_idx, &b_id);
449403831d35Sstevel 	}
449503831d35Sstevel 
449603831d35Sstevel 	return (npanther);
449703831d35Sstevel }
449803831d35Sstevel 
449903831d35Sstevel /*ARGSUSED*/
450003831d35Sstevel sbd_error_t *
drmach_board_disconnect(drmachid_t id,drmach_opts_t * opts)450103831d35Sstevel drmach_board_disconnect(drmachid_t id, drmach_opts_t *opts)
450203831d35Sstevel {
450303831d35Sstevel 	drmach_board_t	*bp;
450403831d35Sstevel 	dr_mbox_msg_t	*obufp;
450503831d35Sstevel 	sbd_error_t	*err = NULL;
450603831d35Sstevel 
450703831d35Sstevel 	sc_gptwocfg_cookie_t	scc;
450803831d35Sstevel 
450903831d35Sstevel 	if (!DRMACH_IS_BOARD_ID(id))
451003831d35Sstevel 		return (drerr_new(0, ESTC_INAPPROP, NULL));
451103831d35Sstevel 	bp = id;
451203831d35Sstevel 
451303831d35Sstevel 	/*
451403831d35Sstevel 	 * Build the casm info portion of the UNCLAIM message.
451503831d35Sstevel 	 * This must be done prior to calling for saf configurator
451603831d35Sstevel 	 * deprobe, to ensure that the associated axq instance
451703831d35Sstevel 	 * is not detached.
451803831d35Sstevel 	 */
451903831d35Sstevel 	obufp = kmem_zalloc(sizeof (dr_mbox_msg_t), KM_SLEEP);
452003831d35Sstevel 	mutex_enter(&drmach_slice_table_lock);
452103831d35Sstevel 	drmach_msg_memslice_init(obufp->msgdata.dm_ur.mem_slice);
452203831d35Sstevel 
452303831d35Sstevel 	/*
452403831d35Sstevel 	 * If disconnecting slot 0 board, update the casm slice table
452503831d35Sstevel 	 * info now, for use by drmach_slot1_lpa_set()
452603831d35Sstevel 	 */
452703831d35Sstevel 	if (DRMACH_BNUM2SLOT(bp->bnum) == 0)
452803831d35Sstevel 		drmach_slice_table_update(bp, 1);
452903831d35Sstevel 
453003831d35Sstevel 	drmach_msg_memregs_init(obufp->msgdata.dm_ur.mem_regs);
453103831d35Sstevel 	mutex_exit(&drmach_slice_table_lock);
453203831d35Sstevel 
453303831d35Sstevel 	/*
453403831d35Sstevel 	 * Update LPA information for slot1 board
453503831d35Sstevel 	 */
453603831d35Sstevel 	drmach_slot1_lpa_set(bp);
453703831d35Sstevel 
453803831d35Sstevel 	/* disable and flush CDC */
453903831d35Sstevel 	if (axq_cdc_disable_flush_all() != DDI_SUCCESS) {
454003831d35Sstevel 		axq_cdc_enable_all();	/* paranoia */
454103831d35Sstevel 		err = DRMACH_INTERNAL_ERROR();
454203831d35Sstevel 	}
454303831d35Sstevel 
454403831d35Sstevel 	/*
454503831d35Sstevel 	 * call saf configurator for deprobe
454603831d35Sstevel 	 * It's done now before sending an UNCLAIM message because
454703831d35Sstevel 	 * IKP will probe boards it doesn't know about <present at boot>
454803831d35Sstevel 	 * prior to unprobing them.  If this happens after sending the
454903831d35Sstevel 	 * UNCLAIM, it will cause a dstop for domain transgression error.
455003831d35Sstevel 	 */
455103831d35Sstevel 
455203831d35Sstevel 	if (!err) {
455303831d35Sstevel 		scc = sc_unprobe_board(bp->bnum);
455403831d35Sstevel 		axq_cdc_enable_all();
455503831d35Sstevel 		if (scc != NULL) {
455603831d35Sstevel 			err = drerr_new(0, ESTC_DEPROBE, bp->cm.name);
455703831d35Sstevel 		}
455803831d35Sstevel 	}
455903831d35Sstevel 
456003831d35Sstevel 	/*
456103831d35Sstevel 	 * If disconnecting a board from a Panther domain, wait a fixed-
456203831d35Sstevel 	 * time delay for pending Safari transactions to complete on the
456303831d35Sstevel 	 * disconnecting board's processors.  The bus sync list read used
456403831d35Sstevel 	 * in drmach_shutdown_asm to synchronize with outstanding Safari
456503831d35Sstevel 	 * transactions assumes no read-bypass-write mode for all memory
456603831d35Sstevel 	 * controllers.  Since Panther supports read-bypass-write, a
456703831d35Sstevel 	 * delay is used that is slightly larger than the maximum Safari
456803831d35Sstevel 	 * timeout value in the Safari/Fireplane Config Reg.
456903831d35Sstevel 	 */
457003831d35Sstevel 	if (drmach_panther_boards() > 0 || drmach_unclaim_delay_all) {
4571d3d50737SRafael Vanoni 		clock_t	stime = ddi_get_lbolt();
457203831d35Sstevel 
457303831d35Sstevel 		delay(drv_usectohz(drmach_unclaim_usec_delay));
457403831d35Sstevel 
4575d3d50737SRafael Vanoni 		stime = ddi_get_lbolt() - stime;
457603831d35Sstevel 		DRMACH_PR("delayed %ld ticks (%ld secs) before disconnecting "
457703831d35Sstevel 		    "board %s from domain\n", stime, stime / hz, bp->cm.name);
457803831d35Sstevel 	}
457903831d35Sstevel 
458003831d35Sstevel 	if (!err) {
458103831d35Sstevel 		obufp->msgdata.dm_ur.mem_clear = 0;
458203831d35Sstevel 
458303831d35Sstevel 		err = drmach_mbox_trans(DRMSG_UNCLAIM, bp->bnum, (caddr_t)obufp,
458403831d35Sstevel 		    sizeof (dr_mbox_msg_t), (caddr_t)NULL, 0);
458503831d35Sstevel 
458603831d35Sstevel 		if (err) {
458703831d35Sstevel 			/*
458803831d35Sstevel 			 * if mailbox timeout or unrecoverable error from SC,
458903831d35Sstevel 			 * board cannot be touched.  Mark the status as
459003831d35Sstevel 			 * unusable.
459103831d35Sstevel 			 */
459203831d35Sstevel 			if ((err->e_code == ESTC_SMS_ERR_UNRECOVERABLE) ||
459303831d35Sstevel 			    (err->e_code == ESTC_MBXRPLY))
459403831d35Sstevel 				bp->cond = SBD_COND_UNUSABLE;
459503831d35Sstevel 			else {
459603831d35Sstevel 				DRMACH_PR("UNCLAIM failed for bnum=%d\n",
459703831d35Sstevel 				    bp->bnum);
459803831d35Sstevel 				DRMACH_PR("calling sc_probe_board: bnum=%d\n",
459903831d35Sstevel 				    bp->bnum);
460003831d35Sstevel 				scc = sc_probe_board(bp->bnum);
460103831d35Sstevel 				if (scc == NULL) {
460203831d35Sstevel 					cmn_err(CE_WARN,
460303831d35Sstevel 					"sc_probe_board failed for bnum=%d",
460403831d35Sstevel 					    bp->bnum);
460503831d35Sstevel 				} else {
460603831d35Sstevel 					if (DRMACH_BNUM2SLOT(bp->bnum) == 0) {
460703831d35Sstevel 						mutex_enter(
460803831d35Sstevel 						    &drmach_slice_table_lock);
460903831d35Sstevel 						drmach_slice_table_update(bp,
461003831d35Sstevel 						    0);
461103831d35Sstevel 						mutex_exit(
461203831d35Sstevel 						    &drmach_slice_table_lock);
461303831d35Sstevel 					}
461403831d35Sstevel 					drmach_slot1_lpa_set(bp);
461503831d35Sstevel 				}
461603831d35Sstevel 			}
461703831d35Sstevel 		} else {
461803831d35Sstevel 			bp->connected = 0;
461903831d35Sstevel 			/*
462003831d35Sstevel 			 * Now that the board has been successfully detached,
462103831d35Sstevel 			 * discard platform-specific DIMM serial id information
462203831d35Sstevel 			 * for the board.
462303831d35Sstevel 			 */
462403831d35Sstevel 			if ((DRMACH_BNUM2SLOT(bp->bnum) == 0) &&
462503831d35Sstevel 			    plat_ecc_capability_sc_get(
462603831d35Sstevel 			    PLAT_ECC_DIMM_SID_MESSAGE)) {
462703831d35Sstevel 				(void) plat_discard_mem_sids(
462803831d35Sstevel 				    DRMACH_BNUM2EXP(bp->bnum));
462903831d35Sstevel 			}
463003831d35Sstevel 		}
463103831d35Sstevel 	}
463203831d35Sstevel 	kmem_free(obufp, sizeof (dr_mbox_msg_t));
463303831d35Sstevel 
463403831d35Sstevel 	return (err);
463503831d35Sstevel }
463603831d35Sstevel 
463703831d35Sstevel static int
drmach_get_portid(drmach_node_t * np)463803831d35Sstevel drmach_get_portid(drmach_node_t *np)
463903831d35Sstevel {
464003831d35Sstevel 	drmach_node_t	pp;
464103831d35Sstevel 	int		portid;
464203831d35Sstevel 	char		type[OBP_MAXPROPNAME];
464303831d35Sstevel 
464403831d35Sstevel 	if (np->n_getprop(np, "portid", &portid, sizeof (portid)) == 0)
464503831d35Sstevel 		return (portid);
464603831d35Sstevel 
464703831d35Sstevel 	/*
464803831d35Sstevel 	 * Get the device_type property to see if we should
464903831d35Sstevel 	 * continue processing this node.
465003831d35Sstevel 	 */
465103831d35Sstevel 	if (np->n_getprop(np, "device_type", &type, sizeof (type)) != 0)
465203831d35Sstevel 		return (-1);
465303831d35Sstevel 
465403831d35Sstevel 	/*
465503831d35Sstevel 	 * If the device is a CPU without a 'portid' property,
465603831d35Sstevel 	 * it is a CMP core. For such cases, the parent node
465703831d35Sstevel 	 * has the portid.
465803831d35Sstevel 	 */
465903831d35Sstevel 	if (strcmp(type, DRMACH_CPU_NAMEPROP) == 0) {
466003831d35Sstevel 		if (np->get_parent(np, &pp) != 0)
466103831d35Sstevel 			return (-1);
466203831d35Sstevel 
466303831d35Sstevel 		if (pp.n_getprop(&pp, "portid", &portid, sizeof (portid)) == 0)
466403831d35Sstevel 			return (portid);
466503831d35Sstevel 	}
466603831d35Sstevel 
466703831d35Sstevel 	return (-1);
466803831d35Sstevel }
466903831d35Sstevel 
467003831d35Sstevel /*
467103831d35Sstevel  * This is a helper function to determine if a given
467203831d35Sstevel  * node should be considered for a dr operation according
467303831d35Sstevel  * to predefined dr type nodes and the node's name.
467403831d35Sstevel  * Formal Parameter : The name of a device node.
467503831d35Sstevel  * Return Value: -1, name does not map to a valid dr type.
467603831d35Sstevel  *		 A value greater or equal to 0, name is a valid dr type.
467703831d35Sstevel  */
467803831d35Sstevel static int
drmach_name2type_idx(char * name)467903831d35Sstevel drmach_name2type_idx(char *name)
468003831d35Sstevel {
468103831d35Sstevel 	int 	index, ntypes;
468203831d35Sstevel 
468303831d35Sstevel 	if (name == NULL)
468403831d35Sstevel 		return (-1);
468503831d35Sstevel 
468603831d35Sstevel 	/*
468703831d35Sstevel 	 * Determine how many possible types are currently supported
468803831d35Sstevel 	 * for dr.
468903831d35Sstevel 	 */
469003831d35Sstevel 	ntypes = sizeof (drmach_name2type) / sizeof (drmach_name2type[0]);
469103831d35Sstevel 
469203831d35Sstevel 	/* Determine if the node's name correspond to a predefined type. */
469303831d35Sstevel 	for (index = 0; index < ntypes; index++) {
469403831d35Sstevel 		if (strcmp(drmach_name2type[index].name, name) == 0)
469503831d35Sstevel 			/* The node is an allowed type for dr. */
469603831d35Sstevel 			return (index);
469703831d35Sstevel 	}
469803831d35Sstevel 
469903831d35Sstevel 	/*
470003831d35Sstevel 	 * If the name of the node does not map to any of the
470103831d35Sstevel 	 * types in the array drmach_name2type then the node is not of
470203831d35Sstevel 	 * interest to dr.
470303831d35Sstevel 	 */
470403831d35Sstevel 	return (-1);
470503831d35Sstevel }
470603831d35Sstevel 
470703831d35Sstevel static int
drmach_board_find_devices_cb(drmach_node_walk_args_t * args)470803831d35Sstevel drmach_board_find_devices_cb(drmach_node_walk_args_t *args)
470903831d35Sstevel {
471003831d35Sstevel 	drmach_node_t			*node = args->node;
471103831d35Sstevel 	drmach_board_cb_data_t		*data = args->data;
471203831d35Sstevel 	drmach_board_t			*obj = data->obj;
471303831d35Sstevel 
471403831d35Sstevel 	int		rv, portid;
471503831d35Sstevel 	drmachid_t	id;
471603831d35Sstevel 	drmach_device_t	*device;
471703831d35Sstevel 	char	name[OBP_MAXDRVNAME];
471803831d35Sstevel 
471903831d35Sstevel 	portid = drmach_get_portid(node);
472003831d35Sstevel 	if (portid == -1) {
472103831d35Sstevel 		/*
472203831d35Sstevel 		 * if the node does not have a portid property, then
472303831d35Sstevel 		 * by that information alone it is known that drmach
472403831d35Sstevel 		 * is not interested in it.
472503831d35Sstevel 		 */
472603831d35Sstevel 		return (0);
472703831d35Sstevel 	}
472803831d35Sstevel 	rv = node->n_getprop(node, "name", name, OBP_MAXDRVNAME);
472903831d35Sstevel 
473003831d35Sstevel 	/* The node must have a name */
473103831d35Sstevel 	if (rv)
473203831d35Sstevel 		return (0);
473303831d35Sstevel 
473403831d35Sstevel 	/*
473503831d35Sstevel 	 * Ignore devices whose portid do not map to this board,
473603831d35Sstevel 	 * or that their name property is not mapped to a valid
473703831d35Sstevel 	 * dr device name.
473803831d35Sstevel 	 */
473903831d35Sstevel 	if ((drmach_portid2bnum(portid) != obj->bnum) ||
474003831d35Sstevel 	    (drmach_name2type_idx(name) < 0))
474103831d35Sstevel 		return (0);
474203831d35Sstevel 
474303831d35Sstevel 	/*
474403831d35Sstevel 	 * Create a device data structure from this node data.
474503831d35Sstevel 	 * The call may yield nothing if the node is not of interest
474603831d35Sstevel 	 * to drmach.
474703831d35Sstevel 	 */
474803831d35Sstevel 	data->err = drmach_device_new(node, obj, portid, &id);
474903831d35Sstevel 	if (data->err)
475003831d35Sstevel 		return (-1);
475103831d35Sstevel 	else if (!id) {
475203831d35Sstevel 		/*
475303831d35Sstevel 		 * drmach_device_new examined the node we passed in
475403831d35Sstevel 		 * and determined that it was either one not of
475503831d35Sstevel 		 * interest to drmach or the PIM dr layer.
475603831d35Sstevel 		 * So, it is skipped.
475703831d35Sstevel 		 */
475803831d35Sstevel 		return (0);
475903831d35Sstevel 	}
476003831d35Sstevel 
476103831d35Sstevel 	rv = drmach_array_set(obj->devices, data->ndevs++, id);
476203831d35Sstevel 	if (rv) {
476303831d35Sstevel 		data->err = DRMACH_INTERNAL_ERROR();
476403831d35Sstevel 		return (-1);
476503831d35Sstevel 	}
476603831d35Sstevel 
476703831d35Sstevel 	device = id;
476803831d35Sstevel 
476903831d35Sstevel #ifdef DEBUG
477003831d35Sstevel 	DRMACH_PR("%d %s %d %p\n", portid, device->type, device->unum, id);
477103831d35Sstevel 	if (DRMACH_IS_IO_ID(id))
477203831d35Sstevel 		DRMACH_PR("ndevs = %d dip/node = %p", data->ndevs, node->here);
477303831d35Sstevel #endif
477403831d35Sstevel 
477503831d35Sstevel 	data->err = (*data->found)(data->a, device->type, device->unum, id);
477603831d35Sstevel 	return (data->err == NULL ? 0 : -1);
477703831d35Sstevel }
477803831d35Sstevel 
477903831d35Sstevel sbd_error_t *
drmach_board_find_devices(drmachid_t id,void * a,sbd_error_t * (* found)(void * a,const char *,int,drmachid_t))478003831d35Sstevel drmach_board_find_devices(drmachid_t id, void *a,
478103831d35Sstevel 	sbd_error_t *(*found)(void *a, const char *, int, drmachid_t))
478203831d35Sstevel {
478303831d35Sstevel 	drmach_board_t		*bp = (drmach_board_t *)id;
478403831d35Sstevel 	sbd_error_t		*err;
478503831d35Sstevel 	int			 max_devices;
478603831d35Sstevel 	int			 rv;
478703831d35Sstevel 	drmach_board_cb_data_t	data;
478803831d35Sstevel 
478903831d35Sstevel 	if (!DRMACH_IS_BOARD_ID(id))
479003831d35Sstevel 		return (drerr_new(0, ESTC_INAPPROP, NULL));
479103831d35Sstevel 
479203831d35Sstevel 	max_devices  = plat_max_cpu_units_per_board();
479303831d35Sstevel 	max_devices += plat_max_mem_units_per_board();
479403831d35Sstevel 	max_devices += plat_max_io_units_per_board();
479503831d35Sstevel 
479603831d35Sstevel 	bp->devices = drmach_array_new(0, max_devices);
479703831d35Sstevel 
479803831d35Sstevel 	if (bp->tree == NULL)
479903831d35Sstevel 		bp->tree = drmach_node_new();
480003831d35Sstevel 
480103831d35Sstevel 	data.obj = bp;
480203831d35Sstevel 	data.ndevs = 0;
480303831d35Sstevel 	data.found = found;
480403831d35Sstevel 	data.a = a;
480503831d35Sstevel 	data.err = NULL;
480603831d35Sstevel 
480703831d35Sstevel 	mutex_enter(&drmach_slice_table_lock);
480803831d35Sstevel 	mutex_enter(&drmach_bus_sync_lock);
480903831d35Sstevel 
481003831d35Sstevel 	rv = drmach_node_walk(bp->tree, &data, drmach_board_find_devices_cb);
481103831d35Sstevel 
481203831d35Sstevel 	drmach_slice_table_update(bp, 0);
481303831d35Sstevel 	drmach_bus_sync_list_update();
481403831d35Sstevel 
481503831d35Sstevel 	mutex_exit(&drmach_bus_sync_lock);
481603831d35Sstevel 	mutex_exit(&drmach_slice_table_lock);
481703831d35Sstevel 
481803831d35Sstevel 	if (rv == 0) {
481903831d35Sstevel 		err = NULL;
482003831d35Sstevel 		drmach_slot1_lpa_set(bp);
482103831d35Sstevel 	} else {
482203831d35Sstevel 		drmach_array_dispose(bp->devices, drmach_device_dispose);
482303831d35Sstevel 		bp->devices = NULL;
482403831d35Sstevel 
482503831d35Sstevel 		if (data.err)
482603831d35Sstevel 			err = data.err;
482703831d35Sstevel 		else
482803831d35Sstevel 			err = DRMACH_INTERNAL_ERROR();
482903831d35Sstevel 	}
483003831d35Sstevel 
483103831d35Sstevel 	return (err);
483203831d35Sstevel }
483303831d35Sstevel 
483403831d35Sstevel int
drmach_board_lookup(int bnum,drmachid_t * id)483503831d35Sstevel drmach_board_lookup(int bnum, drmachid_t *id)
483603831d35Sstevel {
483703831d35Sstevel 	int	rv = 0;
483803831d35Sstevel 
483903831d35Sstevel 	if (!drmach_initialized && drmach_init() == -1) {
484003831d35Sstevel 		*id = 0;
484103831d35Sstevel 		return (-1);
484203831d35Sstevel 	}
484303831d35Sstevel 	rw_enter(&drmach_boards_rwlock, RW_WRITER);
484403831d35Sstevel 	if (drmach_array_get(drmach_boards, bnum, id)) {
484503831d35Sstevel 		*id = 0;
484603831d35Sstevel 		rv = -1;
484703831d35Sstevel 	} else {
484803831d35Sstevel 		caddr_t		obufp;
484903831d35Sstevel 		dr_showboard_t	shb;
485003831d35Sstevel 		sbd_error_t	*err = NULL;
485103831d35Sstevel 		drmach_board_t	*bp;
485203831d35Sstevel 
485303831d35Sstevel 		bp = *id;
485403831d35Sstevel 
485503831d35Sstevel 		if (bp)
485603831d35Sstevel 			rw_downgrade(&drmach_boards_rwlock);
485703831d35Sstevel 
485803831d35Sstevel 		obufp = kmem_zalloc(sizeof (dr_proto_hdr_t), KM_SLEEP);
485903831d35Sstevel 		err = drmach_mbox_trans(DRMSG_SHOWBOARD, bnum, obufp,
486003831d35Sstevel 		    sizeof (dr_proto_hdr_t), (caddr_t)&shb,
486103831d35Sstevel 		    sizeof (dr_showboard_t));
486203831d35Sstevel 		kmem_free(obufp, sizeof (dr_proto_hdr_t));
486303831d35Sstevel 
486403831d35Sstevel 		if (err) {
486503831d35Sstevel 			if (err->e_code == ESTC_UNAVAILABLE) {
486603831d35Sstevel 				*id = 0;
486703831d35Sstevel 				rv = -1;
486803831d35Sstevel 			}
486903831d35Sstevel 			sbd_err_clear(&err);
487003831d35Sstevel 		} else {
487103831d35Sstevel 			if (!bp)
487203831d35Sstevel 				bp = *id  = (drmachid_t)drmach_board_new(bnum);
487303831d35Sstevel 			bp->connected = (shb.bd_assigned && shb.bd_active);
487403831d35Sstevel 			bp->empty = shb.slot_empty;
487503831d35Sstevel 
487603831d35Sstevel 			switch (shb.test_status) {
487703831d35Sstevel 				case DR_TEST_STATUS_UNKNOWN:
487803831d35Sstevel 				case DR_TEST_STATUS_IPOST:
487903831d35Sstevel 				case DR_TEST_STATUS_ABORTED:
488003831d35Sstevel 					bp->cond = SBD_COND_UNKNOWN;
488103831d35Sstevel 					break;
488203831d35Sstevel 				case DR_TEST_STATUS_PASSED:
488303831d35Sstevel 					bp->cond = SBD_COND_OK;
488403831d35Sstevel 					break;
488503831d35Sstevel 				case DR_TEST_STATUS_FAILED:
488603831d35Sstevel 					bp->cond = SBD_COND_FAILED;
488703831d35Sstevel 					break;
488803831d35Sstevel 				default:
488903831d35Sstevel 					bp->cond = SBD_COND_UNKNOWN;
489003831d35Sstevel 				DRMACH_PR("Unknown test status=0x%x from SC\n",
489103831d35Sstevel 				    shb.test_status);
489203831d35Sstevel 					break;
489303831d35Sstevel 			}
489407d06da5SSurya Prakki 			(void) strncpy(bp->type, shb.board_type,
489507d06da5SSurya Prakki 			    sizeof (bp->type));
489603831d35Sstevel 			bp->assigned = shb.bd_assigned;
489703831d35Sstevel 			bp->powered = shb.power_on;
489803831d35Sstevel 		}
489903831d35Sstevel 	}
490003831d35Sstevel 	rw_exit(&drmach_boards_rwlock);
490103831d35Sstevel 	return (rv);
490203831d35Sstevel }
490303831d35Sstevel 
490403831d35Sstevel sbd_error_t *
drmach_board_name(int bnum,char * buf,int buflen)490503831d35Sstevel drmach_board_name(int bnum, char *buf, int buflen)
490603831d35Sstevel {
490707d06da5SSurya Prakki 	(void) snprintf(buf, buflen, "%s%d", DRMACH_BNUM2SLOT(bnum) ?
490803831d35Sstevel 	    "IO" : "SB", DRMACH_BNUM2EXP(bnum));
490903831d35Sstevel 
491003831d35Sstevel 	return (NULL);
491103831d35Sstevel }
491203831d35Sstevel 
491303831d35Sstevel sbd_error_t *
drmach_board_poweroff(drmachid_t id)491403831d35Sstevel drmach_board_poweroff(drmachid_t id)
491503831d35Sstevel {
491603831d35Sstevel 	drmach_board_t	*bp;
491703831d35Sstevel 	sbd_error_t	*err;
491803831d35Sstevel 	drmach_status_t	 stat;
491903831d35Sstevel 
492003831d35Sstevel 	if (!DRMACH_IS_BOARD_ID(id))
492103831d35Sstevel 		return (drerr_new(0, ESTC_INAPPROP, NULL));
492203831d35Sstevel 	bp = id;
492303831d35Sstevel 
492403831d35Sstevel 	err = drmach_board_status(id, &stat);
492503831d35Sstevel 	if (!err) {
492603831d35Sstevel 		if (stat.configured || stat.busy)
492703831d35Sstevel 			err = drerr_new(0, ESTC_CONFIGBUSY, bp->cm.name);
492803831d35Sstevel 		else {
492903831d35Sstevel 			caddr_t	obufp;
493003831d35Sstevel 
493103831d35Sstevel 			obufp = kmem_zalloc(sizeof (dr_proto_hdr_t), KM_SLEEP);
493203831d35Sstevel 			err = drmach_mbox_trans(DRMSG_POWEROFF, bp->bnum, obufp,
493303831d35Sstevel 			    sizeof (dr_proto_hdr_t), (caddr_t)NULL, 0);
493403831d35Sstevel 			kmem_free(obufp, sizeof (dr_proto_hdr_t));
493503831d35Sstevel 			if (!err)
493603831d35Sstevel 				bp->powered = 0;
493703831d35Sstevel 		}
493803831d35Sstevel 	}
493903831d35Sstevel 	return (err);
494003831d35Sstevel }
494103831d35Sstevel 
494203831d35Sstevel sbd_error_t *
drmach_board_poweron(drmachid_t id)494303831d35Sstevel drmach_board_poweron(drmachid_t id)
494403831d35Sstevel {
494503831d35Sstevel 	drmach_board_t	*bp;
494603831d35Sstevel 	caddr_t		obufp;
494703831d35Sstevel 	sbd_error_t	*err;
494803831d35Sstevel 
494903831d35Sstevel 	if (!DRMACH_IS_BOARD_ID(id))
495003831d35Sstevel 		return (drerr_new(0, ESTC_INAPPROP, NULL));
495103831d35Sstevel 	bp = id;
495203831d35Sstevel 
495303831d35Sstevel 	obufp = kmem_zalloc(sizeof (dr_proto_hdr_t), KM_SLEEP);
495403831d35Sstevel 	err = drmach_mbox_trans(DRMSG_POWERON, bp->bnum, obufp,
495503831d35Sstevel 	    sizeof (dr_proto_hdr_t), (caddr_t)NULL, 0);
495603831d35Sstevel 	if (!err)
495703831d35Sstevel 		bp->powered = 1;
495803831d35Sstevel 
495903831d35Sstevel 	kmem_free(obufp, sizeof (dr_proto_hdr_t));
496003831d35Sstevel 
496103831d35Sstevel 	return (err);
496203831d35Sstevel }
496303831d35Sstevel 
496403831d35Sstevel static sbd_error_t *
drmach_board_release(drmachid_t id)496503831d35Sstevel drmach_board_release(drmachid_t id)
496603831d35Sstevel {
496703831d35Sstevel 	if (!DRMACH_IS_BOARD_ID(id))
496803831d35Sstevel 		return (drerr_new(0, ESTC_INAPPROP, NULL));
496903831d35Sstevel 	return (NULL);
497003831d35Sstevel }
497103831d35Sstevel 
497203831d35Sstevel sbd_error_t *
drmach_board_test(drmachid_t id,drmach_opts_t * opts,int force)497303831d35Sstevel drmach_board_test(drmachid_t id, drmach_opts_t *opts, int force)
497403831d35Sstevel {
497503831d35Sstevel 	drmach_board_t		*bp;
497625cf1a30Sjl139090 	drmach_device_t		*dp[MAX_CORES_PER_CMP];
497703831d35Sstevel 	dr_mbox_msg_t		*obufp;
497803831d35Sstevel 	sbd_error_t		*err;
497903831d35Sstevel 	dr_testboard_reply_t	tbr;
498003831d35Sstevel 	int			cpylen;
498103831d35Sstevel 	char			*copts;
498203831d35Sstevel 	int			is_io;
498325cf1a30Sjl139090 	cpu_flag_t		oflags[MAX_CORES_PER_CMP];
498403831d35Sstevel 
498503831d35Sstevel 	if (!DRMACH_IS_BOARD_ID(id))
498603831d35Sstevel 		return (drerr_new(0, ESTC_INAPPROP, NULL));
498703831d35Sstevel 	bp = id;
498803831d35Sstevel 
498903831d35Sstevel 	/*
499003831d35Sstevel 	 * If the board is an I/O or MAXCAT board, setup I/O cage for
499103831d35Sstevel 	 * testing. Slot 1 indicates I/O or MAXCAT board.
499203831d35Sstevel 	 */
499303831d35Sstevel 
499403831d35Sstevel 	is_io = DRMACH_BNUM2SLOT(bp->bnum);
499503831d35Sstevel 
499603831d35Sstevel 	obufp = kmem_zalloc(sizeof (dr_mbox_msg_t), KM_SLEEP);
499703831d35Sstevel 
499803831d35Sstevel 	if (force)
499903831d35Sstevel 		obufp->msgdata.dm_tb.force = 1;
500003831d35Sstevel 
500103831d35Sstevel 	obufp->msgdata.dm_tb.immediate = 1;
500203831d35Sstevel 
500303831d35Sstevel 	if ((opts->size > 0) && ((copts = opts->copts) != NULL)) {
500403831d35Sstevel 		cpylen = (opts->size > DR_HPOPTLEN ? DR_HPOPTLEN : opts->size);
500503831d35Sstevel 		bcopy(copts, obufp->msgdata.dm_tb.hpost_opts, cpylen);
500603831d35Sstevel 	}
500703831d35Sstevel 
500803831d35Sstevel 	if (is_io) {
500903831d35Sstevel 		err = drmach_iocage_setup(&obufp->msgdata.dm_tb, dp, oflags);
501003831d35Sstevel 
501103831d35Sstevel 		if (err) {
501203831d35Sstevel 			kmem_free(obufp, sizeof (dr_mbox_msg_t));
501303831d35Sstevel 			return (err);
501403831d35Sstevel 		}
501503831d35Sstevel 	}
501603831d35Sstevel 
501703831d35Sstevel 	err = drmach_mbox_trans(DRMSG_TESTBOARD, bp->bnum, (caddr_t)obufp,
501803831d35Sstevel 	    sizeof (dr_mbox_msg_t), (caddr_t)&tbr, sizeof (tbr));
501903831d35Sstevel 
502003831d35Sstevel 	if (!err)
502103831d35Sstevel 		bp->cond = SBD_COND_OK;
502203831d35Sstevel 	else
502303831d35Sstevel 		bp->cond = SBD_COND_UNKNOWN;
502403831d35Sstevel 
502503831d35Sstevel 	if ((!err) && (tbr.test_status != DR_TEST_STATUS_PASSED)) {
502603831d35Sstevel 		/* examine test status */
502703831d35Sstevel 		switch (tbr.test_status) {
502803831d35Sstevel 			case DR_TEST_STATUS_IPOST:
502903831d35Sstevel 				bp->cond = SBD_COND_UNKNOWN;
5030d3d50737SRafael Vanoni 				err = drerr_new(0, ESTC_TEST_IN_PROGRESS, NULL);
503103831d35Sstevel 				break;
503203831d35Sstevel 			case DR_TEST_STATUS_UNKNOWN:
503303831d35Sstevel 				bp->cond = SBD_COND_UNKNOWN;
503403831d35Sstevel 				err = drerr_new(1,
503503831d35Sstevel 				    ESTC_TEST_STATUS_UNKNOWN, NULL);
503603831d35Sstevel 				break;
503703831d35Sstevel 			case DR_TEST_STATUS_FAILED:
503803831d35Sstevel 				bp->cond = SBD_COND_FAILED;
5039d3d50737SRafael Vanoni 				err = drerr_new(1, ESTC_TEST_FAILED, NULL);
504003831d35Sstevel 				break;
504103831d35Sstevel 			case DR_TEST_STATUS_ABORTED:
504203831d35Sstevel 				bp->cond = SBD_COND_UNKNOWN;
5043d3d50737SRafael Vanoni 				err = drerr_new(1, ESTC_TEST_ABORTED, NULL);
504403831d35Sstevel 				break;
504503831d35Sstevel 			default:
504603831d35Sstevel 				bp->cond = SBD_COND_UNKNOWN;
5047d3d50737SRafael Vanoni 				err = drerr_new(1, ESTC_TEST_RESULT_UNKNOWN,
504803831d35Sstevel 				    NULL);
504903831d35Sstevel 				break;
505003831d35Sstevel 		}
505103831d35Sstevel 	}
505203831d35Sstevel 
505303831d35Sstevel 	/*
505403831d35Sstevel 	 * If I/O cage test was performed, check for availability of the
505503831d35Sstevel 	 * cpu used.  If cpu has been returned, it's OK to proceed with
505603831d35Sstevel 	 * reconfiguring it for use.
505703831d35Sstevel 	 */
505803831d35Sstevel 	if (is_io) {
505903831d35Sstevel 		DRMACH_PR("drmach_board_test: tbr.cpu_recovered: %d",
506003831d35Sstevel 		    tbr.cpu_recovered);
506103831d35Sstevel 		DRMACH_PR("drmach_board_test: port id: %d",
506203831d35Sstevel 		    tbr.cpu_portid);
506303831d35Sstevel 
506403831d35Sstevel 		/*
506503831d35Sstevel 		 * Check the cpu_recovered flag in the testboard reply, or
506603831d35Sstevel 		 * if the testboard request message was not sent to SMS due
506703831d35Sstevel 		 * to an mboxsc_putmsg() failure, it's OK to recover the
506803831d35Sstevel 		 * cpu since hpost hasn't touched it.
506903831d35Sstevel 		 */
507003831d35Sstevel 		if ((tbr.cpu_recovered && tbr.cpu_portid ==
507103831d35Sstevel 		    obufp->msgdata.dm_tb.cpu_portid) ||
507203831d35Sstevel 		    ((err) && (err->e_code == ESTC_MBXRQST))) {
507303831d35Sstevel 
507403831d35Sstevel 			int i;
507503831d35Sstevel 
507603831d35Sstevel 			mutex_enter(&cpu_lock);
507725cf1a30Sjl139090 			for (i = 0; i < MAX_CORES_PER_CMP; i++) {
507803831d35Sstevel 				if (dp[i] != NULL) {
507903831d35Sstevel 					(void) drmach_iocage_cpu_return(dp[i],
508003831d35Sstevel 					    oflags[i]);
508103831d35Sstevel 				}
508203831d35Sstevel 			}
508303831d35Sstevel 			mutex_exit(&cpu_lock);
508403831d35Sstevel 		} else {
508503831d35Sstevel 			cmn_err(CE_WARN, "Unable to recover port id %d "
508603831d35Sstevel 			    "after I/O cage test: cpu_recovered=%d, "
508703831d35Sstevel 			    "returned portid=%d",
508803831d35Sstevel 			    obufp->msgdata.dm_tb.cpu_portid,
508903831d35Sstevel 			    tbr.cpu_recovered, tbr.cpu_portid);
509003831d35Sstevel 		}
509107d06da5SSurya Prakki 		(void) drmach_iocage_mem_return(&tbr);
509203831d35Sstevel 	}
509303831d35Sstevel 	kmem_free(obufp, sizeof (dr_mbox_msg_t));
509403831d35Sstevel 
509503831d35Sstevel 	return (err);
509603831d35Sstevel }
509703831d35Sstevel 
509803831d35Sstevel sbd_error_t *
drmach_board_unassign(drmachid_t id)509903831d35Sstevel drmach_board_unassign(drmachid_t id)
510003831d35Sstevel {
510103831d35Sstevel 	drmach_board_t	*bp;
510203831d35Sstevel 	sbd_error_t	*err;
510303831d35Sstevel 	drmach_status_t	 stat;
510403831d35Sstevel 	caddr_t		obufp;
510503831d35Sstevel 
510603831d35Sstevel 	rw_enter(&drmach_boards_rwlock, RW_WRITER);
510703831d35Sstevel 
510803831d35Sstevel 	if (!DRMACH_IS_BOARD_ID(id)) {
510903831d35Sstevel 		rw_exit(&drmach_boards_rwlock);
511003831d35Sstevel 		return (drerr_new(0, ESTC_INAPPROP, NULL));
511103831d35Sstevel 	}
511203831d35Sstevel 	bp = id;
511303831d35Sstevel 
511403831d35Sstevel 	err = drmach_board_status(id, &stat);
511503831d35Sstevel 	if (err) {
511603831d35Sstevel 		rw_exit(&drmach_boards_rwlock);
511703831d35Sstevel 		return (err);
511803831d35Sstevel 	}
511903831d35Sstevel 
512003831d35Sstevel 	if (stat.configured || stat.busy) {
512103831d35Sstevel 		err = drerr_new(0, ESTC_CONFIGBUSY, bp->cm.name);
512203831d35Sstevel 	} else {
512303831d35Sstevel 
512403831d35Sstevel 		obufp = kmem_zalloc(sizeof (dr_proto_hdr_t), KM_SLEEP);
512503831d35Sstevel 		err = drmach_mbox_trans(DRMSG_UNASSIGN, bp->bnum, obufp,
512603831d35Sstevel 		    sizeof (dr_proto_hdr_t), (caddr_t)NULL, 0);
512703831d35Sstevel 		kmem_free(obufp, sizeof (dr_proto_hdr_t));
512803831d35Sstevel 		if (!err) {
512903831d35Sstevel 			if (drmach_array_set(drmach_boards, bp->bnum, 0) != 0)
513003831d35Sstevel 				err = DRMACH_INTERNAL_ERROR();
513103831d35Sstevel 			else
513203831d35Sstevel 				drmach_board_dispose(bp);
513303831d35Sstevel 		}
513403831d35Sstevel 	}
513503831d35Sstevel 	rw_exit(&drmach_boards_rwlock);
513603831d35Sstevel 	return (err);
513703831d35Sstevel }
513803831d35Sstevel 
513903831d35Sstevel static sbd_error_t *
drmach_read_reg_addr(drmach_device_t * dp,uint64_t * p)514003831d35Sstevel drmach_read_reg_addr(drmach_device_t *dp, uint64_t *p)
514103831d35Sstevel {
514203831d35Sstevel 	int		len;
514303831d35Sstevel 	drmach_reg_t	reg;
514403831d35Sstevel 	drmach_node_t	pp;
514503831d35Sstevel 	drmach_node_t	*np = dp->node;
514603831d35Sstevel 
514703831d35Sstevel 	/*
514803831d35Sstevel 	 * If the node does not have a portid property,
514903831d35Sstevel 	 * it represents a CMP device. For a CMP, the reg
515003831d35Sstevel 	 * property of the parent holds the information of
515103831d35Sstevel 	 * interest.
515203831d35Sstevel 	 */
515303831d35Sstevel 	if (dp->node->n_getproplen(dp->node, "portid", &len) != 0) {
515403831d35Sstevel 
515503831d35Sstevel 		if (dp->node->get_parent(dp->node, &pp) != 0) {
515603831d35Sstevel 			return (DRMACH_INTERNAL_ERROR());
515703831d35Sstevel 		}
515803831d35Sstevel 		np = &pp;
515903831d35Sstevel 	}
516003831d35Sstevel 
516103831d35Sstevel 	if (np->n_getproplen(np, "reg", &len) != 0)
516203831d35Sstevel 		return (DRMACH_INTERNAL_ERROR());
516303831d35Sstevel 
516403831d35Sstevel 	if (len != sizeof (reg))
516503831d35Sstevel 		return (DRMACH_INTERNAL_ERROR());
516603831d35Sstevel 
516703831d35Sstevel 	if (np->n_getprop(np, "reg", &reg, sizeof (reg)) != 0)
516803831d35Sstevel 		return (DRMACH_INTERNAL_ERROR());
516903831d35Sstevel 
517003831d35Sstevel 	/* reassemble 64-bit base address */
517103831d35Sstevel 	*p = ((uint64_t)reg.reg_addr_hi << 32) | reg.reg_addr_lo;
517203831d35Sstevel 
517303831d35Sstevel 	return (NULL);
517403831d35Sstevel }
517503831d35Sstevel 
517603831d35Sstevel static void
drmach_cpu_read(uint64_t arg1,uint64_t arg2)517703831d35Sstevel drmach_cpu_read(uint64_t arg1, uint64_t arg2)
517803831d35Sstevel {
517903831d35Sstevel 	uint64_t	*saf_config_reg = (uint64_t *)arg1;
518003831d35Sstevel 	uint_t		*reg_read = (uint_t *)arg2;
518103831d35Sstevel 
518203831d35Sstevel 	*saf_config_reg = lddsafconfig();
518303831d35Sstevel 	*reg_read = 0x1;
518403831d35Sstevel }
518503831d35Sstevel 
518603831d35Sstevel /*
518703831d35Sstevel  * A return value of 1 indicates success and 0 indicates a failure
518803831d35Sstevel  */
518903831d35Sstevel static int
drmach_cpu_read_scr(drmach_cpu_t * cp,uint64_t * scr)519003831d35Sstevel drmach_cpu_read_scr(drmach_cpu_t *cp, uint64_t *scr)
519103831d35Sstevel {
519203831d35Sstevel 
519303831d35Sstevel 	int 	rv = 0x0;
519403831d35Sstevel 
519503831d35Sstevel 	*scr = 0x0;
519603831d35Sstevel 
519703831d35Sstevel 	/*
519803831d35Sstevel 	 * Confirm cpu was in ready set when xc was issued.
519903831d35Sstevel 	 * This is done by verifying rv which is
520003831d35Sstevel 	 * set to 0x1 when xc_one is successful.
520103831d35Sstevel 	 */
520203831d35Sstevel 	xc_one(cp->dev.portid, (xcfunc_t *)drmach_cpu_read,
520303831d35Sstevel 	    (uint64_t)scr, (uint64_t)&rv);
520403831d35Sstevel 
520503831d35Sstevel 	return (rv);
520603831d35Sstevel 
520703831d35Sstevel }
520803831d35Sstevel 
520903831d35Sstevel static sbd_error_t *
drmach_cpu_read_cpuid(drmach_cpu_t * cp,processorid_t * cpuid)521003831d35Sstevel drmach_cpu_read_cpuid(drmach_cpu_t *cp, processorid_t *cpuid)
521103831d35Sstevel {
521203831d35Sstevel 	drmach_node_t	*np;
521303831d35Sstevel 
521403831d35Sstevel 	np = cp->dev.node;
521503831d35Sstevel 
521603831d35Sstevel 	/*
521703831d35Sstevel 	 * If a CPU does not have a portid property, it must
521803831d35Sstevel 	 * be a CMP device with a cpuid property.
521903831d35Sstevel 	 */
522003831d35Sstevel 	if (np->n_getprop(np, "portid", cpuid, sizeof (*cpuid)) != 0) {
522103831d35Sstevel 
522203831d35Sstevel 		if (np->n_getprop(np, "cpuid", cpuid, sizeof (*cpuid)) != 0) {
522303831d35Sstevel 			return (DRMACH_INTERNAL_ERROR());
522403831d35Sstevel 		}
522503831d35Sstevel 	}
522603831d35Sstevel 
522703831d35Sstevel 	return (NULL);
522803831d35Sstevel }
522903831d35Sstevel 
523003831d35Sstevel /* Starcat CMP core id is bit 2 of the cpuid */
523103831d35Sstevel #define	DRMACH_COREID_MASK	(1u << 2)
523203831d35Sstevel #define	DRMACH_CPUID2SRAM_IDX(id) \
523303831d35Sstevel 		((id & DRMACH_COREID_MASK) >> 1 | (id & 0x1))
523403831d35Sstevel 
523503831d35Sstevel static sbd_error_t *
drmach_cpu_new(drmach_device_t * proto,drmachid_t * idp)523603831d35Sstevel drmach_cpu_new(drmach_device_t *proto, drmachid_t *idp)
523703831d35Sstevel {
523803831d35Sstevel 	sbd_error_t	*err;
523903831d35Sstevel 	uint64_t	scr_pa;
524003831d35Sstevel 	drmach_cpu_t	*cp = NULL;
524103831d35Sstevel 	pfn_t		pfn;
524203831d35Sstevel 	uint64_t	cpu_stardrb_offset, cpu_sram_pa;
524303831d35Sstevel 	int		idx;
524403831d35Sstevel 	int		impl;
524503831d35Sstevel 	processorid_t	cpuid;
524603831d35Sstevel 
524703831d35Sstevel 	err = drmach_read_reg_addr(proto, &scr_pa);
524803831d35Sstevel 	if (err) {
524903831d35Sstevel 		goto fail;
525003831d35Sstevel 	}
525103831d35Sstevel 
525203831d35Sstevel 	cp = kmem_zalloc(sizeof (drmach_cpu_t), KM_SLEEP);
525303831d35Sstevel 	bcopy(proto, &cp->dev, sizeof (cp->dev));
525403831d35Sstevel 	cp->dev.node = drmach_node_dup(proto->node);
525503831d35Sstevel 	cp->dev.cm.isa = (void *)drmach_cpu_new;
525603831d35Sstevel 	cp->dev.cm.dispose = drmach_cpu_dispose;
525703831d35Sstevel 	cp->dev.cm.release = drmach_cpu_release;
525803831d35Sstevel 	cp->dev.cm.status = drmach_cpu_status;
525903831d35Sstevel 	cp->scr_pa = scr_pa;
526003831d35Sstevel 
526103831d35Sstevel 	err = drmach_cpu_read_cpuid(cp, &cpuid);
526203831d35Sstevel 	if (err) {
526303831d35Sstevel 		goto fail;
526403831d35Sstevel 	}
526503831d35Sstevel 
526603831d35Sstevel 	err = drmach_cpu_get_impl(cp, &impl);
526703831d35Sstevel 	if (err) {
526803831d35Sstevel 		goto fail;
526903831d35Sstevel 	}
527003831d35Sstevel 
527103831d35Sstevel 	cp->cpuid = cpuid;
527203831d35Sstevel 	cp->coreid = STARCAT_CPUID_TO_COREID(cp->cpuid);
527303831d35Sstevel 	cp->dev.unum = STARCAT_CPUID_TO_AGENT(cp->cpuid);
527403831d35Sstevel 
527503831d35Sstevel 	/*
527603831d35Sstevel 	 * Init the board cpu type.  Assumes all board cpus are the same type.
527703831d35Sstevel 	 */
527803831d35Sstevel 	if (cp->dev.bp->cpu_impl == 0) {
527903831d35Sstevel 		cp->dev.bp->cpu_impl = impl;
528003831d35Sstevel 	}
528103831d35Sstevel 	ASSERT(cp->dev.bp->cpu_impl == impl);
528203831d35Sstevel 
528303831d35Sstevel 	/*
528403831d35Sstevel 	 * XXX CHEETAH SUPPORT
528503831d35Sstevel 	 * determine if the domain uses Cheetah procs
528603831d35Sstevel 	 */
528703831d35Sstevel 	if (drmach_is_cheetah < 0) {
528803831d35Sstevel 		drmach_is_cheetah = IS_CHEETAH(impl);
528903831d35Sstevel 	}
529003831d35Sstevel 
529103831d35Sstevel 	/*
529203831d35Sstevel 	 * Initialize TTE for mapping CPU SRAM STARDRB buffer.
529303831d35Sstevel 	 * The STARDRB buffer (16KB on Cheetah+ boards, 32KB on
529403831d35Sstevel 	 * Jaguar/Panther boards) is shared by all cpus in a Safari port
529503831d35Sstevel 	 * pair. Each cpu uses 8KB according to the following layout:
529603831d35Sstevel 	 *
529703831d35Sstevel 	 * Page 0:	even numbered Cheetah+'s and Panther/Jaguar core 0's
529803831d35Sstevel 	 * Page 1:	odd numbered Cheetah+'s and Panther/Jaguar core 0's
529903831d35Sstevel 	 * Page 2:	even numbered Panther/Jaguar core 1's
530003831d35Sstevel 	 * Page 3:	odd numbered Panther/Jaguar core 1's
530103831d35Sstevel 	 */
530203831d35Sstevel 	idx = DRMACH_CPUID2SRAM_IDX(cp->cpuid);
530303831d35Sstevel 	cpu_stardrb_offset = cp->dev.bp->stardrb_offset + (PAGESIZE * idx);
530403831d35Sstevel 	cpu_sram_pa = DRMACH_CPU_SRAM_ADDR + cpu_stardrb_offset;
530503831d35Sstevel 	pfn = cpu_sram_pa >> PAGESHIFT;
530603831d35Sstevel 
530703831d35Sstevel 	ASSERT(drmach_cpu_sram_tte[cp->cpuid].tte_inthi == 0 &&
530803831d35Sstevel 	    drmach_cpu_sram_tte[cp->cpuid].tte_intlo == 0);
530903831d35Sstevel 	drmach_cpu_sram_tte[cp->cpuid].tte_inthi = TTE_PFN_INTHI(pfn) |
531003831d35Sstevel 	    TTE_VALID_INT | TTE_SZ_INT(TTE8K);
531103831d35Sstevel 	drmach_cpu_sram_tte[cp->cpuid].tte_intlo = TTE_PFN_INTLO(pfn) |
531203831d35Sstevel 	    TTE_HWWR_INT | TTE_PRIV_INT | TTE_LCK_INT;
531303831d35Sstevel 
531403831d35Sstevel 	DRMACH_PR("drmach_cpu_new: cpuid=%d, coreid=%d, stardrb_offset=0x%lx, "
531503831d35Sstevel 	    "cpu_sram_offset=0x%lx, idx=%d\n", cp->cpuid, cp->coreid,
531603831d35Sstevel 	    cp->dev.bp->stardrb_offset, cpu_stardrb_offset, idx);
531703831d35Sstevel 
531807d06da5SSurya Prakki 	(void) snprintf(cp->dev.cm.name, sizeof (cp->dev.cm.name), "%s%d",
531903831d35Sstevel 	    cp->dev.type, cp->dev.unum);
532003831d35Sstevel 
532103831d35Sstevel 	*idp = (drmachid_t)cp;
532203831d35Sstevel 	return (NULL);
532303831d35Sstevel 
532403831d35Sstevel fail:
532503831d35Sstevel 	if (cp) {
532603831d35Sstevel 		drmach_node_dispose(cp->dev.node);
532703831d35Sstevel 		kmem_free(cp, sizeof (*cp));
532803831d35Sstevel 	}
532903831d35Sstevel 
533003831d35Sstevel 	*idp = (drmachid_t)0;
533103831d35Sstevel 	return (err);
533203831d35Sstevel }
533303831d35Sstevel 
533403831d35Sstevel static void
drmach_cpu_dispose(drmachid_t id)533503831d35Sstevel drmach_cpu_dispose(drmachid_t id)
533603831d35Sstevel {
533703831d35Sstevel 	drmach_cpu_t	*self;
533803831d35Sstevel 	processorid_t	cpuid;
533903831d35Sstevel 
534003831d35Sstevel 	ASSERT(DRMACH_IS_CPU_ID(id));
534103831d35Sstevel 
534203831d35Sstevel 	self = id;
534303831d35Sstevel 	if (self->dev.node)
534403831d35Sstevel 		drmach_node_dispose(self->dev.node);
534503831d35Sstevel 
534603831d35Sstevel 	cpuid = self->cpuid;
534703831d35Sstevel 	ASSERT(TTE_IS_VALID(&drmach_cpu_sram_tte[cpuid]) &&
534803831d35Sstevel 	    TTE_IS_8K(&drmach_cpu_sram_tte[cpuid]) &&
534903831d35Sstevel 	    TTE_IS_PRIVILEGED(&drmach_cpu_sram_tte[cpuid]) &&
535003831d35Sstevel 	    TTE_IS_LOCKED(&drmach_cpu_sram_tte[cpuid]));
535103831d35Sstevel 	drmach_cpu_sram_tte[cpuid].tte_inthi = 0;
535203831d35Sstevel 	drmach_cpu_sram_tte[cpuid].tte_intlo = 0;
535303831d35Sstevel 
535403831d35Sstevel 	kmem_free(self, sizeof (*self));
535503831d35Sstevel }
535603831d35Sstevel 
535703831d35Sstevel static int
drmach_cpu_start(struct cpu * cp)535803831d35Sstevel drmach_cpu_start(struct cpu *cp)
535903831d35Sstevel {
536003831d35Sstevel 	extern xcfunc_t	drmach_set_lpa;
536103831d35Sstevel 	extern void	restart_other_cpu(int);
536203831d35Sstevel 	int		cpuid = cp->cpu_id;
536303831d35Sstevel 	int		rv, bnum;
536403831d35Sstevel 	drmach_board_t	*bp;
536503831d35Sstevel 
536603831d35Sstevel 	ASSERT(MUTEX_HELD(&cpu_lock));
536703831d35Sstevel 	ASSERT(cpunodes[cpuid].nodeid != (pnode_t)0);
536803831d35Sstevel 
536903831d35Sstevel 	cp->cpu_flags &= ~CPU_POWEROFF;
537003831d35Sstevel 
537103831d35Sstevel 	/*
537203831d35Sstevel 	 * NOTE: restart_other_cpu pauses cpus during the
537303831d35Sstevel 	 *	 slave cpu start.  This helps to quiesce the
537403831d35Sstevel 	 *	 bus traffic a bit which makes the tick sync
537503831d35Sstevel 	 *	 routine in the prom more robust.
537603831d35Sstevel 	 */
537703831d35Sstevel 	DRMACH_PR("COLD START for cpu (%d)\n", cpuid);
537803831d35Sstevel 
537903831d35Sstevel 	if (prom_hotaddcpu(cpuid) != 0) {
538003831d35Sstevel 		cmn_err(CE_PANIC, "prom_hotaddcpu() for cpuid=%d failed.",
538103831d35Sstevel 		    cpuid);
538203831d35Sstevel 	}
538303831d35Sstevel 
538403831d35Sstevel 	restart_other_cpu(cpuid);
538503831d35Sstevel 
538603831d35Sstevel 	bnum = drmach_portid2bnum(cpunodes[cpuid].portid);
538703831d35Sstevel 	rv = drmach_array_get(drmach_boards, bnum, (drmachid_t)&bp);
538803831d35Sstevel 	if (rv == -1 || bp == NULL) {
538903831d35Sstevel 		DRMACH_PR("drmach_cpu_start: cannot read board info for "
539007d06da5SSurya Prakki 		    "cpuid=%d: rv=%d, bp=%p\n", cpuid, rv, (void *)bp);
539103831d35Sstevel 	} else if (DRMACH_L1_SET_LPA(bp) && drmach_reprogram_lpa) {
539203831d35Sstevel 		int exp;
539303831d35Sstevel 		int ntries;
539403831d35Sstevel 
539503831d35Sstevel 		mutex_enter(&drmach_xt_mb_lock);
539603831d35Sstevel 		mutex_enter(&drmach_slice_table_lock);
539703831d35Sstevel 		bzero((void *)drmach_xt_mb, drmach_xt_mb_size);
539803831d35Sstevel 
539903831d35Sstevel 		/*
540003831d35Sstevel 		 * drmach_slice_table[*]
540103831d35Sstevel 		 *	bit 5	valid
540203831d35Sstevel 		 *	bit 0:4	slice number
540303831d35Sstevel 		 *
540403831d35Sstevel 		 * drmach_xt_mb[*] format for drmach_set_lpa
540503831d35Sstevel 		 *	bit 7	valid
540603831d35Sstevel 		 *	bit 6	set null LPA (overrides bits 0:4)
540703831d35Sstevel 		 *	bit 0:4	slice number
540803831d35Sstevel 		 *
540903831d35Sstevel 		 * drmach_set_lpa derives processor CBASE and CBND
541003831d35Sstevel 		 * from bits 6 and 0:4 of drmach_xt_mb.  If bit 6 is
541103831d35Sstevel 		 * set, then CBASE = CBND = 0. Otherwise, CBASE = slice
541203831d35Sstevel 		 * number; CBND = slice number + 1.
541303831d35Sstevel 		 * No action is taken if bit 7 is zero.
541403831d35Sstevel 		 */
541503831d35Sstevel 		exp = (cpuid >> 5) & 0x1f;
541603831d35Sstevel 		if (drmach_slice_table[exp] & 0x20) {
541703831d35Sstevel 			drmach_xt_mb[cpuid] = 0x80 |
541803831d35Sstevel 			    (drmach_slice_table[exp] & 0x1f);
541903831d35Sstevel 		} else {
542003831d35Sstevel 			drmach_xt_mb[cpuid] = 0x80 | 0x40;
542103831d35Sstevel 		}
542203831d35Sstevel 
542303831d35Sstevel 		drmach_xt_ready = 0;
542403831d35Sstevel 
542503831d35Sstevel 		xt_one(cpuid, drmach_set_lpa, NULL, NULL);
542603831d35Sstevel 
542703831d35Sstevel 		ntries = drmach_cpu_ntries;
542803831d35Sstevel 		while (!drmach_xt_ready && ntries) {
542903831d35Sstevel 			DELAY(drmach_cpu_delay);
543003831d35Sstevel 			ntries--;
543103831d35Sstevel 		}
543203831d35Sstevel 
543303831d35Sstevel 		mutex_exit(&drmach_slice_table_lock);
543403831d35Sstevel 		mutex_exit(&drmach_xt_mb_lock);
543503831d35Sstevel 
543603831d35Sstevel 		DRMACH_PR(
543703831d35Sstevel 		    "waited %d out of %d tries for drmach_set_lpa on cpu%d",
543803831d35Sstevel 		    drmach_cpu_ntries - ntries, drmach_cpu_ntries,
543903831d35Sstevel 		    cp->cpu_id);
544003831d35Sstevel 	}
544103831d35Sstevel 
5442d3d50737SRafael Vanoni 	xt_one(cpuid, vtag_flushpage_tl1, (uint64_t)drmach_cpu_sram_va,
5443d3d50737SRafael Vanoni 	    (uint64_t)ksfmmup);
544403831d35Sstevel 
544503831d35Sstevel 	return (0);
544603831d35Sstevel }
544703831d35Sstevel 
544803831d35Sstevel /*
544903831d35Sstevel  * A detaching CPU is xcalled with an xtrap to drmach_cpu_stop_self() after
545003831d35Sstevel  * it has been offlined. The function of this routine is to get the cpu
545103831d35Sstevel  * spinning in a safe place. The requirement is that the system will not
545203831d35Sstevel  * reference anything on the detaching board (memory and i/o is detached
545303831d35Sstevel  * elsewhere) and that the CPU not reference anything on any other board
545403831d35Sstevel  * in the system.  This isolation is required during and after the writes
545503831d35Sstevel  * to the domain masks to remove the board from the domain.
545603831d35Sstevel  *
545703831d35Sstevel  * To accomplish this isolation the following is done:
545803831d35Sstevel  *	1) Create a locked mapping to the STARDRB data buffer located
545903831d35Sstevel  *	   in this cpu's sram. There is one TTE per cpu, initialized in
546003831d35Sstevel  *	   drmach_cpu_new(). The cpuid is used to select which TTE to use.
546103831d35Sstevel  *	   Each Safari port pair shares the CPU SRAM on a Serengeti CPU/MEM
546203831d35Sstevel  *	   board. The STARDRB buffer is 16KB on Cheetah+ boards, 32KB on Jaguar
546303831d35Sstevel  *	   boards. Each STARDRB buffer is logically divided by DR into one
546403831d35Sstevel  *	   8KB page per cpu (or Jaguar core).
546503831d35Sstevel  *	2) Copy the target function (drmach_shutdown_asm) into buffer.
546603831d35Sstevel  *	3) Jump to function now in the cpu sram.
546703831d35Sstevel  *	   Function will:
546803831d35Sstevel  *	   3.1) Flush its Ecache (displacement).
546903831d35Sstevel  *	   3.2) Flush its Dcache with HW mechanism.
547003831d35Sstevel  *	   3.3) Flush its Icache with HW mechanism.
547103831d35Sstevel  *	   3.4) Flush all valid and _unlocked_ D-TLB and I-TLB entries.
547203831d35Sstevel  *	   3.5) Set LPA to NULL
547303831d35Sstevel  *	   3.6) Clear xt_mb to signal completion. Note: cache line is
547403831d35Sstevel  *	        recovered by drmach_cpu_poweroff().
547503831d35Sstevel  *	4) Jump into an infinite loop.
547603831d35Sstevel  */
547703831d35Sstevel 
547803831d35Sstevel static void
drmach_cpu_stop_self(void)547903831d35Sstevel drmach_cpu_stop_self(void)
548003831d35Sstevel {
5481d3d50737SRafael Vanoni 	extern void drmach_shutdown_asm(uint64_t, uint64_t, int, int, uint64_t);
548203831d35Sstevel 	extern void drmach_shutdown_asm_end(void);
548303831d35Sstevel 
548403831d35Sstevel 	tte_t		*tte;
548503831d35Sstevel 	uint_t		*p, *q;
548603831d35Sstevel 	uint64_t	 stack_pointer;
548703831d35Sstevel 
548803831d35Sstevel 	ASSERT(((ptrdiff_t)drmach_shutdown_asm_end -
548903831d35Sstevel 	    (ptrdiff_t)drmach_shutdown_asm) < PAGESIZE);
549003831d35Sstevel 
549103831d35Sstevel 	tte = &drmach_cpu_sram_tte[CPU->cpu_id];
5492d3d50737SRafael Vanoni 	ASSERT(TTE_IS_VALID(tte) && TTE_IS_8K(tte) && TTE_IS_PRIVILEGED(tte) &&
5493d3d50737SRafael Vanoni 	    TTE_IS_LOCKED(tte));
54941e2e7a75Shuah 	sfmmu_dtlb_ld_kva(drmach_cpu_sram_va, tte);
54951e2e7a75Shuah 	sfmmu_itlb_ld_kva(drmach_cpu_sram_va, tte);
549603831d35Sstevel 
549703831d35Sstevel 	/* copy text. standard bcopy not designed to work in nc space */
549803831d35Sstevel 	p = (uint_t *)drmach_cpu_sram_va;
549903831d35Sstevel 	q = (uint_t *)drmach_shutdown_asm;
550003831d35Sstevel 	while (q < (uint_t *)drmach_shutdown_asm_end)
550103831d35Sstevel 		*p++ = *q++;
550203831d35Sstevel 
550303831d35Sstevel 	/* zero to assist debug */
550403831d35Sstevel 	q = (uint_t *)(drmach_cpu_sram_va + PAGESIZE);
550503831d35Sstevel 	while (p < q)
550603831d35Sstevel 		*p++ = 0;
550703831d35Sstevel 
550803831d35Sstevel 	/* a parking spot for the stack pointer */
550903831d35Sstevel 	stack_pointer = (uint64_t)q;
551003831d35Sstevel 
551103831d35Sstevel 	/* call copy of drmach_shutdown_asm */
551203831d35Sstevel 	(*(void (*)())drmach_cpu_sram_va)(
551303831d35Sstevel 	    stack_pointer,
551403831d35Sstevel 	    drmach_iocage_paddr,
551503831d35Sstevel 	    cpunodes[CPU->cpu_id].ecache_size,
551603831d35Sstevel 	    cpunodes[CPU->cpu_id].ecache_linesize,
551703831d35Sstevel 	    va_to_pa((void *)&drmach_xt_mb[CPU->cpu_id]));
551803831d35Sstevel }
551903831d35Sstevel 
552003831d35Sstevel static void
drmach_cpu_shutdown_self(void)552103831d35Sstevel drmach_cpu_shutdown_self(void)
552203831d35Sstevel {
552303831d35Sstevel 	cpu_t		*cp = CPU;
552403831d35Sstevel 	int		cpuid = cp->cpu_id;
552503831d35Sstevel 	extern void	flush_windows(void);
552603831d35Sstevel 
552703831d35Sstevel 	flush_windows();
552803831d35Sstevel 
552903831d35Sstevel 	(void) spl8();
553003831d35Sstevel 
553103831d35Sstevel 	ASSERT(cp->cpu_intr_actv == 0);
553203831d35Sstevel 	ASSERT(cp->cpu_thread == cp->cpu_idle_thread ||
553303831d35Sstevel 	    cp->cpu_thread == cp->cpu_startup_thread);
553403831d35Sstevel 
553503831d35Sstevel 	cp->cpu_flags = CPU_OFFLINE | CPU_QUIESCED | CPU_POWEROFF;
553603831d35Sstevel 
553703831d35Sstevel 	drmach_cpu_stop_self();
553803831d35Sstevel 
553903831d35Sstevel 	cmn_err(CE_PANIC, "CPU %d FAILED TO SHUTDOWN", cpuid);
554003831d35Sstevel }
554103831d35Sstevel 
554203831d35Sstevel static sbd_error_t *
drmach_cpu_release(drmachid_t id)554303831d35Sstevel drmach_cpu_release(drmachid_t id)
554403831d35Sstevel {
554503831d35Sstevel 	drmach_cpu_t	*cp;
554603831d35Sstevel 	struct cpu	*cpu;
554703831d35Sstevel 	sbd_error_t	*err;
554803831d35Sstevel 
554903831d35Sstevel 	if (!DRMACH_IS_CPU_ID(id))
555003831d35Sstevel 		return (drerr_new(0, ESTC_INAPPROP, NULL));
555103831d35Sstevel 	cp = id;
555203831d35Sstevel 
555303831d35Sstevel 	ASSERT(MUTEX_HELD(&cpu_lock));
555403831d35Sstevel 
555503831d35Sstevel 	cpu = cpu_get(cp->cpuid);
555603831d35Sstevel 	if (cpu == NULL)
555703831d35Sstevel 		err = DRMACH_INTERNAL_ERROR();
555803831d35Sstevel 	else
555903831d35Sstevel 		err = NULL;
556003831d35Sstevel 
556103831d35Sstevel 	return (err);
556203831d35Sstevel }
556303831d35Sstevel 
556403831d35Sstevel static sbd_error_t *
drmach_cpu_status(drmachid_t id,drmach_status_t * stat)556503831d35Sstevel drmach_cpu_status(drmachid_t id, drmach_status_t *stat)
556603831d35Sstevel {
556703831d35Sstevel 	drmach_cpu_t	*cp;
556803831d35Sstevel 	drmach_device_t	*dp;
556903831d35Sstevel 
557003831d35Sstevel 	ASSERT(DRMACH_IS_CPU_ID(id));
557103831d35Sstevel 	cp = id;
557203831d35Sstevel 	dp = &cp->dev;
557303831d35Sstevel 
557403831d35Sstevel 	stat->assigned = dp->bp->assigned;
557503831d35Sstevel 	stat->powered = dp->bp->powered;
557603831d35Sstevel 	mutex_enter(&cpu_lock);
557703831d35Sstevel 	stat->configured = (cpu_get(cp->cpuid) != NULL);
557803831d35Sstevel 	mutex_exit(&cpu_lock);
557903831d35Sstevel 	stat->busy = dp->busy;
558007d06da5SSurya Prakki 	(void) strncpy(stat->type, dp->type, sizeof (stat->type));
558103831d35Sstevel 	stat->info[0] = '\0';
558203831d35Sstevel 
558303831d35Sstevel 	return (NULL);
558403831d35Sstevel }
558503831d35Sstevel 
558603831d35Sstevel sbd_error_t *
drmach_cpu_disconnect(drmachid_t id)558703831d35Sstevel drmach_cpu_disconnect(drmachid_t id)
558803831d35Sstevel {
558903831d35Sstevel 	if (!DRMACH_IS_CPU_ID(id))
559003831d35Sstevel 		return (drerr_new(0, ESTC_INAPPROP, NULL));
559103831d35Sstevel 
559203831d35Sstevel 	return (NULL);
559303831d35Sstevel }
559403831d35Sstevel 
559503831d35Sstevel sbd_error_t *
drmach_cpu_get_id(drmachid_t id,processorid_t * cpuid)559603831d35Sstevel drmach_cpu_get_id(drmachid_t id, processorid_t *cpuid)
559703831d35Sstevel {
559803831d35Sstevel 	drmach_cpu_t	*cpu;
559903831d35Sstevel 
560003831d35Sstevel 	if (!DRMACH_IS_CPU_ID(id))
560103831d35Sstevel 		return (drerr_new(0, ESTC_INAPPROP, NULL));
560203831d35Sstevel 	cpu = id;
560303831d35Sstevel 
560403831d35Sstevel 	*cpuid = cpu->cpuid;
560503831d35Sstevel 	return (NULL);
560603831d35Sstevel }
560703831d35Sstevel 
560803831d35Sstevel sbd_error_t *
drmach_cpu_get_impl(drmachid_t id,int * ip)560903831d35Sstevel drmach_cpu_get_impl(drmachid_t id, int *ip)
561003831d35Sstevel {
561103831d35Sstevel 	drmach_node_t	*np;
561203831d35Sstevel 	int		impl;
561303831d35Sstevel 
561403831d35Sstevel 	if (!DRMACH_IS_CPU_ID(id))
561503831d35Sstevel 		return (drerr_new(0, ESTC_INAPPROP, NULL));
561603831d35Sstevel 
561703831d35Sstevel 	np = ((drmach_device_t *)id)->node;
561803831d35Sstevel 
561903831d35Sstevel 	if (np->n_getprop(np, "implementation#", &impl, sizeof (impl)) == -1) {
562003831d35Sstevel 		return (DRMACH_INTERNAL_ERROR());
562103831d35Sstevel 	}
562203831d35Sstevel 
562303831d35Sstevel 	*ip = impl;
562403831d35Sstevel 
562503831d35Sstevel 	return (NULL);
562603831d35Sstevel }
562703831d35Sstevel 
562803831d35Sstevel /*
562903831d35Sstevel  * Flush this cpu's ecache, then ensure all outstanding safari
563003831d35Sstevel  * transactions have retired.
563103831d35Sstevel  */
563203831d35Sstevel void
drmach_cpu_flush_ecache_sync(void)563303831d35Sstevel drmach_cpu_flush_ecache_sync(void)
563403831d35Sstevel {
563503831d35Sstevel 	uint64_t *p;
563603831d35Sstevel 
563703831d35Sstevel 	ASSERT(curthread->t_bound_cpu == CPU);
563803831d35Sstevel 
563903831d35Sstevel 	cpu_flush_ecache();
564003831d35Sstevel 
564103831d35Sstevel 	mutex_enter(&drmach_bus_sync_lock);
564203831d35Sstevel 	for (p = drmach_bus_sync_list; *p; p++)
564303831d35Sstevel 		(void) ldphys(*p);
564403831d35Sstevel 	mutex_exit(&drmach_bus_sync_lock);
564503831d35Sstevel 
564603831d35Sstevel 	cpu_flush_ecache();
564703831d35Sstevel }
564803831d35Sstevel 
564903831d35Sstevel sbd_error_t *
drmach_get_dip(drmachid_t id,dev_info_t ** dip)565003831d35Sstevel drmach_get_dip(drmachid_t id, dev_info_t **dip)
565103831d35Sstevel {
565203831d35Sstevel 	drmach_device_t	*dp;
565303831d35Sstevel 
565403831d35Sstevel 	if (!DRMACH_IS_DEVICE_ID(id))
565503831d35Sstevel 		return (drerr_new(0, ESTC_INAPPROP, NULL));
565603831d35Sstevel 	dp = id;
565703831d35Sstevel 
565803831d35Sstevel 	*dip = dp->node->n_getdip(dp->node);
565903831d35Sstevel 	return (NULL);
566003831d35Sstevel }
566103831d35Sstevel 
566203831d35Sstevel sbd_error_t *
drmach_io_is_attached(drmachid_t id,int * yes)566303831d35Sstevel drmach_io_is_attached(drmachid_t id, int *yes)
566403831d35Sstevel {
566503831d35Sstevel 	drmach_device_t *dp;
566603831d35Sstevel 	dev_info_t	*dip;
566703831d35Sstevel 	int state;
566803831d35Sstevel 
566903831d35Sstevel 	if (!DRMACH_IS_IO_ID(id))
567003831d35Sstevel 		return (drerr_new(0, ESTC_INAPPROP, NULL));
567103831d35Sstevel 	dp = id;
567203831d35Sstevel 
567303831d35Sstevel 	dip = dp->node->n_getdip(dp->node);
567403831d35Sstevel 	if (dip == NULL) {
567503831d35Sstevel 		*yes = 0;
567603831d35Sstevel 		return (NULL);
567703831d35Sstevel 	}
567803831d35Sstevel 
567903831d35Sstevel 	state = ddi_get_devstate(dip);
568003831d35Sstevel 	*yes = i_ddi_devi_attached(dip) || (state == DDI_DEVSTATE_UP);
568103831d35Sstevel 
568203831d35Sstevel 	return (NULL);
568303831d35Sstevel }
568403831d35Sstevel 
568503831d35Sstevel static int
drmach_dip_is_schizo_xmits_0_pci_b(dev_info_t * dip)568603831d35Sstevel drmach_dip_is_schizo_xmits_0_pci_b(dev_info_t *dip)
568703831d35Sstevel {
568803831d35Sstevel 	char			dtype[OBP_MAXPROPNAME];
568903831d35Sstevel 	int			portid;
569003831d35Sstevel 	uint_t			pci_csr_base;
569103831d35Sstevel 	struct pci_phys_spec	*regbuf = NULL;
569203831d35Sstevel 	int			rv, len;
569303831d35Sstevel 
569403831d35Sstevel 	ASSERT(dip != NULL);
569503831d35Sstevel 	rv = ddi_getproplen(DDI_DEV_T_ANY, dip, 0, "device_type", &len);
569603831d35Sstevel 	if ((rv != DDI_PROP_SUCCESS) || (len > sizeof (dtype)))
569703831d35Sstevel 		return (0);
569803831d35Sstevel 
569903831d35Sstevel 	if (ddi_getlongprop_buf(DDI_DEV_T_ANY, dip, 0, "device_type",
570003831d35Sstevel 	    (caddr_t)dtype, &len) == DDI_PROP_SUCCESS) {
570103831d35Sstevel 
570203831d35Sstevel 		if (strncmp(dtype, "pci", 3) == 0) {
570303831d35Sstevel 
570403831d35Sstevel 			/*
570503831d35Sstevel 			 * Get safari portid. All schizo/xmits 0
570603831d35Sstevel 			 * safari IDs end in 0x1C.
570703831d35Sstevel 			 */
5708d3d50737SRafael Vanoni 			rv = ddi_getproplen(DDI_DEV_T_ANY, dip, 0, "portid",
5709d3d50737SRafael Vanoni 			    &len);
571003831d35Sstevel 
571103831d35Sstevel 			if ((rv != DDI_PROP_SUCCESS) ||
571203831d35Sstevel 			    (len > sizeof (portid)))
571303831d35Sstevel 				return (0);
571403831d35Sstevel 
571503831d35Sstevel 			rv = ddi_getlongprop_buf(DDI_DEV_T_ANY, dip, 0,
571603831d35Sstevel 			    "portid", (caddr_t)&portid, &len);
571703831d35Sstevel 
571803831d35Sstevel 			if (rv != DDI_PROP_SUCCESS)
571903831d35Sstevel 				return (0);
572003831d35Sstevel 
572103831d35Sstevel 			if ((portid & 0x1F) != 0x1C)
572203831d35Sstevel 				return (0);
572303831d35Sstevel 
572403831d35Sstevel 			if (ddi_getlongprop(DDI_DEV_T_ANY, dip,
572503831d35Sstevel 			    DDI_PROP_DONTPASS, "reg", (caddr_t)&regbuf,
572603831d35Sstevel 			    &len) == DDI_PROP_SUCCESS) {
572703831d35Sstevel 
572803831d35Sstevel 				pci_csr_base = regbuf[0].pci_phys_mid &
572903831d35Sstevel 				    PCI_CONF_ADDR_MASK;
573003831d35Sstevel 				kmem_free(regbuf, len);
573103831d35Sstevel 				/*
573203831d35Sstevel 				 * All PCI B-Leafs are at configspace 0x70.0000.
573303831d35Sstevel 				 */
573403831d35Sstevel 				if (pci_csr_base == 0x700000)
573503831d35Sstevel 					return (1);
573603831d35Sstevel 			}
573703831d35Sstevel 		}
573803831d35Sstevel 	}
573903831d35Sstevel 	return (0);
574003831d35Sstevel }
574103831d35Sstevel 
574203831d35Sstevel #define	SCHIZO_BINDING_NAME		"pci108e,8001"
574303831d35Sstevel #define	XMITS_BINDING_NAME		"pci108e,8002"
574403831d35Sstevel 
574503831d35Sstevel /*
574603831d35Sstevel  * Verify if the dip is an instance of MAN 'eri'.
574703831d35Sstevel  */
574803831d35Sstevel static int
drmach_dip_is_man_eri(dev_info_t * dip)574903831d35Sstevel drmach_dip_is_man_eri(dev_info_t *dip)
575003831d35Sstevel {
575103831d35Sstevel 	struct pci_phys_spec	*regbuf = NULL;
575203831d35Sstevel 	dev_info_t		*parent_dip;
575303831d35Sstevel 	char			*name;
575403831d35Sstevel 	uint_t			pci_device;
575503831d35Sstevel 	uint_t			pci_function;
575603831d35Sstevel 	int			len;
575703831d35Sstevel 
575803831d35Sstevel 	if (dip == NULL)
575903831d35Sstevel 		return (0);
576003831d35Sstevel 	/*
576103831d35Sstevel 	 * Verify if the parent is schizo(xmits)0 and pci B leaf.
576203831d35Sstevel 	 */
576303831d35Sstevel 	if (((parent_dip = ddi_get_parent(dip)) == NULL) ||
576403831d35Sstevel 	    ((name = ddi_binding_name(parent_dip)) == NULL))
576503831d35Sstevel 		return (0);
576603831d35Sstevel 	if (strcmp(name, SCHIZO_BINDING_NAME) != 0) {
576703831d35Sstevel 		/*
576803831d35Sstevel 		 * This RIO could be on XMITS, so get the dip to
576903831d35Sstevel 		 * XMITS PCI Leaf.
577003831d35Sstevel 		 */
577103831d35Sstevel 		if ((parent_dip = ddi_get_parent(parent_dip)) == NULL)
577203831d35Sstevel 			return (0);
577303831d35Sstevel 		if (((name = ddi_binding_name(parent_dip)) == NULL) ||
577403831d35Sstevel 		    (strcmp(name, XMITS_BINDING_NAME) != 0)) {
577503831d35Sstevel 			return (0);
577603831d35Sstevel 		}
577703831d35Sstevel 	}
577803831d35Sstevel 	if (!drmach_dip_is_schizo_xmits_0_pci_b(parent_dip))
577903831d35Sstevel 		return (0);
578003831d35Sstevel 	/*
578103831d35Sstevel 	 * Finally make sure it is the MAN eri.
578203831d35Sstevel 	 */
578303831d35Sstevel 	if (ddi_getlongprop(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
578403831d35Sstevel 	    "reg", (caddr_t)&regbuf, &len) == DDI_PROP_SUCCESS) {
578503831d35Sstevel 
578603831d35Sstevel 		pci_device = PCI_REG_DEV_G(regbuf->pci_phys_hi);
578703831d35Sstevel 		pci_function = PCI_REG_FUNC_G(regbuf->pci_phys_hi);
578803831d35Sstevel 		kmem_free(regbuf, len);
578903831d35Sstevel 
579003831d35Sstevel 		/*
579103831d35Sstevel 		 * The network function of the RIO ASIC will always be
579203831d35Sstevel 		 * device 3 and function 1 ("network@3,1").
579303831d35Sstevel 		 */
579403831d35Sstevel 		if ((pci_device == 3) && (pci_function == 1))
579503831d35Sstevel 			return (1);
579603831d35Sstevel 	}
579703831d35Sstevel 	return (0);
579803831d35Sstevel }
579903831d35Sstevel 
580003831d35Sstevel typedef struct {
580103831d35Sstevel 	int		iosram_inst;
580203831d35Sstevel 	dev_info_t	*eri_dip;
580303831d35Sstevel 	int		bnum;
580403831d35Sstevel } drmach_io_inst_t;
580503831d35Sstevel 
580603831d35Sstevel int
drmach_board_find_io_insts(dev_info_t * dip,void * args)580703831d35Sstevel drmach_board_find_io_insts(dev_info_t *dip, void *args)
580803831d35Sstevel {
580903831d35Sstevel 	drmach_io_inst_t	*ios = (drmach_io_inst_t *)args;
581003831d35Sstevel 
581103831d35Sstevel 	int	rv;
581203831d35Sstevel 	int	len;
581303831d35Sstevel 	int	portid;
581403831d35Sstevel 	char	name[OBP_MAXDRVNAME];
581503831d35Sstevel 
581603831d35Sstevel 	rv = ddi_getproplen(DDI_DEV_T_ANY, dip, 0, "portid", &len);
581703831d35Sstevel 
581803831d35Sstevel 	if ((rv != DDI_PROP_SUCCESS) || (len > sizeof (portid))) {
581903831d35Sstevel 		return (DDI_WALK_CONTINUE);
582003831d35Sstevel 	}
582103831d35Sstevel 
582203831d35Sstevel 	rv = ddi_getlongprop_buf(DDI_DEV_T_ANY, dip, 0,
582303831d35Sstevel 	    "portid", (caddr_t)&portid, &len);
582403831d35Sstevel 	if (rv != DDI_PROP_SUCCESS)
582503831d35Sstevel 		return (DDI_WALK_CONTINUE);
582603831d35Sstevel 
582703831d35Sstevel 	/* ignore devices that are not on this board */
582803831d35Sstevel 	if (drmach_portid2bnum(portid) != ios->bnum)
582903831d35Sstevel 		return (DDI_WALK_CONTINUE);
583003831d35Sstevel 
583103831d35Sstevel 	if ((ios->iosram_inst < 0) || (ios->eri_dip == NULL)) {
5832d3d50737SRafael Vanoni 		rv = ddi_getproplen(DDI_DEV_T_ANY, dip, 0, "name", &len);
583303831d35Sstevel 		if (rv == DDI_PROP_SUCCESS) {
583403831d35Sstevel 
583503831d35Sstevel 			rv = ddi_getlongprop_buf(DDI_DEV_T_ANY, dip,
583603831d35Sstevel 			    0, "name",
583703831d35Sstevel 			    (caddr_t)name, &len);
583803831d35Sstevel 			if (rv != DDI_PROP_SUCCESS)
583903831d35Sstevel 				return (DDI_WALK_CONTINUE);
584003831d35Sstevel 
584103831d35Sstevel 			if (strncmp("iosram", name, 6) == 0) {
584203831d35Sstevel 				ios->iosram_inst = ddi_get_instance(dip);
584303831d35Sstevel 				if (ios->eri_dip == NULL)
584403831d35Sstevel 					return (DDI_WALK_CONTINUE);
584503831d35Sstevel 				else
584603831d35Sstevel 					return (DDI_WALK_TERMINATE);
584703831d35Sstevel 			} else {
584803831d35Sstevel 				if (drmach_dip_is_man_eri(dip)) {
584903831d35Sstevel 					ASSERT(ios->eri_dip == NULL);
585003831d35Sstevel 					ndi_hold_devi(dip);
585103831d35Sstevel 					ios->eri_dip = dip;
585203831d35Sstevel 					if (ios->iosram_inst < 0)
585303831d35Sstevel 						return (DDI_WALK_CONTINUE);
585403831d35Sstevel 					else
585503831d35Sstevel 						return (DDI_WALK_TERMINATE);
585603831d35Sstevel 				}
585703831d35Sstevel 			}
585803831d35Sstevel 		}
585903831d35Sstevel 	}
586003831d35Sstevel 	return (DDI_WALK_CONTINUE);
586103831d35Sstevel }
586203831d35Sstevel 
586303831d35Sstevel sbd_error_t *
drmach_io_pre_release(drmachid_t id)586403831d35Sstevel drmach_io_pre_release(drmachid_t id)
586503831d35Sstevel {
586603831d35Sstevel 	drmach_io_inst_t	ios;
586703831d35Sstevel 	drmach_board_t		*bp;
586803831d35Sstevel 	int			rv = 0;
586903831d35Sstevel 	sbd_error_t		*err = NULL;
587003831d35Sstevel 	drmach_device_t		*dp;
587103831d35Sstevel 	dev_info_t		*rdip;
587203831d35Sstevel 	int			circ;
587303831d35Sstevel 
587403831d35Sstevel 	if (!DRMACH_IS_IO_ID(id))
587503831d35Sstevel 		return (drerr_new(0, ESTC_INAPPROP, NULL));
587603831d35Sstevel 	dp = id;
587703831d35Sstevel 	bp = dp->bp;
587803831d35Sstevel 
587903831d35Sstevel 	rdip = dp->node->n_getdip(dp->node);
588003831d35Sstevel 
588103831d35Sstevel 	/* walk device tree to find iosram instance for the board */
588203831d35Sstevel 	ios.iosram_inst = -1;
588303831d35Sstevel 	ios.eri_dip = NULL;
588403831d35Sstevel 	ios.bnum = bp->bnum;
588503831d35Sstevel 
588603831d35Sstevel 	ndi_devi_enter(rdip, &circ);
588703831d35Sstevel 	ddi_walk_devs(ddi_get_child(rdip), drmach_board_find_io_insts,
588803831d35Sstevel 	    (void *)&ios);
588903831d35Sstevel 
589003831d35Sstevel 	DRMACH_PR("drmach_io_pre_release: bnum=%d iosram=%d eri=0x%p\n",
589107d06da5SSurya Prakki 	    ios.bnum, ios.iosram_inst, (void *)ios.eri_dip);
589203831d35Sstevel 	ndi_devi_exit(rdip, circ);
589303831d35Sstevel 
589403831d35Sstevel 	if (ios.eri_dip) {
589503831d35Sstevel 		/*
589603831d35Sstevel 		 * Release hold acquired in drmach_board_find_io_insts()
589703831d35Sstevel 		 */
589803831d35Sstevel 		ndi_rele_devi(ios.eri_dip);
589903831d35Sstevel 	}
590003831d35Sstevel 	if (ios.iosram_inst >= 0) {
590103831d35Sstevel 		/* call for tunnel switch */
590203831d35Sstevel 		do {
590303831d35Sstevel 			DRMACH_PR("calling iosram_switchfrom(%d)\n",
590403831d35Sstevel 			    ios.iosram_inst);
590503831d35Sstevel 			rv = iosram_switchfrom(ios.iosram_inst);
590603831d35Sstevel 			if (rv)
590703831d35Sstevel 				DRMACH_PR("iosram_switchfrom returned %d\n",
590803831d35Sstevel 				    rv);
590903831d35Sstevel 		} while (rv == EAGAIN);
591003831d35Sstevel 
591103831d35Sstevel 		if (rv)
591203831d35Sstevel 			err = drerr_new(0, ESTC_IOSWITCH, NULL);
591303831d35Sstevel 	}
591403831d35Sstevel 	return (err);
591503831d35Sstevel }
591603831d35Sstevel 
591703831d35Sstevel sbd_error_t *
drmach_io_unrelease(drmachid_t id)591803831d35Sstevel drmach_io_unrelease(drmachid_t id)
591903831d35Sstevel {
592003831d35Sstevel 	dev_info_t	*dip;
592103831d35Sstevel 	sbd_error_t	*err = NULL;
592203831d35Sstevel 	drmach_device_t	*dp;
592303831d35Sstevel 
592403831d35Sstevel 	if (!DRMACH_IS_IO_ID(id))
592503831d35Sstevel 		return (drerr_new(0, ESTC_INAPPROP, NULL));
592603831d35Sstevel 	dp = id;
592703831d35Sstevel 
592803831d35Sstevel 	dip = dp->node->n_getdip(dp->node);
592903831d35Sstevel 
593003831d35Sstevel 	if (dip == NULL)
593103831d35Sstevel 		err = DRMACH_INTERNAL_ERROR();
593203831d35Sstevel 	else {
593303831d35Sstevel 		int (*func)(dev_info_t *dip);
593403831d35Sstevel 
593503831d35Sstevel 		func = (int (*)(dev_info_t *))kobj_getsymvalue("man_dr_attach",
593603831d35Sstevel 		    0);
593703831d35Sstevel 
593803831d35Sstevel 		if (func) {
593903831d35Sstevel 			drmach_io_inst_t ios;
594003831d35Sstevel 			dev_info_t	*pdip;
594103831d35Sstevel 			int		circ;
594203831d35Sstevel 
594303831d35Sstevel 			/*
594403831d35Sstevel 			 * Walk device tree to find rio dip for the board
594503831d35Sstevel 			 * Since we are not interested in iosram instance here,
594603831d35Sstevel 			 * initialize it to 0, so that the walk terminates as
594703831d35Sstevel 			 * soon as eri dip is found.
594803831d35Sstevel 			 */
594903831d35Sstevel 			ios.iosram_inst = 0;
595003831d35Sstevel 			ios.eri_dip = NULL;
595103831d35Sstevel 			ios.bnum = dp->bp->bnum;
595203831d35Sstevel 
595303831d35Sstevel 			if (pdip = ddi_get_parent(dip)) {
595403831d35Sstevel 				ndi_hold_devi(pdip);
595503831d35Sstevel 				ndi_devi_enter(pdip, &circ);
595603831d35Sstevel 			}
595703831d35Sstevel 			/*
595803831d35Sstevel 			 * Root node doesn't have to be held in any way.
595903831d35Sstevel 			 */
5960d3d50737SRafael Vanoni 			ddi_walk_devs(dip, drmach_board_find_io_insts,
5961d3d50737SRafael Vanoni 			    (void *)&ios);
596203831d35Sstevel 
596303831d35Sstevel 			if (pdip) {
596403831d35Sstevel 				ndi_devi_exit(pdip, circ);
596503831d35Sstevel 				ndi_rele_devi(pdip);
596603831d35Sstevel 			}
596703831d35Sstevel 
596803831d35Sstevel 			DRMACH_PR("drmach_io_unrelease: bnum=%d eri=0x%p\n",
596907d06da5SSurya Prakki 			    ios.bnum, (void *)ios.eri_dip);
597003831d35Sstevel 
597103831d35Sstevel 			if (ios.eri_dip) {
597203831d35Sstevel 				DRMACH_PR("calling man_dr_attach\n");
597303831d35Sstevel 				if ((*func)(ios.eri_dip))
5974d3d50737SRafael Vanoni 					err = drerr_new(0, ESTC_NWSWITCH, NULL);
597503831d35Sstevel 				/*
597603831d35Sstevel 				 * Release hold acquired in
597703831d35Sstevel 				 * drmach_board_find_io_insts()
597803831d35Sstevel 				 */
597903831d35Sstevel 				ndi_rele_devi(ios.eri_dip);
598003831d35Sstevel 			}
598103831d35Sstevel 		} else
598203831d35Sstevel 			DRMACH_PR("man_dr_attach NOT present\n");
598303831d35Sstevel 	}
598403831d35Sstevel 	return (err);
598503831d35Sstevel }
598603831d35Sstevel 
598703831d35Sstevel static sbd_error_t *
drmach_io_release(drmachid_t id)598803831d35Sstevel drmach_io_release(drmachid_t id)
598903831d35Sstevel {
599003831d35Sstevel 	dev_info_t	*dip;
599103831d35Sstevel 	sbd_error_t	*err = NULL;
599203831d35Sstevel 	drmach_device_t	*dp;
599303831d35Sstevel 
599403831d35Sstevel 	if (!DRMACH_IS_IO_ID(id))
599503831d35Sstevel 		return (drerr_new(0, ESTC_INAPPROP, NULL));
599603831d35Sstevel 	dp = id;
599703831d35Sstevel 
599803831d35Sstevel 	dip = dp->node->n_getdip(dp->node);
599903831d35Sstevel 
600003831d35Sstevel 	if (dip == NULL)
600103831d35Sstevel 		err = DRMACH_INTERNAL_ERROR();
600203831d35Sstevel 	else {
600303831d35Sstevel 		int (*func)(dev_info_t *dip);
600403831d35Sstevel 
600503831d35Sstevel 		func = (int (*)(dev_info_t *))kobj_getsymvalue("man_dr_detach",
600603831d35Sstevel 		    0);
600703831d35Sstevel 
600803831d35Sstevel 		if (func) {
600903831d35Sstevel 			drmach_io_inst_t ios;
601003831d35Sstevel 			dev_info_t	*pdip;
601103831d35Sstevel 			int		circ;
601203831d35Sstevel 
601303831d35Sstevel 			/*
601403831d35Sstevel 			 * Walk device tree to find rio dip for the board
601503831d35Sstevel 			 * Since we are not interested in iosram instance here,
601603831d35Sstevel 			 * initialize it to 0, so that the walk terminates as
601703831d35Sstevel 			 * soon as eri dip is found.
601803831d35Sstevel 			 */
601903831d35Sstevel 			ios.iosram_inst = 0;
602003831d35Sstevel 			ios.eri_dip = NULL;
602103831d35Sstevel 			ios.bnum = dp->bp->bnum;
602203831d35Sstevel 
602303831d35Sstevel 			if (pdip = ddi_get_parent(dip)) {
602403831d35Sstevel 				ndi_hold_devi(pdip);
602503831d35Sstevel 				ndi_devi_enter(pdip, &circ);
602603831d35Sstevel 			}
602703831d35Sstevel 			/*
602803831d35Sstevel 			 * Root node doesn't have to be held in any way.
602903831d35Sstevel 			 */
6030d3d50737SRafael Vanoni 			ddi_walk_devs(dip, drmach_board_find_io_insts,
6031d3d50737SRafael Vanoni 			    (void *)&ios);
603203831d35Sstevel 
603303831d35Sstevel 			if (pdip) {
603403831d35Sstevel 				ndi_devi_exit(pdip, circ);
603503831d35Sstevel 				ndi_rele_devi(pdip);
603603831d35Sstevel 			}
603703831d35Sstevel 
603803831d35Sstevel 			DRMACH_PR("drmach_io_release: bnum=%d eri=0x%p\n",
603907d06da5SSurya Prakki 			    ios.bnum, (void *)ios.eri_dip);
604003831d35Sstevel 
604103831d35Sstevel 			if (ios.eri_dip) {
604203831d35Sstevel 				DRMACH_PR("calling man_dr_detach\n");
604303831d35Sstevel 				if ((*func)(ios.eri_dip))
6044d3d50737SRafael Vanoni 					err = drerr_new(0, ESTC_NWSWITCH, NULL);
604503831d35Sstevel 				/*
604603831d35Sstevel 				 * Release hold acquired in
604703831d35Sstevel 				 * drmach_board_find_io_insts()
604803831d35Sstevel 				 */
604903831d35Sstevel 				ndi_rele_devi(ios.eri_dip);
605003831d35Sstevel 			}
605103831d35Sstevel 		} else
605203831d35Sstevel 			DRMACH_PR("man_dr_detach NOT present\n");
605303831d35Sstevel 	}
605403831d35Sstevel 	return (err);
605503831d35Sstevel }
605603831d35Sstevel 
605703831d35Sstevel sbd_error_t *
drmach_io_post_release(drmachid_t id)605803831d35Sstevel drmach_io_post_release(drmachid_t id)
605903831d35Sstevel {
606003831d35Sstevel 	char 		*path;
606103831d35Sstevel 	dev_info_t	*rdip;
606203831d35Sstevel 	drmach_device_t	*dp;
606303831d35Sstevel 
606403831d35Sstevel 	if (!DRMACH_IS_DEVICE_ID(id))
606503831d35Sstevel 		return (drerr_new(0, ESTC_INAPPROP, NULL));
606603831d35Sstevel 	dp = id;
606703831d35Sstevel 
606803831d35Sstevel 	rdip = dp->node->n_getdip(dp->node);
606903831d35Sstevel 
607003831d35Sstevel 	/*
607103831d35Sstevel 	 * Always called after drmach_unconfigure() which on Starcat
607203831d35Sstevel 	 * unconfigures the branch but doesn't remove it so the
607303831d35Sstevel 	 * dip must always exist.
607403831d35Sstevel 	 */
607503831d35Sstevel 	ASSERT(rdip);
607603831d35Sstevel 
607703831d35Sstevel 	ASSERT(e_ddi_branch_held(rdip));
607803831d35Sstevel #ifdef DEBUG
607903831d35Sstevel 	path = kmem_alloc(MAXPATHLEN, KM_SLEEP);
608003831d35Sstevel 	(void) ddi_pathname(rdip, path);
608103831d35Sstevel 	DRMACH_PR("post_release dip path is: %s\n", path);
608203831d35Sstevel 	kmem_free(path, MAXPATHLEN);
608303831d35Sstevel #endif
608403831d35Sstevel 
608503831d35Sstevel 	if (strcmp(dp->type, DRMACH_DEVTYPE_PCI) == 0) {
608603831d35Sstevel 		if (schpc_remove_pci(rdip)) {
608703831d35Sstevel 			DRMACH_PR("schpc_remove_pci failed\n");
608803831d35Sstevel 			return (drerr_new(0, ESBD_OFFLINE, NULL));
608903831d35Sstevel 		} else {
609003831d35Sstevel 			DRMACH_PR("schpc_remove_pci succeeded\n");
609103831d35Sstevel 		}
609203831d35Sstevel 	}
609303831d35Sstevel 
609403831d35Sstevel 	return (NULL);
609503831d35Sstevel }
609603831d35Sstevel 
609703831d35Sstevel sbd_error_t *
drmach_io_post_attach(drmachid_t id)609803831d35Sstevel drmach_io_post_attach(drmachid_t id)
609903831d35Sstevel {
610003831d35Sstevel 	int		circ;
610103831d35Sstevel 	dev_info_t	*dip;
610203831d35Sstevel 	dev_info_t	*pdip;
610303831d35Sstevel 	drmach_device_t	*dp;
610403831d35Sstevel 	drmach_io_inst_t ios;
610503831d35Sstevel 
610603831d35Sstevel 	if (!DRMACH_IS_DEVICE_ID(id))
610703831d35Sstevel 		return (drerr_new(0, ESTC_INAPPROP, NULL));
610803831d35Sstevel 	dp = id;
610903831d35Sstevel 
611003831d35Sstevel 	dip = dp->node->n_getdip(dp->node);
611103831d35Sstevel 
611203831d35Sstevel 	/*
611303831d35Sstevel 	 * We held the branch rooted at dip earlier, so at a minimum the
611403831d35Sstevel 	 * root i.e. dip must be present in the device tree.
611503831d35Sstevel 	 */
611603831d35Sstevel 	ASSERT(dip);
611703831d35Sstevel 
611803831d35Sstevel 	if (strcmp(dp->type, DRMACH_DEVTYPE_PCI) == 0) {
611903831d35Sstevel 		if (schpc_add_pci(dip)) {
612003831d35Sstevel 			DRMACH_PR("schpc_add_pci failed\n");
612103831d35Sstevel 		} else {
612203831d35Sstevel 			DRMACH_PR("schpc_add_pci succeeded\n");
612303831d35Sstevel 		}
612403831d35Sstevel 	}
612503831d35Sstevel 
612603831d35Sstevel 	/*
612703831d35Sstevel 	 * Walk device tree to find rio dip for the board
612803831d35Sstevel 	 * Since we are not interested in iosram instance here,
612903831d35Sstevel 	 * initialize it to 0, so that the walk terminates as
613003831d35Sstevel 	 * soon as eri dip is found.
613103831d35Sstevel 	 */
613203831d35Sstevel 	ios.iosram_inst = 0;
613303831d35Sstevel 	ios.eri_dip = NULL;
613403831d35Sstevel 	ios.bnum = dp->bp->bnum;
613503831d35Sstevel 
613603831d35Sstevel 	if (pdip = ddi_get_parent(dip)) {
613703831d35Sstevel 		ndi_hold_devi(pdip);
613803831d35Sstevel 		ndi_devi_enter(pdip, &circ);
613903831d35Sstevel 	}
614003831d35Sstevel 	/*
614103831d35Sstevel 	 * Root node doesn't have to be held in any way.
614203831d35Sstevel 	 */
6143d3d50737SRafael Vanoni 	ddi_walk_devs(dip, drmach_board_find_io_insts, (void *)&ios);
614403831d35Sstevel 	if (pdip) {
614503831d35Sstevel 		ndi_devi_exit(pdip, circ);
614603831d35Sstevel 		ndi_rele_devi(pdip);
614703831d35Sstevel 	}
614803831d35Sstevel 
614907d06da5SSurya Prakki 	DRMACH_PR("drmach_io_post_attach: bnum=%d eri=0x%p\n",
615007d06da5SSurya Prakki 	    ios.bnum, (void *)ios.eri_dip);
615103831d35Sstevel 
615203831d35Sstevel 	if (ios.eri_dip) {
615303831d35Sstevel 		int (*func)(dev_info_t *dip);
615403831d35Sstevel 
615503831d35Sstevel 		func =
615603831d35Sstevel 		    (int (*)(dev_info_t *))kobj_getsymvalue("man_dr_attach", 0);
615703831d35Sstevel 
615803831d35Sstevel 		if (func) {
615903831d35Sstevel 			DRMACH_PR("calling man_dr_attach\n");
616003831d35Sstevel 			(void) (*func)(ios.eri_dip);
616103831d35Sstevel 		} else {
616203831d35Sstevel 			DRMACH_PR("man_dr_attach NOT present\n");
616303831d35Sstevel 		}
616403831d35Sstevel 
616503831d35Sstevel 		/*
616603831d35Sstevel 		 * Release hold acquired in drmach_board_find_io_insts()
616703831d35Sstevel 		 */
616803831d35Sstevel 		ndi_rele_devi(ios.eri_dip);
616903831d35Sstevel 
617003831d35Sstevel 	}
617103831d35Sstevel 
617203831d35Sstevel 	return (NULL);
617303831d35Sstevel }
617403831d35Sstevel 
617503831d35Sstevel static sbd_error_t *
drmach_io_status(drmachid_t id,drmach_status_t * stat)617603831d35Sstevel drmach_io_status(drmachid_t id, drmach_status_t *stat)
617703831d35Sstevel {
617803831d35Sstevel 	drmach_device_t *dp;
617903831d35Sstevel 	sbd_error_t	*err;
618003831d35Sstevel 	int		 configured;
618103831d35Sstevel 
618203831d35Sstevel 	ASSERT(DRMACH_IS_IO_ID(id));
618303831d35Sstevel 	dp = id;
618403831d35Sstevel 
618503831d35Sstevel 	err = drmach_io_is_attached(id, &configured);
618603831d35Sstevel 	if (err)
618703831d35Sstevel 		return (err);
618803831d35Sstevel 
618903831d35Sstevel 	stat->assigned = dp->bp->assigned;
619003831d35Sstevel 	stat->powered = dp->bp->powered;
619103831d35Sstevel 	stat->configured = (configured != 0);
619203831d35Sstevel 	stat->busy = dp->busy;
619307d06da5SSurya Prakki 	(void) strncpy(stat->type, dp->type, sizeof (stat->type));
619403831d35Sstevel 	stat->info[0] = '\0';
619503831d35Sstevel 
619603831d35Sstevel 	return (NULL);
619703831d35Sstevel }
619803831d35Sstevel 
619903831d35Sstevel sbd_error_t *
drmach_mem_init_size(drmachid_t id)620003831d35Sstevel drmach_mem_init_size(drmachid_t id)
620103831d35Sstevel {
620203831d35Sstevel 	drmach_mem_t	*mp;
620303831d35Sstevel 	sbd_error_t	*err;
620403831d35Sstevel 	gdcd_t		*gdcd;
620503831d35Sstevel 	mem_chunk_t	*chunk;
620603831d35Sstevel 	uint64_t	 chunks, pa, mask, sz;
620703831d35Sstevel 
620803831d35Sstevel 	if (!DRMACH_IS_MEM_ID(id))
620903831d35Sstevel 		return (drerr_new(0, ESTC_INAPPROP, NULL));
621003831d35Sstevel 	mp = id;
621103831d35Sstevel 
621203831d35Sstevel 	err = drmach_mem_get_base_physaddr(id, &pa);
621303831d35Sstevel 	if (err)
621403831d35Sstevel 		return (err);
621503831d35Sstevel 
621603831d35Sstevel 	mask = ~ (DRMACH_MEM_SLICE_SIZE - 1);
621703831d35Sstevel 	pa &= mask;
621803831d35Sstevel 
621903831d35Sstevel 	gdcd = drmach_gdcd_new();
622003831d35Sstevel 	if (gdcd == NULL)
622103831d35Sstevel 		return (DRMACH_INTERNAL_ERROR());
622203831d35Sstevel 
622303831d35Sstevel 	sz = 0;
622403831d35Sstevel 	chunk = gdcd->dcd_chunk_list.dcl_chunk;
622503831d35Sstevel 	chunks = gdcd->dcd_chunk_list.dcl_chunks;
622603831d35Sstevel 	while (chunks-- != 0) {
622703831d35Sstevel 		if ((chunk->mc_base_pa & mask) == pa) {
622803831d35Sstevel 			sz += chunk->mc_mbytes * 1048576;
622903831d35Sstevel 		}
623003831d35Sstevel 
623103831d35Sstevel 		++chunk;
623203831d35Sstevel 	}
623303831d35Sstevel 	mp->nbytes = sz;
623403831d35Sstevel 
623503831d35Sstevel 	drmach_gdcd_dispose(gdcd);
623603831d35Sstevel 	return (NULL);
623703831d35Sstevel }
623803831d35Sstevel 
623903831d35Sstevel /*
624003831d35Sstevel  * Hardware registers are organized into consecutively
624103831d35Sstevel  * addressed registers.  The reg property's hi and lo fields
624203831d35Sstevel  * together describe the base address of the register set for
624303831d35Sstevel  * this memory-controller.  Register descriptions and offsets
624403831d35Sstevel  * (from the base address) are as follows:
624503831d35Sstevel  *
624603831d35Sstevel  * Description				Offset	Size (bytes)
624703831d35Sstevel  * Memory Timing Control Register I	0x00	8
624803831d35Sstevel  * Memory Timing Control Register II	0x08	8
624903831d35Sstevel  * Memory Address Decoding Register I	0x10	8
625003831d35Sstevel  * Memory Address Decoding Register II	0x18	8
625103831d35Sstevel  * Memory Address Decoding Register III	0x20	8
625203831d35Sstevel  * Memory Address Decoding Register IV	0x28	8
625303831d35Sstevel  * Memory Address Control Register	0x30	8
625403831d35Sstevel  * Memory Timing Control Register III	0x38	8
625503831d35Sstevel  * Memory Timing Control Register IV	0x40	8
625603831d35Sstevel  * Memory Timing Control Register V  	0x48	8 (Jaguar, Panther only)
625703831d35Sstevel  * EMU Activity Status Register		0x50	8 (Panther only)
625803831d35Sstevel  *
625903831d35Sstevel  * Only the Memory Address Decoding Register and EMU Activity Status
626003831d35Sstevel  * Register addresses are needed for DRMACH.
626103831d35Sstevel  */
626203831d35Sstevel static sbd_error_t *
drmach_mem_new(drmach_device_t * proto,drmachid_t * idp)626303831d35Sstevel drmach_mem_new(drmach_device_t *proto, drmachid_t *idp)
626403831d35Sstevel {
626503831d35Sstevel 	sbd_error_t	*err;
626603831d35Sstevel 	uint64_t	 madr_pa;
626703831d35Sstevel 	drmach_mem_t	*mp;
626803831d35Sstevel 	int		 bank, count;
626903831d35Sstevel 
627003831d35Sstevel 	err = drmach_read_reg_addr(proto, &madr_pa);
627103831d35Sstevel 	if (err)
627203831d35Sstevel 		return (err);
627303831d35Sstevel 
627403831d35Sstevel 	mp = kmem_zalloc(sizeof (drmach_mem_t), KM_SLEEP);
627503831d35Sstevel 	bcopy(proto, &mp->dev, sizeof (mp->dev));
627603831d35Sstevel 	mp->dev.node = drmach_node_dup(proto->node);
627703831d35Sstevel 	mp->dev.cm.isa = (void *)drmach_mem_new;
627803831d35Sstevel 	mp->dev.cm.dispose = drmach_mem_dispose;
627903831d35Sstevel 	mp->dev.cm.release = drmach_mem_release;
628003831d35Sstevel 	mp->dev.cm.status = drmach_mem_status;
628103831d35Sstevel 	mp->madr_pa = madr_pa;
628203831d35Sstevel 
628307d06da5SSurya Prakki 	(void) snprintf(mp->dev.cm.name,
628407d06da5SSurya Prakki 	    sizeof (mp->dev.cm.name), "%s", mp->dev.type);
628503831d35Sstevel 
628603831d35Sstevel 	for (count = bank = 0; bank < DRMACH_MC_NBANKS; bank++) {
628703831d35Sstevel 		uint64_t madr;
628803831d35Sstevel 
628903831d35Sstevel 		drmach_mem_read_madr(mp, bank, &madr);
629003831d35Sstevel 		if (madr & DRMACH_MC_VALID_MASK) {
629103831d35Sstevel 			count += 1;
629203831d35Sstevel 			break;
629303831d35Sstevel 		}
629403831d35Sstevel 	}
629503831d35Sstevel 
629603831d35Sstevel 	/*
629703831d35Sstevel 	 * If none of the banks had their valid bit set, that means
629803831d35Sstevel 	 * post did not configure this MC to participate in the
629903831d35Sstevel 	 * domain.  So, pretend this node does not exist by returning
630003831d35Sstevel 	 * a drmachid of zero.
630103831d35Sstevel 	 */
630203831d35Sstevel 	if (count == 0) {
630303831d35Sstevel 		/* drmach_mem_dispose frees board mem list */
630403831d35Sstevel 		drmach_node_dispose(mp->dev.node);
630503831d35Sstevel 		kmem_free(mp, sizeof (*mp));
630603831d35Sstevel 		*idp = (drmachid_t)0;
630703831d35Sstevel 		return (NULL);
630803831d35Sstevel 	}
630903831d35Sstevel 
631003831d35Sstevel 	/*
631103831d35Sstevel 	 * Only one mem unit per board is exposed to the
631203831d35Sstevel 	 * PIM layer.  The first mem unit encountered during
631303831d35Sstevel 	 * tree walk is used to represent all mem units on
631403831d35Sstevel 	 * the same board.
631503831d35Sstevel 	 */
631603831d35Sstevel 	if (mp->dev.bp->mem == NULL) {
631703831d35Sstevel 		/* start list of mem units on this board */
631803831d35Sstevel 		mp->dev.bp->mem = mp;
631903831d35Sstevel 
632003831d35Sstevel 		/*
632103831d35Sstevel 		 * force unum to zero since this is the only mem unit
632203831d35Sstevel 		 * that will be visible to the PIM layer.
632303831d35Sstevel 		 */
632403831d35Sstevel 		mp->dev.unum = 0;
632503831d35Sstevel 
632603831d35Sstevel 		/*
632703831d35Sstevel 		 * board memory size kept in this mem unit only
632803831d35Sstevel 		 */
632903831d35Sstevel 		err = drmach_mem_init_size(mp);
633003831d35Sstevel 		if (err) {
633103831d35Sstevel 			mp->dev.bp->mem = NULL;
633203831d35Sstevel 			/* drmach_mem_dispose frees board mem list */
633303831d35Sstevel 			drmach_node_dispose(mp->dev.node);
633403831d35Sstevel 			kmem_free(mp, sizeof (*mp));
633503831d35Sstevel 			*idp = (drmachid_t)0;
633603831d35Sstevel 			return (NULL);
633703831d35Sstevel 		}
633803831d35Sstevel 
633903831d35Sstevel 		/*
634003831d35Sstevel 		 * allow this instance (the first encountered on this board)
634103831d35Sstevel 		 * to be visible to the PIM layer.
634203831d35Sstevel 		 */
634303831d35Sstevel 		*idp = (drmachid_t)mp;
634403831d35Sstevel 	} else {
634503831d35Sstevel 		drmach_mem_t *lp;
634603831d35Sstevel 
634703831d35Sstevel 		/* hide this mem instance behind the first. */
634803831d35Sstevel 		for (lp = mp->dev.bp->mem; lp->next; lp = lp->next)
634903831d35Sstevel 			;
635003831d35Sstevel 		lp->next = mp;
635103831d35Sstevel 
635203831d35Sstevel 		/*
635303831d35Sstevel 		 * hide this instance from the caller.
635403831d35Sstevel 		 * See drmach_board_find_devices_cb() for details.
635503831d35Sstevel 		 */
635603831d35Sstevel 		*idp = (drmachid_t)0;
635703831d35Sstevel 	}
635803831d35Sstevel 
635903831d35Sstevel 	return (NULL);
636003831d35Sstevel }
636103831d35Sstevel 
636203831d35Sstevel static void
drmach_mem_dispose(drmachid_t id)636303831d35Sstevel drmach_mem_dispose(drmachid_t id)
636403831d35Sstevel {
636503831d35Sstevel 	drmach_mem_t *mp, *next;
636603831d35Sstevel 	drmach_board_t *bp;
636703831d35Sstevel 
636803831d35Sstevel 	ASSERT(DRMACH_IS_MEM_ID(id));
636903831d35Sstevel 
637003831d35Sstevel 	mutex_enter(&drmach_bus_sync_lock);
637103831d35Sstevel 
637203831d35Sstevel 	mp = id;
637303831d35Sstevel 	bp = mp->dev.bp;
637403831d35Sstevel 
637503831d35Sstevel 	do {
637603831d35Sstevel 		if (mp->dev.node)
637703831d35Sstevel 			drmach_node_dispose(mp->dev.node);
637803831d35Sstevel 
637903831d35Sstevel 		next = mp->next;
638003831d35Sstevel 		kmem_free(mp, sizeof (*mp));
638103831d35Sstevel 		mp = next;
638203831d35Sstevel 	} while (mp);
638303831d35Sstevel 
638403831d35Sstevel 	bp->mem = NULL;
638503831d35Sstevel 
638603831d35Sstevel 	drmach_bus_sync_list_update();
638703831d35Sstevel 	mutex_exit(&drmach_bus_sync_lock);
638803831d35Sstevel }
638903831d35Sstevel 
639003831d35Sstevel sbd_error_t *
drmach_mem_add_span(drmachid_t id,uint64_t basepa,uint64_t size)639103831d35Sstevel drmach_mem_add_span(drmachid_t id, uint64_t basepa, uint64_t size)
639203831d35Sstevel {
639303831d35Sstevel 	pfn_t		basepfn = (pfn_t)(basepa >> PAGESHIFT);
639403831d35Sstevel 	pgcnt_t		npages = (pgcnt_t)(size >> PAGESHIFT);
639503831d35Sstevel 	int		rv;
639603831d35Sstevel 
639703831d35Sstevel 	ASSERT(size != 0);
639803831d35Sstevel 
639903831d35Sstevel 	if (!DRMACH_IS_MEM_ID(id))
640003831d35Sstevel 		return (drerr_new(0, ESTC_INAPPROP, NULL));
640103831d35Sstevel 
640285f58038Sdp78419 	rv = kcage_range_add(basepfn, npages, KCAGE_DOWN);
640303831d35Sstevel 	if (rv == ENOMEM) {
640403831d35Sstevel 		cmn_err(CE_WARN, "%lu megabytes not available"
640503831d35Sstevel 		    " to kernel cage", size >> 20);
640603831d35Sstevel 	} else if (rv != 0) {
640703831d35Sstevel 		/* catch this in debug kernels */
640803831d35Sstevel 		ASSERT(0);
640903831d35Sstevel 
641003831d35Sstevel 		cmn_err(CE_WARN, "unexpected kcage_range_add"
641103831d35Sstevel 		    " return value %d", rv);
641203831d35Sstevel 	}
641303831d35Sstevel 
641403831d35Sstevel 	return (NULL);
641503831d35Sstevel }
641603831d35Sstevel 
641703831d35Sstevel sbd_error_t *
drmach_mem_del_span(drmachid_t id,uint64_t basepa,uint64_t size)641803831d35Sstevel drmach_mem_del_span(drmachid_t id, uint64_t basepa, uint64_t size)
641903831d35Sstevel {
642003831d35Sstevel 	pfn_t		 basepfn = (pfn_t)(basepa >> PAGESHIFT);
642103831d35Sstevel 	pgcnt_t		 npages = (pgcnt_t)(size >> PAGESHIFT);
642203831d35Sstevel 	int		 rv;
642303831d35Sstevel 
642403831d35Sstevel 	if (!DRMACH_IS_MEM_ID(id))
642503831d35Sstevel 		return (drerr_new(0, ESTC_INAPPROP, NULL));
642603831d35Sstevel 
642703831d35Sstevel 	if (size > 0) {
642803831d35Sstevel 		rv = kcage_range_delete_post_mem_del(basepfn, npages);
642903831d35Sstevel 		if (rv != 0) {
643003831d35Sstevel 			cmn_err(CE_WARN,
643103831d35Sstevel 			    "unexpected kcage_range_delete_post_mem_del"
643203831d35Sstevel 			    " return value %d", rv);
643303831d35Sstevel 			return (DRMACH_INTERNAL_ERROR());
643403831d35Sstevel 		}
643503831d35Sstevel 	}
643603831d35Sstevel 
643703831d35Sstevel 	return (NULL);
643803831d35Sstevel }
643903831d35Sstevel 
644003831d35Sstevel sbd_error_t *
drmach_mem_disable(drmachid_t id)644103831d35Sstevel drmach_mem_disable(drmachid_t id)
644203831d35Sstevel {
644303831d35Sstevel 	if (!DRMACH_IS_MEM_ID(id))
644403831d35Sstevel 		return (drerr_new(0, ESTC_INAPPROP, NULL));
644503831d35Sstevel 	else
644603831d35Sstevel 		return (NULL);
644703831d35Sstevel }
644803831d35Sstevel 
644903831d35Sstevel sbd_error_t *
drmach_mem_enable(drmachid_t id)645003831d35Sstevel drmach_mem_enable(drmachid_t id)
645103831d35Sstevel {
645203831d35Sstevel 	if (!DRMACH_IS_MEM_ID(id))
645303831d35Sstevel 		return (drerr_new(0, ESTC_INAPPROP, NULL));
645403831d35Sstevel 	else
645503831d35Sstevel 		return (NULL);
645603831d35Sstevel }
645703831d35Sstevel 
645803831d35Sstevel sbd_error_t *
drmach_mem_get_alignment(drmachid_t id,uint64_t * mask)645903831d35Sstevel drmach_mem_get_alignment(drmachid_t id, uint64_t *mask)
646003831d35Sstevel {
646103831d35Sstevel #define	MB(mb) ((mb) * 1048576ull)
646203831d35Sstevel 
646303831d35Sstevel 	static struct {
646403831d35Sstevel 		uint_t		uk;
646503831d35Sstevel 		uint64_t	segsz;
646603831d35Sstevel 	}  uk2segsz[] = {
646703831d35Sstevel 		{ 0x003,	MB(256)	  },
646803831d35Sstevel 		{ 0x007,	MB(512)	  },
646903831d35Sstevel 		{ 0x00f,	MB(1024)  },
647003831d35Sstevel 		{ 0x01f,	MB(2048)  },
647103831d35Sstevel 		{ 0x03f,	MB(4096)  },
647203831d35Sstevel 		{ 0x07f,	MB(8192)  },
647303831d35Sstevel 		{ 0x0ff,	MB(16384) },
647403831d35Sstevel 		{ 0x1ff,	MB(32768) },
647503831d35Sstevel 		{ 0x3ff,	MB(65536) },
647603831d35Sstevel 		{ 0x7ff,	MB(131072) }
647703831d35Sstevel 	};
647803831d35Sstevel 	static int len = sizeof (uk2segsz) / sizeof (uk2segsz[0]);
647903831d35Sstevel 
648003831d35Sstevel #undef MB
648103831d35Sstevel 
648203831d35Sstevel 	uint64_t	 largest_sz = 0;
648303831d35Sstevel 	drmach_mem_t	*mp;
648403831d35Sstevel 
648503831d35Sstevel 	if (!DRMACH_IS_MEM_ID(id))
648603831d35Sstevel 		return (drerr_new(0, ESTC_INAPPROP, NULL));
648703831d35Sstevel 
648803831d35Sstevel 	/* prime the result with a default value */
648903831d35Sstevel 	*mask = (DRMACH_MEM_SLICE_SIZE - 1);
649003831d35Sstevel 
649103831d35Sstevel 	for (mp = id; mp; mp = mp->next) {
649203831d35Sstevel 		int bank;
649303831d35Sstevel 
649403831d35Sstevel 		for (bank = 0; bank < DRMACH_MC_NBANKS; bank++) {
649503831d35Sstevel 			int		i;
649603831d35Sstevel 			uint_t		uk;
649703831d35Sstevel 			uint64_t	madr;
649803831d35Sstevel 
649903831d35Sstevel 			/* get register value, extract uk and normalize */
650003831d35Sstevel 			drmach_mem_read_madr(mp, bank, &madr);
650103831d35Sstevel 
650203831d35Sstevel 			if (!(madr & DRMACH_MC_VALID_MASK))
650303831d35Sstevel 				continue;
650403831d35Sstevel 
650503831d35Sstevel 			uk = DRMACH_MC_UK(madr);
650603831d35Sstevel 
650703831d35Sstevel 			/* match uk value */
650803831d35Sstevel 			for (i = 0; i < len; i++)
650903831d35Sstevel 				if (uk == uk2segsz[i].uk)
651003831d35Sstevel 					break;
651103831d35Sstevel 
651203831d35Sstevel 			if (i < len) {
651303831d35Sstevel 				uint64_t sz = uk2segsz[i].segsz;
651403831d35Sstevel 
651503831d35Sstevel 				/*
651603831d35Sstevel 				 * remember largest segment size,
651703831d35Sstevel 				 * update mask result
651803831d35Sstevel 				 */
651903831d35Sstevel 				if (sz > largest_sz) {
652003831d35Sstevel 					largest_sz = sz;
652103831d35Sstevel 					*mask = sz - 1;
652203831d35Sstevel 				}
652303831d35Sstevel 			} else {
652403831d35Sstevel 				/*
652503831d35Sstevel 				 * uk not in table, punt using
652603831d35Sstevel 				 * entire slice size. no longer any
652703831d35Sstevel 				 * reason to check other banks.
652803831d35Sstevel 				 */
652903831d35Sstevel 				*mask = (DRMACH_MEM_SLICE_SIZE - 1);
653003831d35Sstevel 				return (NULL);
653103831d35Sstevel 			}
653203831d35Sstevel 		}
653303831d35Sstevel 	}
653403831d35Sstevel 
653503831d35Sstevel 	return (NULL);
653603831d35Sstevel }
653703831d35Sstevel 
653803831d35Sstevel sbd_error_t *
drmach_mem_get_base_physaddr(drmachid_t id,uint64_t * base_addr)653903831d35Sstevel drmach_mem_get_base_physaddr(drmachid_t id, uint64_t *base_addr)
654003831d35Sstevel {
654103831d35Sstevel 	drmach_mem_t *mp;
654203831d35Sstevel 
654303831d35Sstevel 	if (!DRMACH_IS_MEM_ID(id))
654403831d35Sstevel 		return (drerr_new(0, ESTC_INAPPROP, NULL));
654503831d35Sstevel 
654603831d35Sstevel 	*base_addr = (uint64_t)-1;
654703831d35Sstevel 	for (mp = id; mp; mp = mp->next) {
654803831d35Sstevel 		int bank;
654903831d35Sstevel 
655003831d35Sstevel 		for (bank = 0; bank < DRMACH_MC_NBANKS; bank++) {
655103831d35Sstevel 			uint64_t addr, madr;
655203831d35Sstevel 
655303831d35Sstevel 			drmach_mem_read_madr(mp, bank, &madr);
655403831d35Sstevel 			if (madr & DRMACH_MC_VALID_MASK) {
655503831d35Sstevel 				addr = DRMACH_MC_UM_TO_PA(madr) |
655603831d35Sstevel 				    DRMACH_MC_LM_TO_PA(madr);
655703831d35Sstevel 
655803831d35Sstevel 				if (addr < *base_addr)
655903831d35Sstevel 					*base_addr = addr;
656003831d35Sstevel 			}
656103831d35Sstevel 		}
656203831d35Sstevel 	}
656303831d35Sstevel 
656403831d35Sstevel 	/* should not happen, but ... */
656503831d35Sstevel 	if (*base_addr == (uint64_t)-1)
656603831d35Sstevel 		return (DRMACH_INTERNAL_ERROR());
656703831d35Sstevel 
656803831d35Sstevel 	return (NULL);
656903831d35Sstevel }
657003831d35Sstevel 
657103831d35Sstevel void
drmach_bus_sync_list_update(void)657203831d35Sstevel drmach_bus_sync_list_update(void)
657303831d35Sstevel {
657403831d35Sstevel 	int		rv, idx, cnt = 0;
657503831d35Sstevel 	drmachid_t	id;
657603831d35Sstevel 
657703831d35Sstevel 	ASSERT(MUTEX_HELD(&drmach_bus_sync_lock));
657803831d35Sstevel 
657903831d35Sstevel 	rv = drmach_array_first(drmach_boards, &idx, &id);
658003831d35Sstevel 	while (rv == 0) {
658103831d35Sstevel 		drmach_board_t		*bp = id;
658203831d35Sstevel 		drmach_mem_t		*mp = bp->mem;
658303831d35Sstevel 
658403831d35Sstevel 		while (mp) {
658503831d35Sstevel 			int bank;
658603831d35Sstevel 
658703831d35Sstevel 			for (bank = 0; bank < DRMACH_MC_NBANKS; bank++) {
658803831d35Sstevel 				uint64_t madr;
658903831d35Sstevel 
659003831d35Sstevel 				drmach_mem_read_madr(mp, bank, &madr);
659103831d35Sstevel 				if (madr & DRMACH_MC_VALID_MASK) {
659203831d35Sstevel 					uint64_t pa;
659303831d35Sstevel 
659403831d35Sstevel 					pa  = DRMACH_MC_UM_TO_PA(madr);
659503831d35Sstevel 					pa |= DRMACH_MC_LM_TO_PA(madr);
659603831d35Sstevel 
659703831d35Sstevel 					/*
659803831d35Sstevel 					 * The list is zero terminated.
659903831d35Sstevel 					 * Offset the pa by a doubleword
660003831d35Sstevel 					 * to avoid confusing a pa value of
660103831d35Sstevel 					 * of zero with the terminator.
660203831d35Sstevel 					 */
660303831d35Sstevel 					pa += sizeof (uint64_t);
660403831d35Sstevel 
660503831d35Sstevel 					drmach_bus_sync_list[cnt++] = pa;
660603831d35Sstevel 				}
660703831d35Sstevel 			}
660803831d35Sstevel 
660903831d35Sstevel 			mp = mp->next;
661003831d35Sstevel 		}
661103831d35Sstevel 
661203831d35Sstevel 		rv = drmach_array_next(drmach_boards, &idx, &id);
661303831d35Sstevel 	}
661403831d35Sstevel 
661503831d35Sstevel 	drmach_bus_sync_list[cnt] = 0;
661603831d35Sstevel }
661703831d35Sstevel 
661803831d35Sstevel sbd_error_t *
drmach_mem_get_memlist(drmachid_t id,struct memlist ** ml)661903831d35Sstevel drmach_mem_get_memlist(drmachid_t id, struct memlist **ml)
662003831d35Sstevel {
662103831d35Sstevel 	sbd_error_t	*err;
662203831d35Sstevel 	struct memlist	*mlist;
662303831d35Sstevel 	gdcd_t		*gdcd;
662403831d35Sstevel 	mem_chunk_t	*chunk;
662503831d35Sstevel 	uint64_t	 chunks, pa, mask;
662603831d35Sstevel 
662703831d35Sstevel 	err = drmach_mem_get_base_physaddr(id, &pa);
662803831d35Sstevel 	if (err)
662903831d35Sstevel 		return (err);
663003831d35Sstevel 
663103831d35Sstevel 	gdcd = drmach_gdcd_new();
663203831d35Sstevel 	if (gdcd == NULL)
663303831d35Sstevel 		return (DRMACH_INTERNAL_ERROR());
663403831d35Sstevel 
663503831d35Sstevel 	mask = ~ (DRMACH_MEM_SLICE_SIZE - 1);
663603831d35Sstevel 	pa &= mask;
663703831d35Sstevel 
663803831d35Sstevel 	mlist = NULL;
663903831d35Sstevel 	chunk = gdcd->dcd_chunk_list.dcl_chunk;
664003831d35Sstevel 	chunks = gdcd->dcd_chunk_list.dcl_chunks;
664103831d35Sstevel 	while (chunks-- != 0) {
664203831d35Sstevel 		if ((chunk->mc_base_pa & mask) == pa) {
6643d3d50737SRafael Vanoni 			mlist = memlist_add_span(mlist, chunk->mc_base_pa,
6644d3d50737SRafael Vanoni 			    chunk->mc_mbytes * 1048576);
664503831d35Sstevel 		}
664603831d35Sstevel 
664703831d35Sstevel 		++chunk;
664803831d35Sstevel 	}
664903831d35Sstevel 
665003831d35Sstevel 	drmach_gdcd_dispose(gdcd);
665103831d35Sstevel 
665203831d35Sstevel #ifdef DEBUG
665303831d35Sstevel 	DRMACH_PR("GDCD derived memlist:");
665403831d35Sstevel 	memlist_dump(mlist);
665503831d35Sstevel #endif
665603831d35Sstevel 
665703831d35Sstevel 	*ml = mlist;
665803831d35Sstevel 	return (NULL);
665903831d35Sstevel }
666003831d35Sstevel 
666103831d35Sstevel sbd_error_t *
drmach_mem_get_size(drmachid_t id,uint64_t * bytes)666203831d35Sstevel drmach_mem_get_size(drmachid_t id, uint64_t *bytes)
666303831d35Sstevel {
666403831d35Sstevel 	drmach_mem_t	*mp;
666503831d35Sstevel 
666603831d35Sstevel 	if (!DRMACH_IS_MEM_ID(id))
666703831d35Sstevel 		return (drerr_new(0, ESTC_INAPPROP, NULL));
666803831d35Sstevel 	mp = id;
666903831d35Sstevel 
667003831d35Sstevel 	ASSERT(mp->nbytes != 0);
667103831d35Sstevel 	*bytes = mp->nbytes;
667203831d35Sstevel 
667303831d35Sstevel 	return (NULL);
667403831d35Sstevel }
667503831d35Sstevel 
667603831d35Sstevel sbd_error_t *
drmach_mem_get_slice_size(drmachid_t id,uint64_t * bytes)667703831d35Sstevel drmach_mem_get_slice_size(drmachid_t id, uint64_t *bytes)
667803831d35Sstevel {
667903831d35Sstevel 	sbd_error_t	*err;
668003831d35Sstevel 	drmach_device_t	*mp;
668103831d35Sstevel 
668203831d35Sstevel 	if (!DRMACH_IS_MEM_ID(id))
668303831d35Sstevel 		return (drerr_new(0, ESTC_INAPPROP, NULL));
668403831d35Sstevel 	mp = id;
668503831d35Sstevel 
668603831d35Sstevel 	switch (DRMACH_BNUM2SLOT(mp->bp->bnum)) {
668703831d35Sstevel 		case 0:	*bytes = DRMACH_MEM_USABLE_SLICE_SIZE;
668803831d35Sstevel 			err = NULL;
668903831d35Sstevel 			break;
669003831d35Sstevel 
669103831d35Sstevel 		case 1: *bytes = 0;
669203831d35Sstevel 			err = NULL;
669303831d35Sstevel 			break;
669403831d35Sstevel 
669503831d35Sstevel 		default:
669603831d35Sstevel 			err = DRMACH_INTERNAL_ERROR();
669703831d35Sstevel 			break;
669803831d35Sstevel 	}
669903831d35Sstevel 
670003831d35Sstevel 	return (err);
670103831d35Sstevel }
670203831d35Sstevel 
670303831d35Sstevel processorid_t drmach_mem_cpu_affinity_nail;
670403831d35Sstevel 
670503831d35Sstevel processorid_t
drmach_mem_cpu_affinity(drmachid_t id)670603831d35Sstevel drmach_mem_cpu_affinity(drmachid_t id)
670703831d35Sstevel {
670803831d35Sstevel 	drmach_device_t	*mp;
670903831d35Sstevel 	drmach_board_t	*bp;
671003831d35Sstevel 	processorid_t	 cpuid;
671103831d35Sstevel 
671203831d35Sstevel 	if (!DRMACH_IS_MEM_ID(id))
671303831d35Sstevel 		return (CPU_CURRENT);
671403831d35Sstevel 
671503831d35Sstevel 	if (drmach_mem_cpu_affinity_nail) {
671603831d35Sstevel 		cpuid = drmach_mem_cpu_affinity_nail;
671703831d35Sstevel 
671803831d35Sstevel 		if (cpuid < 0 || cpuid > NCPU)
671903831d35Sstevel 			return (CPU_CURRENT);
672003831d35Sstevel 
672103831d35Sstevel 		mutex_enter(&cpu_lock);
672203831d35Sstevel 		if (cpu[cpuid] == NULL || !CPU_ACTIVE(cpu[cpuid]))
672303831d35Sstevel 			cpuid = CPU_CURRENT;
672403831d35Sstevel 		mutex_exit(&cpu_lock);
672503831d35Sstevel 
672603831d35Sstevel 		return (cpuid);
672703831d35Sstevel 	}
672803831d35Sstevel 
672903831d35Sstevel 	/* try to choose a proc on the target board */
673003831d35Sstevel 	mp = id;
673103831d35Sstevel 	bp = mp->bp;
673203831d35Sstevel 	if (bp->devices) {
673303831d35Sstevel 		int		 rv;
673403831d35Sstevel 		int		 d_idx;
673503831d35Sstevel 		drmachid_t	 d_id;
673603831d35Sstevel 
673703831d35Sstevel 		rv = drmach_array_first(bp->devices, &d_idx, &d_id);
673803831d35Sstevel 		while (rv == 0) {
673903831d35Sstevel 			if (DRMACH_IS_CPU_ID(d_id)) {
674003831d35Sstevel 				drmach_cpu_t	*cp = d_id;
674103831d35Sstevel 
674203831d35Sstevel 				mutex_enter(&cpu_lock);
674303831d35Sstevel 				cpuid = cp->cpuid;
674403831d35Sstevel 				if (cpu[cpuid] && CPU_ACTIVE(cpu[cpuid])) {
674503831d35Sstevel 					mutex_exit(&cpu_lock);
674603831d35Sstevel 					return (cpuid);
674703831d35Sstevel 				} else {
674803831d35Sstevel 					mutex_exit(&cpu_lock);
674903831d35Sstevel 				}
675003831d35Sstevel 			}
675103831d35Sstevel 
675203831d35Sstevel 			rv = drmach_array_next(bp->devices, &d_idx, &d_id);
675303831d35Sstevel 		}
675403831d35Sstevel 	}
675503831d35Sstevel 
675603831d35Sstevel 	/* otherwise, this proc, wherever it is */
675703831d35Sstevel 	return (CPU_CURRENT);
675803831d35Sstevel }
675903831d35Sstevel 
676003831d35Sstevel static sbd_error_t *
drmach_mem_release(drmachid_t id)676103831d35Sstevel drmach_mem_release(drmachid_t id)
676203831d35Sstevel {
676303831d35Sstevel 	if (!DRMACH_IS_MEM_ID(id))
676403831d35Sstevel 		return (drerr_new(0, ESTC_INAPPROP, NULL));
676503831d35Sstevel 	return (NULL);
676603831d35Sstevel }
676703831d35Sstevel 
676803831d35Sstevel static sbd_error_t *
drmach_mem_status(drmachid_t id,drmach_status_t * stat)676903831d35Sstevel drmach_mem_status(drmachid_t id, drmach_status_t *stat)
677003831d35Sstevel {
677103831d35Sstevel 	drmach_mem_t	*mp;
677203831d35Sstevel 	sbd_error_t	*err;
677303831d35Sstevel 	uint64_t	 pa, slice_size;
677403831d35Sstevel 	struct memlist	*ml;
677503831d35Sstevel 
677603831d35Sstevel 	ASSERT(DRMACH_IS_MEM_ID(id));
677703831d35Sstevel 	mp = id;
677803831d35Sstevel 
677903831d35Sstevel 	/* get starting physical address of target memory */
678003831d35Sstevel 	err = drmach_mem_get_base_physaddr(id, &pa);
678103831d35Sstevel 	if (err)
678203831d35Sstevel 		return (err);
678303831d35Sstevel 
678403831d35Sstevel 	/* round down to slice boundary */
678503831d35Sstevel 	slice_size = DRMACH_MEM_SLICE_SIZE;
678603831d35Sstevel 	pa &= ~ (slice_size - 1);
678703831d35Sstevel 
678803831d35Sstevel 	/* stop at first span that is in slice */
678903831d35Sstevel 	memlist_read_lock();
679056f33205SJonathan Adams 	for (ml = phys_install; ml; ml = ml->ml_next)
679156f33205SJonathan Adams 		if (ml->ml_address >= pa && ml->ml_address < pa + slice_size)
679203831d35Sstevel 			break;
679303831d35Sstevel 	memlist_read_unlock();
679403831d35Sstevel 
679503831d35Sstevel 	stat->assigned = mp->dev.bp->assigned;
679603831d35Sstevel 	stat->powered = mp->dev.bp->powered;
679703831d35Sstevel 	stat->configured = (ml != NULL);
679803831d35Sstevel 	stat->busy = mp->dev.busy;
679907d06da5SSurya Prakki 	(void) strncpy(stat->type, mp->dev.type, sizeof (stat->type));
680003831d35Sstevel 	stat->info[0] = '\0';
680103831d35Sstevel 
680203831d35Sstevel 	return (NULL);
680303831d35Sstevel }
680403831d35Sstevel 
680503831d35Sstevel sbd_error_t *
drmach_board_deprobe(drmachid_t id)680603831d35Sstevel drmach_board_deprobe(drmachid_t id)
680703831d35Sstevel {
680803831d35Sstevel 	drmach_board_t	*bp;
680903831d35Sstevel 	sbd_error_t	*err = NULL;
681003831d35Sstevel 
681103831d35Sstevel 	if (!DRMACH_IS_BOARD_ID(id))
681203831d35Sstevel 		return (drerr_new(0, ESTC_INAPPROP, NULL));
681303831d35Sstevel 	bp = id;
681403831d35Sstevel 
681503831d35Sstevel 	if (bp->tree) {
681603831d35Sstevel 		drmach_node_dispose(bp->tree);
681703831d35Sstevel 		bp->tree = NULL;
681803831d35Sstevel 	}
681903831d35Sstevel 	if (bp->devices) {
682003831d35Sstevel 		drmach_array_dispose(bp->devices, drmach_device_dispose);
682103831d35Sstevel 		bp->devices = NULL;
682203831d35Sstevel 		bp->mem = NULL;  /* TODO: still needed? */
682303831d35Sstevel 	}
682403831d35Sstevel 	return (err);
682503831d35Sstevel }
682603831d35Sstevel 
682703831d35Sstevel /*ARGSUSED1*/
682803831d35Sstevel static sbd_error_t *
drmach_pt_showlpa(drmachid_t id,drmach_opts_t * opts)682903831d35Sstevel drmach_pt_showlpa(drmachid_t id, drmach_opts_t *opts)
683003831d35Sstevel {
683103831d35Sstevel 	drmach_device_t	*dp;
683203831d35Sstevel 	uint64_t	val;
683303831d35Sstevel 	int		err = 1;
683403831d35Sstevel 
683503831d35Sstevel 	if (DRMACH_IS_CPU_ID(id)) {
683603831d35Sstevel 		drmach_cpu_t *cp = id;
683703831d35Sstevel 		if (drmach_cpu_read_scr(cp, &val))
683803831d35Sstevel 			err = 0;
683903831d35Sstevel 	} else if (DRMACH_IS_IO_ID(id) && ((drmach_io_t *)id)->scsr_pa != 0) {
684003831d35Sstevel 		drmach_io_t *io = id;
684103831d35Sstevel 		val = lddphysio(io->scsr_pa);
684203831d35Sstevel 		err = 0;
684303831d35Sstevel 	}
684403831d35Sstevel 	if (err)
684503831d35Sstevel 		return (drerr_new(0, ESTC_INAPPROP, NULL));
684603831d35Sstevel 
684703831d35Sstevel 	dp = id;
684803831d35Sstevel 	uprintf("showlpa %s::%s portid %d, base pa %lx, bound pa %lx\n",
684903831d35Sstevel 	    dp->bp->cm.name,
685003831d35Sstevel 	    dp->cm.name,
685103831d35Sstevel 	    dp->portid,
685207d06da5SSurya Prakki 	    (long)(DRMACH_LPA_BASE_TO_PA(val)),
685307d06da5SSurya Prakki 	    (long)(DRMACH_LPA_BND_TO_PA(val)));
685403831d35Sstevel 
685503831d35Sstevel 	return (NULL);
685603831d35Sstevel }
685703831d35Sstevel 
685803831d35Sstevel /*ARGSUSED*/
685903831d35Sstevel static sbd_error_t *
drmach_pt_ikprobe(drmachid_t id,drmach_opts_t * opts)686003831d35Sstevel drmach_pt_ikprobe(drmachid_t id, drmach_opts_t *opts)
686103831d35Sstevel {
686203831d35Sstevel 	drmach_board_t		*bp = (drmach_board_t *)id;
686303831d35Sstevel 	sbd_error_t		*err;
686403831d35Sstevel 	sc_gptwocfg_cookie_t	scc;
686503831d35Sstevel 
686603831d35Sstevel 	if (!DRMACH_IS_BOARD_ID(id))
686703831d35Sstevel 		return (drerr_new(0, ESTC_INAPPROP, NULL));
686803831d35Sstevel 
686903831d35Sstevel 	/* do saf configurator stuff */
687003831d35Sstevel 	DRMACH_PR("calling sc_probe_board for bnum=%d\n", bp->bnum);
687103831d35Sstevel 	scc = sc_probe_board(bp->bnum);
687203831d35Sstevel 	if (scc == NULL) {
687303831d35Sstevel 		err = drerr_new(0, ESTC_PROBE, bp->cm.name);
687403831d35Sstevel 		return (err);
687503831d35Sstevel 	}
687603831d35Sstevel 
687703831d35Sstevel 	return (err);
687803831d35Sstevel }
687903831d35Sstevel 
688003831d35Sstevel /*ARGSUSED*/
688103831d35Sstevel static sbd_error_t *
drmach_pt_ikdeprobe(drmachid_t id,drmach_opts_t * opts)688203831d35Sstevel drmach_pt_ikdeprobe(drmachid_t id, drmach_opts_t *opts)
688303831d35Sstevel {
688403831d35Sstevel 	drmach_board_t	*bp;
688503831d35Sstevel 	sbd_error_t	*err = NULL;
688603831d35Sstevel 	sc_gptwocfg_cookie_t	scc;
688703831d35Sstevel 
688803831d35Sstevel 	if (!DRMACH_IS_BOARD_ID(id))
688903831d35Sstevel 		return (drerr_new(0, ESTC_INAPPROP, NULL));
689003831d35Sstevel 	bp = id;
689103831d35Sstevel 
689203831d35Sstevel 	cmn_err(CE_CONT, "DR: in-kernel unprobe board %d\n", bp->bnum);
689303831d35Sstevel 	scc = sc_unprobe_board(bp->bnum);
689403831d35Sstevel 	if (scc != NULL) {
689503831d35Sstevel 		err = drerr_new(0, ESTC_DEPROBE, bp->cm.name);
689603831d35Sstevel 	}
689703831d35Sstevel 
689803831d35Sstevel 	if (err == NULL)
689903831d35Sstevel 		err = drmach_board_deprobe(id);
690003831d35Sstevel 
690103831d35Sstevel 	return (err);
690203831d35Sstevel }
690303831d35Sstevel 
690403831d35Sstevel static sbd_error_t *
drmach_pt_readmem(drmachid_t id,drmach_opts_t * opts)690503831d35Sstevel drmach_pt_readmem(drmachid_t id, drmach_opts_t *opts)
690603831d35Sstevel {
690703831d35Sstevel 	_NOTE(ARGUNUSED(id))
690803831d35Sstevel 	_NOTE(ARGUNUSED(opts))
690903831d35Sstevel 
691003831d35Sstevel 	struct memlist	*ml;
691103831d35Sstevel 	uint64_t	src_pa;
691203831d35Sstevel 	uint64_t	dst_pa;
691303831d35Sstevel 	uint64_t	dst;
691403831d35Sstevel 
691503831d35Sstevel 	dst_pa = va_to_pa(&dst);
691603831d35Sstevel 
691703831d35Sstevel 	memlist_read_lock();
691856f33205SJonathan Adams 	for (ml = phys_install; ml; ml = ml->ml_next) {
691903831d35Sstevel 		uint64_t	nbytes;
692003831d35Sstevel 
692156f33205SJonathan Adams 		src_pa = ml->ml_address;
692256f33205SJonathan Adams 		nbytes = ml->ml_size;
692303831d35Sstevel 
692403831d35Sstevel 		while (nbytes != 0ull) {
692503831d35Sstevel 
692603831d35Sstevel 			/* copy 32 bytes at src_pa to dst_pa */
692703831d35Sstevel 			bcopy32_il(src_pa, dst_pa);
692803831d35Sstevel 
692903831d35Sstevel 			/* increment by 32 bytes */
693003831d35Sstevel 			src_pa += (4 * sizeof (uint64_t));
693103831d35Sstevel 
693203831d35Sstevel 			/* decrement by 32 bytes */
693303831d35Sstevel 			nbytes -= (4 * sizeof (uint64_t));
693403831d35Sstevel 		}
693503831d35Sstevel 	}
693603831d35Sstevel 	memlist_read_unlock();
693703831d35Sstevel 
693803831d35Sstevel 	return (NULL);
693903831d35Sstevel }
694003831d35Sstevel 
694103831d35Sstevel static sbd_error_t *
drmach_pt_recovercpu(drmachid_t id,drmach_opts_t * opts)694203831d35Sstevel drmach_pt_recovercpu(drmachid_t id, drmach_opts_t *opts)
694303831d35Sstevel {
694403831d35Sstevel 	_NOTE(ARGUNUSED(opts))
694503831d35Sstevel 
694603831d35Sstevel 	drmach_cpu_t	*cp;
694703831d35Sstevel 
694803831d35Sstevel 	if (!DRMACH_IS_CPU_ID(id))
694903831d35Sstevel 		return (drerr_new(0, ESTC_INAPPROP, NULL));
695003831d35Sstevel 	cp = id;
695103831d35Sstevel 
695203831d35Sstevel 	mutex_enter(&cpu_lock);
695303831d35Sstevel 	(void) drmach_iocage_cpu_return(&(cp->dev),
695403831d35Sstevel 	    CPU_ENABLE | CPU_EXISTS | CPU_READY | CPU_RUNNING);
695503831d35Sstevel 	mutex_exit(&cpu_lock);
695603831d35Sstevel 
695703831d35Sstevel 	return (NULL);
695803831d35Sstevel }
695903831d35Sstevel 
696003831d35Sstevel /*
696103831d35Sstevel  * Starcat DR passthrus are for debugging purposes only.
696203831d35Sstevel  */
696303831d35Sstevel static struct {
696403831d35Sstevel 	const char	*name;
696503831d35Sstevel 	sbd_error_t	*(*handler)(drmachid_t id, drmach_opts_t *opts);
696603831d35Sstevel } drmach_pt_arr[] = {
696703831d35Sstevel 	{ "showlpa",		drmach_pt_showlpa		},
696803831d35Sstevel 	{ "ikprobe",		drmach_pt_ikprobe		},
696903831d35Sstevel 	{ "ikdeprobe",		drmach_pt_ikdeprobe		},
697003831d35Sstevel 	{ "readmem",		drmach_pt_readmem		},
697103831d35Sstevel 	{ "recovercpu",		drmach_pt_recovercpu		},
697203831d35Sstevel 
697303831d35Sstevel 	/* the following line must always be last */
697403831d35Sstevel 	{ NULL,			NULL				}
697503831d35Sstevel };
697603831d35Sstevel 
697703831d35Sstevel /*ARGSUSED*/
697803831d35Sstevel sbd_error_t *
drmach_passthru(drmachid_t id,drmach_opts_t * opts)697903831d35Sstevel drmach_passthru(drmachid_t id, drmach_opts_t *opts)
698003831d35Sstevel {
698103831d35Sstevel 	int		i;
698203831d35Sstevel 	sbd_error_t	*err;
698303831d35Sstevel 
698403831d35Sstevel 	i = 0;
698503831d35Sstevel 	while (drmach_pt_arr[i].name != NULL) {
698603831d35Sstevel 		int len = strlen(drmach_pt_arr[i].name);
698703831d35Sstevel 
698803831d35Sstevel 		if (strncmp(drmach_pt_arr[i].name, opts->copts, len) == 0)
698903831d35Sstevel 			break;
699003831d35Sstevel 
699103831d35Sstevel 		i += 1;
699203831d35Sstevel 	}
699303831d35Sstevel 
699403831d35Sstevel 	if (drmach_pt_arr[i].name == NULL)
699503831d35Sstevel 		err = drerr_new(0, ESTC_UNKPTCMD, opts->copts);
699603831d35Sstevel 	else
699703831d35Sstevel 		err = (*drmach_pt_arr[i].handler)(id, opts);
699803831d35Sstevel 
699903831d35Sstevel 	return (err);
700003831d35Sstevel }
700103831d35Sstevel 
700203831d35Sstevel sbd_error_t *
drmach_release(drmachid_t id)700303831d35Sstevel drmach_release(drmachid_t id)
700403831d35Sstevel {
700503831d35Sstevel 	drmach_common_t *cp;
700603831d35Sstevel 
700703831d35Sstevel 	if (!DRMACH_IS_DEVICE_ID(id))
700803831d35Sstevel 		return (drerr_new(0, ESTC_INAPPROP, NULL));
700903831d35Sstevel 	cp = id;
701003831d35Sstevel 
701103831d35Sstevel 	return (cp->release(id));
701203831d35Sstevel }
701303831d35Sstevel 
701403831d35Sstevel sbd_error_t *
drmach_status(drmachid_t id,drmach_status_t * stat)701503831d35Sstevel drmach_status(drmachid_t id, drmach_status_t *stat)
701603831d35Sstevel {
701703831d35Sstevel 	drmach_common_t *cp;
701803831d35Sstevel 	sbd_error_t	*err;
701903831d35Sstevel 
702003831d35Sstevel 	rw_enter(&drmach_boards_rwlock, RW_READER);
702103831d35Sstevel 
702203831d35Sstevel 	if (!DRMACH_IS_ID(id)) {
702303831d35Sstevel 		rw_exit(&drmach_boards_rwlock);
702403831d35Sstevel 		return (drerr_new(0, ESTC_NOTID, NULL));
702503831d35Sstevel 	}
702603831d35Sstevel 
702703831d35Sstevel 	cp = id;
702803831d35Sstevel 
702903831d35Sstevel 	err = cp->status(id, stat);
703003831d35Sstevel 	rw_exit(&drmach_boards_rwlock);
703103831d35Sstevel 	return (err);
703203831d35Sstevel }
703303831d35Sstevel 
703403831d35Sstevel static sbd_error_t *
drmach_i_status(drmachid_t id,drmach_status_t * stat)703503831d35Sstevel drmach_i_status(drmachid_t id, drmach_status_t *stat)
703603831d35Sstevel {
703703831d35Sstevel 	drmach_common_t *cp;
703803831d35Sstevel 
703903831d35Sstevel 	if (!DRMACH_IS_ID(id))
704003831d35Sstevel 		return (drerr_new(0, ESTC_NOTID, NULL));
704103831d35Sstevel 	cp = id;
704203831d35Sstevel 
704303831d35Sstevel 	return (cp->status(id, stat));
704403831d35Sstevel }
704503831d35Sstevel 
704603831d35Sstevel /*ARGSUSED*/
704703831d35Sstevel sbd_error_t *
drmach_unconfigure(drmachid_t id,int flags)704803831d35Sstevel drmach_unconfigure(drmachid_t id, int flags)
704903831d35Sstevel {
705003831d35Sstevel 	drmach_device_t	*dp;
705103831d35Sstevel 	dev_info_t 	*rdip;
705203831d35Sstevel 
705303831d35Sstevel 	char	name[OBP_MAXDRVNAME];
705403831d35Sstevel 	int rv;
705503831d35Sstevel 
705603831d35Sstevel 	/*
705703831d35Sstevel 	 * Since CPU nodes are not configured, it is
705803831d35Sstevel 	 * necessary to skip the unconfigure step as
705903831d35Sstevel 	 * well.
706003831d35Sstevel 	 */
706103831d35Sstevel 	if (DRMACH_IS_CPU_ID(id)) {
706203831d35Sstevel 		return (NULL);
706303831d35Sstevel 	}
706403831d35Sstevel 
706503831d35Sstevel 	for (; id; ) {
706603831d35Sstevel 		dev_info_t	*fdip = NULL;
706703831d35Sstevel 
706803831d35Sstevel 		if (!DRMACH_IS_DEVICE_ID(id))
706903831d35Sstevel 			return (drerr_new(0, ESTC_INAPPROP, NULL));
707003831d35Sstevel 		dp = id;
707103831d35Sstevel 
707203831d35Sstevel 		rdip = dp->node->n_getdip(dp->node);
707303831d35Sstevel 
707403831d35Sstevel 		/*
707503831d35Sstevel 		 * drmach_unconfigure() is always called on a configured branch.
707603831d35Sstevel 		 * So the root of the branch was held earlier and must exist.
707703831d35Sstevel 		 */
707803831d35Sstevel 		ASSERT(rdip);
707903831d35Sstevel 
708003831d35Sstevel 		DRMACH_PR("drmach_unconfigure: unconfiguring DDI branch");
708103831d35Sstevel 
708203831d35Sstevel 		rv = dp->node->n_getprop(dp->node,
708303831d35Sstevel 		    "name", name, OBP_MAXDRVNAME);
708403831d35Sstevel 
708503831d35Sstevel 		/* The node must have a name */
708603831d35Sstevel 		if (rv)
708703831d35Sstevel 			return (0);
708803831d35Sstevel 
708903831d35Sstevel 		if (drmach_name2type_idx(name) < 0) {
709003831d35Sstevel 			if (DRMACH_IS_MEM_ID(id)) {
709103831d35Sstevel 				drmach_mem_t	*mp = id;
709203831d35Sstevel 				id = mp->next;
709303831d35Sstevel 			} else {
709403831d35Sstevel 				id = NULL;
709503831d35Sstevel 			}
709603831d35Sstevel 			continue;
709703831d35Sstevel 		}
709803831d35Sstevel 
709903831d35Sstevel 		/*
710003831d35Sstevel 		 * NOTE: FORCE flag is no longer needed under devfs
710103831d35Sstevel 		 */
710203831d35Sstevel 		ASSERT(e_ddi_branch_held(rdip));
710303831d35Sstevel 		if (e_ddi_branch_unconfigure(rdip, &fdip, 0) != 0) {
710403831d35Sstevel 			sbd_error_t *err = NULL;
7105d3d50737SRafael Vanoni 			char *path = kmem_alloc(MAXPATHLEN, KM_SLEEP);
710603831d35Sstevel 
710703831d35Sstevel 			/*
710803831d35Sstevel 			 * If non-NULL, fdip is returned held and must be
710903831d35Sstevel 			 * released.
711003831d35Sstevel 			 */
711103831d35Sstevel 			if (fdip != NULL) {
711203831d35Sstevel 				(void) ddi_pathname(fdip, path);
711303831d35Sstevel 				ddi_release_devi(fdip);
711403831d35Sstevel 			} else {
711503831d35Sstevel 				(void) ddi_pathname(rdip, path);
711603831d35Sstevel 			}
711703831d35Sstevel 
711803831d35Sstevel 			err = drerr_new(1, ESTC_DRVFAIL, path);
711903831d35Sstevel 
712003831d35Sstevel 			kmem_free(path, MAXPATHLEN);
712103831d35Sstevel 
712203831d35Sstevel 			/*
712303831d35Sstevel 			 * If we were unconfiguring an IO board, a call was
712403831d35Sstevel 			 * made to man_dr_detach.  We now need to call
712503831d35Sstevel 			 * man_dr_attach to regain man use of the eri.
712603831d35Sstevel 			 */
712703831d35Sstevel 			if (DRMACH_IS_IO_ID(id)) {
712803831d35Sstevel 				int (*func)(dev_info_t *dip);
712903831d35Sstevel 
713003831d35Sstevel 				func = (int (*)(dev_info_t *))kobj_getsymvalue\
713103831d35Sstevel 				    ("man_dr_attach", 0);
713203831d35Sstevel 
713303831d35Sstevel 				if (func) {
713403831d35Sstevel 					drmach_io_inst_t ios;
713503831d35Sstevel 					dev_info_t 	*pdip;
713603831d35Sstevel 					int		circ;
713703831d35Sstevel 
713803831d35Sstevel 					/*
713903831d35Sstevel 					 * Walk device tree to find rio dip for
714003831d35Sstevel 					 * the board
714103831d35Sstevel 					 * Since we are not interested in iosram
714203831d35Sstevel 					 * instance here, initialize it to 0, so
714303831d35Sstevel 					 * that the walk terminates as soon as
714403831d35Sstevel 					 * eri dip is found.
714503831d35Sstevel 					 */
714603831d35Sstevel 					ios.iosram_inst = 0;
714703831d35Sstevel 					ios.eri_dip = NULL;
714803831d35Sstevel 					ios.bnum = dp->bp->bnum;
714903831d35Sstevel 
715003831d35Sstevel 					if (pdip = ddi_get_parent(rdip)) {
715103831d35Sstevel 						ndi_hold_devi(pdip);
715203831d35Sstevel 						ndi_devi_enter(pdip, &circ);
715303831d35Sstevel 					}
715403831d35Sstevel 					/*
715503831d35Sstevel 					 * Root node doesn't have to be held in
715603831d35Sstevel 					 * any way.
715703831d35Sstevel 					 */
715803831d35Sstevel 					ASSERT(e_ddi_branch_held(rdip));
715903831d35Sstevel 					ddi_walk_devs(rdip,
716003831d35Sstevel 					    drmach_board_find_io_insts,
716103831d35Sstevel 					    (void *)&ios);
716203831d35Sstevel 
716303831d35Sstevel 					DRMACH_PR("drmach_unconfigure: bnum=%d"
716403831d35Sstevel 					    " eri=0x%p\n",
716507d06da5SSurya Prakki 					    ios.bnum, (void *)ios.eri_dip);
716603831d35Sstevel 
716703831d35Sstevel 					if (pdip) {
716803831d35Sstevel 						ndi_devi_exit(pdip, circ);
716903831d35Sstevel 						ndi_rele_devi(pdip);
717003831d35Sstevel 					}
717103831d35Sstevel 
717203831d35Sstevel 					if (ios.eri_dip) {
717303831d35Sstevel 						DRMACH_PR("calling"
717403831d35Sstevel 						    " man_dr_attach\n");
717503831d35Sstevel 						(void) (*func)(ios.eri_dip);
717603831d35Sstevel 						/*
717703831d35Sstevel 						 * Release hold acquired in
717803831d35Sstevel 						 * drmach_board_find_io_insts()
717903831d35Sstevel 						 */
718003831d35Sstevel 						ndi_rele_devi(ios.eri_dip);
718103831d35Sstevel 					}
718203831d35Sstevel 				}
718303831d35Sstevel 			}
718403831d35Sstevel 			return (err);
718503831d35Sstevel 		}
718603831d35Sstevel 
718703831d35Sstevel 		if (DRMACH_IS_MEM_ID(id)) {
718803831d35Sstevel 			drmach_mem_t	*mp = id;
718903831d35Sstevel 			id = mp->next;
719003831d35Sstevel 		} else {
719103831d35Sstevel 			id = NULL;
719203831d35Sstevel 		}
719303831d35Sstevel 	}
719403831d35Sstevel 
719503831d35Sstevel 	return (NULL);
719603831d35Sstevel }
719703831d35Sstevel 
719803831d35Sstevel /*
719903831d35Sstevel  * drmach interfaces to legacy Starfire platmod logic
720003831d35Sstevel  * linkage via runtime symbol look up, called from plat_cpu_power*
720103831d35Sstevel  */
720203831d35Sstevel 
720303831d35Sstevel /*
720403831d35Sstevel  * Start up a cpu.  It is possible that we're attempting to restart
720503831d35Sstevel  * the cpu after an UNCONFIGURE in which case the cpu will be
720603831d35Sstevel  * spinning in its cache.  So, all we have to do is wakeup him up.
720703831d35Sstevel  * Under normal circumstances the cpu will be coming from a previous
720803831d35Sstevel  * CONNECT and thus will be spinning in OBP.  In both cases, the
720903831d35Sstevel  * startup sequence is the same.
721003831d35Sstevel  */
721103831d35Sstevel int
drmach_cpu_poweron(struct cpu * cp)721203831d35Sstevel drmach_cpu_poweron(struct cpu *cp)
721303831d35Sstevel {
721403831d35Sstevel 	DRMACH_PR("drmach_cpu_poweron: starting cpuid %d\n", cp->cpu_id);
721503831d35Sstevel 
721603831d35Sstevel 	ASSERT(MUTEX_HELD(&cpu_lock));
721703831d35Sstevel 
721803831d35Sstevel 	if (drmach_cpu_start(cp) != 0)
721903831d35Sstevel 		return (EBUSY);
722003831d35Sstevel 	else
722103831d35Sstevel 		return (0);
722203831d35Sstevel }
722303831d35Sstevel 
722403831d35Sstevel int
drmach_cpu_poweroff(struct cpu * cp)722503831d35Sstevel drmach_cpu_poweroff(struct cpu *cp)
722603831d35Sstevel {
722703831d35Sstevel 	int		ntries;
722803831d35Sstevel 	processorid_t	cpuid;
722903831d35Sstevel 	void		drmach_cpu_shutdown_self(void);
723003831d35Sstevel 
723103831d35Sstevel 	DRMACH_PR("drmach_cpu_poweroff: stopping cpuid %d\n", cp->cpu_id);
723203831d35Sstevel 
723303831d35Sstevel 	ASSERT(MUTEX_HELD(&cpu_lock));
723403831d35Sstevel 
723503831d35Sstevel 	/*
723603831d35Sstevel 	 * XXX CHEETAH SUPPORT
723703831d35Sstevel 	 * for cheetah, we need to grab the iocage lock since iocage
723803831d35Sstevel 	 * memory is used for e$ flush.
723903831d35Sstevel 	 */
724003831d35Sstevel 	if (drmach_is_cheetah) {
724103831d35Sstevel 		mutex_enter(&drmach_iocage_lock);
724203831d35Sstevel 		while (drmach_iocage_is_busy)
724303831d35Sstevel 			cv_wait(&drmach_iocage_cv, &drmach_iocage_lock);
724403831d35Sstevel 		drmach_iocage_is_busy = 1;
724503831d35Sstevel 		drmach_iocage_mem_scrub(ecache_size * 2);
724603831d35Sstevel 		mutex_exit(&drmach_iocage_lock);
724703831d35Sstevel 	}
724803831d35Sstevel 
724903831d35Sstevel 	cpuid = cp->cpu_id;
725003831d35Sstevel 
725103831d35Sstevel 	/*
725203831d35Sstevel 	 * Set affinity to ensure consistent reading and writing of
725303831d35Sstevel 	 * drmach_xt_mb[cpuid] by one "master" CPU directing
725403831d35Sstevel 	 * the shutdown of the target CPU.
725503831d35Sstevel 	 */
725603831d35Sstevel 	affinity_set(CPU->cpu_id);
725703831d35Sstevel 
725803831d35Sstevel 	/*
725903831d35Sstevel 	 * Capture all CPUs (except for detaching proc) to prevent
726003831d35Sstevel 	 * crosscalls to the detaching proc until it has cleared its
726103831d35Sstevel 	 * bit in cpu_ready_set.
726203831d35Sstevel 	 *
726303831d35Sstevel 	 * The CPUs remain paused and the prom_mutex is known to be free.
726403831d35Sstevel 	 * This prevents blocking when doing prom IEEE-1275 calls at a
726503831d35Sstevel 	 * high PIL level.
726603831d35Sstevel 	 */
726703831d35Sstevel 	promsafe_pause_cpus();
726803831d35Sstevel 
726903831d35Sstevel 	/*
727003831d35Sstevel 	 * Quiesce interrupts on the target CPU. We do this by setting
727103831d35Sstevel 	 * the CPU 'not ready'- (i.e. removing the CPU from cpu_ready_set) to
727203831d35Sstevel 	 * prevent it from receiving cross calls and cross traps.
727303831d35Sstevel 	 * This prevents the processor from receiving any new soft interrupts.
727403831d35Sstevel 	 */
727503831d35Sstevel 	mp_cpu_quiesce(cp);
727603831d35Sstevel 
727707d06da5SSurya Prakki 	(void) prom_hotremovecpu(cpuid);
727803831d35Sstevel 
727903831d35Sstevel 	start_cpus();
728003831d35Sstevel 
728103831d35Sstevel 	/* setup xt_mb, will be cleared by drmach_shutdown_asm when ready */
728203831d35Sstevel 	drmach_xt_mb[cpuid] = 0x80;
728303831d35Sstevel 
728403831d35Sstevel 	xt_one_unchecked(cp->cpu_id, (xcfunc_t *)idle_stop_xcall,
728503831d35Sstevel 	    (uint64_t)drmach_cpu_shutdown_self, NULL);
728603831d35Sstevel 
728703831d35Sstevel 	ntries = drmach_cpu_ntries;
728803831d35Sstevel 	while (drmach_xt_mb[cpuid] && ntries) {
728903831d35Sstevel 		DELAY(drmach_cpu_delay);
729003831d35Sstevel 		ntries--;
729103831d35Sstevel 	}
729203831d35Sstevel 
729303831d35Sstevel 	drmach_xt_mb[cpuid] = 0;	/* steal the cache line back */
729403831d35Sstevel 
729503831d35Sstevel 	membar_sync();			/* make sure copy-back retires */
729603831d35Sstevel 
729703831d35Sstevel 	affinity_clear();
729803831d35Sstevel 
729903831d35Sstevel 	/*
730003831d35Sstevel 	 * XXX CHEETAH SUPPORT
730103831d35Sstevel 	 */
730203831d35Sstevel 	if (drmach_is_cheetah) {
730303831d35Sstevel 		mutex_enter(&drmach_iocage_lock);
730403831d35Sstevel 		drmach_iocage_mem_scrub(ecache_size * 2);
730503831d35Sstevel 		drmach_iocage_is_busy = 0;
730603831d35Sstevel 		cv_signal(&drmach_iocage_cv);
730703831d35Sstevel 		mutex_exit(&drmach_iocage_lock);
730803831d35Sstevel 	}
730903831d35Sstevel 
731003831d35Sstevel 	DRMACH_PR("waited %d out of %d tries for "
731103831d35Sstevel 	    "drmach_cpu_shutdown_self on cpu%d",
731203831d35Sstevel 	    drmach_cpu_ntries - ntries, drmach_cpu_ntries, cp->cpu_id);
731303831d35Sstevel 
731403831d35Sstevel 	/*
731503831d35Sstevel 	 * Do this here instead of drmach_cpu_shutdown_self() to
731603831d35Sstevel 	 * avoid an assertion failure panic in turnstile.c.
731703831d35Sstevel 	 */
731803831d35Sstevel 	CPU_SIGNATURE(OS_SIG, SIGST_DETACHED, SIGSUBST_NULL, cpuid);
731903831d35Sstevel 
732003831d35Sstevel 	return (0);
732103831d35Sstevel }
732203831d35Sstevel 
732303831d35Sstevel void
drmach_iocage_mem_scrub(uint64_t nbytes)732403831d35Sstevel drmach_iocage_mem_scrub(uint64_t nbytes)
732503831d35Sstevel {
732607d06da5SSurya Prakki 	extern uint32_t drmach_bc_bzero(void*, size_t);
732707d06da5SSurya Prakki 	uint32_t	rv;
732803831d35Sstevel 
732903831d35Sstevel 	ASSERT(MUTEX_HELD(&cpu_lock));
733003831d35Sstevel 
733103831d35Sstevel 	affinity_set(CPU->cpu_id);
733203831d35Sstevel 
733303831d35Sstevel 	rv = drmach_bc_bzero(drmach_iocage_vaddr, nbytes);
733403831d35Sstevel 	if (rv != 0) {
733503831d35Sstevel 		DRMACH_PR(
733603831d35Sstevel 		"iocage scrub failed, drmach_bc_bzero returned %d\n", rv);
7337d3d50737SRafael Vanoni 		rv = drmach_bc_bzero(drmach_iocage_vaddr, drmach_iocage_size);
733803831d35Sstevel 		if (rv != 0)
733903831d35Sstevel 			cmn_err(CE_PANIC,
734003831d35Sstevel 			    "iocage scrub failed, drmach_bc_bzero rv=%d\n",
734103831d35Sstevel 			    rv);
734203831d35Sstevel 	}
734303831d35Sstevel 
734403831d35Sstevel 	cpu_flush_ecache();
734503831d35Sstevel 
734603831d35Sstevel 	affinity_clear();
734703831d35Sstevel }
734803831d35Sstevel 
734903831d35Sstevel #define	ALIGN(x, a)	((a) == 0 ? (uintptr_t)(x) : \
735003831d35Sstevel 	(((uintptr_t)(x) + (uintptr_t)(a) - 1l) & ~((uintptr_t)(a) - 1l)))
735103831d35Sstevel 
735203831d35Sstevel static sbd_error_t *
drmach_iocage_mem_get(dr_testboard_req_t * tbrq)735303831d35Sstevel drmach_iocage_mem_get(dr_testboard_req_t *tbrq)
735403831d35Sstevel {
735503831d35Sstevel 	pfn_t		basepfn;
735603831d35Sstevel 	pgcnt_t		npages;
735703831d35Sstevel 	extern int	memscrub_delete_span(pfn_t, pgcnt_t);
735803831d35Sstevel 	uint64_t	drmach_iocage_paddr_mbytes;
735903831d35Sstevel 
736003831d35Sstevel 	ASSERT(drmach_iocage_paddr != -1);
736103831d35Sstevel 
736203831d35Sstevel 	basepfn = (pfn_t)(drmach_iocage_paddr >> PAGESHIFT);
736303831d35Sstevel 	npages = (pgcnt_t)(drmach_iocage_size >> PAGESHIFT);
736403831d35Sstevel 
736507d06da5SSurya Prakki 	(void) memscrub_delete_span(basepfn, npages);
736603831d35Sstevel 
736703831d35Sstevel 	mutex_enter(&cpu_lock);
736803831d35Sstevel 	drmach_iocage_mem_scrub(drmach_iocage_size);
736903831d35Sstevel 	mutex_exit(&cpu_lock);
737003831d35Sstevel 
737103831d35Sstevel 	/*
737203831d35Sstevel 	 * HPOST wants the address of the cage to be 64 megabyte-aligned
737303831d35Sstevel 	 * and in megabyte units.
737403831d35Sstevel 	 * The size of the cage is also in megabyte units.
737503831d35Sstevel 	 */
737603831d35Sstevel 	ASSERT(drmach_iocage_paddr == ALIGN(drmach_iocage_paddr, 0x4000000));
737703831d35Sstevel 
737803831d35Sstevel 	drmach_iocage_paddr_mbytes = drmach_iocage_paddr / 0x100000;
737903831d35Sstevel 
738003831d35Sstevel 	tbrq->memaddrhi = (uint32_t)(drmach_iocage_paddr_mbytes >> 32);
738103831d35Sstevel 	tbrq->memaddrlo = (uint32_t)drmach_iocage_paddr_mbytes;
738203831d35Sstevel 	tbrq->memlen = drmach_iocage_size / 0x100000;
738303831d35Sstevel 
738403831d35Sstevel 	DRMACH_PR("drmach_iocage_mem_get: hi: 0x%x", tbrq->memaddrhi);
738503831d35Sstevel 	DRMACH_PR("drmach_iocage_mem_get: lo: 0x%x", tbrq->memaddrlo);
738603831d35Sstevel 	DRMACH_PR("drmach_iocage_mem_get: size: 0x%x", tbrq->memlen);
738703831d35Sstevel 
738803831d35Sstevel 	return (NULL);
738903831d35Sstevel }
739003831d35Sstevel 
739103831d35Sstevel static sbd_error_t *
drmach_iocage_mem_return(dr_testboard_reply_t * tbr)739203831d35Sstevel drmach_iocage_mem_return(dr_testboard_reply_t *tbr)
739303831d35Sstevel {
739403831d35Sstevel 	_NOTE(ARGUNUSED(tbr))
739503831d35Sstevel 
739603831d35Sstevel 	pfn_t		basepfn;
739703831d35Sstevel 	pgcnt_t		npages;
739803831d35Sstevel 	extern int	memscrub_add_span(pfn_t, pgcnt_t);
739903831d35Sstevel 
740003831d35Sstevel 	ASSERT(drmach_iocage_paddr != -1);
740103831d35Sstevel 
740203831d35Sstevel 	basepfn = (pfn_t)(drmach_iocage_paddr >> PAGESHIFT);
740303831d35Sstevel 	npages = (pgcnt_t)(drmach_iocage_size >> PAGESHIFT);
740403831d35Sstevel 
740507d06da5SSurya Prakki 	(void) memscrub_add_span(basepfn, npages);
740603831d35Sstevel 
740703831d35Sstevel 	mutex_enter(&cpu_lock);
740803831d35Sstevel 	mutex_enter(&drmach_iocage_lock);
740903831d35Sstevel 	drmach_iocage_mem_scrub(drmach_iocage_size);
741003831d35Sstevel 	drmach_iocage_is_busy = 0;
741103831d35Sstevel 	cv_signal(&drmach_iocage_cv);
741203831d35Sstevel 	mutex_exit(&drmach_iocage_lock);
741303831d35Sstevel 	mutex_exit(&cpu_lock);
741403831d35Sstevel 
741503831d35Sstevel 	return (NULL);
741603831d35Sstevel }
741703831d35Sstevel 
741803831d35Sstevel static int
drmach_cpu_intr_disable(cpu_t * cp)741903831d35Sstevel drmach_cpu_intr_disable(cpu_t *cp)
742003831d35Sstevel {
742103831d35Sstevel 	if (cpu_intr_disable(cp) != 0)
742203831d35Sstevel 		return (-1);
742303831d35Sstevel 	return (0);
742403831d35Sstevel }
742503831d35Sstevel 
742603831d35Sstevel static int
drmach_iocage_cpu_acquire(drmach_device_t * dp,cpu_flag_t * oflags)742703831d35Sstevel drmach_iocage_cpu_acquire(drmach_device_t *dp, cpu_flag_t *oflags)
742803831d35Sstevel {
742903831d35Sstevel 	struct cpu	*cp;
743003831d35Sstevel 	processorid_t	cpuid;
743103831d35Sstevel 	static char	*fn = "drmach_iocage_cpu_acquire";
743203831d35Sstevel 	sbd_error_t 	*err;
743303831d35Sstevel 	int 		impl;
743403831d35Sstevel 
743503831d35Sstevel 	ASSERT(DRMACH_IS_CPU_ID(dp));
743603831d35Sstevel 	ASSERT(MUTEX_HELD(&cpu_lock));
743703831d35Sstevel 
743803831d35Sstevel 	cpuid = ((drmach_cpu_t *)dp)->cpuid;
743903831d35Sstevel 
744003831d35Sstevel 	DRMACH_PR("%s: attempting to acquire CPU id %d", fn, cpuid);
744103831d35Sstevel 
744203831d35Sstevel 	if (dp->busy)
744303831d35Sstevel 		return (-1);
744403831d35Sstevel 
744503831d35Sstevel 	if ((cp = cpu_get(cpuid)) == NULL) {
744603831d35Sstevel 		DRMACH_PR("%s: cpu_get(%d) returned NULL", fn, cpuid);
744703831d35Sstevel 		return (-1);
744803831d35Sstevel 	}
744903831d35Sstevel 
745003831d35Sstevel 	if (!CPU_ACTIVE(cp)) {
745103831d35Sstevel 		DRMACH_PR("%s: skipping offlined CPU id %d", fn, cpuid);
745203831d35Sstevel 		return (-1);
745303831d35Sstevel 	}
745403831d35Sstevel 
745503831d35Sstevel 	/*
745603831d35Sstevel 	 * There is a known HW bug where a Jaguar CPU in Safari port 0 (SBX/P0)
745703831d35Sstevel 	 * can fail to receive an XIR. To workaround this issue until a hardware
745803831d35Sstevel 	 * fix is implemented, we will exclude the selection of these CPUs.
745903831d35Sstevel 	 *
746003831d35Sstevel 	 * Once a fix is implemented in hardware, this code should be updated
746103831d35Sstevel 	 * to allow Jaguar CPUs that have the fix to be used. However, support
746203831d35Sstevel 	 * must be retained to skip revisions that do not have this fix.
746303831d35Sstevel 	 */
746403831d35Sstevel 
746503831d35Sstevel 	err = drmach_cpu_get_impl(dp, &impl);
746603831d35Sstevel 	if (err) {
746703831d35Sstevel 		DRMACH_PR("%s: error getting impl. of CPU id %d", fn, cpuid);
746803831d35Sstevel 		sbd_err_clear(&err);
746903831d35Sstevel 		return (-1);
747003831d35Sstevel 	}
747103831d35Sstevel 
747203831d35Sstevel 	if (IS_JAGUAR(impl) && (STARCAT_CPUID_TO_LPORT(cpuid) == 0) &&
747303831d35Sstevel 	    drmach_iocage_exclude_jaguar_port_zero) {
747403831d35Sstevel 		DRMACH_PR("%s: excluding CPU id %d: port 0 on jaguar",
747503831d35Sstevel 		    fn, cpuid);
747603831d35Sstevel 		return (-1);
747703831d35Sstevel 	}
747803831d35Sstevel 
747903831d35Sstevel 	ASSERT(oflags);
748003831d35Sstevel 	*oflags = cp->cpu_flags;
748103831d35Sstevel 
748203831d35Sstevel 	if (cpu_offline(cp, 0)) {
748303831d35Sstevel 		DRMACH_PR("%s: cpu_offline failed for CPU id %d", fn, cpuid);
748403831d35Sstevel 		return (-1);
748503831d35Sstevel 	}
748603831d35Sstevel 
748703831d35Sstevel 	if (cpu_poweroff(cp)) {
748803831d35Sstevel 		DRMACH_PR("%s: cpu_poweroff failed for CPU id %d", fn, cpuid);
748903831d35Sstevel 		if (cpu_online(cp)) {
749003831d35Sstevel 			cmn_err(CE_WARN, "failed to online CPU id %d "
749103831d35Sstevel 			    "during I/O cage test selection", cpuid);
749203831d35Sstevel 		}
749303831d35Sstevel 		if (CPU_ACTIVE(cp) && cpu_flagged_nointr(*oflags) &&
749403831d35Sstevel 		    drmach_cpu_intr_disable(cp) != 0) {
749503831d35Sstevel 			cmn_err(CE_WARN, "failed to restore CPU id %d "
749603831d35Sstevel 			    "no-intr during I/O cage test selection", cpuid);
749703831d35Sstevel 		}
749803831d35Sstevel 		return (-1);
749903831d35Sstevel 	}
750003831d35Sstevel 
750103831d35Sstevel 	if (cpu_unconfigure(cpuid)) {
750203831d35Sstevel 		DRMACH_PR("%s: cpu_unconfigure failed for CPU id %d", fn,
750303831d35Sstevel 		    cpuid);
750403831d35Sstevel 		(void) cpu_configure(cpuid);
750503831d35Sstevel 		if ((cp = cpu_get(cpuid)) == NULL) {
750603831d35Sstevel 			cmn_err(CE_WARN, "failed to reconfigure CPU id %d "
750703831d35Sstevel 			    "during I/O cage test selection", cpuid);
750803831d35Sstevel 			dp->busy = 1;
750903831d35Sstevel 			return (-1);
751003831d35Sstevel 		}
751103831d35Sstevel 		if (cpu_poweron(cp) || cpu_online(cp)) {
751203831d35Sstevel 			cmn_err(CE_WARN, "failed to %s CPU id %d "
751303831d35Sstevel 			    "during I/O cage test selection",
751403831d35Sstevel 			    cpu_is_poweredoff(cp) ?
751503831d35Sstevel 			    "poweron" : "online", cpuid);
751603831d35Sstevel 		}
751703831d35Sstevel 		if (CPU_ACTIVE(cp) && cpu_flagged_nointr(*oflags) &&
751803831d35Sstevel 		    drmach_cpu_intr_disable(cp) != 0) {
751903831d35Sstevel 			cmn_err(CE_WARN, "failed to restore CPU id %d "
752003831d35Sstevel 			    "no-intr during I/O cage test selection", cpuid);
752103831d35Sstevel 		}
752203831d35Sstevel 		return (-1);
752303831d35Sstevel 	}
752403831d35Sstevel 
752503831d35Sstevel 	dp->busy = 1;
752603831d35Sstevel 
752703831d35Sstevel 	DRMACH_PR("%s: acquired CPU id %d", fn, cpuid);
752803831d35Sstevel 
752903831d35Sstevel 	return (0);
753003831d35Sstevel }
753103831d35Sstevel 
753203831d35Sstevel /*
753303831d35Sstevel  * Attempt to acquire all the CPU devices passed in. It is
753403831d35Sstevel  * assumed that all the devices in the list are the cores of
753503831d35Sstevel  * a single CMP device. Non CMP devices can be handled as a
753603831d35Sstevel  * single core CMP by passing in a one element list.
753703831d35Sstevel  *
753803831d35Sstevel  * Success is only returned if *all* the devices in the list
753903831d35Sstevel  * can be acquired. In the failure case, none of the devices
754003831d35Sstevel  * in the list will be held as acquired.
754103831d35Sstevel  */
754203831d35Sstevel static int
drmach_iocage_cmp_acquire(drmach_device_t ** dpp,cpu_flag_t * oflags)754303831d35Sstevel drmach_iocage_cmp_acquire(drmach_device_t **dpp, cpu_flag_t *oflags)
754403831d35Sstevel {
754503831d35Sstevel 	int	curr;
754603831d35Sstevel 	int	i;
754703831d35Sstevel 	int	rv = 0;
754803831d35Sstevel 
754903831d35Sstevel 	ASSERT((dpp != NULL) && (*dpp != NULL));
755003831d35Sstevel 
755103831d35Sstevel 	/*
755203831d35Sstevel 	 * Walk the list of CPU devices (cores of a CMP)
755303831d35Sstevel 	 * and attempt to acquire them. Bail out if an
755403831d35Sstevel 	 * error is encountered.
755503831d35Sstevel 	 */
755625cf1a30Sjl139090 	for (curr = 0; curr < MAX_CORES_PER_CMP; curr++) {
755703831d35Sstevel 
755803831d35Sstevel 		/* check for the end of the list */
755903831d35Sstevel 		if (dpp[curr] == NULL) {
756003831d35Sstevel 			break;
756103831d35Sstevel 		}
756203831d35Sstevel 
756303831d35Sstevel 		ASSERT(DRMACH_IS_CPU_ID(dpp[curr]));
756403831d35Sstevel 		ASSERT(dpp[curr]->portid == (*dpp)->portid);
756503831d35Sstevel 
756603831d35Sstevel 		rv = drmach_iocage_cpu_acquire(dpp[curr], &oflags[curr]);
756703831d35Sstevel 		if (rv != 0) {
756803831d35Sstevel 			break;
756903831d35Sstevel 		}
757003831d35Sstevel 	}
757103831d35Sstevel 
757203831d35Sstevel 	/*
757303831d35Sstevel 	 * Check for an error.
757403831d35Sstevel 	 */
757503831d35Sstevel 	if (rv != 0) {
757603831d35Sstevel 		/*
757703831d35Sstevel 		 * Make a best effort attempt to return any cores
757803831d35Sstevel 		 * that were already acquired before the error was
757903831d35Sstevel 		 * encountered.
758003831d35Sstevel 		 */
758103831d35Sstevel 		for (i = 0; i < curr; i++) {
758203831d35Sstevel 			(void) drmach_iocage_cpu_return(dpp[i], oflags[i]);
758303831d35Sstevel 		}
758403831d35Sstevel 	}
758503831d35Sstevel 
758603831d35Sstevel 	return (rv);
758703831d35Sstevel }
758803831d35Sstevel 
758903831d35Sstevel static int
drmach_iocage_cpu_return(drmach_device_t * dp,cpu_flag_t oflags)759003831d35Sstevel drmach_iocage_cpu_return(drmach_device_t *dp, cpu_flag_t oflags)
759103831d35Sstevel {
759203831d35Sstevel 	processorid_t	cpuid;
759303831d35Sstevel 	struct cpu	*cp;
759403831d35Sstevel 	int		rv = 0;
759503831d35Sstevel 	static char	*fn = "drmach_iocage_cpu_return";
759603831d35Sstevel 
759703831d35Sstevel 	ASSERT(DRMACH_IS_CPU_ID(dp));
759803831d35Sstevel 	ASSERT(MUTEX_HELD(&cpu_lock));
759903831d35Sstevel 
760003831d35Sstevel 	cpuid = ((drmach_cpu_t *)dp)->cpuid;
760103831d35Sstevel 
760203831d35Sstevel 	DRMACH_PR("%s: attempting to return CPU id: %d", fn, cpuid);
760303831d35Sstevel 
760403831d35Sstevel 	if (cpu_configure(cpuid)) {
760503831d35Sstevel 		cmn_err(CE_WARN, "failed to reconfigure CPU id %d "
760603831d35Sstevel 		    "after I/O cage test", cpuid);
760703831d35Sstevel 		/*
760803831d35Sstevel 		 * The component was never set to unconfigured during the IO
760903831d35Sstevel 		 * cage test, so we need to leave marked as busy to prevent
761003831d35Sstevel 		 * further DR operations involving this component.
761103831d35Sstevel 		 */
761203831d35Sstevel 		return (-1);
761303831d35Sstevel 	}
761403831d35Sstevel 
761503831d35Sstevel 	if ((cp = cpu_get(cpuid)) == NULL) {
761603831d35Sstevel 		cmn_err(CE_WARN, "cpu_get failed on CPU id %d after "
761703831d35Sstevel 		    "I/O cage test", cpuid);
761803831d35Sstevel 		dp->busy = 0;
761903831d35Sstevel 		return (-1);
762003831d35Sstevel 	}
762103831d35Sstevel 
762203831d35Sstevel 	if (cpu_poweron(cp) || cpu_online(cp)) {
762303831d35Sstevel 		cmn_err(CE_WARN, "failed to %s CPU id %d after I/O "
762403831d35Sstevel 		    "cage test", cpu_is_poweredoff(cp) ?
762503831d35Sstevel 		    "poweron" : "online", cpuid);
762603831d35Sstevel 		rv = -1;
762703831d35Sstevel 	}
762803831d35Sstevel 
762903831d35Sstevel 	/*
763003831d35Sstevel 	 * drmach_iocage_cpu_acquire will accept cpus in state P_ONLINE or
763103831d35Sstevel 	 * P_NOINTR. Need to return to previous user-visible state.
763203831d35Sstevel 	 */
763303831d35Sstevel 	if (CPU_ACTIVE(cp) && cpu_flagged_nointr(oflags) &&
763403831d35Sstevel 	    drmach_cpu_intr_disable(cp) != 0) {
763503831d35Sstevel 		cmn_err(CE_WARN, "failed to restore CPU id %d "
763603831d35Sstevel 		    "no-intr after I/O cage test", cpuid);
763703831d35Sstevel 		rv = -1;
763803831d35Sstevel 	}
763903831d35Sstevel 
764003831d35Sstevel 	dp->busy = 0;
764103831d35Sstevel 
764203831d35Sstevel 	DRMACH_PR("%s: returned CPU id: %d", fn, cpuid);
764303831d35Sstevel 
764403831d35Sstevel 	return (rv);
764503831d35Sstevel }
764603831d35Sstevel 
764703831d35Sstevel static sbd_error_t *
drmach_iocage_cpu_get(dr_testboard_req_t * tbrq,drmach_device_t ** dpp,cpu_flag_t * oflags)764803831d35Sstevel drmach_iocage_cpu_get(dr_testboard_req_t *tbrq, drmach_device_t **dpp,
764903831d35Sstevel     cpu_flag_t *oflags)
765003831d35Sstevel {
765103831d35Sstevel 	drmach_board_t	*bp;
765203831d35Sstevel 	int		b_rv;
765303831d35Sstevel 	int		b_idx;
765403831d35Sstevel 	drmachid_t	b_id;
765503831d35Sstevel 	int		found;
765603831d35Sstevel 
765703831d35Sstevel 	mutex_enter(&cpu_lock);
765803831d35Sstevel 
765903831d35Sstevel 	ASSERT(drmach_boards != NULL);
766003831d35Sstevel 
766103831d35Sstevel 	found = 0;
766203831d35Sstevel 
766303831d35Sstevel 	/*
766403831d35Sstevel 	 * Walk the board list.
766503831d35Sstevel 	 */
766603831d35Sstevel 	b_rv = drmach_array_first(drmach_boards, &b_idx, &b_id);
766703831d35Sstevel 
766803831d35Sstevel 	while (b_rv == 0) {
766903831d35Sstevel 
767003831d35Sstevel 		int		d_rv;
767103831d35Sstevel 		int		d_idx;
767203831d35Sstevel 		drmachid_t	d_id;
767303831d35Sstevel 
767403831d35Sstevel 		bp = b_id;
767503831d35Sstevel 
767603831d35Sstevel 		if (bp->connected == 0 || bp->devices == NULL) {
767703831d35Sstevel 			b_rv = drmach_array_next(drmach_boards, &b_idx, &b_id);
767803831d35Sstevel 			continue;
767903831d35Sstevel 		}
768003831d35Sstevel 
768103831d35Sstevel 		/* An AXQ restriction disqualifies MCPU's as candidates. */
768203831d35Sstevel 		if (DRMACH_BNUM2SLOT(bp->bnum) == 1) {
768303831d35Sstevel 			b_rv = drmach_array_next(drmach_boards, &b_idx, &b_id);
768403831d35Sstevel 			continue;
768503831d35Sstevel 		}
768603831d35Sstevel 
768703831d35Sstevel 		/*
768803831d35Sstevel 		 * Walk the device list of this board.
768903831d35Sstevel 		 */
769003831d35Sstevel 		d_rv = drmach_array_first(bp->devices, &d_idx, &d_id);
769103831d35Sstevel 
769203831d35Sstevel 		while (d_rv == 0) {
769303831d35Sstevel 
769403831d35Sstevel 			drmach_device_t	*ndp;
769503831d35Sstevel 
769603831d35Sstevel 			/* only interested in CPU devices */
769703831d35Sstevel 			if (!DRMACH_IS_CPU_ID(d_id)) {
769803831d35Sstevel 				d_rv = drmach_array_next(bp->devices, &d_idx,
769903831d35Sstevel 				    &d_id);
770003831d35Sstevel 				continue;
770103831d35Sstevel 			}
770203831d35Sstevel 
770303831d35Sstevel 			/*
770403831d35Sstevel 			 * The following code assumes two properties
770503831d35Sstevel 			 * of a CMP device:
770603831d35Sstevel 			 *
770703831d35Sstevel 			 *   1. All cores of a CMP are grouped together
770803831d35Sstevel 			 *	in the device list.
770903831d35Sstevel 			 *
771003831d35Sstevel 			 *   2. There will only be a maximum of two cores
771103831d35Sstevel 			 *	present in the CMP.
771203831d35Sstevel 			 *
771303831d35Sstevel 			 * If either of these two properties change,
771403831d35Sstevel 			 * this code will have to be revisited.
771503831d35Sstevel 			 */
771603831d35Sstevel 
771703831d35Sstevel 			dpp[0] = d_id;
771803831d35Sstevel 			dpp[1] = NULL;
771903831d35Sstevel 
772003831d35Sstevel 			/*
772103831d35Sstevel 			 * Get the next device. It may or may not be used.
772203831d35Sstevel 			 */
772303831d35Sstevel 			d_rv = drmach_array_next(bp->devices, &d_idx, &d_id);
772403831d35Sstevel 			ndp = d_id;
772503831d35Sstevel 
772603831d35Sstevel 			if ((d_rv == 0) && DRMACH_IS_CPU_ID(d_id)) {
772703831d35Sstevel 				/*
772803831d35Sstevel 				 * The second device is only interesting for
772903831d35Sstevel 				 * this pass if it has the same portid as the
773003831d35Sstevel 				 * first device. This implies that both are
773103831d35Sstevel 				 * cores of the same CMP.
773203831d35Sstevel 				 */
773303831d35Sstevel 				if (dpp[0]->portid == ndp->portid) {
773403831d35Sstevel 					dpp[1] = d_id;
773503831d35Sstevel 				}
773603831d35Sstevel 			}
773703831d35Sstevel 
773803831d35Sstevel 			/*
773903831d35Sstevel 			 * Attempt to acquire all cores of the CMP.
774003831d35Sstevel 			 */
774103831d35Sstevel 			if (drmach_iocage_cmp_acquire(dpp, oflags) == 0) {
774203831d35Sstevel 				found = 1;
774303831d35Sstevel 				break;
774403831d35Sstevel 			}
774503831d35Sstevel 
774603831d35Sstevel 			/*
774703831d35Sstevel 			 * Check if the search for the second core was
774803831d35Sstevel 			 * successful. If not, the next iteration should
774903831d35Sstevel 			 * use that device.
775003831d35Sstevel 			 */
775103831d35Sstevel 			if (dpp[1] == NULL) {
775203831d35Sstevel 				continue;
775303831d35Sstevel 			}
775403831d35Sstevel 
775503831d35Sstevel 			d_rv = drmach_array_next(bp->devices, &d_idx, &d_id);
775603831d35Sstevel 		}
775703831d35Sstevel 
775803831d35Sstevel 		if (found)
775903831d35Sstevel 			break;
776003831d35Sstevel 
776103831d35Sstevel 		b_rv = drmach_array_next(drmach_boards, &b_idx, &b_id);
776203831d35Sstevel 	}
776303831d35Sstevel 
776403831d35Sstevel 	mutex_exit(&cpu_lock);
776503831d35Sstevel 
776603831d35Sstevel 	if (!found) {
776703831d35Sstevel 		return (drerr_new(1, ESTC_IOCAGE_NO_CPU_AVAIL, NULL));
776803831d35Sstevel 	}
776903831d35Sstevel 
777003831d35Sstevel 	tbrq->cpu_portid = (*dpp)->portid;
777103831d35Sstevel 
777203831d35Sstevel 	return (NULL);
777303831d35Sstevel }
777403831d35Sstevel 
777503831d35Sstevel /*
777603831d35Sstevel  * Setup an iocage by acquiring a cpu and memory.
777703831d35Sstevel  */
777803831d35Sstevel static sbd_error_t *
drmach_iocage_setup(dr_testboard_req_t * tbrq,drmach_device_t ** dpp,cpu_flag_t * oflags)777903831d35Sstevel drmach_iocage_setup(dr_testboard_req_t *tbrq, drmach_device_t **dpp,
778003831d35Sstevel     cpu_flag_t *oflags)
778103831d35Sstevel {
778203831d35Sstevel 	sbd_error_t *err;
778303831d35Sstevel 
778403831d35Sstevel 	err = drmach_iocage_cpu_get(tbrq, dpp, oflags);
778503831d35Sstevel 	if (!err) {
778603831d35Sstevel 		mutex_enter(&drmach_iocage_lock);
778703831d35Sstevel 		while (drmach_iocage_is_busy)
778803831d35Sstevel 			cv_wait(&drmach_iocage_cv, &drmach_iocage_lock);
778903831d35Sstevel 		drmach_iocage_is_busy = 1;
779003831d35Sstevel 		mutex_exit(&drmach_iocage_lock);
779103831d35Sstevel 		err = drmach_iocage_mem_get(tbrq);
779203831d35Sstevel 		if (err) {
779303831d35Sstevel 			mutex_enter(&drmach_iocage_lock);
779403831d35Sstevel 			drmach_iocage_is_busy = 0;
779503831d35Sstevel 			cv_signal(&drmach_iocage_cv);
779603831d35Sstevel 			mutex_exit(&drmach_iocage_lock);
779703831d35Sstevel 		}
779803831d35Sstevel 	}
779903831d35Sstevel 	return (err);
780003831d35Sstevel }
780103831d35Sstevel 
780203831d35Sstevel #define	DRMACH_SCHIZO_PCI_LEAF_MAX	2
780303831d35Sstevel #define	DRMACH_SCHIZO_PCI_SLOT_MAX	8
780403831d35Sstevel #define	DRMACH_S1P_SAMPLE_MAX		2
780503831d35Sstevel 
780603831d35Sstevel typedef enum {
780703831d35Sstevel 	DRMACH_POST_SUSPEND = 0,
780803831d35Sstevel 	DRMACH_PRE_RESUME
780903831d35Sstevel } drmach_sr_iter_t;
781003831d35Sstevel 
781103831d35Sstevel typedef struct {
781203831d35Sstevel 	dev_info_t	*dip;
781303831d35Sstevel 	uint32_t	portid;
781403831d35Sstevel 	uint32_t	pcr_sel_save;
781503831d35Sstevel 	uint32_t	pic_l2_io_q[DRMACH_S1P_SAMPLE_MAX];
781603831d35Sstevel 	uint64_t	reg_basepa;
781703831d35Sstevel } drmach_s1p_axq_t;
781803831d35Sstevel 
781903831d35Sstevel typedef struct {
782003831d35Sstevel 	dev_info_t		*dip;
782103831d35Sstevel 	uint32_t		portid;
782203831d35Sstevel 	uint64_t		csr_basepa;
782303831d35Sstevel 	struct {
782403831d35Sstevel 		uint64_t 	slot_intr_state_diag;
782503831d35Sstevel 		uint64_t 	obio_intr_state_diag;
782603831d35Sstevel 		uint_t		nmap_regs;
782703831d35Sstevel 		uint64_t	*intr_map_regs;
782803831d35Sstevel 	} regs[DRMACH_S1P_SAMPLE_MAX];
782903831d35Sstevel } drmach_s1p_pci_t;
783003831d35Sstevel 
783103831d35Sstevel typedef struct {
783203831d35Sstevel 	uint64_t		csr_basepa;
783303831d35Sstevel 	struct {
783403831d35Sstevel 		uint64_t	csr;
783503831d35Sstevel 		uint64_t	errctrl;
783603831d35Sstevel 		uint64_t	errlog;
783703831d35Sstevel 	} regs[DRMACH_S1P_SAMPLE_MAX];
783803831d35Sstevel 	drmach_s1p_pci_t	pci[DRMACH_SCHIZO_PCI_LEAF_MAX];
783903831d35Sstevel } drmach_s1p_schizo_t;
784003831d35Sstevel 
784103831d35Sstevel typedef struct {
784203831d35Sstevel 	drmach_s1p_axq_t	axq;
784303831d35Sstevel 	drmach_s1p_schizo_t	schizo[STARCAT_SLOT1_IO_MAX];
784403831d35Sstevel } drmach_slot1_pause_t;
784503831d35Sstevel 
784603831d35Sstevel /*
784703831d35Sstevel  * Table of saved state for paused slot1 devices.
784803831d35Sstevel  */
784903831d35Sstevel static drmach_slot1_pause_t *drmach_slot1_paused[STARCAT_BDSET_MAX];
785003831d35Sstevel static int drmach_slot1_pause_init = 1;
785103831d35Sstevel 
785203831d35Sstevel #ifdef DEBUG
785303831d35Sstevel int drmach_slot1_pause_debug = 1;
785403831d35Sstevel #else
785503831d35Sstevel int drmach_slot1_pause_debug = 0;
785603831d35Sstevel #endif /* DEBUG */
785703831d35Sstevel 
785803831d35Sstevel static int
drmach_is_slot1_pause_axq(dev_info_t * dip,char * name,int * id,uint64_t * reg)785903831d35Sstevel drmach_is_slot1_pause_axq(dev_info_t *dip, char *name, int *id, uint64_t *reg)
786003831d35Sstevel {
786103831d35Sstevel 	int		portid, exp, slot, i;
786203831d35Sstevel 	drmach_reg_t	regs[2];
786303831d35Sstevel 	int		reglen = sizeof (regs);
786403831d35Sstevel 
786503831d35Sstevel 	if ((portid = ddi_getprop(DDI_DEV_T_ANY, dip,
786603831d35Sstevel 	    DDI_PROP_DONTPASS, "portid", -1)) == -1) {
786703831d35Sstevel 		return (0);
786803831d35Sstevel 	}
786903831d35Sstevel 
787003831d35Sstevel 	exp = (portid >> 5) & 0x1f;
787103831d35Sstevel 	slot = portid & 0x1;
787203831d35Sstevel 
787303831d35Sstevel 	if (slot == 0 || strncmp(name, DRMACH_AXQ_NAMEPROP,
787403831d35Sstevel 	    strlen(DRMACH_AXQ_NAMEPROP))) {
787503831d35Sstevel 		return (0);
787603831d35Sstevel 	}
787703831d35Sstevel 
787803831d35Sstevel 	mutex_enter(&cpu_lock);
787903831d35Sstevel 	for (i = 0; i < STARCAT_SLOT1_CPU_MAX; i++) {
788003831d35Sstevel 		if (cpu[MAKE_CPUID(exp, slot, i)]) {
788103831d35Sstevel 			/* maxcat cpu present */
788203831d35Sstevel 			mutex_exit(&cpu_lock);
788303831d35Sstevel 			return (0);
788403831d35Sstevel 		}
788503831d35Sstevel 	}
788603831d35Sstevel 	mutex_exit(&cpu_lock);
788703831d35Sstevel 
788803831d35Sstevel 	if (ddi_getlongprop_buf(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
788903831d35Sstevel 	    "reg", (caddr_t)regs, &reglen) != DDI_PROP_SUCCESS) {
789003831d35Sstevel 		DRMACH_PR("drmach_is_slot1_pause_axq: no reg prop for "
789107d06da5SSurya Prakki 		    "axq dip=%p\n", (void *)dip);
789203831d35Sstevel 		return (0);
789303831d35Sstevel 	}
789403831d35Sstevel 
789503831d35Sstevel 	ASSERT(id && reg);
789603831d35Sstevel 	*reg = (uint64_t)regs[0].reg_addr_hi << 32;
789703831d35Sstevel 	*reg |= (uint64_t)regs[0].reg_addr_lo;
789803831d35Sstevel 	*id = portid;
789903831d35Sstevel 
790003831d35Sstevel 	return (1);
790103831d35Sstevel }
790203831d35Sstevel 
790303831d35Sstevel /*
790403831d35Sstevel  * Allocate an entry in the slot1_paused state table.
790503831d35Sstevel  */
790603831d35Sstevel static void
drmach_slot1_pause_add_axq(dev_info_t * axq_dip,char * axq_name,int axq_portid,uint64_t reg,drmach_slot1_pause_t ** slot1_paused)790703831d35Sstevel drmach_slot1_pause_add_axq(dev_info_t *axq_dip, char *axq_name, int axq_portid,
790803831d35Sstevel     uint64_t reg, drmach_slot1_pause_t **slot1_paused)
790903831d35Sstevel {
791003831d35Sstevel 	int	axq_exp;
791103831d35Sstevel 	drmach_slot1_pause_t *slot1;
791203831d35Sstevel 
791303831d35Sstevel 	axq_exp = (axq_portid >> 5) & 0x1f;
791403831d35Sstevel 
791503831d35Sstevel 	ASSERT(axq_portid & 0x1);
791603831d35Sstevel 	ASSERT(slot1_paused[axq_exp] == NULL);
791703831d35Sstevel 	ASSERT(strncmp(axq_name, DRMACH_AXQ_NAMEPROP,
791803831d35Sstevel 	    strlen(DRMACH_AXQ_NAMEPROP)) == 0);
791903831d35Sstevel 
792003831d35Sstevel 	slot1 = kmem_zalloc(sizeof (*slot1), KM_SLEEP);
792103831d35Sstevel 
792203831d35Sstevel 	/*
792303831d35Sstevel 	 * XXX This dip should really be held (via ndi_hold_devi())
792403831d35Sstevel 	 * before saving it in the axq pause structure. However that
792503831d35Sstevel 	 * would prevent DR as the pause data structures persist until
792603831d35Sstevel 	 * the next suspend. drmach code should be modified to free the
792703831d35Sstevel 	 * the slot 1 pause data structures for a boardset when its
792803831d35Sstevel 	 * slot 1 board is DRed out. The dip can then be released via
792903831d35Sstevel 	 * ndi_rele_devi() when the pause data structure is freed
793003831d35Sstevel 	 * allowing DR to proceed. Until this change is made, drmach
793103831d35Sstevel 	 * code should be careful about dereferencing the saved dip
793203831d35Sstevel 	 * as it may no longer exist.
793303831d35Sstevel 	 */
793403831d35Sstevel 	slot1->axq.dip = axq_dip;
793503831d35Sstevel 	slot1->axq.portid = axq_portid;
793603831d35Sstevel 	slot1->axq.reg_basepa = reg;
793703831d35Sstevel 	slot1_paused[axq_exp] = slot1;
793803831d35Sstevel }
793903831d35Sstevel 
794003831d35Sstevel static void
drmach_s1p_pci_free(drmach_s1p_pci_t * pci)794103831d35Sstevel drmach_s1p_pci_free(drmach_s1p_pci_t *pci)
794203831d35Sstevel {
794303831d35Sstevel 	int	i;
794403831d35Sstevel 
794503831d35Sstevel 	for (i = 0; i < DRMACH_S1P_SAMPLE_MAX; i++) {
794603831d35Sstevel 		if (pci->regs[i].intr_map_regs != NULL) {
794703831d35Sstevel 			ASSERT(pci->regs[i].nmap_regs > 0);
794803831d35Sstevel 			kmem_free(pci->regs[i].intr_map_regs,
794903831d35Sstevel 			    pci->regs[i].nmap_regs * sizeof (uint64_t));
795003831d35Sstevel 		}
795103831d35Sstevel 	}
795203831d35Sstevel }
795303831d35Sstevel 
795403831d35Sstevel static void
drmach_slot1_pause_free(drmach_slot1_pause_t ** slot1_paused)795503831d35Sstevel drmach_slot1_pause_free(drmach_slot1_pause_t **slot1_paused)
795603831d35Sstevel {
795703831d35Sstevel 	int	i, j, k;
795803831d35Sstevel 	drmach_slot1_pause_t *slot1;
795903831d35Sstevel 
796003831d35Sstevel 	for (i = 0; i < STARCAT_BDSET_MAX; i++) {
796103831d35Sstevel 		if ((slot1 = slot1_paused[i]) == NULL)
796203831d35Sstevel 			continue;
796303831d35Sstevel 
796403831d35Sstevel 		for (j = 0; j < STARCAT_SLOT1_IO_MAX; j++)
796503831d35Sstevel 			for (k = 0; k < DRMACH_SCHIZO_PCI_LEAF_MAX; k++)
796603831d35Sstevel 				drmach_s1p_pci_free(&slot1->schizo[j].pci[k]);
796703831d35Sstevel 
796803831d35Sstevel 		kmem_free(slot1, sizeof (*slot1));
796903831d35Sstevel 		slot1_paused[i] = NULL;
797003831d35Sstevel 	}
797103831d35Sstevel }
797203831d35Sstevel 
797303831d35Sstevel /*
797403831d35Sstevel  * Tree walk callback routine. If dip represents a Schizo PCI leaf,
797503831d35Sstevel  * fill in the appropriate info in the slot1_paused state table.
797603831d35Sstevel  */
797703831d35Sstevel static int
drmach_find_slot1_io(dev_info_t * dip,void * arg)797803831d35Sstevel drmach_find_slot1_io(dev_info_t *dip, void *arg)
797903831d35Sstevel {
798003831d35Sstevel 	int		portid, exp, ioc_unum, leaf_unum;
798103831d35Sstevel 	char		buf[OBP_MAXDRVNAME];
798203831d35Sstevel 	int		buflen = sizeof (buf);
798303831d35Sstevel 	drmach_reg_t	regs[3];
798403831d35Sstevel 	int		reglen = sizeof (regs);
798503831d35Sstevel 	uint32_t	leaf_offset;
798603831d35Sstevel 	uint64_t	schizo_csr_pa, pci_csr_pa;
798703831d35Sstevel 	drmach_s1p_pci_t *pci;
798803831d35Sstevel 	drmach_slot1_pause_t **slot1_paused = (drmach_slot1_pause_t **)arg;
798903831d35Sstevel 
799003831d35Sstevel 	if (ddi_getlongprop_buf(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
799103831d35Sstevel 	    "name", (caddr_t)buf, &buflen) != DDI_PROP_SUCCESS ||
799203831d35Sstevel 	    strncmp(buf, DRMACH_PCI_NAMEPROP, strlen(DRMACH_PCI_NAMEPROP))) {
799303831d35Sstevel 		return (DDI_WALK_CONTINUE);
799403831d35Sstevel 	}
799503831d35Sstevel 
799603831d35Sstevel 	if ((portid = ddi_getprop(DDI_DEV_T_ANY, dip,
799703831d35Sstevel 	    DDI_PROP_DONTPASS, "portid", -1)) == -1) {
799803831d35Sstevel 		return (DDI_WALK_CONTINUE);
799903831d35Sstevel 	}
800003831d35Sstevel 
800103831d35Sstevel 	if (ddi_getlongprop_buf(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
800203831d35Sstevel 	    "reg", (caddr_t)regs, &reglen) != DDI_PROP_SUCCESS) {
800303831d35Sstevel 		DRMACH_PR("drmach_find_slot1_io: no reg prop for pci "
800407d06da5SSurya Prakki 		    "dip=%p\n", (void *)dip);
800503831d35Sstevel 		return (DDI_WALK_CONTINUE);
800603831d35Sstevel 	}
800703831d35Sstevel 
800803831d35Sstevel 	exp = portid >> 5;
800903831d35Sstevel 	ioc_unum = portid & 0x1;
801003831d35Sstevel 	leaf_offset = regs[0].reg_addr_lo & 0x7fffff;
801103831d35Sstevel 	pci_csr_pa = (uint64_t)regs[0].reg_addr_hi << 32;
801203831d35Sstevel 	pci_csr_pa |= (uint64_t)regs[0].reg_addr_lo;
801303831d35Sstevel 	schizo_csr_pa = (uint64_t)regs[1].reg_addr_hi << 32;
801403831d35Sstevel 	schizo_csr_pa |= (uint64_t)regs[1].reg_addr_lo;
801503831d35Sstevel 
801603831d35Sstevel 	ASSERT(exp >= 0 && exp < STARCAT_BDSET_MAX);
801703831d35Sstevel 	ASSERT(slot1_paused[exp] != NULL);
801803831d35Sstevel 	ASSERT(leaf_offset == 0x600000 || leaf_offset == 0x700000);
801903831d35Sstevel 	ASSERT(slot1_paused[exp]->schizo[ioc_unum].csr_basepa == 0x0UL ||
802003831d35Sstevel 	    slot1_paused[exp]->schizo[ioc_unum].csr_basepa == schizo_csr_pa);
802103831d35Sstevel 
802203831d35Sstevel 	leaf_unum = (leaf_offset == 0x600000) ? 0 : 1;
802303831d35Sstevel 	slot1_paused[exp]->schizo[ioc_unum].csr_basepa = schizo_csr_pa;
802403831d35Sstevel 	pci = &slot1_paused[exp]->schizo[ioc_unum].pci[leaf_unum];
802503831d35Sstevel 
802603831d35Sstevel 	/*
802703831d35Sstevel 	 * XXX This dip should really be held (via ndi_hold_devi())
802803831d35Sstevel 	 * before saving it in the pci pause structure. However that
802903831d35Sstevel 	 * would prevent DR as the pause data structures persist until
803003831d35Sstevel 	 * the next suspend. drmach code should be modified to free the
803103831d35Sstevel 	 * the slot 1 pause data structures for a boardset when its
803203831d35Sstevel 	 * slot 1 board is DRed out. The dip can then be released via
803303831d35Sstevel 	 * ndi_rele_devi() when the pause data structure is freed
803403831d35Sstevel 	 * allowing DR to proceed. Until this change is made, drmach
803503831d35Sstevel 	 * code should be careful about dereferencing the saved dip as
803603831d35Sstevel 	 * it may no longer exist.
803703831d35Sstevel 	 */
803803831d35Sstevel 	pci->dip = dip;
803903831d35Sstevel 	pci->portid = portid;
804003831d35Sstevel 	pci->csr_basepa = pci_csr_pa;
804103831d35Sstevel 
804203831d35Sstevel 	DRMACH_PR("drmach_find_slot1_io: name=%s, portid=0x%x, dip=%p\n",
804307d06da5SSurya Prakki 	    buf, portid, (void *)dip);
804403831d35Sstevel 
804503831d35Sstevel 	return (DDI_WALK_PRUNECHILD);
804603831d35Sstevel }
804703831d35Sstevel 
804803831d35Sstevel static void
drmach_slot1_pause_add_io(drmach_slot1_pause_t ** slot1_paused)804903831d35Sstevel drmach_slot1_pause_add_io(drmach_slot1_pause_t **slot1_paused)
805003831d35Sstevel {
805103831d35Sstevel 	/*
805203831d35Sstevel 	 * Root node doesn't have to be held
805303831d35Sstevel 	 */
805403831d35Sstevel 	ddi_walk_devs(ddi_root_node(), drmach_find_slot1_io,
805503831d35Sstevel 	    (void *)slot1_paused);
805603831d35Sstevel }
805703831d35Sstevel 
805803831d35Sstevel /*
805903831d35Sstevel  * Save the interrupt mapping registers for each non-idle interrupt
806003831d35Sstevel  * represented by the bit pairs in the saved interrupt state
806103831d35Sstevel  * diagnostic registers for this PCI leaf.
806203831d35Sstevel  */
806303831d35Sstevel static void
drmach_s1p_intr_map_reg_save(drmach_s1p_pci_t * pci,drmach_sr_iter_t iter)806403831d35Sstevel drmach_s1p_intr_map_reg_save(drmach_s1p_pci_t *pci, drmach_sr_iter_t iter)
806503831d35Sstevel {
806603831d35Sstevel 	int	 i, cnt, ino;
806703831d35Sstevel 	uint64_t reg;
806803831d35Sstevel 	char	 *dname;
806903831d35Sstevel 	uchar_t	 Xmits;
807003831d35Sstevel 
807103831d35Sstevel 	dname = ddi_binding_name(pci->dip);
807203831d35Sstevel 	Xmits = (strcmp(dname, XMITS_BINDING_NAME) == 0)  ?  1 : 0;
807303831d35Sstevel 
807403831d35Sstevel 	/*
807503831d35Sstevel 	 * 1st pass allocates, 2nd pass populates.
807603831d35Sstevel 	 */
807703831d35Sstevel 	for (i = 0; i < 2; i++) {
807803831d35Sstevel 		cnt = ino = 0;
807903831d35Sstevel 
808003831d35Sstevel 		/*
808103831d35Sstevel 		 * PCI slot interrupts
808203831d35Sstevel 		 */
808303831d35Sstevel 		reg = pci->regs[iter].slot_intr_state_diag;
808403831d35Sstevel 		while (reg) {
808503831d35Sstevel 			/*
808603831d35Sstevel 			 * Xmits Interrupt Number Offset(ino) Assignments
808703831d35Sstevel 			 *   00-17 PCI Slot Interrupts
808803831d35Sstevel 			 *   18-1f Not Used
808903831d35Sstevel 			 */
809003831d35Sstevel 			if ((Xmits) && (ino > 0x17))
809103831d35Sstevel 				break;
809203831d35Sstevel 			if ((reg & COMMON_CLEAR_INTR_REG_MASK) !=
809303831d35Sstevel 			    COMMON_CLEAR_INTR_REG_IDLE) {
809403831d35Sstevel 				if (i) {
809503831d35Sstevel 					pci->regs[iter].intr_map_regs[cnt] =
809603831d35Sstevel 					    lddphysio(pci->csr_basepa +
809703831d35Sstevel 					    SCHIZO_IB_INTR_MAP_REG_OFFSET +
809803831d35Sstevel 					    ino * sizeof (reg));
809903831d35Sstevel 				}
810003831d35Sstevel 				++cnt;
810103831d35Sstevel 			}
810203831d35Sstevel 			++ino;
810303831d35Sstevel 			reg >>= 2;
810403831d35Sstevel 		}
810503831d35Sstevel 
810603831d35Sstevel 		/*
810703831d35Sstevel 		 * Xmits Interrupt Number Offset(ino) Assignments
810803831d35Sstevel 		 *   20-2f Not Used
810903831d35Sstevel 		 *   30-37 Internal interrupts
811003831d35Sstevel 		 *   38-3e Not Used
811103831d35Sstevel 		 */
811203831d35Sstevel 		ino = (Xmits)  ?  0x30 : 0x20;
811303831d35Sstevel 
811403831d35Sstevel 		/*
811503831d35Sstevel 		 * OBIO and internal schizo interrupts
811603831d35Sstevel 		 * Each PCI leaf has a set of mapping registers for all
811703831d35Sstevel 		 * possible interrupt sources except the NewLink interrupts.
811803831d35Sstevel 		 */
811903831d35Sstevel 		reg = pci->regs[iter].obio_intr_state_diag;
812003831d35Sstevel 		while (reg && ino <= 0x38) {
812103831d35Sstevel 			if ((reg & COMMON_CLEAR_INTR_REG_MASK) !=
812203831d35Sstevel 			    COMMON_CLEAR_INTR_REG_IDLE) {
812303831d35Sstevel 				if (i) {
812403831d35Sstevel 					pci->regs[iter].intr_map_regs[cnt] =
812503831d35Sstevel 					    lddphysio(pci->csr_basepa +
812603831d35Sstevel 					    SCHIZO_IB_INTR_MAP_REG_OFFSET +
812703831d35Sstevel 					    ino * sizeof (reg));
812803831d35Sstevel 				}
812903831d35Sstevel 				++cnt;
813003831d35Sstevel 			}
813103831d35Sstevel 			++ino;
813203831d35Sstevel 			reg >>= 2;
813303831d35Sstevel 		}
813403831d35Sstevel 
813503831d35Sstevel 		if (!i) {
813603831d35Sstevel 			pci->regs[iter].nmap_regs = cnt;
813703831d35Sstevel 			pci->regs[iter].intr_map_regs =
813803831d35Sstevel 			    kmem_zalloc(cnt * sizeof (reg), KM_SLEEP);
813903831d35Sstevel 		}
814003831d35Sstevel 	}
814103831d35Sstevel }
814203831d35Sstevel 
814303831d35Sstevel static void
drmach_s1p_axq_update(drmach_s1p_axq_t * axq,drmach_sr_iter_t iter)814403831d35Sstevel drmach_s1p_axq_update(drmach_s1p_axq_t *axq, drmach_sr_iter_t iter)
814503831d35Sstevel {
814603831d35Sstevel 	uint32_t	reg;
814703831d35Sstevel 
814803831d35Sstevel 	if (axq->reg_basepa == 0x0UL)
814903831d35Sstevel 		return;
815003831d35Sstevel 
815103831d35Sstevel 	if (iter == DRMACH_POST_SUSPEND) {
815203831d35Sstevel 		axq->pcr_sel_save = ldphysio(axq->reg_basepa +
815303831d35Sstevel 		    AXQ_SLOT1_PERFCNT_SEL);
815403831d35Sstevel 		/*
815503831d35Sstevel 		 * Select l2_io_queue counter by writing L2_IO_Q mux
815603831d35Sstevel 		 * input to bits 0-6 of perf cntr select reg.
815703831d35Sstevel 		 */
815803831d35Sstevel 		reg = axq->pcr_sel_save;
815903831d35Sstevel 		reg &= ~AXQ_PIC_CLEAR_MASK;
816003831d35Sstevel 		reg |= L2_IO_Q;
816103831d35Sstevel 
816203831d35Sstevel 		stphysio(axq->reg_basepa + AXQ_SLOT1_PERFCNT_SEL, reg);
816303831d35Sstevel 	}
816403831d35Sstevel 
816503831d35Sstevel 	axq->pic_l2_io_q[iter] = ldphysio(axq->reg_basepa + AXQ_SLOT1_PERFCNT0);
816603831d35Sstevel 
816703831d35Sstevel 	if (iter == DRMACH_PRE_RESUME) {
816803831d35Sstevel 		stphysio(axq->reg_basepa + AXQ_SLOT1_PERFCNT_SEL,
816903831d35Sstevel 		    axq->pcr_sel_save);
817003831d35Sstevel 	}
817103831d35Sstevel 
817203831d35Sstevel 	DRMACH_PR("drmach_s1p_axq_update: axq #%d pic_l2_io_q[%d]=%d\n",
817303831d35Sstevel 	    ddi_get_instance(axq->dip), iter, axq->pic_l2_io_q[iter]);
817403831d35Sstevel }
817503831d35Sstevel 
817603831d35Sstevel static void
drmach_s1p_schizo_update(drmach_s1p_schizo_t * schizo,drmach_sr_iter_t iter)817703831d35Sstevel drmach_s1p_schizo_update(drmach_s1p_schizo_t *schizo, drmach_sr_iter_t iter)
817803831d35Sstevel {
817903831d35Sstevel 	int	i;
818003831d35Sstevel 	drmach_s1p_pci_t *pci;
818103831d35Sstevel 
818203831d35Sstevel 	if (schizo->csr_basepa == 0x0UL)
818303831d35Sstevel 		return;
818403831d35Sstevel 
818503831d35Sstevel 	schizo->regs[iter].csr =
818603831d35Sstevel 	    lddphysio(schizo->csr_basepa + SCHIZO_CB_CSR_OFFSET);
818703831d35Sstevel 	schizo->regs[iter].errctrl =
818803831d35Sstevel 	    lddphysio(schizo->csr_basepa + SCHIZO_CB_ERRCTRL_OFFSET);
818903831d35Sstevel 	schizo->regs[iter].errlog =
819003831d35Sstevel 	    lddphysio(schizo->csr_basepa + SCHIZO_CB_ERRLOG_OFFSET);
819103831d35Sstevel 
819203831d35Sstevel 	for (i = 0; i < DRMACH_SCHIZO_PCI_LEAF_MAX; i++) {
819303831d35Sstevel 		pci = &schizo->pci[i];
819403831d35Sstevel 		if (pci->dip != NULL && pci->csr_basepa != 0x0UL) {
819503831d35Sstevel 			pci->regs[iter].slot_intr_state_diag =
819603831d35Sstevel 			    lddphysio(pci->csr_basepa +
819703831d35Sstevel 			    COMMON_IB_SLOT_INTR_STATE_DIAG_REG);
819803831d35Sstevel 
819903831d35Sstevel 			pci->regs[iter].obio_intr_state_diag =
820003831d35Sstevel 			    lddphysio(pci->csr_basepa +
820103831d35Sstevel 			    COMMON_IB_OBIO_INTR_STATE_DIAG_REG);
820203831d35Sstevel 
820303831d35Sstevel 			drmach_s1p_intr_map_reg_save(pci, iter);
820403831d35Sstevel 		}
820503831d35Sstevel 	}
820603831d35Sstevel }
820703831d35Sstevel 
820803831d35Sstevel /*
820903831d35Sstevel  * Called post-suspend and pre-resume to snapshot the suspend state
821003831d35Sstevel  * of slot1 AXQs and Schizos.
821103831d35Sstevel  */
821203831d35Sstevel static void
drmach_slot1_pause_update(drmach_slot1_pause_t ** slot1_paused,drmach_sr_iter_t iter)821303831d35Sstevel drmach_slot1_pause_update(drmach_slot1_pause_t **slot1_paused,
821403831d35Sstevel     drmach_sr_iter_t iter)
821503831d35Sstevel {
821603831d35Sstevel 	int	i, j;
821703831d35Sstevel 	drmach_slot1_pause_t *slot1;
821803831d35Sstevel 
821903831d35Sstevel 	for (i = 0; i < STARCAT_BDSET_MAX; i++) {
822003831d35Sstevel 		if ((slot1 = slot1_paused[i]) == NULL)
822103831d35Sstevel 			continue;
822203831d35Sstevel 
822303831d35Sstevel 		drmach_s1p_axq_update(&slot1->axq, iter);
822403831d35Sstevel 		for (j = 0; j < STARCAT_SLOT1_IO_MAX; j++)
822503831d35Sstevel 			drmach_s1p_schizo_update(&slot1->schizo[j], iter);
822603831d35Sstevel 	}
822703831d35Sstevel }
822803831d35Sstevel 
822903831d35Sstevel /*
823003831d35Sstevel  * Starcat hPCI Schizo devices.
823103831d35Sstevel  *
823203831d35Sstevel  * The name field is overloaded. NULL means the slot (interrupt concentrator
823303831d35Sstevel  * bus) is not used. intr_mask is a bit mask representing the 4 possible
823403831d35Sstevel  * interrupts per slot, on if valid (rio does not use interrupt lines 0, 1).
823503831d35Sstevel  */
823603831d35Sstevel static struct {
823703831d35Sstevel 	char	*name;
823803831d35Sstevel 	uint8_t	intr_mask;
823903831d35Sstevel } drmach_schz_slot_intr[][DRMACH_SCHIZO_PCI_LEAF_MAX] = {
824003831d35Sstevel 	/* Schizo 0 */		/* Schizo 1 */
824103831d35Sstevel 	{{"C3V0", 0xf},		{"C3V1", 0xf}},		/* slot 0 */
824203831d35Sstevel 	{{"C5V0", 0xf},		{"C5V1", 0xf}},		/* slot 1 */
824303831d35Sstevel 	{{"rio", 0xc},		{NULL, 0x0}},		/* slot 2 */
824403831d35Sstevel 	{{NULL, 0x0},		{NULL, 0x0}},		/* slot 3 */
824503831d35Sstevel 	{{"sbbc", 0xf},		{NULL, 0x0}},		/* slot 4 */
824603831d35Sstevel 	{{NULL, 0x0},		{NULL, 0x0}},		/* slot 5 */
824703831d35Sstevel 	{{NULL, 0x0},		{NULL, 0x0}},		/* slot 6 */
824803831d35Sstevel 	{{NULL, 0x0},		{NULL, 0x0}}		/* slot 7 */
824903831d35Sstevel };
825003831d35Sstevel 
825103831d35Sstevel /*
825203831d35Sstevel  * See Schizo Specification, Revision 51 (May 23, 2001), Section 22.4.4
825303831d35Sstevel  * "Interrupt Registers", Table 22-69, page 306.
825403831d35Sstevel  */
825503831d35Sstevel static char *
drmach_schz_internal_ino2str(int ino)825603831d35Sstevel drmach_schz_internal_ino2str(int ino)
825703831d35Sstevel {
825803831d35Sstevel 	int	intr;
825903831d35Sstevel 
826003831d35Sstevel 	ASSERT(ino >= 0x30 && ino <= 0x37);
826103831d35Sstevel 
826203831d35Sstevel 	intr = ino & 0x7;
826303831d35Sstevel 	switch (intr) {
826403831d35Sstevel 		case (0x0):	return ("Uncorrectable ECC error");
826503831d35Sstevel 		case (0x1):	return ("Correctable ECC error");
826603831d35Sstevel 		case (0x2):	return ("PCI Bus A Error");
826703831d35Sstevel 		case (0x3):	return ("PCI Bus B Error");
826803831d35Sstevel 		case (0x4):	return ("Safari Bus Error");
826903831d35Sstevel 		default:	return ("Reserved");
827003831d35Sstevel 	}
827103831d35Sstevel }
827203831d35Sstevel 
827303831d35Sstevel #define	DRMACH_INTR_MASK_SHIFT(ino)	((ino) << 1)
827403831d35Sstevel 
827503831d35Sstevel static void
drmach_s1p_decode_slot_intr(int exp,int unum,drmach_s1p_pci_t * pci,int ino,drmach_sr_iter_t iter)827603831d35Sstevel drmach_s1p_decode_slot_intr(int exp, int unum, drmach_s1p_pci_t *pci,
827703831d35Sstevel     int ino, drmach_sr_iter_t iter)
827803831d35Sstevel {
827903831d35Sstevel 	uint8_t		intr_mask;
828003831d35Sstevel 	char		*slot_devname;
828103831d35Sstevel 	char		namebuf[OBP_MAXDRVNAME];
828203831d35Sstevel 	int		slot, intr_line, slot_valid, intr_valid;
828303831d35Sstevel 
828403831d35Sstevel 	ASSERT(ino >= 0 && ino <= 0x1f);
828503831d35Sstevel 	ASSERT((pci->regs[iter].slot_intr_state_diag &
828603831d35Sstevel 	    (COMMON_CLEAR_INTR_REG_MASK << DRMACH_INTR_MASK_SHIFT(ino))) !=
828703831d35Sstevel 	    COMMON_CLEAR_INTR_REG_IDLE);
828803831d35Sstevel 
828903831d35Sstevel 	slot = (ino >> 2) & 0x7;
829003831d35Sstevel 	intr_line = ino & 0x3;
829103831d35Sstevel 
829203831d35Sstevel 	slot_devname = drmach_schz_slot_intr[slot][unum].name;
829303831d35Sstevel 	slot_valid = (slot_devname == NULL) ? 0 : 1;
829403831d35Sstevel 	if (!slot_valid) {
829507d06da5SSurya Prakki 		(void) snprintf(namebuf, sizeof (namebuf), "slot %d (INVALID)",
829607d06da5SSurya Prakki 		    slot);
829703831d35Sstevel 		slot_devname = namebuf;
829803831d35Sstevel 	}
829903831d35Sstevel 
830003831d35Sstevel 	intr_mask = drmach_schz_slot_intr[slot][unum].intr_mask;
830103831d35Sstevel 	intr_valid = (1 << intr_line) & intr_mask;
830203831d35Sstevel 
830303831d35Sstevel 	prom_printf("IO%d/P%d PCI slot interrupt: ino=0x%x, source device=%s, "
830403831d35Sstevel 	    "interrupt line=%d%s\n", exp, unum, ino, slot_devname, intr_line,
830503831d35Sstevel 	    (slot_valid && !intr_valid) ? " (INVALID)" : "");
830603831d35Sstevel }
830703831d35Sstevel 
830803831d35Sstevel /*
830903831d35Sstevel  * Log interrupt source device info for all valid, pending interrupts
831003831d35Sstevel  * on each Schizo PCI leaf. Called if Schizo has logged a Safari bus
831103831d35Sstevel  * error in the error ctrl reg.
831203831d35Sstevel  */
831303831d35Sstevel static void
drmach_s1p_schizo_log_intr(drmach_s1p_schizo_t * schizo,int exp,int unum,drmach_sr_iter_t iter)831403831d35Sstevel drmach_s1p_schizo_log_intr(drmach_s1p_schizo_t *schizo, int exp,
831503831d35Sstevel     int unum, drmach_sr_iter_t iter)
831603831d35Sstevel {
831703831d35Sstevel 	uint64_t	reg;
831803831d35Sstevel 	int		i, n, ino;
831903831d35Sstevel 	drmach_s1p_pci_t *pci;
832003831d35Sstevel 
832103831d35Sstevel 	ASSERT(exp >= 0 && exp < STARCAT_BDSET_MAX);
832203831d35Sstevel 	ASSERT(unum < STARCAT_SLOT1_IO_MAX);
832303831d35Sstevel 
832403831d35Sstevel 	/*
832503831d35Sstevel 	 * Check the saved interrupt mapping registers. If interrupt is valid,
832603831d35Sstevel 	 * map the ino to the Schizo source device and check that the pci
832703831d35Sstevel 	 * slot and interrupt line are valid.
832803831d35Sstevel 	 */
832903831d35Sstevel 	for (i = 0; i < DRMACH_SCHIZO_PCI_LEAF_MAX; i++) {
833003831d35Sstevel 		pci = &schizo->pci[i];
833103831d35Sstevel 		for (n = 0; n < pci->regs[iter].nmap_regs; n++) {
833203831d35Sstevel 			reg = pci->regs[iter].intr_map_regs[n];
833303831d35Sstevel 			if (reg & COMMON_INTR_MAP_REG_VALID) {
833403831d35Sstevel 				ino = reg & COMMON_INTR_MAP_REG_INO;
833503831d35Sstevel 
833603831d35Sstevel 				if (ino <= 0x1f) {
833703831d35Sstevel 					/*
833803831d35Sstevel 					 * PCI slot interrupt
833903831d35Sstevel 					 */
834003831d35Sstevel 					drmach_s1p_decode_slot_intr(exp, unum,
834103831d35Sstevel 					    pci, ino, iter);
834203831d35Sstevel 				} else if (ino <= 0x2f) {
834303831d35Sstevel 					/*
834403831d35Sstevel 					 * OBIO interrupt
834503831d35Sstevel 					 */
834603831d35Sstevel 					prom_printf("IO%d/P%d OBIO interrupt: "
834703831d35Sstevel 					    "ino=0x%x\n", exp, unum, ino);
834803831d35Sstevel 				} else if (ino <= 0x37) {
834903831d35Sstevel 					/*
835003831d35Sstevel 					 * Internal interrupt
835103831d35Sstevel 					 */
835203831d35Sstevel 					prom_printf("IO%d/P%d Internal "
835303831d35Sstevel 					    "interrupt: ino=0x%x (%s)\n",
835403831d35Sstevel 					    exp, unum, ino,
835503831d35Sstevel 					    drmach_schz_internal_ino2str(ino));
835603831d35Sstevel 				} else {
835703831d35Sstevel 					/*
835803831d35Sstevel 					 * NewLink interrupt
835903831d35Sstevel 					 */
836003831d35Sstevel 					prom_printf("IO%d/P%d NewLink "
836103831d35Sstevel 					    "interrupt: ino=0x%x\n", exp,
836203831d35Sstevel 					    unum, ino);
836303831d35Sstevel 				}
836403831d35Sstevel 
836503831d35Sstevel 				DRMACH_PR("drmach_s1p_schizo_log_intr: "
836603831d35Sstevel 				    "exp=%d, schizo=%d, pci_leaf=%c, "
836703831d35Sstevel 				    "ino=0x%x, intr_map_reg=0x%lx\n",
836803831d35Sstevel 				    exp, unum, (i == 0) ? 'A' : 'B', ino, reg);
836903831d35Sstevel 			}
837003831d35Sstevel 		}
837103831d35Sstevel 	}
837203831d35Sstevel }
837303831d35Sstevel 
837403831d35Sstevel /*
837503831d35Sstevel  * See Schizo Specification, Revision 51 (May 23, 2001), Section 22.2.4
837603831d35Sstevel  * "Safari Error Control/Log Registers", Table 22-11, page 248.
837703831d35Sstevel  */
837803831d35Sstevel #define	DRMACH_SCHIZO_SAFARI_UNMAPPED_ERR	(0x1ull << 4)
837903831d35Sstevel 
838003831d35Sstevel /*
838103831d35Sstevel  * Check for possible error indicators prior to resuming the
838203831d35Sstevel  * AXQ driver, which will de-assert slot1 AXQ_DOMCTRL_PAUSE.
838303831d35Sstevel  */
838403831d35Sstevel static void
drmach_slot1_pause_verify(drmach_slot1_pause_t ** slot1_paused,drmach_sr_iter_t iter)838503831d35Sstevel drmach_slot1_pause_verify(drmach_slot1_pause_t **slot1_paused,
838603831d35Sstevel     drmach_sr_iter_t iter)
838703831d35Sstevel {
838803831d35Sstevel 	int	i, j;
838903831d35Sstevel 	int 	errflag = 0;
839003831d35Sstevel 	drmach_slot1_pause_t *slot1;
839103831d35Sstevel 
839203831d35Sstevel 	/*
839303831d35Sstevel 	 * Check for logged schizo bus error and pending interrupts.
839403831d35Sstevel 	 */
839503831d35Sstevel 	for (i = 0; i < STARCAT_BDSET_MAX; i++) {
839603831d35Sstevel 		if ((slot1 = slot1_paused[i]) == NULL)
839703831d35Sstevel 			continue;
839803831d35Sstevel 
839903831d35Sstevel 		for (j = 0; j < STARCAT_SLOT1_IO_MAX; j++) {
840003831d35Sstevel 			if (slot1->schizo[j].csr_basepa == 0x0UL)
840103831d35Sstevel 				continue;
840203831d35Sstevel 
840303831d35Sstevel 			if (slot1->schizo[j].regs[iter].errlog &
840403831d35Sstevel 			    DRMACH_SCHIZO_SAFARI_UNMAPPED_ERR) {
840503831d35Sstevel 				if (!errflag) {
840603831d35Sstevel 					prom_printf("DR WARNING: interrupt "
840703831d35Sstevel 					    "attempt detected during "
840803831d35Sstevel 					    "copy-rename (%s):\n",
840903831d35Sstevel 					    (iter == DRMACH_POST_SUSPEND) ?
841003831d35Sstevel 					    "post suspend" : "pre resume");
841103831d35Sstevel 					++errflag;
841203831d35Sstevel 				}
841303831d35Sstevel 				drmach_s1p_schizo_log_intr(&slot1->schizo[j],
841403831d35Sstevel 				    i, j, iter);
841503831d35Sstevel 			}
841603831d35Sstevel 		}
841703831d35Sstevel 	}
841803831d35Sstevel 
841903831d35Sstevel 	/*
842003831d35Sstevel 	 * Check for changes in axq l2_io_q performance counters (2nd pass only)
842103831d35Sstevel 	 */
842203831d35Sstevel 	if (iter == DRMACH_PRE_RESUME) {
842303831d35Sstevel 		for (i = 0; i < STARCAT_BDSET_MAX; i++) {
842403831d35Sstevel 			if ((slot1 = slot1_paused[i]) == NULL)
842503831d35Sstevel 				continue;
842603831d35Sstevel 
842703831d35Sstevel 			if (slot1->axq.pic_l2_io_q[DRMACH_POST_SUSPEND] !=
842803831d35Sstevel 			    slot1->axq.pic_l2_io_q[DRMACH_PRE_RESUME]) {
842903831d35Sstevel 				prom_printf("DR WARNING: IO transactions "
843003831d35Sstevel 				    "detected on IO%d during copy-rename: "
843103831d35Sstevel 				    "AXQ l2_io_q performance counter "
843203831d35Sstevel 				    "start=%d, end=%d\n", i,
843303831d35Sstevel 				    slot1->axq.pic_l2_io_q[DRMACH_POST_SUSPEND],
843403831d35Sstevel 				    slot1->axq.pic_l2_io_q[DRMACH_PRE_RESUME]);
843503831d35Sstevel 			}
843603831d35Sstevel 		}
843703831d35Sstevel 	}
843803831d35Sstevel }
843903831d35Sstevel 
844003831d35Sstevel struct drmach_sr_list {
844103831d35Sstevel 	dev_info_t		*dip;
844203831d35Sstevel 	struct drmach_sr_list	*next;
844303831d35Sstevel 	struct drmach_sr_list	*prev;
844403831d35Sstevel };
844503831d35Sstevel 
844603831d35Sstevel static struct drmach_sr_ordered {
844703831d35Sstevel 	char			*name;
844803831d35Sstevel 	struct drmach_sr_list	*ring;
844903831d35Sstevel } drmach_sr_ordered[] = {
845003831d35Sstevel 	{ "iosram",			NULL },
845103831d35Sstevel 	{ "address-extender-queue",	NULL },
845203831d35Sstevel 	{ NULL,				NULL }, /* terminator -- required */
845303831d35Sstevel };
845403831d35Sstevel 
845503831d35Sstevel static void
drmach_sr_insert(struct drmach_sr_list ** lp,dev_info_t * dip)845603831d35Sstevel drmach_sr_insert(struct drmach_sr_list **lp, dev_info_t *dip)
845703831d35Sstevel {
845803831d35Sstevel 	struct drmach_sr_list *np;
845903831d35Sstevel 
846007d06da5SSurya Prakki 	DRMACH_PR("drmach_sr_insert: adding dip %p\n", (void *)dip);
846103831d35Sstevel 
846203831d35Sstevel 	np = (struct drmach_sr_list *)kmem_alloc(
846303831d35Sstevel 	    sizeof (struct drmach_sr_list), KM_SLEEP);
846403831d35Sstevel 
846503831d35Sstevel 	ndi_hold_devi(dip);
846603831d35Sstevel 	np->dip = dip;
846703831d35Sstevel 
846803831d35Sstevel 	if (*lp == NULL) {
846903831d35Sstevel 		/* establish list */
847003831d35Sstevel 		*lp = np->next = np->prev = np;
847103831d35Sstevel 	} else {
847203831d35Sstevel 		/* place new node behind head node on ring list */
847303831d35Sstevel 		np->prev = (*lp)->prev;
847403831d35Sstevel 		np->next = *lp;
847503831d35Sstevel 		np->prev->next = np;
847603831d35Sstevel 		np->next->prev = np;
847703831d35Sstevel 	}
847803831d35Sstevel }
847903831d35Sstevel 
848003831d35Sstevel static void
drmach_sr_delete(struct drmach_sr_list ** lp,dev_info_t * dip)848103831d35Sstevel drmach_sr_delete(struct drmach_sr_list **lp, dev_info_t *dip)
848203831d35Sstevel {
848307d06da5SSurya Prakki 	DRMACH_PR("drmach_sr_delete: searching for dip %p\n", (void *)dip);
848403831d35Sstevel 
848503831d35Sstevel 	if (*lp) {
848603831d35Sstevel 		struct drmach_sr_list *xp;
848703831d35Sstevel 
848803831d35Sstevel 		/* start search with mostly likely node */
848903831d35Sstevel 		xp = (*lp)->prev;
849003831d35Sstevel 		do {
849103831d35Sstevel 			if (xp->dip == dip) {
849203831d35Sstevel 				xp->prev->next = xp->next;
849303831d35Sstevel 				xp->next->prev = xp->prev;
849403831d35Sstevel 
849503831d35Sstevel 				if (xp == *lp)
849603831d35Sstevel 					*lp = xp->next;
849703831d35Sstevel 				if (xp == *lp)
849803831d35Sstevel 					*lp = NULL;
849903831d35Sstevel 				xp->dip = NULL;
850003831d35Sstevel 				ndi_rele_devi(dip);
850103831d35Sstevel 				kmem_free(xp, sizeof (*xp));
850203831d35Sstevel 
850303831d35Sstevel 				DRMACH_PR("drmach_sr_delete:"
850407d06da5SSurya Prakki 				    " disposed sr node for dip %p",
850507d06da5SSurya Prakki 				    (void *)dip);
850603831d35Sstevel 				return;
850703831d35Sstevel 			}
850803831d35Sstevel 
850903831d35Sstevel 			DRMACH_PR("drmach_sr_delete: still searching\n");
851003831d35Sstevel 
851103831d35Sstevel 			xp = xp->prev;
851203831d35Sstevel 		} while (xp != (*lp)->prev);
851303831d35Sstevel 	}
851403831d35Sstevel 
851503831d35Sstevel 	/* every dip should be found during resume */
851607d06da5SSurya Prakki 	DRMACH_PR("ERROR: drmach_sr_delete: can't find dip %p", (void *)dip);
851703831d35Sstevel }
851803831d35Sstevel 
851903831d35Sstevel int
drmach_verify_sr(dev_info_t * dip,int sflag)852003831d35Sstevel drmach_verify_sr(dev_info_t *dip, int sflag)
852103831d35Sstevel {
852203831d35Sstevel 	int	rv;
852303831d35Sstevel 	int	len;
852403831d35Sstevel 	char    name[OBP_MAXDRVNAME];
852503831d35Sstevel 
852603831d35Sstevel 	if (drmach_slot1_pause_debug) {
852703831d35Sstevel 		if (sflag && drmach_slot1_pause_init) {
852803831d35Sstevel 			drmach_slot1_pause_free(drmach_slot1_paused);
852903831d35Sstevel 			drmach_slot1_pause_init = 0;
853003831d35Sstevel 		} else if (!sflag && !drmach_slot1_pause_init) {
853103831d35Sstevel 			/* schedule init for next suspend */
853203831d35Sstevel 			drmach_slot1_pause_init = 1;
853303831d35Sstevel 		}
853403831d35Sstevel 	}
853503831d35Sstevel 
853603831d35Sstevel 	rv = ddi_getproplen(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
853703831d35Sstevel 	    "name", &len);
853803831d35Sstevel 	if (rv == DDI_PROP_SUCCESS) {
853903831d35Sstevel 		int		portid;
854003831d35Sstevel 		uint64_t	reg;
854103831d35Sstevel 		struct drmach_sr_ordered *op;
854203831d35Sstevel 
854303831d35Sstevel 		rv = ddi_getlongprop_buf(DDI_DEV_T_ANY, dip,
854403831d35Sstevel 		    DDI_PROP_DONTPASS, "name", (caddr_t)name, &len);
854503831d35Sstevel 
854603831d35Sstevel 		if (rv != DDI_PROP_SUCCESS)
854703831d35Sstevel 			return (0);
854803831d35Sstevel 
854903831d35Sstevel 		if (drmach_slot1_pause_debug && sflag &&
855003831d35Sstevel 		    drmach_is_slot1_pause_axq(dip, name, &portid, &reg)) {
855103831d35Sstevel 			drmach_slot1_pause_add_axq(dip, name, portid, reg,
855203831d35Sstevel 			    drmach_slot1_paused);
855303831d35Sstevel 		}
855403831d35Sstevel 
855503831d35Sstevel 		for (op = drmach_sr_ordered; op->name; op++) {
855603831d35Sstevel 			if (strncmp(op->name, name, strlen(op->name)) == 0) {
855703831d35Sstevel 				if (sflag)
855803831d35Sstevel 					drmach_sr_insert(&op->ring, dip);
855903831d35Sstevel 				else
856003831d35Sstevel 					drmach_sr_delete(&op->ring, dip);
856103831d35Sstevel 				return (1);
856203831d35Sstevel 			}
856303831d35Sstevel 		}
856403831d35Sstevel 	}
856503831d35Sstevel 
856603831d35Sstevel 	return (0);
856703831d35Sstevel }
856803831d35Sstevel 
856903831d35Sstevel static void
drmach_sr_dip(dev_info_t * dip,int suspend)857003831d35Sstevel drmach_sr_dip(dev_info_t *dip, int suspend)
857103831d35Sstevel {
857203831d35Sstevel 	int	 rv;
857303831d35Sstevel 	major_t	 maj;
857403831d35Sstevel 	char	*name, *name_addr, *aka;
857503831d35Sstevel 
857603831d35Sstevel 	if ((name = ddi_get_name(dip)) == NULL)
857703831d35Sstevel 		name = "<null name>";
857803831d35Sstevel 	else if ((maj = ddi_name_to_major(name)) != -1)
857903831d35Sstevel 		aka = ddi_major_to_name(maj);
858003831d35Sstevel 	else
858103831d35Sstevel 		aka = "<unknown>";
858203831d35Sstevel 
858303831d35Sstevel 	if ((name_addr = ddi_get_name_addr(dip)) == NULL)
858403831d35Sstevel 		name_addr = "<null>";
858503831d35Sstevel 
858603831d35Sstevel 	prom_printf("\t%s %s@%s (aka %s)\n",
858703831d35Sstevel 	    suspend ? "suspending" : "resuming",
858803831d35Sstevel 	    name, name_addr, aka);
858903831d35Sstevel 
859003831d35Sstevel 	if (suspend) {
859103831d35Sstevel 		rv = devi_detach(dip, DDI_SUSPEND);
859203831d35Sstevel 	} else {
859303831d35Sstevel 		rv = devi_attach(dip, DDI_RESUME);
859403831d35Sstevel 	}
859503831d35Sstevel 
859603831d35Sstevel 	if (rv != DDI_SUCCESS) {
859703831d35Sstevel 		prom_printf("\tFAILED to %s %s@%s\n",
859803831d35Sstevel 		    suspend ? "suspend" : "resume",
859903831d35Sstevel 		    name, name_addr);
860003831d35Sstevel 	}
860103831d35Sstevel }
860203831d35Sstevel 
860303831d35Sstevel void
drmach_suspend_last()860403831d35Sstevel drmach_suspend_last()
860503831d35Sstevel {
860603831d35Sstevel 	struct drmach_sr_ordered *op;
860703831d35Sstevel 
860803831d35Sstevel 	if (drmach_slot1_pause_debug)
860903831d35Sstevel 		drmach_slot1_pause_add_io(drmach_slot1_paused);
861003831d35Sstevel 
861103831d35Sstevel 	/*
861203831d35Sstevel 	 * The ordering array declares the strict sequence in which
861303831d35Sstevel 	 * the named drivers are to suspended. Each element in
861403831d35Sstevel 	 * the array may have a double-linked ring list of driver
861503831d35Sstevel 	 * instances (dip) in the order in which they were presented
861603831d35Sstevel 	 * to drmach_verify_sr. If present, walk the list in the
861703831d35Sstevel 	 * forward direction to suspend each instance.
861803831d35Sstevel 	 */
861903831d35Sstevel 	for (op = drmach_sr_ordered; op->name; op++) {
862003831d35Sstevel 		if (op->ring) {
862103831d35Sstevel 			struct drmach_sr_list *rp;
862203831d35Sstevel 
862303831d35Sstevel 			rp = op->ring;
862403831d35Sstevel 			do {
862503831d35Sstevel 				drmach_sr_dip(rp->dip, 1);
862603831d35Sstevel 				rp = rp->next;
862703831d35Sstevel 			} while (rp != op->ring);
862803831d35Sstevel 		}
862903831d35Sstevel 	}
863003831d35Sstevel 
863103831d35Sstevel 	if (drmach_slot1_pause_debug) {
863203831d35Sstevel 		drmach_slot1_pause_update(drmach_slot1_paused,
863303831d35Sstevel 		    DRMACH_POST_SUSPEND);
863403831d35Sstevel 		drmach_slot1_pause_verify(drmach_slot1_paused,
863503831d35Sstevel 		    DRMACH_POST_SUSPEND);
863603831d35Sstevel 	}
863703831d35Sstevel }
863803831d35Sstevel 
863903831d35Sstevel void
drmach_resume_first()864003831d35Sstevel drmach_resume_first()
864103831d35Sstevel {
864203831d35Sstevel 	struct drmach_sr_ordered *op = drmach_sr_ordered +
864303831d35Sstevel 	    (sizeof (drmach_sr_ordered) / sizeof (drmach_sr_ordered[0]));
864403831d35Sstevel 
864503831d35Sstevel 	if (drmach_slot1_pause_debug) {
864603831d35Sstevel 		drmach_slot1_pause_update(drmach_slot1_paused,
864703831d35Sstevel 		    DRMACH_PRE_RESUME);
864803831d35Sstevel 		drmach_slot1_pause_verify(drmach_slot1_paused,
864903831d35Sstevel 		    DRMACH_PRE_RESUME);
865003831d35Sstevel 	}
865103831d35Sstevel 
865203831d35Sstevel 	op -= 1;	/* point at terminating element */
865303831d35Sstevel 
865403831d35Sstevel 	/*
865503831d35Sstevel 	 * walk ordering array and rings backwards to resume dips
865603831d35Sstevel 	 * in reverse order in which they were suspended
865703831d35Sstevel 	 */
865803831d35Sstevel 	while (--op >= drmach_sr_ordered) {
865903831d35Sstevel 		if (op->ring) {
866003831d35Sstevel 			struct drmach_sr_list *rp;
866103831d35Sstevel 
866203831d35Sstevel 			rp = op->ring->prev;
866303831d35Sstevel 			do {
866403831d35Sstevel 				drmach_sr_dip(rp->dip, 0);
866503831d35Sstevel 				rp = rp->prev;
866603831d35Sstevel 			} while (rp != op->ring->prev);
866703831d35Sstevel 		}
866803831d35Sstevel 	}
866903831d35Sstevel }
867003831d35Sstevel 
867103831d35Sstevel /*
867203831d35Sstevel  * Log a DR sysevent.
867303831d35Sstevel  * Return value: 0 success, non-zero failure.
867403831d35Sstevel  */
867503831d35Sstevel int
drmach_log_sysevent(int board,char * hint,int flag,int verbose)867603831d35Sstevel drmach_log_sysevent(int board, char *hint, int flag, int verbose)
867703831d35Sstevel {
867803831d35Sstevel 	sysevent_t			*ev;
867903831d35Sstevel 	sysevent_id_t			eid;
868003831d35Sstevel 	int				rv, km_flag;
868103831d35Sstevel 	sysevent_value_t		evnt_val;
868203831d35Sstevel 	sysevent_attr_list_t		*evnt_attr_list = NULL;
868303831d35Sstevel 	char				attach_pnt[MAXNAMELEN];
868403831d35Sstevel 
868503831d35Sstevel 	km_flag = (flag == SE_SLEEP) ? KM_SLEEP : KM_NOSLEEP;
868603831d35Sstevel 	attach_pnt[0] = '\0';
868703831d35Sstevel 	if (drmach_board_name(board, attach_pnt, MAXNAMELEN)) {
868803831d35Sstevel 		rv = -1;
868903831d35Sstevel 		goto logexit;
869003831d35Sstevel 	}
869103831d35Sstevel 	if (verbose)
869203831d35Sstevel 		DRMACH_PR("drmach_log_sysevent: %s %s, flag: %d, verbose: %d\n",
869303831d35Sstevel 		    attach_pnt, hint, flag, verbose);
869403831d35Sstevel 
869503831d35Sstevel 	if ((ev = sysevent_alloc(EC_DR, ESC_DR_AP_STATE_CHANGE,
869603831d35Sstevel 	    SUNW_KERN_PUB"dr", km_flag)) == NULL) {
869703831d35Sstevel 		rv = -2;
869803831d35Sstevel 		goto logexit;
869903831d35Sstevel 	}
870003831d35Sstevel 	evnt_val.value_type = SE_DATA_TYPE_STRING;
870103831d35Sstevel 	evnt_val.value.sv_string = attach_pnt;
870203831d35Sstevel 	if ((rv = sysevent_add_attr(&evnt_attr_list, DR_AP_ID,
870303831d35Sstevel 	    &evnt_val, km_flag)) != 0)
870403831d35Sstevel 		goto logexit;
870503831d35Sstevel 
870603831d35Sstevel 	evnt_val.value_type = SE_DATA_TYPE_STRING;
870703831d35Sstevel 	evnt_val.value.sv_string = hint;
870803831d35Sstevel 	if ((rv = sysevent_add_attr(&evnt_attr_list, DR_HINT,
870903831d35Sstevel 	    &evnt_val, km_flag)) != 0) {
871003831d35Sstevel 		sysevent_free_attr(evnt_attr_list);
871103831d35Sstevel 		goto logexit;
871203831d35Sstevel 	}
871303831d35Sstevel 
871403831d35Sstevel 	(void) sysevent_attach_attributes(ev, evnt_attr_list);
871503831d35Sstevel 
871603831d35Sstevel 	/*
871703831d35Sstevel 	 * Log the event but do not sleep waiting for its
871803831d35Sstevel 	 * delivery. This provides insulation from syseventd.
871903831d35Sstevel 	 */
872003831d35Sstevel 	rv = log_sysevent(ev, SE_NOSLEEP, &eid);
872103831d35Sstevel 
872203831d35Sstevel logexit:
872303831d35Sstevel 	if (ev)
872403831d35Sstevel 		sysevent_free(ev);
872503831d35Sstevel 	if ((rv != 0) && verbose)
872603831d35Sstevel 		cmn_err(CE_WARN,
872703831d35Sstevel 		    "drmach_log_sysevent failed (rv %d) for %s  %s\n",
872803831d35Sstevel 		    rv, attach_pnt, hint);
872903831d35Sstevel 
873003831d35Sstevel 	return (rv);
873103831d35Sstevel }
873203831d35Sstevel 
873303831d35Sstevel /*
873403831d35Sstevel  * Initialize the mem_slice portion of a claim/unconfig/unclaim mailbox message.
873503831d35Sstevel  * Only the valid entries are modified, so the array should be zeroed out
873603831d35Sstevel  * initially.
873703831d35Sstevel  */
873803831d35Sstevel static void
drmach_msg_memslice_init(dr_memslice_t slice_arr[])873903831d35Sstevel drmach_msg_memslice_init(dr_memslice_t slice_arr[]) {
874003831d35Sstevel 	int	i;
874103831d35Sstevel 	char	c;
874203831d35Sstevel 
874303831d35Sstevel 	ASSERT(mutex_owned(&drmach_slice_table_lock));
874403831d35Sstevel 
874503831d35Sstevel 	for (i = 0; i < AXQ_MAX_EXP; i++) {
874603831d35Sstevel 		c = drmach_slice_table[i];
874703831d35Sstevel 
874803831d35Sstevel 		if (c & 0x20) {
874903831d35Sstevel 			slice_arr[i].valid = 1;
875003831d35Sstevel 			slice_arr[i].slice = c & 0x1f;
875103831d35Sstevel 		}
875203831d35Sstevel 	}
875303831d35Sstevel }
875403831d35Sstevel 
875503831d35Sstevel /*
875603831d35Sstevel  * Initialize the mem_regs portion of a claim/unconfig/unclaim mailbox message.
875703831d35Sstevel  * Only the valid entries are modified, so the array should be zeroed out
875803831d35Sstevel  * initially.
875903831d35Sstevel  */
876003831d35Sstevel static void
drmach_msg_memregs_init(dr_memregs_t regs_arr[])876103831d35Sstevel drmach_msg_memregs_init(dr_memregs_t regs_arr[]) {
876203831d35Sstevel 	int		rv, exp, mcnum, bank;
876303831d35Sstevel 	uint64_t	madr;
876403831d35Sstevel 	drmachid_t	id;
876503831d35Sstevel 	drmach_board_t	*bp;
876603831d35Sstevel 	drmach_mem_t	*mp;
876703831d35Sstevel 	dr_memregs_t	*memregs;
876803831d35Sstevel 
876903831d35Sstevel 	/* CONSTCOND */
877003831d35Sstevel 	ASSERT(DRMACH_MC_NBANKS == (PMBANKS_PER_PORT * LMBANKS_PER_PMBANK));
877103831d35Sstevel 
877203831d35Sstevel 	for (exp = 0; exp < 18; exp++) {
877303831d35Sstevel 		rv = drmach_array_get(drmach_boards,
877403831d35Sstevel 		    DRMACH_EXPSLOT2BNUM(exp, 0), &id);
877503831d35Sstevel 		ASSERT(rv == 0);	/* should never be out of bounds */
877603831d35Sstevel 		if (id == NULL) {
877703831d35Sstevel 			continue;
877803831d35Sstevel 		}
877903831d35Sstevel 
878003831d35Sstevel 		memregs = &regs_arr[exp];
878103831d35Sstevel 		bp = (drmach_board_t *)id;
878203831d35Sstevel 		for (mp = bp->mem; mp != NULL; mp = mp->next) {
878303831d35Sstevel 			mcnum = mp->dev.portid & 0x3;
878403831d35Sstevel 			for (bank = 0; bank < DRMACH_MC_NBANKS; bank++) {
878503831d35Sstevel 				drmach_mem_read_madr(mp, bank, &madr);
878603831d35Sstevel 				if (madr & DRMACH_MC_VALID_MASK) {
878703831d35Sstevel 					DRMACH_PR("%d.%d.%d.madr = 0x%lx\n",
878803831d35Sstevel 					    exp, mcnum, bank, madr);
878903831d35Sstevel 					memregs->madr[mcnum][bank].hi =
879003831d35Sstevel 					    DRMACH_U64_TO_MCREGHI(madr);
879103831d35Sstevel 					memregs->madr[mcnum][bank].lo =
879203831d35Sstevel 					    DRMACH_U64_TO_MCREGLO(madr);
879303831d35Sstevel 				}
879403831d35Sstevel 			}
879503831d35Sstevel 		}
879603831d35Sstevel 	}
879703831d35Sstevel }
879803831d35Sstevel 
879903831d35Sstevel /*
880003831d35Sstevel  * Do not allow physical address range modification if either board on this
880103831d35Sstevel  * expander has processors in NULL LPA mode (CBASE=CBND=NULL).
880203831d35Sstevel  *
880303831d35Sstevel  * A side effect of NULL proc LPA mode in Starcat SSM is that local reads will
880403831d35Sstevel  * install the cache line as owned/dirty as a result of the RTSR transaction.
880503831d35Sstevel  * See section 5.2.3 of the Safari spec.  All processors will read the bus sync
880603831d35Sstevel  * list before the rename after flushing local caches.  When copy-rename
880703831d35Sstevel  * requires changing the physical address ranges (i.e. smaller memory target),
880803831d35Sstevel  * the bus sync list contains physical addresses that will not exist after the
880903831d35Sstevel  * rename.  If these cache lines are owned due to a RTSR, a system error can
881003831d35Sstevel  * occur following the rename when these cache lines are evicted and a writeback
881103831d35Sstevel  * is attempted.
881203831d35Sstevel  *
881303831d35Sstevel  * Incoming parameter represents either the copy-rename source or a candidate
881403831d35Sstevel  * target memory board.  On Starcat, only slot0 boards may have memory.
881503831d35Sstevel  */
881603831d35Sstevel int
drmach_allow_memrange_modify(drmachid_t s0id)881703831d35Sstevel drmach_allow_memrange_modify(drmachid_t s0id)
881803831d35Sstevel {
881903831d35Sstevel 	drmach_board_t	*s0bp, *s1bp;
882003831d35Sstevel 	drmachid_t	s1id;
882103831d35Sstevel 	int		rv;
882203831d35Sstevel 
882303831d35Sstevel 	s0bp = s0id;
882403831d35Sstevel 
882503831d35Sstevel 	ASSERT(DRMACH_IS_BOARD_ID(s0id));
882603831d35Sstevel 	ASSERT(DRMACH_BNUM2SLOT(s0bp->bnum) == 0);
882703831d35Sstevel 
882803831d35Sstevel 	if (s0bp->flags & DRMACH_NULL_PROC_LPA) {
882903831d35Sstevel 		/*
883003831d35Sstevel 		 * This is reason enough to fail the request, no need
883103831d35Sstevel 		 * to check the device list for cpus.
883203831d35Sstevel 		 */
883303831d35Sstevel 		return (0);
883403831d35Sstevel 	}
883503831d35Sstevel 
883603831d35Sstevel 	/*
883703831d35Sstevel 	 * Check for MCPU board on the same expander.
883803831d35Sstevel 	 *
883903831d35Sstevel 	 * The board flag DRMACH_NULL_PROC_LPA can be set for all board
884003831d35Sstevel 	 * types, as it is derived at from the POST gdcd board flag
884103831d35Sstevel 	 * L1SSFLG_THIS_L1_NULL_PROC_LPA, which can be set (and should be
884203831d35Sstevel 	 * ignored) for boards with no processors.  Since NULL proc LPA
884303831d35Sstevel 	 * applies only to processors, we walk the devices array to detect
884403831d35Sstevel 	 * MCPUs.
884503831d35Sstevel 	 */
884603831d35Sstevel 	rv = drmach_array_get(drmach_boards, s0bp->bnum + 1, &s1id);
884703831d35Sstevel 	s1bp = s1id;
884803831d35Sstevel 	if (rv == 0 && s1bp != NULL) {
884903831d35Sstevel 
885003831d35Sstevel 		ASSERT(DRMACH_IS_BOARD_ID(s1id));
885103831d35Sstevel 		ASSERT(DRMACH_BNUM2SLOT(s1bp->bnum) == 1);
885203831d35Sstevel 		ASSERT(DRMACH_BNUM2EXP(s0bp->bnum) ==
885303831d35Sstevel 		    DRMACH_BNUM2EXP(s1bp->bnum));
885403831d35Sstevel 
885503831d35Sstevel 		if ((s1bp->flags & DRMACH_NULL_PROC_LPA) &&
885603831d35Sstevel 		    s1bp->devices != NULL) {
885703831d35Sstevel 			int		d_idx;
885803831d35Sstevel 			drmachid_t	d_id;
885903831d35Sstevel 
886003831d35Sstevel 			rv = drmach_array_first(s1bp->devices, &d_idx, &d_id);
886103831d35Sstevel 			while (rv == 0) {
886203831d35Sstevel 				if (DRMACH_IS_CPU_ID(d_id)) {
886303831d35Sstevel 					/*
886403831d35Sstevel 					 * Fail MCPU in NULL LPA mode.
886503831d35Sstevel 					 */
886603831d35Sstevel 					return (0);
886703831d35Sstevel 				}
886803831d35Sstevel 
886903831d35Sstevel 				rv = drmach_array_next(s1bp->devices, &d_idx,
887003831d35Sstevel 				    &d_id);
887103831d35Sstevel 			}
887203831d35Sstevel 		}
887303831d35Sstevel 	}
887403831d35Sstevel 
887503831d35Sstevel 	return (1);
887603831d35Sstevel }
8877