xref: /onnv-gate/usr/src/uts/sun4u/starcat/io/drmach.c (revision 11474:857f9db4ef05)
11708Sstevel /*
21708Sstevel  * CDDL HEADER START
31708Sstevel  *
41708Sstevel  * The contents of this file are subject to the terms of the
51772Sjl139090  * Common Development and Distribution License (the "License").
61772Sjl139090  * You may not use this file except in compliance with the License.
71708Sstevel  *
81708Sstevel  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
91708Sstevel  * or http://www.opensolaris.org/os/licensing.
101708Sstevel  * See the License for the specific language governing permissions
111708Sstevel  * and limitations under the License.
121708Sstevel  *
131708Sstevel  * When distributing Covered Code, include this CDDL HEADER in each
141708Sstevel  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
151708Sstevel  * If applicable, add the following below this CDDL HEADER, with the
161708Sstevel  * fields enclosed by brackets "[]" replaced with your own identifying
171708Sstevel  * information: Portions Copyright [yyyy] [name of copyright owner]
181708Sstevel  *
191708Sstevel  * CDDL HEADER END
201708Sstevel  */
2111311SSurya.Prakki@Sun.COM 
221708Sstevel /*
23*11474SJonathan.Adams@Sun.COM  * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
241708Sstevel  * Use is subject to license terms.
251708Sstevel  */
261708Sstevel 
271708Sstevel #include <sys/note.h>
281708Sstevel #include <sys/debug.h>
291708Sstevel #include <sys/types.h>
301708Sstevel #include <sys/varargs.h>
311708Sstevel #include <sys/errno.h>
321708Sstevel #include <sys/cred.h>
331708Sstevel #include <sys/dditypes.h>
341708Sstevel #include <sys/devops.h>
351708Sstevel #include <sys/modctl.h>
361708Sstevel #include <sys/poll.h>
371708Sstevel #include <sys/conf.h>
381708Sstevel #include <sys/ddi.h>
391708Sstevel #include <sys/sunddi.h>
401708Sstevel #include <sys/sunndi.h>
411708Sstevel #include <sys/ndi_impldefs.h>
421708Sstevel #include <sys/stat.h>
431708Sstevel #include <sys/kmem.h>
441708Sstevel #include <sys/vmem.h>
451708Sstevel #include <sys/disp.h>
461708Sstevel #include <sys/processor.h>
471708Sstevel #include <sys/cheetahregs.h>
481708Sstevel #include <sys/cpuvar.h>
491708Sstevel #include <sys/mem_config.h>
501708Sstevel #include <sys/ddi_impldefs.h>
511708Sstevel #include <sys/systm.h>
521708Sstevel #include <sys/machsystm.h>
531708Sstevel #include <sys/autoconf.h>
541708Sstevel #include <sys/cmn_err.h>
551708Sstevel #include <sys/sysmacros.h>
561708Sstevel #include <sys/x_call.h>
571708Sstevel #include <sys/promif.h>
581708Sstevel #include <sys/prom_plat.h>
591708Sstevel #include <sys/membar.h>
601708Sstevel #include <vm/seg_kmem.h>
611708Sstevel #include <sys/mem_cage.h>
621708Sstevel #include <sys/stack.h>
631708Sstevel #include <sys/archsystm.h>
641708Sstevel #include <vm/hat_sfmmu.h>
651708Sstevel #include <sys/pte.h>
661708Sstevel #include <sys/mmu.h>
671708Sstevel #include <sys/cpu_module.h>
681708Sstevel #include <sys/obpdefs.h>
691708Sstevel #include <sys/mboxsc.h>
701708Sstevel #include <sys/plat_ecc_dimm.h>
711708Sstevel 
721708Sstevel #include <sys/hotplug/hpctrl.h>		/* XXX should be included by schpc.h */
731708Sstevel #include <sys/schpc.h>
741708Sstevel #include <sys/pci.h>
751708Sstevel 
761708Sstevel #include <sys/starcat.h>
771708Sstevel #include <sys/cpu_sgnblk_defs.h>
781708Sstevel #include <sys/drmach.h>
791708Sstevel #include <sys/dr_util.h>
801708Sstevel #include <sys/dr_mbx.h>
811708Sstevel #include <sys/sc_gptwocfg.h>
821708Sstevel #include <sys/iosramreg.h>
831708Sstevel #include <sys/iosramio.h>
841708Sstevel #include <sys/iosramvar.h>
851708Sstevel #include <sys/axq.h>
861708Sstevel #include <sys/post/scat_dcd.h>
871708Sstevel #include <sys/kobj.h>
881708Sstevel #include <sys/taskq.h>
891708Sstevel #include <sys/cmp.h>
901708Sstevel #include <sys/sbd_ioctl.h>
911708Sstevel 
921708Sstevel #include <sys/sysevent.h>
931708Sstevel #include <sys/sysevent/dr.h>
941708Sstevel #include <sys/sysevent/eventdefs.h>
951708Sstevel 
961708Sstevel #include <sys/pci/pcisch.h>
971708Sstevel #include <sys/pci/pci_regs.h>
981708Sstevel 
991708Sstevel #include <sys/ontrap.h>
1001708Sstevel 
1011708Sstevel /* defined in ../ml/drmach.il.cpp */
1021708Sstevel extern void		bcopy32_il(uint64_t, uint64_t);
1031708Sstevel extern void		flush_ecache_il(int64_t physaddr, int size, int linesz);
1041708Sstevel extern void		flush_dcache_il(void);
1051708Sstevel extern void		flush_icache_il(void);
1061708Sstevel extern void		flush_pcache_il(void);
1071708Sstevel 
1081708Sstevel /* defined in ../ml/drmach_asm.s */
1091708Sstevel extern uint64_t		lddmcdecode(uint64_t physaddr);
1101708Sstevel extern uint64_t		lddsafconfig(void);
1111708Sstevel 
1121708Sstevel /* XXX here until provided by sys/dman.h */
1131708Sstevel extern int man_dr_attach(dev_info_t *);
1141708Sstevel extern int man_dr_detach(dev_info_t *);
1151708Sstevel 
1161708Sstevel #define	DRMACH_BNUM2EXP(bnum)		((bnum) >> 1)
1171708Sstevel #define	DRMACH_BNUM2SLOT(bnum)		((bnum) & 1)
1181708Sstevel #define	DRMACH_EXPSLOT2BNUM(exp, slot)	(((exp) << 1) + (slot))
1191708Sstevel 
1201708Sstevel #define	DRMACH_SLICE_MASK		0x1Full
1211708Sstevel #define	DRMACH_SLICE_TO_PA(s)		(((s) & DRMACH_SLICE_MASK) << 37)
1221708Sstevel #define	DRMACH_PA_TO_SLICE(a)		(((a) >> 37) & DRMACH_SLICE_MASK)
1231708Sstevel 
1241708Sstevel /*
1251708Sstevel  * DRMACH_MEM_SLICE_SIZE and DRMACH_MEM_USABLE_SLICE_SIZE define the
1261708Sstevel  * available address space and the usable address space for every slice.
1271708Sstevel  * There must be a distinction between the available and usable do to a
1281708Sstevel  * restriction imposed by CDC memory size.
1291708Sstevel  */
1301708Sstevel 
1311708Sstevel #define	DRMACH_MEM_SLICE_SIZE		(1ull << 37)	/* 128GB */
1321708Sstevel #define	DRMACH_MEM_USABLE_SLICE_SIZE	(1ull << 36)	/* 64GB */
1331708Sstevel 
1341708Sstevel #define	DRMACH_MC_NBANKS		4
1351708Sstevel 
1361708Sstevel #define	DRMACH_MC_ADDR(mp, bank)	((mp)->madr_pa + 16 + 8 * (bank))
1371708Sstevel #define	DRMACH_MC_ASI_ADDR(mp, bank)	(DRMACH_MC_ADDR(mp, bank) & 0xFF)
1381708Sstevel 
1391708Sstevel #define	DRMACH_EMU_ACT_STATUS_OFFSET	0x50
1401708Sstevel #define	DRMACH_EMU_ACT_STATUS_ADDR(mp)	\
1411708Sstevel 	((mp)->madr_pa + DRMACH_EMU_ACT_STATUS_OFFSET)
1421708Sstevel 
1431708Sstevel /*
1441708Sstevel  * The Cheetah's Safari Configuration Register and the Schizo's
1451708Sstevel  * Safari Control/Status Register place the LPA base and bound fields in
1461708Sstevel  * same bit locations with in their register word. This source code takes
1471708Sstevel  * advantage of this by defining only one set of LPA encoding/decoding macros
1481708Sstevel  * which are shared by various Cheetah and Schizo drmach routines.
1491708Sstevel  */
1501708Sstevel #define	DRMACH_LPA_BASE_MASK		(0x3Full	<< 3)
1511708Sstevel #define	DRMACH_LPA_BND_MASK		(0x3Full	<< 9)
1521708Sstevel 
1531708Sstevel #define	DRMACH_LPA_BASE_TO_PA(scr)	(((scr) & DRMACH_LPA_BASE_MASK) << 34)
1541708Sstevel #define	DRMACH_LPA_BND_TO_PA(scr)	(((scr) & DRMACH_LPA_BND_MASK) << 28)
1551708Sstevel #define	DRMACH_PA_TO_LPA_BASE(pa)	(((pa) >> 34) & DRMACH_LPA_BASE_MASK)
1561708Sstevel #define	DRMACH_PA_TO_LPA_BND(pa)	(((pa) >> 28) & DRMACH_LPA_BND_MASK)
1571708Sstevel 
1581708Sstevel #define	DRMACH_L1_SET_LPA(b)		\
1591708Sstevel 	(((b)->flags & DRMACH_NULL_PROC_LPA) == 0)
1601708Sstevel 
1611708Sstevel #define	DRMACH_CPU_SRAM_ADDR    	0x7fff0900000ull
1621708Sstevel #define	DRMACH_CPU_SRAM_SIZE    	0x20000ull
1631708Sstevel 
1641708Sstevel /*
1651708Sstevel  * Name properties for frequently accessed device nodes.
1661708Sstevel  */
1671708Sstevel #define	DRMACH_CPU_NAMEPROP		"cpu"
1681708Sstevel #define	DRMACH_CMP_NAMEPROP		"cmp"
1691708Sstevel #define	DRMACH_AXQ_NAMEPROP		"address-extender-queue"
1701708Sstevel #define	DRMACH_PCI_NAMEPROP		"pci"
1711708Sstevel 
1721708Sstevel /*
1731708Sstevel  * Maximum value of processor Safari Timeout Log (TOL) field of
1741708Sstevel  * Safari Config reg (7 secs).
1751708Sstevel  */
1761708Sstevel #define	DRMACH_SAF_TOL_MAX		7 * 1000000
1771708Sstevel 
1781708Sstevel /*
1791708Sstevel  * drmach_board_t flag definitions
1801708Sstevel  */
1811708Sstevel #define	DRMACH_NULL_PROC_LPA		0x1
1821708Sstevel 
1831708Sstevel typedef struct {
1841708Sstevel 	uint32_t	reg_addr_hi;
1851708Sstevel 	uint32_t	reg_addr_lo;
1861708Sstevel 	uint32_t	reg_size_hi;
1871708Sstevel 	uint32_t	reg_size_lo;
1881708Sstevel } drmach_reg_t;
1891708Sstevel 
1901708Sstevel typedef struct {
1911708Sstevel 	struct drmach_node	*node;
1921708Sstevel 	void			*data;
1931708Sstevel } drmach_node_walk_args_t;
1941708Sstevel 
1951708Sstevel typedef struct drmach_node {
1961708Sstevel 	void		*here;
1971708Sstevel 
1981708Sstevel 	pnode_t		 (*get_dnode)(struct drmach_node *node);
1991708Sstevel 	int		 (*walk)(struct drmach_node *node, void *data,
2001708Sstevel 				int (*cb)(drmach_node_walk_args_t *args));
2011708Sstevel 	dev_info_t	*(*n_getdip)(struct drmach_node *node);
2021708Sstevel 	int		 (*n_getproplen)(struct drmach_node *node, char *name,
2031708Sstevel 				int *len);
2041708Sstevel 	int		 (*n_getprop)(struct drmach_node *node, char *name,
2051708Sstevel 				void *buf, int len);
2061708Sstevel 	int		 (*get_parent)(struct drmach_node *node,
2071708Sstevel 				struct drmach_node *pnode);
2081708Sstevel } drmach_node_t;
2091708Sstevel 
2101708Sstevel typedef struct {
2111708Sstevel 	int		 min_index;
2121708Sstevel 	int		 max_index;
2131708Sstevel 	int		 arr_sz;
2141708Sstevel 	drmachid_t	*arr;
2151708Sstevel } drmach_array_t;
2161708Sstevel 
2171708Sstevel typedef struct {
2181708Sstevel 	void		*isa;
2191708Sstevel 
2201708Sstevel 	void		 (*dispose)(drmachid_t);
2211708Sstevel 	sbd_error_t	*(*release)(drmachid_t);
2221708Sstevel 	sbd_error_t	*(*status)(drmachid_t, drmach_status_t *);
2231708Sstevel 
2241708Sstevel 	char		 name[MAXNAMELEN];
2251708Sstevel } drmach_common_t;
2261708Sstevel 
2271708Sstevel struct drmach_board;
2281708Sstevel typedef struct drmach_board drmach_board_t;
2291708Sstevel 
2301708Sstevel typedef struct {
2311708Sstevel 	drmach_common_t	 cm;
2321708Sstevel 	const char	*type;
2331708Sstevel 	drmach_board_t	*bp;
2341708Sstevel 	drmach_node_t	*node;
2351708Sstevel 	int		 portid;
2361708Sstevel 	int		 unum;
2371708Sstevel 	int		 busy;
2381708Sstevel 	int		 powered;
2391708Sstevel } drmach_device_t;
2401708Sstevel 
2411708Sstevel typedef struct drmach_cpu {
2421708Sstevel 	drmach_device_t	 dev;
2431708Sstevel 	uint64_t	 scr_pa;
2441708Sstevel 	processorid_t	 cpuid;
2451708Sstevel 	int		 coreid;
2461708Sstevel } drmach_cpu_t;
2471708Sstevel 
2481708Sstevel typedef struct drmach_mem {
2491708Sstevel 	drmach_device_t	 dev;
2501708Sstevel 	struct drmach_mem *next;
2511708Sstevel 	uint64_t	 nbytes;
2521708Sstevel 	uint64_t	 madr_pa;
2531708Sstevel } drmach_mem_t;
2541708Sstevel 
2551708Sstevel typedef struct drmach_io {
2561708Sstevel 	drmach_device_t	 dev;
2571708Sstevel 	uint64_t	 scsr_pa; /* PA of Schizo Control/Status Register */
2581708Sstevel } drmach_io_t;
2591708Sstevel 
2601708Sstevel struct drmach_board {
2611708Sstevel 	drmach_common_t	 cm;
2621708Sstevel 	int		 bnum;
2631708Sstevel 	int		 assigned;
2641708Sstevel 	int		 powered;
2651708Sstevel 	int		 connected;
2661708Sstevel 	int		 empty;
2671708Sstevel 	int		 cond;
2681708Sstevel 	uint_t		 cpu_impl;
2691708Sstevel 	uint_t		 flags;
2701708Sstevel 	drmach_node_t	*tree;
2711708Sstevel 	drmach_array_t	*devices;
2721708Sstevel 	drmach_mem_t	*mem;
2731708Sstevel 	uint64_t	 stardrb_offset;
2741708Sstevel 	char		 type[BD_TYPELEN];
2751708Sstevel };
2761708Sstevel 
2771708Sstevel typedef struct {
2781708Sstevel 	int		 flags;
2791708Sstevel 	drmach_device_t	*dp;
2801708Sstevel 	sbd_error_t	*err;
2811708Sstevel 	dev_info_t	*fdip;
2821708Sstevel } drmach_config_args_t;
2831708Sstevel 
2841708Sstevel typedef struct {
2851708Sstevel 	drmach_board_t	*obj;
2861708Sstevel 	int		 ndevs;
2871708Sstevel 	void		*a;
2881708Sstevel 	sbd_error_t	*(*found)(void *a, const char *, int, drmachid_t);
2891708Sstevel 	sbd_error_t	*err;
2901708Sstevel } drmach_board_cb_data_t;
2911708Sstevel 
2921708Sstevel typedef struct drmach_casmslot {
2931708Sstevel 	int	valid;
2941708Sstevel 	int	slice;
2951708Sstevel } drmach_casmslot_t;
2961708Sstevel 
2971708Sstevel typedef enum {
2981708Sstevel 	DRMACH_CR_OK,
2991708Sstevel 	DRMACH_CR_MC_IDLE_ERR,
3001708Sstevel 	DRMACH_CR_IOPAUSE_ERR,
3011708Sstevel 	DRMACH_CR_ONTRAP_ERR
3021708Sstevel } drmach_cr_err_t;
3031708Sstevel 
3041708Sstevel typedef struct {
3051708Sstevel 	void		*isa;
3061708Sstevel 	caddr_t		 data;
3071708Sstevel 	drmach_mem_t	*s_mp;
3081708Sstevel 	drmach_mem_t	*t_mp;
3091708Sstevel 	struct memlist	*c_ml;
3101708Sstevel 	uint64_t	 s_copybasepa;
3111708Sstevel 	uint64_t	 t_copybasepa;
3121708Sstevel 	drmach_cr_err_t	 ecode;
3131708Sstevel 	void		*earg;
3141708Sstevel } drmach_copy_rename_t;
3151708Sstevel 
3161708Sstevel /*
3171708Sstevel  * The following global is read as a boolean value, non-zero is true.
3181708Sstevel  * If zero, DR copy-rename and cpu poweron will not set the processor
3191708Sstevel  * LPA settings (CBASE, CBND of Safari config register) to correspond
3201708Sstevel  * to the current memory slice map. LPAs of processors present at boot
3211708Sstevel  * will remain as programmed by POST. LPAs of processors on boards added
3221708Sstevel  * by DR will remain NULL, as programmed by POST. This can be used to
3231708Sstevel  * to override the per-board L1SSFLG_THIS_L1_NULL_PROC_LPA flag set by
3241708Sstevel  * POST in the LDCD (and copied to the GDCD by SMS).
3251708Sstevel  *
3261708Sstevel  * drmach_reprogram_lpa and L1SSFLG_THIS_L1_NULL_PROC_LPA do not apply
3271708Sstevel  * to Schizo device LPAs. These are always set by DR.
3281708Sstevel  */
3291708Sstevel static int		 drmach_reprogram_lpa = 1;
3301708Sstevel 
3311708Sstevel /*
3321708Sstevel  * There is a known HW bug where a Jaguar CPU in Safari port 0 (SBX/P0)
3331708Sstevel  * can fail to receive an XIR. To workaround this issue until a hardware
3341708Sstevel  * fix is implemented, we will exclude the selection of these CPUs.
3351708Sstevel  * Setting this to 0 will allow their selection again.
3361708Sstevel  */
3371708Sstevel static int		 drmach_iocage_exclude_jaguar_port_zero = 1;
3381708Sstevel 
3391708Sstevel static int		 drmach_initialized;
3401708Sstevel static drmach_array_t	*drmach_boards;
3411708Sstevel 
3421708Sstevel static int		 drmach_cpu_delay = 1000;
3431708Sstevel static int		 drmach_cpu_ntries = 50000;
3441708Sstevel 
3451708Sstevel static uint32_t		 drmach_slice_table[AXQ_MAX_EXP];
3461708Sstevel static kmutex_t		 drmach_slice_table_lock;
3471708Sstevel 
3481708Sstevel tte_t			 drmach_cpu_sram_tte[NCPU];
3491708Sstevel caddr_t			 drmach_cpu_sram_va;
3501708Sstevel 
3511708Sstevel /*
3521708Sstevel  * Setting to non-zero will enable delay before all disconnect ops.
3531708Sstevel  */
3541708Sstevel static int		 drmach_unclaim_delay_all;
3551708Sstevel /*
3561708Sstevel  * Default delay is slightly greater than the max processor Safari timeout.
3571708Sstevel  * This delay is intended to ensure the outstanding Safari activity has
3581708Sstevel  * retired on this board prior to a board disconnect.
3591708Sstevel  */
3601708Sstevel static clock_t		 drmach_unclaim_usec_delay = DRMACH_SAF_TOL_MAX + 10;
3611708Sstevel 
3621708Sstevel /*
3631708Sstevel  * By default, DR of non-Panther procs is not allowed into a Panther
3641708Sstevel  * domain with large page sizes enabled.  Setting this to 0 will remove
3651708Sstevel  * the restriction.
3661708Sstevel  */
3671708Sstevel static int		 drmach_large_page_restriction = 1;
3681708Sstevel 
3691708Sstevel /*
3701708Sstevel  * Used to pass updated LPA values to procs.
3711708Sstevel  * Protocol is to clear the array before use.
3721708Sstevel  */
3731708Sstevel volatile uchar_t	*drmach_xt_mb;
3741708Sstevel volatile uint64_t	 drmach_xt_ready;
3751708Sstevel static kmutex_t		 drmach_xt_mb_lock;
3761708Sstevel static int		 drmach_xt_mb_size;
3771708Sstevel 
3781708Sstevel uint64_t		 drmach_bus_sync_list[18 * 4 * 4 + 1];
3791708Sstevel static kmutex_t		 drmach_bus_sync_lock;
3801708Sstevel 
3811708Sstevel static sbd_error_t	*drmach_device_new(drmach_node_t *,
3821708Sstevel 				drmach_board_t *, int, drmachid_t *);
3831708Sstevel static sbd_error_t	*drmach_cpu_new(drmach_device_t *, drmachid_t *);
3841708Sstevel static sbd_error_t	*drmach_mem_new(drmach_device_t *, drmachid_t *);
3851708Sstevel static sbd_error_t	*drmach_pci_new(drmach_device_t *, drmachid_t *);
3861708Sstevel static sbd_error_t	*drmach_io_new(drmach_device_t *, drmachid_t *);
3871708Sstevel 
3881708Sstevel static dev_info_t	*drmach_node_ddi_get_dip(drmach_node_t *np);
3891708Sstevel static int		 drmach_node_ddi_get_prop(drmach_node_t *np,
3901708Sstevel 				char *name, void *buf, int len);
3911708Sstevel static int		 drmach_node_ddi_get_proplen(drmach_node_t *np,
3921708Sstevel 				char *name, int *len);
3931708Sstevel 
3941708Sstevel static dev_info_t	*drmach_node_obp_get_dip(drmach_node_t *np);
3951708Sstevel static int		 drmach_node_obp_get_prop(drmach_node_t *np,
3961708Sstevel 				char *name, void *buf, int len);
3971708Sstevel static int		 drmach_node_obp_get_proplen(drmach_node_t *np,
3981708Sstevel 				char *name, int *len);
3991708Sstevel 
4001708Sstevel static sbd_error_t	*drmach_mbox_trans(uint8_t msgtype, int bnum,
4011708Sstevel 				caddr_t obufp, int olen,
4021708Sstevel 				caddr_t ibufp, int ilen);
4031708Sstevel 
4041708Sstevel sbd_error_t		*drmach_io_post_attach(drmachid_t id);
4051708Sstevel sbd_error_t		*drmach_io_post_release(drmachid_t id);
4061708Sstevel 
4071708Sstevel static sbd_error_t	*drmach_iocage_setup(dr_testboard_req_t *,
4081708Sstevel 				drmach_device_t **dpp, cpu_flag_t *oflags);
4091708Sstevel static int		drmach_iocage_cpu_return(drmach_device_t *dp,
4101708Sstevel 				cpu_flag_t oflags);
4111708Sstevel static sbd_error_t	*drmach_iocage_mem_return(dr_testboard_reply_t *tbr);
4121708Sstevel void			drmach_iocage_mem_scrub(uint64_t nbytes);
4131708Sstevel 
4141708Sstevel static sbd_error_t 	*drmach_i_status(drmachid_t id, drmach_status_t *stat);
4151708Sstevel 
4161708Sstevel static void		drmach_slot1_lpa_set(drmach_board_t *bp);
4171708Sstevel 
4181708Sstevel static void		drmach_cpu_read(uint64_t arg1, uint64_t arg2);
4191708Sstevel static int		drmach_cpu_read_scr(drmach_cpu_t *cp, uint64_t *scr);
4201708Sstevel 
4211708Sstevel static void		 drmach_bus_sync_list_update(void);
4221708Sstevel static void		 drmach_slice_table_update(drmach_board_t *, int);
4231708Sstevel static int		 drmach_portid2bnum(int);
4241708Sstevel 
4251708Sstevel static void		drmach_msg_memslice_init(dr_memslice_t slice_arr[]);
4261708Sstevel static void		drmach_msg_memregs_init(dr_memregs_t regs_arr[]);
4271708Sstevel 
4281708Sstevel static int		drmach_panther_boards(void);
4291708Sstevel 
4301708Sstevel static int		drmach_name2type_idx(char *);
4311708Sstevel 
4321708Sstevel #ifdef DEBUG
4331708Sstevel 
4341708Sstevel #define	DRMACH_PR		if (drmach_debug) printf
4351708Sstevel #define	DRMACH_MEMLIST_DUMP	if (drmach_debug) MEMLIST_DUMP
4361708Sstevel int drmach_debug = 0;		 /* set to non-zero to enable debug messages */
4371708Sstevel #else
4381708Sstevel 
4391708Sstevel #define	DRMACH_PR		_NOTE(CONSTANTCONDITION) if (0) printf
4401708Sstevel #define	DRMACH_MEMLIST_DUMP	_NOTE(CONSTANTCONDITION) if (0) MEMLIST_DUMP
4411708Sstevel #endif /* DEBUG */
4421708Sstevel 
4431708Sstevel #define	DRMACH_OBJ(id)		((drmach_common_t *)id)
4441708Sstevel 
4451708Sstevel #define	DRMACH_IS_BOARD_ID(id)	\
4461708Sstevel 	((id != 0) &&		\
4471708Sstevel 	(DRMACH_OBJ(id)->isa == (void *)drmach_board_new))
4481708Sstevel 
4491708Sstevel #define	DRMACH_IS_CPU_ID(id)	\
4501708Sstevel 	((id != 0) &&		\
4511708Sstevel 	(DRMACH_OBJ(id)->isa == (void *)drmach_cpu_new))
4521708Sstevel 
4531708Sstevel #define	DRMACH_IS_MEM_ID(id)	\
4541708Sstevel 	((id != 0) &&		\
4551708Sstevel 	(DRMACH_OBJ(id)->isa == (void *)drmach_mem_new))
4561708Sstevel 
4571708Sstevel #define	DRMACH_IS_IO_ID(id)	\
4581708Sstevel 	((id != 0) &&		\
4591708Sstevel 	(DRMACH_OBJ(id)->isa == (void *)drmach_io_new))
4601708Sstevel 
4611708Sstevel #define	DRMACH_IS_DEVICE_ID(id)					\
4621708Sstevel 	((id != 0) &&						\
4631708Sstevel 	(DRMACH_OBJ(id)->isa == (void *)drmach_cpu_new ||	\
4641708Sstevel 	    DRMACH_OBJ(id)->isa == (void *)drmach_mem_new ||	\
4651708Sstevel 	    DRMACH_OBJ(id)->isa == (void *)drmach_io_new))
4661708Sstevel 
4671708Sstevel #define	DRMACH_IS_ID(id)					\
4681708Sstevel 	((id != 0) &&						\
4691708Sstevel 	(DRMACH_OBJ(id)->isa == (void *)drmach_board_new ||	\
4701708Sstevel 	    DRMACH_OBJ(id)->isa == (void *)drmach_cpu_new ||	\
4711708Sstevel 	    DRMACH_OBJ(id)->isa == (void *)drmach_mem_new ||	\
4721708Sstevel 	    DRMACH_OBJ(id)->isa == (void *)drmach_io_new))
4731708Sstevel 
4741708Sstevel #define	DRMACH_INTERNAL_ERROR() \
4751708Sstevel 	drerr_new(1, ESTC_INTERNAL, drmach_ie_fmt, __LINE__)
4761708Sstevel static char		*drmach_ie_fmt = "drmach.c %d";
4771708Sstevel 
4781708Sstevel static struct {
4791708Sstevel 	const char	 *name;
4801708Sstevel 	const char	 *type;
4811708Sstevel 	sbd_error_t	 *(*new)(drmach_device_t *, drmachid_t *);
4821708Sstevel } drmach_name2type[] = {
4831708Sstevel 	{"cmp",			    DRMACH_DEVTYPE_CMP,    NULL },
4841708Sstevel 	{"cpu",			    DRMACH_DEVTYPE_CPU,    drmach_cpu_new },
4851708Sstevel 	{"SUNW,UltraSPARC-III",	    DRMACH_DEVTYPE_CPU,    drmach_cpu_new },
4861708Sstevel 	{"SUNW,UltraSPARC-III+",    DRMACH_DEVTYPE_CPU,    drmach_cpu_new },
4871708Sstevel 	{"memory-controller",	    DRMACH_DEVTYPE_MEM,    drmach_mem_new },
4881708Sstevel 	{"pci",			    DRMACH_DEVTYPE_PCI,    drmach_pci_new },
4891708Sstevel 	{"SUNW,wci",		    DRMACH_DEVTYPE_WCI,    drmach_io_new  },
4901708Sstevel };
4911708Sstevel 
4921708Sstevel /*
4931708Sstevel  * drmach autoconfiguration data structures and interfaces
4941708Sstevel  */
4951708Sstevel 
4961708Sstevel extern struct mod_ops mod_miscops;
4971708Sstevel 
4981708Sstevel static struct modlmisc modlmisc = {
4991708Sstevel 	&mod_miscops,
5007799SRichard.Bean@Sun.COM 	"Sun Fire 15000 DR"
5011708Sstevel };
5021708Sstevel 
5031708Sstevel static struct modlinkage modlinkage = {
5041708Sstevel 	MODREV_1,
5051708Sstevel 	(void *)&modlmisc,
5061708Sstevel 	NULL
5071708Sstevel };
5081708Sstevel 
5091708Sstevel /*
5101708Sstevel  * drmach_boards_rwlock is used to synchronize read/write
5111708Sstevel  * access to drmach_boards array between status and board lookup
5121708Sstevel  * as READERS, and assign, and unassign threads as WRITERS.
5131708Sstevel  */
5141708Sstevel static krwlock_t	drmach_boards_rwlock;
5151708Sstevel 
5161708Sstevel static kmutex_t		drmach_i_lock;
5171708Sstevel static kmutex_t		drmach_iocage_lock;
5181708Sstevel static kcondvar_t 	drmach_iocage_cv;
5191708Sstevel static int		drmach_iocage_is_busy = 0;
5201708Sstevel uint64_t		drmach_iocage_paddr;
5211708Sstevel static caddr_t		drmach_iocage_vaddr;
5221708Sstevel static int		drmach_iocage_size = 0;
5231708Sstevel static int		drmach_is_cheetah = -1;
5241708Sstevel 
5251708Sstevel int
_init(void)5261708Sstevel _init(void)
5271708Sstevel {
5281708Sstevel 	int	err;
5291708Sstevel 
5301708Sstevel 	mutex_init(&drmach_i_lock, NULL, MUTEX_DRIVER, NULL);
5311708Sstevel 	rw_init(&drmach_boards_rwlock, NULL, RW_DEFAULT, NULL);
5321708Sstevel 	drmach_xt_mb_size = NCPU * sizeof (uchar_t);
5331708Sstevel 	drmach_xt_mb = (uchar_t *)vmem_alloc(static_alloc_arena,
5341708Sstevel 	    drmach_xt_mb_size, VM_SLEEP);
5351708Sstevel 	bzero((void *)drmach_xt_mb, drmach_xt_mb_size);
5361708Sstevel 	if ((err = mod_install(&modlinkage)) != 0) {
5371708Sstevel 		mutex_destroy(&drmach_i_lock);
5381708Sstevel 		rw_destroy(&drmach_boards_rwlock);
5391708Sstevel 		vmem_free(static_alloc_arena, (void *)drmach_xt_mb,
5401708Sstevel 		    drmach_xt_mb_size);
5411708Sstevel 	}
5421708Sstevel 
5431708Sstevel 	return (err);
5441708Sstevel }
5451708Sstevel 
5461708Sstevel int
_fini(void)5471708Sstevel _fini(void)
5481708Sstevel {
5491708Sstevel 	static void	drmach_fini(void);
5501708Sstevel 	int		err;
5511708Sstevel 
5521708Sstevel 	if ((err = mod_remove(&modlinkage)) == 0)
5531708Sstevel 		drmach_fini();
5541708Sstevel 
5551708Sstevel 	return (err);
5561708Sstevel }
5571708Sstevel 
5581708Sstevel int
_info(struct modinfo * modinfop)5591708Sstevel _info(struct modinfo *modinfop)
5601708Sstevel {
5611708Sstevel 	return (mod_info(&modlinkage, modinfop));
5621708Sstevel }
5631708Sstevel 
5641708Sstevel /*
5651708Sstevel  * drmach_node_* routines serve the purpose of separating the
5661708Sstevel  * rest of the code from the device tree and OBP.  This is necessary
5671708Sstevel  * because of In-Kernel-Probing.  Devices probed after stod, are probed
5681708Sstevel  * by the in-kernel-prober, not OBP.  These devices, therefore, do not
5691708Sstevel  * have dnode ids.
5701708Sstevel  */
5711708Sstevel 
5721708Sstevel static int
drmach_node_obp_get_parent(drmach_node_t * np,drmach_node_t * pp)5731708Sstevel drmach_node_obp_get_parent(drmach_node_t *np, drmach_node_t *pp)
5741708Sstevel {
5751708Sstevel 	pnode_t		nodeid;
5761708Sstevel 	static char	*fn = "drmach_node_obp_get_parent";
5771708Sstevel 
5781708Sstevel 	nodeid = np->get_dnode(np);
5791708Sstevel 	if (nodeid == OBP_NONODE) {
5801708Sstevel 		cmn_err(CE_WARN, "%s: invalid dnode", fn);
5811708Sstevel 		return (-1);
5821708Sstevel 	}
5831708Sstevel 
5841708Sstevel 	bcopy(np, pp, sizeof (drmach_node_t));
5851708Sstevel 
5861708Sstevel 	pp->here = (void *)(uintptr_t)prom_parentnode(nodeid);
5871708Sstevel 	if (pp->here == OBP_NONODE) {
5881708Sstevel 		cmn_err(CE_WARN, "%s: invalid parent dnode", fn);
5891708Sstevel 		return (-1);
5901708Sstevel 	}
5911708Sstevel 
5921708Sstevel 	return (0);
5931708Sstevel }
5941708Sstevel 
5951708Sstevel static pnode_t
drmach_node_obp_get_dnode(drmach_node_t * np)5961708Sstevel drmach_node_obp_get_dnode(drmach_node_t *np)
5971708Sstevel {
5981708Sstevel 	return ((pnode_t)(uintptr_t)np->here);
5991708Sstevel }
6001708Sstevel 
6011708Sstevel typedef struct {
6021708Sstevel 	drmach_node_walk_args_t	*nwargs;
6031708Sstevel 	int 			(*cb)(drmach_node_walk_args_t *args);
6041708Sstevel 	int			err;
6051708Sstevel } drmach_node_ddi_walk_args_t;
6061708Sstevel 
6071708Sstevel int
drmach_node_ddi_walk_cb(dev_info_t * dip,void * arg)6081708Sstevel drmach_node_ddi_walk_cb(dev_info_t *dip, void *arg)
6091708Sstevel {
6101708Sstevel 	drmach_node_ddi_walk_args_t	*nargs;
6111708Sstevel 
6121708Sstevel 	nargs = (drmach_node_ddi_walk_args_t *)arg;
6131708Sstevel 
6141708Sstevel 	/*
6151708Sstevel 	 * dip doesn't have to be held here as we are called
6161708Sstevel 	 * from ddi_walk_devs() which holds the dip.
6171708Sstevel 	 */
6181708Sstevel 	nargs->nwargs->node->here = (void *)dip;
6191708Sstevel 
6201708Sstevel 	nargs->err = nargs->cb(nargs->nwargs);
6211708Sstevel 
6221708Sstevel 	/*
6231708Sstevel 	 * Set "here" to NULL so that unheld dip is not accessible
6241708Sstevel 	 * outside ddi_walk_devs()
6251708Sstevel 	 */
6261708Sstevel 	nargs->nwargs->node->here = NULL;
6271708Sstevel 
6281708Sstevel 	if (nargs->err)
6291708Sstevel 		return (DDI_WALK_TERMINATE);
6301708Sstevel 	else
6311708Sstevel 		return (DDI_WALK_CONTINUE);
6321708Sstevel }
6331708Sstevel 
6341708Sstevel static int
drmach_node_ddi_walk(drmach_node_t * np,void * data,int (* cb)(drmach_node_walk_args_t * args))6351708Sstevel drmach_node_ddi_walk(drmach_node_t *np, void *data,
6361708Sstevel 		int (*cb)(drmach_node_walk_args_t *args))
6371708Sstevel {
6381708Sstevel 	drmach_node_walk_args_t		args;
6391708Sstevel 	drmach_node_ddi_walk_args_t	nargs;
6401708Sstevel 
6411708Sstevel 	/* initialized args structure for callback */
6421708Sstevel 	args.node = np;
6431708Sstevel 	args.data = data;
6441708Sstevel 
6451708Sstevel 	nargs.nwargs = &args;
6461708Sstevel 	nargs.cb = cb;
6471708Sstevel 	nargs.err = 0;
6481708Sstevel 
6491708Sstevel 	/*
6501708Sstevel 	 * Root node doesn't have to be held in any way.
6511708Sstevel 	 */
65211066Srafael.vanoni@sun.com 	ddi_walk_devs(ddi_root_node(), drmach_node_ddi_walk_cb, (void *)&nargs);
6531708Sstevel 
6541708Sstevel 	return (nargs.err);
6551708Sstevel }
6561708Sstevel 
6571708Sstevel static int
drmach_node_obp_walk(drmach_node_t * np,void * data,int (* cb)(drmach_node_walk_args_t * args))6581708Sstevel drmach_node_obp_walk(drmach_node_t *np, void *data,
6591708Sstevel 		int (*cb)(drmach_node_walk_args_t *args))
6601708Sstevel {
6611708Sstevel 	pnode_t			nodeid;
6621708Sstevel 	int			rv;
6631708Sstevel 	drmach_node_walk_args_t	args;
6641708Sstevel 
6651708Sstevel 	/* initialized args structure for callback */
6661708Sstevel 	args.node = np;
6671708Sstevel 	args.data = data;
6681708Sstevel 
6691708Sstevel 	nodeid = prom_childnode(prom_rootnode());
6701708Sstevel 
6711708Sstevel 	/* save our new position within the tree */
6721708Sstevel 	np->here = (void *)(uintptr_t)nodeid;
6731708Sstevel 
6741708Sstevel 	rv = 0;
6751708Sstevel 	while (nodeid != OBP_NONODE) {
6761708Sstevel 
6771708Sstevel 		pnode_t child;
6781708Sstevel 
6791708Sstevel 		rv = (*cb)(&args);
6801708Sstevel 		if (rv)
6811708Sstevel 			break;
6821708Sstevel 
6831708Sstevel 		child = prom_childnode(nodeid);
6841708Sstevel 		np->here = (void *)(uintptr_t)child;
6851708Sstevel 
6861708Sstevel 		while (child != OBP_NONODE) {
6871708Sstevel 			rv = (*cb)(&args);
6881708Sstevel 			if (rv)
6891708Sstevel 				break;
6901708Sstevel 
6911708Sstevel 			child = prom_nextnode(child);
6921708Sstevel 			np->here = (void *)(uintptr_t)child;
6931708Sstevel 		}
6941708Sstevel 
6951708Sstevel 		nodeid = prom_nextnode(nodeid);
6961708Sstevel 
6971708Sstevel 		/* save our new position within the tree */
6981708Sstevel 		np->here = (void *)(uintptr_t)nodeid;
6991708Sstevel 	}
7001708Sstevel 
7011708Sstevel 	return (rv);
7021708Sstevel }
7031708Sstevel 
7041708Sstevel static int
drmach_node_ddi_get_parent(drmach_node_t * np,drmach_node_t * pp)7051708Sstevel drmach_node_ddi_get_parent(drmach_node_t *np, drmach_node_t *pp)
7061708Sstevel {
7071708Sstevel 	dev_info_t	*ndip;
7081708Sstevel 	static char	*fn = "drmach_node_ddi_get_parent";
7091708Sstevel 
7101708Sstevel 	ndip = np->n_getdip(np);
7111708Sstevel 	if (ndip == NULL) {
7121708Sstevel 		cmn_err(CE_WARN, "%s: NULL dip", fn);
7131708Sstevel 		return (-1);
7141708Sstevel 	}
7151708Sstevel 
7161708Sstevel 	bcopy(np, pp, sizeof (drmach_node_t));
7171708Sstevel 
7181708Sstevel 	pp->here = (void *)ddi_get_parent(ndip);
7191708Sstevel 	if (pp->here == NULL) {
7201708Sstevel 		cmn_err(CE_WARN, "%s: NULL parent dip", fn);
7211708Sstevel 		return (-1);
7221708Sstevel 	}
7231708Sstevel 
7241708Sstevel 	return (0);
7251708Sstevel }
7261708Sstevel 
7271708Sstevel /*ARGSUSED*/
7281708Sstevel static pnode_t
drmach_node_ddi_get_dnode(drmach_node_t * np)7291708Sstevel drmach_node_ddi_get_dnode(drmach_node_t *np)
7301708Sstevel {
7311708Sstevel 	return ((pnode_t)NULL);
7321708Sstevel }
7331708Sstevel 
7341708Sstevel static drmach_node_t *
drmach_node_new(void)7351708Sstevel drmach_node_new(void)
7361708Sstevel {
7371708Sstevel 	drmach_node_t *np;
7381708Sstevel 
7391708Sstevel 	np = kmem_zalloc(sizeof (drmach_node_t), KM_SLEEP);
7401708Sstevel 
7411708Sstevel 	if (drmach_initialized) {
7421708Sstevel 		np->get_dnode = drmach_node_ddi_get_dnode;
7431708Sstevel 		np->walk = drmach_node_ddi_walk;
7441708Sstevel 		np->n_getdip = drmach_node_ddi_get_dip;
7451708Sstevel 		np->n_getproplen = drmach_node_ddi_get_proplen;
7461708Sstevel 		np->n_getprop = drmach_node_ddi_get_prop;
7471708Sstevel 		np->get_parent = drmach_node_ddi_get_parent;
7481708Sstevel 	} else {
7491708Sstevel 		np->get_dnode = drmach_node_obp_get_dnode;
7501708Sstevel 		np->walk = drmach_node_obp_walk;
7511708Sstevel 		np->n_getdip = drmach_node_obp_get_dip;
7521708Sstevel 		np->n_getproplen = drmach_node_obp_get_proplen;
7531708Sstevel 		np->n_getprop = drmach_node_obp_get_prop;
7541708Sstevel 		np->get_parent = drmach_node_obp_get_parent;
7551708Sstevel 	}
7561708Sstevel 
7571708Sstevel 	return (np);
7581708Sstevel }
7591708Sstevel 
7601708Sstevel static void
drmach_node_dispose(drmach_node_t * np)7611708Sstevel drmach_node_dispose(drmach_node_t *np)
7621708Sstevel {
7631708Sstevel 	kmem_free(np, sizeof (*np));
7641708Sstevel }
7651708Sstevel 
7661708Sstevel /*
7671708Sstevel  * Check if a CPU node is part of a CMP.
7681708Sstevel  */
7691708Sstevel static int
drmach_is_cmp_child(dev_info_t * dip)7701708Sstevel drmach_is_cmp_child(dev_info_t *dip)
7711708Sstevel {
7721708Sstevel 	dev_info_t *pdip;
7731708Sstevel 
7741708Sstevel 	if (strcmp(ddi_node_name(dip), DRMACH_CPU_NAMEPROP) != 0) {
7751708Sstevel 		return (0);
7761708Sstevel 	}
7771708Sstevel 
7781708Sstevel 	pdip = ddi_get_parent(dip);
7791708Sstevel 
7801708Sstevel 	ASSERT(pdip);
7811708Sstevel 
7821708Sstevel 	if (strcmp(ddi_node_name(pdip), DRMACH_CMP_NAMEPROP) == 0) {
7831708Sstevel 		return (1);
7841708Sstevel 	}
7851708Sstevel 
7861708Sstevel 	return (0);
7871708Sstevel }
7881708Sstevel 
7891708Sstevel static dev_info_t *
drmach_node_obp_get_dip(drmach_node_t * np)7901708Sstevel drmach_node_obp_get_dip(drmach_node_t *np)
7911708Sstevel {
7921708Sstevel 	pnode_t		nodeid;
7931708Sstevel 	dev_info_t	*dip;
7941708Sstevel 
7951708Sstevel 	nodeid = np->get_dnode(np);
7961708Sstevel 	if (nodeid == OBP_NONODE)
7971708Sstevel 		return (NULL);
7981708Sstevel 
7991708Sstevel 	dip = e_ddi_nodeid_to_dip(nodeid);
8001708Sstevel 	if (dip) {
8011708Sstevel 		/*
8021708Sstevel 		 * The branch rooted at dip will have been previously
8031708Sstevel 		 * held, or it will be the child of a CMP. In either
8041708Sstevel 		 * case, the hold acquired in e_ddi_nodeid_to_dip()
8051708Sstevel 		 * is not needed.
8061708Sstevel 		 */
8071708Sstevel 		ddi_release_devi(dip);
8081708Sstevel 		ASSERT(drmach_is_cmp_child(dip) || e_ddi_branch_held(dip));
8091708Sstevel 	}
8101708Sstevel 
8111708Sstevel 	return (dip);
8121708Sstevel }
8131708Sstevel 
8141708Sstevel static dev_info_t *
drmach_node_ddi_get_dip(drmach_node_t * np)8151708Sstevel drmach_node_ddi_get_dip(drmach_node_t *np)
8161708Sstevel {
8171708Sstevel 	return ((dev_info_t *)np->here);
8181708Sstevel }
8191708Sstevel 
8201708Sstevel static int
drmach_node_walk(drmach_node_t * np,void * param,int (* cb)(drmach_node_walk_args_t * args))8211708Sstevel drmach_node_walk(drmach_node_t *np, void *param,
8221708Sstevel 		int (*cb)(drmach_node_walk_args_t *args))
8231708Sstevel {
8241708Sstevel 	return (np->walk(np, param, cb));
8251708Sstevel }
8261708Sstevel 
8271708Sstevel static int
drmach_node_ddi_get_prop(drmach_node_t * np,char * name,void * buf,int len)8281708Sstevel drmach_node_ddi_get_prop(drmach_node_t *np, char *name, void *buf, int len)
8291708Sstevel {
8301708Sstevel 	int		rv = 0;
8311708Sstevel 	dev_info_t	*ndip;
8321708Sstevel 	static char	*fn = "drmach_node_ddi_get_prop";
8331708Sstevel 
8341708Sstevel 	ndip = np->n_getdip(np);
8351708Sstevel 	if (ndip == NULL) {
8361708Sstevel 		cmn_err(CE_WARN, "%s: NULL dip", fn);
8371708Sstevel 		rv = -1;
8381708Sstevel 	} else if (ddi_getlongprop_buf(DDI_DEV_T_ANY, ndip,
8391708Sstevel 	    DDI_PROP_DONTPASS | DDI_PROP_NOTPROM, name,
8401708Sstevel 	    (caddr_t)buf, &len) != DDI_PROP_SUCCESS) {
8411708Sstevel 		rv = -1;
8421708Sstevel 	}
8431708Sstevel 
8441708Sstevel 	return (rv);
8451708Sstevel }
8461708Sstevel 
8471708Sstevel /* ARGSUSED */
8481708Sstevel static int
drmach_node_obp_get_prop(drmach_node_t * np,char * name,void * buf,int len)8491708Sstevel drmach_node_obp_get_prop(drmach_node_t *np, char *name, void *buf, int len)
8501708Sstevel {
8511708Sstevel 	int		rv = 0;
8521708Sstevel 	pnode_t		nodeid;
8531708Sstevel 	static char	*fn = "drmach_node_obp_get_prop";
8541708Sstevel 
8551708Sstevel 	nodeid = np->get_dnode(np);
8561708Sstevel 	if (nodeid == OBP_NONODE) {
8571708Sstevel 		cmn_err(CE_WARN, "%s: invalid dnode", fn);
8581708Sstevel 		rv = -1;
8591708Sstevel 	} else if (prom_getproplen(nodeid, (caddr_t)name) < 0) {
8601708Sstevel 		rv = -1;
8611708Sstevel 	} else {
8621708Sstevel 		(void) prom_getprop(nodeid, (caddr_t)name, (caddr_t)buf);
8631708Sstevel 	}
8641708Sstevel 
8651708Sstevel 	return (rv);
8661708Sstevel }
8671708Sstevel 
8681708Sstevel static int
drmach_node_ddi_get_proplen(drmach_node_t * np,char * name,int * len)8691708Sstevel drmach_node_ddi_get_proplen(drmach_node_t *np, char *name, int *len)
8701708Sstevel {
8711708Sstevel 	int		rv = 0;
8721708Sstevel 	dev_info_t	*ndip;
8731708Sstevel 
8741708Sstevel 	ndip = np->n_getdip(np);
8751708Sstevel 	if (ndip == NULL) {
8761708Sstevel 		rv = -1;
8771708Sstevel 	} else if (ddi_getproplen(DDI_DEV_T_ANY, ndip, DDI_PROP_DONTPASS,
87811066Srafael.vanoni@sun.com 	    name, len) != DDI_PROP_SUCCESS) {
8791708Sstevel 		rv = -1;
8801708Sstevel 	}
8811708Sstevel 
8821708Sstevel 	return (rv);
8831708Sstevel }
8841708Sstevel 
8851708Sstevel static int
drmach_node_obp_get_proplen(drmach_node_t * np,char * name,int * len)8861708Sstevel drmach_node_obp_get_proplen(drmach_node_t *np, char *name, int *len)
8871708Sstevel {
8881708Sstevel 	pnode_t	 nodeid;
8891708Sstevel 	int	 rv;
8901708Sstevel 
8911708Sstevel 	nodeid = np->get_dnode(np);
8921708Sstevel 	if (nodeid == OBP_NONODE)
8931708Sstevel 		rv = -1;
8941708Sstevel 	else {
8951708Sstevel 		*len = prom_getproplen(nodeid, (caddr_t)name);
8961708Sstevel 		rv = (*len < 0 ? -1 : 0);
8971708Sstevel 	}
8981708Sstevel 
8991708Sstevel 	return (rv);
9001708Sstevel }
9011708Sstevel 
9021708Sstevel static drmachid_t
drmach_node_dup(drmach_node_t * np)9031708Sstevel drmach_node_dup(drmach_node_t *np)
9041708Sstevel {
9051708Sstevel 	drmach_node_t *dup;
9061708Sstevel 
9071708Sstevel 	dup = drmach_node_new();
9081708Sstevel 	dup->here = np->here;
9091708Sstevel 	dup->get_dnode = np->get_dnode;
9101708Sstevel 	dup->walk = np->walk;
9111708Sstevel 	dup->n_getdip = np->n_getdip;
9121708Sstevel 	dup->n_getproplen = np->n_getproplen;
9131708Sstevel 	dup->n_getprop = np->n_getprop;
9141708Sstevel 	dup->get_parent = np->get_parent;
9151708Sstevel 
9161708Sstevel 	return (dup);
9171708Sstevel }
9181708Sstevel 
9191708Sstevel /*
9201708Sstevel  * drmach_array provides convenient array construction, access,
9211708Sstevel  * bounds checking and array destruction logic.
9221708Sstevel  */
9231708Sstevel 
9241708Sstevel static drmach_array_t *
drmach_array_new(int min_index,int max_index)9251708Sstevel drmach_array_new(int min_index, int max_index)
9261708Sstevel {
9271708Sstevel 	drmach_array_t *arr;
9281708Sstevel 
9291708Sstevel 	arr = kmem_zalloc(sizeof (drmach_array_t), KM_SLEEP);
9301708Sstevel 
9311708Sstevel 	arr->arr_sz = (max_index - min_index + 1) * sizeof (void *);
9321708Sstevel 	if (arr->arr_sz > 0) {
9331708Sstevel 		arr->min_index = min_index;
9341708Sstevel 		arr->max_index = max_index;
9351708Sstevel 
9361708Sstevel 		arr->arr = kmem_zalloc(arr->arr_sz, KM_SLEEP);
9371708Sstevel 		return (arr);
9381708Sstevel 	} else {
9391708Sstevel 		kmem_free(arr, sizeof (*arr));
9401708Sstevel 		return (0);
9411708Sstevel 	}
9421708Sstevel }
9431708Sstevel 
9441708Sstevel static int
drmach_array_set(drmach_array_t * arr,int idx,drmachid_t val)9451708Sstevel drmach_array_set(drmach_array_t *arr, int idx, drmachid_t val)
9461708Sstevel {
9471708Sstevel 	if (idx < arr->min_index || idx > arr->max_index)
9481708Sstevel 		return (-1);
9491708Sstevel 	else {
9501708Sstevel 		arr->arr[idx - arr->min_index] = val;
9511708Sstevel 		return (0);
9521708Sstevel 	}
9531708Sstevel 	/*NOTREACHED*/
9541708Sstevel }
9551708Sstevel 
9561708Sstevel static int
drmach_array_get(drmach_array_t * arr,int idx,drmachid_t * val)9571708Sstevel drmach_array_get(drmach_array_t *arr, int idx, drmachid_t *val)
9581708Sstevel {
9591708Sstevel 	if (idx < arr->min_index || idx > arr->max_index)
9601708Sstevel 		return (-1);
9611708Sstevel 	else {
9621708Sstevel 		*val = arr->arr[idx - arr->min_index];
9631708Sstevel 		return (0);
9641708Sstevel 	}
9651708Sstevel 	/*NOTREACHED*/
9661708Sstevel }
9671708Sstevel 
9681708Sstevel static int
drmach_array_first(drmach_array_t * arr,int * idx,drmachid_t * val)9691708Sstevel drmach_array_first(drmach_array_t *arr, int *idx, drmachid_t *val)
9701708Sstevel {
9711708Sstevel 	int rv;
9721708Sstevel 
9731708Sstevel 	*idx = arr->min_index;
9741708Sstevel 	while ((rv = drmach_array_get(arr, *idx, val)) == 0 && *val == NULL)
9751708Sstevel 		*idx += 1;
9761708Sstevel 
9771708Sstevel 	return (rv);
9781708Sstevel }
9791708Sstevel 
9801708Sstevel static int
drmach_array_next(drmach_array_t * arr,int * idx,drmachid_t * val)9811708Sstevel drmach_array_next(drmach_array_t *arr, int *idx, drmachid_t *val)
9821708Sstevel {
9831708Sstevel 	int rv;
9841708Sstevel 
9851708Sstevel 	*idx += 1;
9861708Sstevel 	while ((rv = drmach_array_get(arr, *idx, val)) == 0 && *val == NULL)
9871708Sstevel 		*idx += 1;
9881708Sstevel 
9891708Sstevel 	return (rv);
9901708Sstevel }
9911708Sstevel 
9921708Sstevel static void
drmach_array_dispose(drmach_array_t * arr,void (* disposer)(drmachid_t))9931708Sstevel drmach_array_dispose(drmach_array_t *arr, void (*disposer)(drmachid_t))
9941708Sstevel {
9951708Sstevel 	drmachid_t	val;
9961708Sstevel 	int		idx;
9971708Sstevel 	int		rv;
9981708Sstevel 
9991708Sstevel 	rv = drmach_array_first(arr, &idx, &val);
10001708Sstevel 	while (rv == 0) {
10011708Sstevel 		(*disposer)(val);
10021708Sstevel 
10031708Sstevel 		/* clear the array entry */
10041708Sstevel 		rv = drmach_array_set(arr, idx, NULL);
10051708Sstevel 		ASSERT(rv == 0);
10061708Sstevel 
10071708Sstevel 		rv = drmach_array_next(arr, &idx, &val);
10081708Sstevel 	}
10091708Sstevel 
10101708Sstevel 	kmem_free(arr->arr, arr->arr_sz);
10111708Sstevel 	kmem_free(arr, sizeof (*arr));
10121708Sstevel }
10131708Sstevel 
10141708Sstevel 
10151708Sstevel static gdcd_t *
drmach_gdcd_new()10161708Sstevel drmach_gdcd_new()
10171708Sstevel {
10181708Sstevel 	gdcd_t *gdcd;
10191708Sstevel 
10201708Sstevel 	gdcd = kmem_zalloc(sizeof (gdcd_t), KM_SLEEP);
10211708Sstevel 
10221708Sstevel 	/* read the gdcd, bail if magic or ver #s are not what is expected */
10231708Sstevel 	if (iosram_rd(GDCD_MAGIC, 0, sizeof (gdcd_t), (caddr_t)gdcd)) {
10241708Sstevel bail:
10251708Sstevel 		kmem_free(gdcd, sizeof (gdcd_t));
10261708Sstevel 		return (NULL);
10271708Sstevel 	} else if (gdcd->h.dcd_magic != GDCD_MAGIC) {
10281708Sstevel 		goto bail;
10291708Sstevel 	} else if (gdcd->h.dcd_version != DCD_VERSION) {
10301708Sstevel 		goto bail;
10311708Sstevel 	}
10321708Sstevel 
10331708Sstevel 	return (gdcd);
10341708Sstevel }
10351708Sstevel 
10361708Sstevel static void
drmach_gdcd_dispose(gdcd_t * gdcd)10371708Sstevel drmach_gdcd_dispose(gdcd_t *gdcd)
10381708Sstevel {
10391708Sstevel 	kmem_free(gdcd, sizeof (gdcd_t));
10401708Sstevel }
10411708Sstevel 
10421708Sstevel /*ARGSUSED*/
10431708Sstevel sbd_error_t *
drmach_configure(drmachid_t id,int flags)10441708Sstevel drmach_configure(drmachid_t id, int flags)
10451708Sstevel {
10461708Sstevel 	drmach_device_t	*dp;
10471708Sstevel 	dev_info_t	*rdip;
10481708Sstevel 	sbd_error_t	*err = NULL;
10491708Sstevel 
10501708Sstevel 	/*
10511708Sstevel 	 * On Starcat, there is no CPU driver, so it is
10521708Sstevel 	 * not necessary to configure any CPU nodes.
10531708Sstevel 	 */
10541708Sstevel 	if (DRMACH_IS_CPU_ID(id)) {
10551708Sstevel 		return (NULL);
10561708Sstevel 	}
10571708Sstevel 
10581708Sstevel 	for (; id; ) {
10591708Sstevel 		dev_info_t	*fdip = NULL;
10601708Sstevel 
10611708Sstevel 		if (!DRMACH_IS_DEVICE_ID(id))
10621708Sstevel 			return (drerr_new(0, ESTC_INAPPROP, NULL));
10631708Sstevel 		dp = id;
10641708Sstevel 
10651708Sstevel 		rdip = dp->node->n_getdip(dp->node);
10661708Sstevel 
10671708Sstevel 		/*
10681708Sstevel 		 * We held this branch earlier, so at a minimum its
10691708Sstevel 		 * root should still be present in the device tree.
10701708Sstevel 		 */
10711708Sstevel 		ASSERT(rdip);
10721708Sstevel 
10731708Sstevel 		DRMACH_PR("drmach_configure: configuring DDI branch");
10741708Sstevel 
10751708Sstevel 		ASSERT(e_ddi_branch_held(rdip));
10761708Sstevel 		if (e_ddi_branch_configure(rdip, &fdip, 0) != 0) {
10771708Sstevel 			if (err == NULL) {
10781708Sstevel 				/*
10791708Sstevel 				 * Record first failure but don't stop
10801708Sstevel 				 */
10811708Sstevel 				char *path = kmem_alloc(MAXPATHLEN, KM_SLEEP);
10821708Sstevel 				dev_info_t *dip = (fdip != NULL) ? fdip : rdip;
10831708Sstevel 
10841708Sstevel 				(void) ddi_pathname(dip, path);
10851708Sstevel 				err = drerr_new(1, ESTC_DRVFAIL, path);
10861708Sstevel 
10871708Sstevel 				kmem_free(path, MAXPATHLEN);
10881708Sstevel 			}
10891708Sstevel 
10901708Sstevel 			/*
10911708Sstevel 			 * If non-NULL, fdip is returned held and must be
10921708Sstevel 			 * released.
10931708Sstevel 			 */
10941708Sstevel 			if (fdip != NULL) {
10951708Sstevel 				ddi_release_devi(fdip);
10961708Sstevel 			}
10971708Sstevel 		}
10981708Sstevel 
10991708Sstevel 		if (DRMACH_IS_MEM_ID(id)) {
11001708Sstevel 			drmach_mem_t	*mp = id;
11011708Sstevel 			id = mp->next;
11021708Sstevel 		} else {
11031708Sstevel 			id = NULL;
11041708Sstevel 		}
11051708Sstevel 	}
11061708Sstevel 
11071708Sstevel 	return (err);
11081708Sstevel }
11091708Sstevel 
11101708Sstevel static sbd_error_t *
drmach_device_new(drmach_node_t * node,drmach_board_t * bp,int portid,drmachid_t * idp)11111708Sstevel drmach_device_new(drmach_node_t *node,
11121708Sstevel 	drmach_board_t *bp, int portid, drmachid_t *idp)
11131708Sstevel {
11141708Sstevel 	int		i, rv, device_id, unum;
11151708Sstevel 	char		name[OBP_MAXDRVNAME];
11161708Sstevel 	drmach_device_t	proto;
11171708Sstevel 
11181708Sstevel 	rv = node->n_getprop(node, "name", name, OBP_MAXDRVNAME);
11191708Sstevel 	if (rv) {
11201708Sstevel 		sbd_error_t *err;
11211708Sstevel 
11221708Sstevel 		/* every node is expected to have a name */
11231708Sstevel 		err = drerr_new(1, ESTC_GETPROP,
112411066Srafael.vanoni@sun.com 		    "dip: 0x%p: property %s",
112511066Srafael.vanoni@sun.com 		    node->n_getdip(node), OBP_NAME);
11261708Sstevel 
11271708Sstevel 		return (err);
11281708Sstevel 	}
11291708Sstevel 
11301708Sstevel 	i = drmach_name2type_idx(name);
11311708Sstevel 
11321708Sstevel 	if (i < 0 || strcmp(name, "cmp") == 0) {
11331708Sstevel 		/*
11341708Sstevel 		 * Not a node of interest to dr - including "cmp",
11351708Sstevel 		 * but it is in drmach_name2type[], which lets gptwocfg
11361708Sstevel 		 * driver to check if node is OBP created.
11371708Sstevel 		 */
11381708Sstevel 		*idp = (drmachid_t)0;
11391708Sstevel 		return (NULL);
11401708Sstevel 	}
11411708Sstevel 
11421708Sstevel 	/*
11431708Sstevel 	 * Derive a best-guess unit number from the portid value.
11441708Sstevel 	 * Some drmach_*_new constructors (drmach_pci_new, for example)
11451708Sstevel 	 * will overwrite the prototype unum value with one that is more
11461708Sstevel 	 * appropriate for the device.
11471708Sstevel 	 */
11481708Sstevel 	device_id = portid & 0x1f;
11491708Sstevel 	if (device_id < 4)
11501708Sstevel 		unum = device_id;
11511708Sstevel 	else if (device_id == 8) {
11521708Sstevel 		unum = 0;
11531708Sstevel 	} else if (device_id == 9) {
11541708Sstevel 		unum = 1;
11551708Sstevel 	} else if (device_id == 0x1c) {
11561708Sstevel 		unum = 0;
11571708Sstevel 	} else if (device_id == 0x1d) {
11581708Sstevel 		unum = 1;
11591708Sstevel 	} else {
11601708Sstevel 		return (DRMACH_INTERNAL_ERROR());
11611708Sstevel 	}
11621708Sstevel 
11631708Sstevel 	bzero(&proto, sizeof (proto));
11641708Sstevel 	proto.type = drmach_name2type[i].type;
11651708Sstevel 	proto.bp = bp;
11661708Sstevel 	proto.node = node;
11671708Sstevel 	proto.portid = portid;
11681708Sstevel 	proto.unum = unum;
11691708Sstevel 
11701708Sstevel 	return (drmach_name2type[i].new(&proto, idp));
11711708Sstevel }
11721708Sstevel 
11731708Sstevel static void
drmach_device_dispose(drmachid_t id)11741708Sstevel drmach_device_dispose(drmachid_t id)
11751708Sstevel {
11761708Sstevel 	drmach_device_t *self = id;
11771708Sstevel 
11781708Sstevel 	self->cm.dispose(id);
11791708Sstevel }
11801708Sstevel 
11811708Sstevel static drmach_board_t *
drmach_board_new(int bnum)11821708Sstevel drmach_board_new(int bnum)
11831708Sstevel {
11841708Sstevel 	static sbd_error_t *drmach_board_release(drmachid_t);
11851708Sstevel 	static sbd_error_t *drmach_board_status(drmachid_t, drmach_status_t *);
11861708Sstevel 
11871708Sstevel 	drmach_board_t	*bp;
11881708Sstevel 
11891708Sstevel 	bp = kmem_zalloc(sizeof (drmach_board_t), KM_SLEEP);
11901708Sstevel 
11911708Sstevel 	bp->cm.isa = (void *)drmach_board_new;
11921708Sstevel 	bp->cm.release = drmach_board_release;
11931708Sstevel 	bp->cm.status = drmach_board_status;
11941708Sstevel 
11951708Sstevel 	(void) drmach_board_name(bnum, bp->cm.name, sizeof (bp->cm.name));
11961708Sstevel 
11971708Sstevel 	bp->bnum = bnum;
11981708Sstevel 	bp->devices = NULL;
11991708Sstevel 	bp->tree = drmach_node_new();
12001708Sstevel 
120111311SSurya.Prakki@Sun.COM 	(void) drmach_array_set(drmach_boards, bnum, bp);
12021708Sstevel 	return (bp);
12031708Sstevel }
12041708Sstevel 
12051708Sstevel static void
drmach_board_dispose(drmachid_t id)12061708Sstevel drmach_board_dispose(drmachid_t id)
12071708Sstevel {
12081708Sstevel 	drmach_board_t *bp;
12091708Sstevel 
12101708Sstevel 	ASSERT(DRMACH_IS_BOARD_ID(id));
12111708Sstevel 	bp = id;
12121708Sstevel 
12131708Sstevel 	if (bp->tree)
12141708Sstevel 		drmach_node_dispose(bp->tree);
12151708Sstevel 
12161708Sstevel 	if (bp->devices)
12171708Sstevel 		drmach_array_dispose(bp->devices, drmach_device_dispose);
12181708Sstevel 
12191708Sstevel 	kmem_free(bp, sizeof (*bp));
12201708Sstevel }
12211708Sstevel 
12221708Sstevel static sbd_error_t *
drmach_board_status(drmachid_t id,drmach_status_t * stat)12231708Sstevel drmach_board_status(drmachid_t id, drmach_status_t *stat)
12241708Sstevel {
12251708Sstevel 	sbd_error_t	*err = NULL;
12261708Sstevel 	drmach_board_t	*bp;
12271708Sstevel 	caddr_t		obufp;
12281708Sstevel 	dr_showboard_t	shb;
12291708Sstevel 
12301708Sstevel 	if (!DRMACH_IS_BOARD_ID(id))
12311708Sstevel 		return (drerr_new(0, ESTC_INAPPROP, NULL));
12321708Sstevel 
12331708Sstevel 	bp = id;
12341708Sstevel 
12351708Sstevel 	/*
12361708Sstevel 	 * we need to know if the board's connected before
12371708Sstevel 	 * issuing a showboard message.  If it's connected, we just
12381708Sstevel 	 * reply with status composed of cached info
12391708Sstevel 	 */
12401708Sstevel 
12411708Sstevel 	if (!bp->connected) {
12421708Sstevel 		obufp = kmem_zalloc(sizeof (dr_proto_hdr_t), KM_SLEEP);
12431708Sstevel 		err = drmach_mbox_trans(DRMSG_SHOWBOARD, bp->bnum, obufp,
124411066Srafael.vanoni@sun.com 		    sizeof (dr_proto_hdr_t), (caddr_t)&shb,
124511066Srafael.vanoni@sun.com 		    sizeof (dr_showboard_t));
12461708Sstevel 
12471708Sstevel 		kmem_free(obufp, sizeof (dr_proto_hdr_t));
12481708Sstevel 		if (err)
12491708Sstevel 			return (err);
12501708Sstevel 
12511708Sstevel 		bp->connected = (shb.bd_assigned && shb.bd_active);
125211311SSurya.Prakki@Sun.COM 		(void) strncpy(bp->type, shb.board_type, sizeof (bp->type));
12531708Sstevel 		stat->assigned = bp->assigned = shb.bd_assigned;
12541708Sstevel 		stat->powered = bp->powered = shb.power_on;
12551708Sstevel 		stat->empty = bp->empty = shb.slot_empty;
12561708Sstevel 
12571708Sstevel 		switch (shb.test_status) {
12581708Sstevel 			case DR_TEST_STATUS_UNKNOWN:
12591708Sstevel 			case DR_TEST_STATUS_IPOST:
12601708Sstevel 			case DR_TEST_STATUS_ABORTED:
12611708Sstevel 				stat->cond = bp->cond = SBD_COND_UNKNOWN;
12621708Sstevel 				break;
12631708Sstevel 			case DR_TEST_STATUS_PASSED:
12641708Sstevel 				stat->cond = bp->cond = SBD_COND_OK;
12651708Sstevel 				break;
12661708Sstevel 			case DR_TEST_STATUS_FAILED:
12671708Sstevel 				stat->cond = bp->cond = SBD_COND_FAILED;
12681708Sstevel 				break;
12691708Sstevel 			default:
12701708Sstevel 				stat->cond = bp->cond = SBD_COND_UNKNOWN;
12711708Sstevel 				DRMACH_PR("Unknown test status=0x%x from SC\n",
127211066Srafael.vanoni@sun.com 				    shb.test_status);
12731708Sstevel 				break;
12741708Sstevel 
12751708Sstevel 		}
12761708Sstevel 
127711311SSurya.Prakki@Sun.COM 		(void) strncpy(stat->type, shb.board_type, sizeof (stat->type));
127811311SSurya.Prakki@Sun.COM 		(void) snprintf(stat->info, sizeof (stat->info),
127911311SSurya.Prakki@Sun.COM 		    "Test Level=%d", shb.test_level);
12801708Sstevel 	} else {
12811708Sstevel 		stat->assigned = bp->assigned;
12821708Sstevel 		stat->powered = bp->powered;
12831708Sstevel 		stat->empty = bp->empty;
12841708Sstevel 		stat->cond = bp->cond;
128511311SSurya.Prakki@Sun.COM 		(void) strncpy(stat->type, bp->type, sizeof (stat->type));
12861708Sstevel 	}
12871708Sstevel 
12881708Sstevel 	stat->busy = 0;			/* assume not busy */
12891708Sstevel 	stat->configured = 0;		/* assume not configured */
12901708Sstevel 	if (bp->devices) {
12911708Sstevel 		int		 rv;
12921708Sstevel 		int		 d_idx;
12931708Sstevel 		drmachid_t	 d_id;
12941708Sstevel 
12951708Sstevel 		rv = drmach_array_first(bp->devices, &d_idx, &d_id);
12961708Sstevel 		while (rv == 0) {
12971708Sstevel 			drmach_status_t	d_stat;
12981708Sstevel 
12991708Sstevel 			err = drmach_i_status(d_id, &d_stat);
13001708Sstevel 			if (err)
13011708Sstevel 				break;
13021708Sstevel 
13031708Sstevel 			stat->busy |= d_stat.busy;
13041708Sstevel 			stat->configured |= d_stat.configured;
13051708Sstevel 
13061708Sstevel 			rv = drmach_array_next(bp->devices, &d_idx, &d_id);
13071708Sstevel 		}
13081708Sstevel 	}
13091708Sstevel 
13101708Sstevel 	return (err);
13111708Sstevel }
13121708Sstevel 
13131708Sstevel typedef struct drmach_msglist {
13141708Sstevel 	kcondvar_t		s_cv; 		/* condvar for sending msg */
13151708Sstevel 	kmutex_t		s_lock;		/* mutex for sending */
13161708Sstevel 	kcondvar_t		g_cv;		/* condvar for getting reply */
13171708Sstevel 	kmutex_t		g_lock;		/* mutex for getting reply */
13181708Sstevel 	struct drmach_msglist	*prev;		/* link to previous entry */
13191708Sstevel 	struct drmach_msglist	*next;		/* link to next entry */
13201708Sstevel 	struct drmach_msglist	*link;		/* link to related entry */
13211708Sstevel 	caddr_t			o_buf;		/* address of output buffer */
13221708Sstevel 	caddr_t			i_buf; 		/* address of input buffer */
13231708Sstevel 	uint32_t		o_buflen;	/* output buffer length */
13241708Sstevel 	uint32_t		i_buflen;	/* input buffer length */
13251708Sstevel 	uint32_t		msgid;		/* message identifier */
13261708Sstevel 	int			o_nretry;	/* number of sending retries */
13271708Sstevel 	int			f_error;	/* mailbox framework error */
13281708Sstevel 	uint8_t			e_code;		/* error code returned by SC */
13291708Sstevel 	uint8_t			p_flag	:1,	/* successfully putmsg */
13301708Sstevel 				m_reply	:1,	/* msg reply received */
13311708Sstevel 				unused	:6;
13321708Sstevel } drmach_msglist_t;
13331708Sstevel 
13341708Sstevel kmutex_t		drmach_g_mbox_mutex;	/* mutex for mailbox globals */
13351708Sstevel kmutex_t		drmach_ri_mbox_mutex;	/* mutex for mailbox reinit */
13361708Sstevel kmutex_t		drmach_msglist_mutex;	/* mutex for message list */
13371708Sstevel drmach_msglist_t	*drmach_msglist_first;	/* first entry in msg list */
13381708Sstevel drmach_msglist_t	*drmach_msglist_last;	/* last entry in msg list */
13391708Sstevel uint32_t		drmach_msgid;		/* current message id */
13401708Sstevel kthread_t		*drmach_getmsg_thread;	/* ptr to getmsg thread */
13411708Sstevel volatile int		drmach_getmsg_thread_run; /* run flag for getmsg thr */
13421708Sstevel kmutex_t		drmach_sendmsg_mutex;	/* mutex for sendmsg cv */
13431708Sstevel kcondvar_t		drmach_sendmsg_cv;	/* signaled to send new msg */
13441708Sstevel kthread_t		*drmach_sendmsg_thread; /* ptr to sendmsg thread */
13451708Sstevel volatile int		drmach_sendmsg_thread_run; /* run flag for sendmsg */
13461708Sstevel int			drmach_mbox_istate;	/* mailbox init state */
13471708Sstevel int			drmach_mbox_iflag;	/* set if init'd with SC */
13481708Sstevel int			drmach_mbox_ipending;	/* set if reinit scheduled */
13491708Sstevel 
13501708Sstevel /*
13511708Sstevel  * Timeout values (in seconds) used when waiting for replies (from the SC) to
13521708Sstevel  * requests that we sent.  Since we only receive boardevent messages, and they
13531708Sstevel  * are events rather than replies, there is no boardevent timeout.
13541708Sstevel  */
13551708Sstevel int	drmach_to_mbxinit	= 60;		/* 1 minute */
13561708Sstevel int	drmach_to_assign	= 60;		/* 1 minute */
13571708Sstevel int	drmach_to_unassign	= 60;		/* 1 minute */
13581708Sstevel int	drmach_to_claim		= 3600;		/* 1 hour */
13591708Sstevel int	drmach_to_unclaim	= 3600;		/* 1 hour */
13601708Sstevel int	drmach_to_poweron	= 480;		/* 8 minutes */
13611708Sstevel int	drmach_to_poweroff	= 480;		/* 8 minutes */
13621708Sstevel int	drmach_to_testboard	= 43200;	/* 12 hours */
13631708Sstevel int	drmach_to_aborttest	= 180;		/* 3 minutes */
13641708Sstevel int	drmach_to_showboard	= 180;		/* 3 minutes */
13651708Sstevel int	drmach_to_unconfig	= 180;		/* 3 minutes */
13661708Sstevel 
13671708Sstevel /*
13681708Sstevel  * Delay (in seconds) used after receiving a non-transient error indication from
13691708Sstevel  * an mboxsc_getmsg call in the thread that loops waiting for incoming messages.
13701708Sstevel  */
13711708Sstevel int	drmach_mbxerr_delay	= 15;		/* 15 seconds */
13721708Sstevel 
13731708Sstevel /*
13741708Sstevel  * Timeout values (in milliseconds) for mboxsc_putmsg and mboxsc_getmsg calls.
13751708Sstevel  */
13761708Sstevel clock_t	drmach_to_putmsg;			/* set in drmach_mbox_init */
13771708Sstevel clock_t	drmach_to_getmsg	= 31000;	/* 31 seconds */
13781708Sstevel 
13791708Sstevel /*
13801708Sstevel  * Normally, drmach_to_putmsg is set dynamically during initialization in
13811708Sstevel  * drmach_mbox_init.  This has the potentially undesirable side effect of
13821708Sstevel  * clobbering any value that might have been set in /etc/system.  To prevent
13831708Sstevel  * dynamic setting of drmach_to_putmsg (thereby allowing it to be tuned in
13841708Sstevel  * /etc/system), set drmach_use_tuned_putmsg_to to 1.
13851708Sstevel  */
13861708Sstevel int	drmach_use_tuned_putmsg_to	= 0;
13871708Sstevel 
13881708Sstevel 
13891708Sstevel /* maximum conceivable message size for future mailbox protocol versions */
13901708Sstevel #define	DRMACH_MAX_MBOX_MSG_SIZE	4096
13911708Sstevel 
13921708Sstevel /*ARGSUSED*/
13931708Sstevel void
drmach_mbox_prmsg(dr_mbox_msg_t * mbp,int dir)13941708Sstevel drmach_mbox_prmsg(dr_mbox_msg_t *mbp, int dir)
13951708Sstevel {
13961708Sstevel 	int		i, j;
13971708Sstevel 	dr_memregs_t	*memregs;
13981708Sstevel 	dr_proto_hdr_t	*php = &mbp->p_hdr;
13991708Sstevel 	dr_msg_t	*mp = &mbp->msgdata;
14001708Sstevel 
14011708Sstevel #ifdef DEBUG
14021708Sstevel 	switch (php->command) {
14031708Sstevel 		case DRMSG_BOARDEVENT:
14041708Sstevel 			if (dir) {
14051708Sstevel 				DRMACH_PR("ERROR!! outgoing BOARDEVENT\n");
14061708Sstevel 			} else {
14071708Sstevel 				DRMACH_PR("BOARDEVENT received:\n");
14081708Sstevel 				DRMACH_PR("init=%d ins=%d rem=%d asgn=%d\n",
140911066Srafael.vanoni@sun.com 				    mp->dm_be.initialized,
141011066Srafael.vanoni@sun.com 				    mp->dm_be.board_insertion,
141111066Srafael.vanoni@sun.com 				    mp->dm_be.board_removal,
141211066Srafael.vanoni@sun.com 				    mp->dm_be.slot_assign);
14131708Sstevel 				DRMACH_PR("unasgn=%d avail=%d unavail=%d\n",
141411066Srafael.vanoni@sun.com 				    mp->dm_be.slot_unassign,
141511066Srafael.vanoni@sun.com 				    mp->dm_be.slot_avail,
141611066Srafael.vanoni@sun.com 				    mp->dm_be.slot_unavail);
14171708Sstevel 			}
14181708Sstevel 			break;
14191708Sstevel 		case DRMSG_MBOX_INIT:
14201708Sstevel 			if (dir) {
14211708Sstevel 				DRMACH_PR("MBOX_INIT Request:\n");
14221708Sstevel 			} else {
14231708Sstevel 				DRMACH_PR("MBOX_INIT Reply:\n");
14241708Sstevel 			}
14251708Sstevel 			break;
14261708Sstevel 		case DRMSG_ASSIGN:
14271708Sstevel 			if (dir) {
14281708Sstevel 				DRMACH_PR("ASSIGN Request:\n");
14291708Sstevel 			} else {
14301708Sstevel 				DRMACH_PR("ASSIGN Reply:\n");
14311708Sstevel 			}
14321708Sstevel 			break;
14331708Sstevel 		case DRMSG_UNASSIGN:
14341708Sstevel 			if (dir) {
14351708Sstevel 				DRMACH_PR("UNASSIGN Request:\n");
14361708Sstevel 			} else {
14371708Sstevel 				DRMACH_PR("UNASSIGN Reply:\n");
14381708Sstevel 			}
14391708Sstevel 			break;
14401708Sstevel 		case DRMSG_CLAIM:
14411708Sstevel 			if (!dir) {
14421708Sstevel 				DRMACH_PR("CLAIM Reply:\n");
14431708Sstevel 				break;
14441708Sstevel 			}
14451708Sstevel 
14461708Sstevel 			DRMACH_PR("CLAIM Request:\n");
14471708Sstevel 			for (i = 0; i < 18; ++i) {
14481708Sstevel 				DRMACH_PR("exp%d: val=%d slice=0x%x\n", i,
144911066Srafael.vanoni@sun.com 				    mp->dm_cr.mem_slice[i].valid,
145011066Srafael.vanoni@sun.com 				    mp->dm_cr.mem_slice[i].slice);
14511708Sstevel 				memregs = &(mp->dm_cr.mem_regs[i]);
14521708Sstevel 				for (j = 0; j < S0_LPORT_COUNT; j++) {
14531708Sstevel 					DRMACH_PR("  MC %2d: "
145411066Srafael.vanoni@sun.com 					    "MADR[%d] = 0x%lx, "
145511066Srafael.vanoni@sun.com 					    "MADR[%d] = 0x%lx\n", j,
145611066Srafael.vanoni@sun.com 					    0, DRMACH_MCREG_TO_U64(
145711066Srafael.vanoni@sun.com 					    memregs->madr[j][0]),
145811066Srafael.vanoni@sun.com 					    1, DRMACH_MCREG_TO_U64(
145911066Srafael.vanoni@sun.com 					    memregs->madr[j][1]));
14601708Sstevel 					DRMACH_PR("       : "
146111066Srafael.vanoni@sun.com 					    "MADR[%d] = 0x%lx, "
146211066Srafael.vanoni@sun.com 					    "MADR[%d] = 0x%lx\n",
146311066Srafael.vanoni@sun.com 					    2, DRMACH_MCREG_TO_U64(
146411066Srafael.vanoni@sun.com 					    memregs->madr[j][2]),
146511066Srafael.vanoni@sun.com 					    3, DRMACH_MCREG_TO_U64(
146611066Srafael.vanoni@sun.com 					    memregs->madr[j][3]));
14671708Sstevel 				}
14681708Sstevel 			}
14691708Sstevel 			break;
14701708Sstevel 		case DRMSG_UNCLAIM:
14711708Sstevel 			if (!dir) {
14721708Sstevel 				DRMACH_PR("UNCLAIM Reply:\n");
14731708Sstevel 				break;
14741708Sstevel 			}
14751708Sstevel 
14761708Sstevel 			DRMACH_PR("UNCLAIM Request:\n");
14771708Sstevel 			for (i = 0; i < 18; ++i) {
14781708Sstevel 				DRMACH_PR("exp%d: val=%d slice=0x%x\n", i,
147911066Srafael.vanoni@sun.com 				    mp->dm_ur.mem_slice[i].valid,
148011066Srafael.vanoni@sun.com 				    mp->dm_ur.mem_slice[i].slice);
14811708Sstevel 				memregs = &(mp->dm_ur.mem_regs[i]);
14821708Sstevel 				for (j = 0; j < S0_LPORT_COUNT; j++) {
14831708Sstevel 					DRMACH_PR("  MC %2d: "
148411066Srafael.vanoni@sun.com 					    "MADR[%d] = 0x%lx, "
148511066Srafael.vanoni@sun.com 					    "MADR[%d] = 0x%lx\n", j,
148611066Srafael.vanoni@sun.com 					    0, DRMACH_MCREG_TO_U64(
148711066Srafael.vanoni@sun.com 					    memregs->madr[j][0]),
148811066Srafael.vanoni@sun.com 					    1, DRMACH_MCREG_TO_U64(
148911066Srafael.vanoni@sun.com 					    memregs->madr[j][1]));
14901708Sstevel 					DRMACH_PR("       : "
149111066Srafael.vanoni@sun.com 					    "MADR[%d] = 0x%lx, "
149211066Srafael.vanoni@sun.com 					    "MADR[%d] = 0x%lx\n",
149311066Srafael.vanoni@sun.com 					    2, DRMACH_MCREG_TO_U64(
149411066Srafael.vanoni@sun.com 					    memregs->madr[j][2]),
149511066Srafael.vanoni@sun.com 					    3, DRMACH_MCREG_TO_U64(
149611066Srafael.vanoni@sun.com 					    memregs->madr[j][3]));
14971708Sstevel 				}
14981708Sstevel 			}
14991708Sstevel 			DRMACH_PR(" mem_clear=%d\n", mp->dm_ur.mem_clear);
15001708Sstevel 			break;
15011708Sstevel 		case DRMSG_UNCONFIG:
15021708Sstevel 			if (!dir) {
15031708Sstevel 				DRMACH_PR("UNCONFIG Reply:\n");
15041708Sstevel 				break;
15051708Sstevel 			}
15061708Sstevel 
15071708Sstevel 			DRMACH_PR("UNCONFIG Request:\n");
15081708Sstevel 			for (i = 0; i < 18; ++i) {
15091708Sstevel 				DRMACH_PR("exp%d: val=%d slice=0x%x\n", i,
151011066Srafael.vanoni@sun.com 				    mp->dm_uc.mem_slice[i].valid,
151111066Srafael.vanoni@sun.com 				    mp->dm_uc.mem_slice[i].slice);
15121708Sstevel 				memregs = &(mp->dm_uc.mem_regs[i]);
15131708Sstevel 				for (j = 0; j < S0_LPORT_COUNT; j++) {
15141708Sstevel 					DRMACH_PR("  MC %2d: "
151511066Srafael.vanoni@sun.com 					    "MADR[%d] = 0x%lx, "
151611066Srafael.vanoni@sun.com 					    "MADR[%d] = 0x%lx\n", j,
151711066Srafael.vanoni@sun.com 					    0, DRMACH_MCREG_TO_U64(
151811066Srafael.vanoni@sun.com 					    memregs->madr[j][0]),
151911066Srafael.vanoni@sun.com 					    1, DRMACH_MCREG_TO_U64(
152011066Srafael.vanoni@sun.com 					    memregs->madr[j][1]));
15211708Sstevel 					DRMACH_PR("       : "
152211066Srafael.vanoni@sun.com 					    "MADR[%d] = 0x%lx, "
152311066Srafael.vanoni@sun.com 					    "MADR[%d] = 0x%lx\n",
152411066Srafael.vanoni@sun.com 					    2, DRMACH_MCREG_TO_U64(
152511066Srafael.vanoni@sun.com 					    memregs->madr[j][2]),
152611066Srafael.vanoni@sun.com 					    3, DRMACH_MCREG_TO_U64(
152711066Srafael.vanoni@sun.com 					    memregs->madr[j][3]));
15281708Sstevel 				}
15291708Sstevel 			}
15301708Sstevel 			break;
15311708Sstevel 		case DRMSG_POWERON:
15321708Sstevel 			if (dir) {
15331708Sstevel 				DRMACH_PR("POWERON Request:\n");
15341708Sstevel 			} else {
15351708Sstevel 				DRMACH_PR("POWERON Reply:\n");
15361708Sstevel 			}
15371708Sstevel 			break;
15381708Sstevel 		case DRMSG_POWEROFF:
15391708Sstevel 			if (dir) {
15401708Sstevel 				DRMACH_PR("POWEROFF Request:\n");
15411708Sstevel 			} else {
15421708Sstevel 				DRMACH_PR("POWEROFF Reply:\n");
15431708Sstevel 			}
15441708Sstevel 			break;
15451708Sstevel 		case DRMSG_TESTBOARD:
15461708Sstevel 			if (dir) {
15471708Sstevel 				DRMACH_PR("TESTBOARD Request:\n");
15481708Sstevel 				DRMACH_PR("\tmemaddrhi=0x%x memaddrlo=0x%x ",
154911066Srafael.vanoni@sun.com 				    mp->dm_tb.memaddrhi,
155011066Srafael.vanoni@sun.com 				    mp->dm_tb.memaddrlo);
15511708Sstevel 				DRMACH_PR("memlen=0x%x cpu_portid=0x%x\n",
155211066Srafael.vanoni@sun.com 				    mp->dm_tb.memlen, mp->dm_tb.cpu_portid);
15531708Sstevel 				DRMACH_PR("\tforce=0x%x imm=0x%x\n",
155411066Srafael.vanoni@sun.com 				    mp->dm_tb.force, mp->dm_tb.immediate);
15551708Sstevel 			} else {
15561708Sstevel 				DRMACH_PR("TESTBOARD Reply:\n");
15571708Sstevel 				DRMACH_PR("\tmemaddrhi=0x%x memaddrlo=0x%x ",
155811066Srafael.vanoni@sun.com 				    mp->dm_tr.memaddrhi,
155911066Srafael.vanoni@sun.com 				    mp->dm_tr.memaddrlo);
15601708Sstevel 				DRMACH_PR("memlen=0x%x cpu_portid=0x%x\n",
156111066Srafael.vanoni@sun.com 				    mp->dm_tr.memlen, mp->dm_tr.cpu_portid);
15621708Sstevel 				DRMACH_PR("\trecovered=0x%x test status=0x%x\n",
156311066Srafael.vanoni@sun.com 				    mp->dm_tr.cpu_recovered,
156411066Srafael.vanoni@sun.com 				    mp->dm_tr.test_status);
15651708Sstevel 
15661708Sstevel 			}
15671708Sstevel 			break;
15681708Sstevel 		case DRMSG_ABORT_TEST:
15691708Sstevel 			if (dir) {
15701708Sstevel 				DRMACH_PR("ABORT_TEST Request:\n");
15711708Sstevel 			} else {
15721708Sstevel 				DRMACH_PR("ABORT_TEST Reply:\n");
15731708Sstevel 			}
15741708Sstevel 
15751708Sstevel 			DRMACH_PR("\tmemaddrhi=0x%x memaddrlo=0x%x ",
157611066Srafael.vanoni@sun.com 			    mp->dm_ta.memaddrhi,
157711066Srafael.vanoni@sun.com 			    mp->dm_ta.memaddrlo);
15781708Sstevel 			DRMACH_PR("memlen=0x%x cpu_portid=0x%x\n",
157911066Srafael.vanoni@sun.com 			    mp->dm_ta.memlen, mp->dm_ta.cpu_portid);
15801708Sstevel 			break;
15811708Sstevel 		case DRMSG_SHOWBOARD:
15821708Sstevel 			if (dir) {
15831708Sstevel 				DRMACH_PR("SHOWBOARD Request:\n");
15841708Sstevel 			} else {
15851708Sstevel 				DRMACH_PR("SHOWBOARD Reply:\n");
15861708Sstevel 
15871708Sstevel 				DRMACH_PR(": empty=%d power=%d assigned=%d",
158811066Srafael.vanoni@sun.com 				    mp->dm_sb.slot_empty,
158911066Srafael.vanoni@sun.com 				    mp->dm_sb.power_on,
159011066Srafael.vanoni@sun.com 				    mp->dm_sb.bd_assigned);
15911708Sstevel 				DRMACH_PR(": active=%d t_status=%d t_level=%d ",
159211066Srafael.vanoni@sun.com 				    mp->dm_sb.bd_active,
159311066Srafael.vanoni@sun.com 				    mp->dm_sb.test_status,
159411066Srafael.vanoni@sun.com 				    mp->dm_sb.test_level);
15951708Sstevel 				DRMACH_PR(": type=%s ", mp->dm_sb.board_type);
15961708Sstevel 			}
15971708Sstevel 			break;
15981708Sstevel 		default:
15991708Sstevel 			DRMACH_PR("Unknown message type\n");
16001708Sstevel 			break;
16011708Sstevel 	}
16021708Sstevel 
16031708Sstevel 	DRMACH_PR("dr hdr:\n\tid=0x%x vers=0x%x cmd=0x%x exp=0x%x slot=0x%x\n",
160411066Srafael.vanoni@sun.com 	    php->message_id, php->drproto_version, php->command,
160511066Srafael.vanoni@sun.com 	    php->expbrd, php->slot);
16061708Sstevel #endif
16071708Sstevel 	DRMACH_PR("\treply_status=0x%x error_code=0x%x\n", php->reply_status,
160811066Srafael.vanoni@sun.com 	    php->error_code);
16091708Sstevel }
16101708Sstevel 
16111708Sstevel /*
16121708Sstevel  * Callback function passed to taskq_dispatch when a mailbox reinitialization
16131708Sstevel  * handshake needs to be scheduled.  The handshake can't be performed by the
16141708Sstevel  * thread that determines it is needed, in most cases, so this function is
16151708Sstevel  * dispatched on the system-wide taskq pool of threads.  Failure is reported but
16161708Sstevel  * otherwise ignored, since any situation that requires a mailbox initialization
16171708Sstevel  * handshake will continue to request the handshake until it succeeds.
16181708Sstevel  */
16191708Sstevel static void
drmach_mbox_reinit(void * unused)16201708Sstevel drmach_mbox_reinit(void *unused)
16211708Sstevel {
16221708Sstevel 	_NOTE(ARGUNUSED(unused))
16231708Sstevel 
16241708Sstevel 	caddr_t		obufp = NULL;
16251708Sstevel 	sbd_error_t	*serr = NULL;
16261708Sstevel 
16271708Sstevel 	DRMACH_PR("scheduled mailbox reinit running\n");
16281708Sstevel 
16291708Sstevel 	mutex_enter(&drmach_ri_mbox_mutex);
16301708Sstevel 	mutex_enter(&drmach_g_mbox_mutex);
16311708Sstevel 	if (drmach_mbox_iflag == 0) {
16321708Sstevel 		/* need to initialize the mailbox */
16331708Sstevel 		mutex_exit(&drmach_g_mbox_mutex);
16341708Sstevel 
16351708Sstevel 		cmn_err(CE_NOTE, "!reinitializing DR mailbox");
16361708Sstevel 		obufp = kmem_zalloc(sizeof (dr_proto_hdr_t), KM_SLEEP);
16371708Sstevel 		serr = drmach_mbox_trans(DRMSG_MBOX_INIT, 0, obufp,
163811066Srafael.vanoni@sun.com 		    sizeof (dr_proto_hdr_t), (caddr_t)NULL, 0);
16391708Sstevel 		kmem_free(obufp, sizeof (dr_proto_hdr_t));
16401708Sstevel 
16411708Sstevel 		if (serr) {
16421708Sstevel 			cmn_err(CE_WARN,
164311066Srafael.vanoni@sun.com 			    "mbox_init: MBOX_INIT failed ecode=0x%x",
164411066Srafael.vanoni@sun.com 			    serr->e_code);
16451708Sstevel 			sbd_err_clear(&serr);
16461708Sstevel 		}
16471708Sstevel 		mutex_enter(&drmach_g_mbox_mutex);
16481708Sstevel 		if (!serr) {
16491708Sstevel 			drmach_mbox_iflag = 1;
16501708Sstevel 		}
16511708Sstevel 	}
16521708Sstevel 	drmach_mbox_ipending = 0;
16531708Sstevel 	mutex_exit(&drmach_g_mbox_mutex);
16541708Sstevel 	mutex_exit(&drmach_ri_mbox_mutex);
16551708Sstevel }
16561708Sstevel 
16571708Sstevel /*
16581708Sstevel  * To ensure sufficient compatibility with future versions of the DR mailbox
16591708Sstevel  * protocol, we use a buffer that is large enough to receive the largest message
16601708Sstevel  * that could possibly be sent to us.  However, since that ends up being fairly
16611708Sstevel  * large, allocating it on the stack is a bad idea.  Fortunately, this function
16621708Sstevel  * does not need to be MT-safe since it is only invoked by the mailbox
16631708Sstevel  * framework, which will never invoke it multiple times concurrently.  Since
16641708Sstevel  * that is the case, we can use a static buffer.
16651708Sstevel  */
16661708Sstevel void
drmach_mbox_event(void)16671708Sstevel drmach_mbox_event(void)
16681708Sstevel {
16691708Sstevel 	static uint8_t	buf[DRMACH_MAX_MBOX_MSG_SIZE];
16701708Sstevel 	dr_mbox_msg_t	*msg = (dr_mbox_msg_t *)buf;
16711708Sstevel 	int		err;
16721708Sstevel 	uint32_t	type = MBOXSC_MSG_EVENT;
16731708Sstevel 	uint32_t	command = DRMSG_BOARDEVENT;
16741708Sstevel 	uint64_t	transid = 0;
16751708Sstevel 	uint32_t	length = DRMACH_MAX_MBOX_MSG_SIZE;
16761708Sstevel 	char		*hint = "";
16771708Sstevel 	int		logsys = 0;
16781708Sstevel 
16791708Sstevel 	do {
168011066Srafael.vanoni@sun.com 		err = mboxsc_getmsg(KEY_SCDR, &type, &command, &transid,
168111066Srafael.vanoni@sun.com 		    &length, (void *)msg, 0);
16821708Sstevel 	} while (err == EAGAIN);
16831708Sstevel 
16841708Sstevel 	/* don't try to interpret anything with the wrong version number */
16851708Sstevel 	if ((err == 0) && (msg->p_hdr.drproto_version != DRMBX_VERSION)) {
16861708Sstevel 		cmn_err(CE_WARN, "mailbox version mismatch 0x%x vs 0x%x",
168711066Srafael.vanoni@sun.com 		    msg->p_hdr.drproto_version, DRMBX_VERSION);
16881708Sstevel 		mutex_enter(&drmach_g_mbox_mutex);
16891708Sstevel 		drmach_mbox_iflag = 0;
16901708Sstevel 		/* schedule a reinit handshake if one isn't pending */
16911708Sstevel 		if (!drmach_mbox_ipending) {
16921708Sstevel 			if (taskq_dispatch(system_taskq, drmach_mbox_reinit,
169311066Srafael.vanoni@sun.com 			    NULL, TQ_NOSLEEP) != NULL) {
16941708Sstevel 				drmach_mbox_ipending = 1;
16951708Sstevel 			} else {
16961708Sstevel 				cmn_err(CE_WARN,
169711066Srafael.vanoni@sun.com 				    "failed to schedule mailbox reinit");
16981708Sstevel 			}
16991708Sstevel 		}
17001708Sstevel 		mutex_exit(&drmach_g_mbox_mutex);
17011708Sstevel 		return;
17021708Sstevel 	}
17031708Sstevel 
17041708Sstevel 	if ((err != 0) || (msg->p_hdr.reply_status != DRMSG_REPLY_OK)) {
17051708Sstevel 		cmn_err(CE_WARN,
170611066Srafael.vanoni@sun.com 		    "Unsolicited mboxsc_getmsg failed: err=0x%x code=0x%x",
170711066Srafael.vanoni@sun.com 		    err, msg->p_hdr.error_code);
17081708Sstevel 	} else {
17091708Sstevel 		dr_boardevent_t	*be;
17101708Sstevel 		be = (dr_boardevent_t *)&msg->msgdata;
17111708Sstevel 
17121708Sstevel 		/* check for initialization event */
17131708Sstevel 		if (be->initialized) {
17141708Sstevel 			mutex_enter(&drmach_g_mbox_mutex);
17151708Sstevel 			drmach_mbox_iflag = 0;
17161708Sstevel 			/* schedule a reinit handshake if one isn't pending */
17171708Sstevel 			if (!drmach_mbox_ipending) {
17181708Sstevel 				if (taskq_dispatch(system_taskq,
171911066Srafael.vanoni@sun.com 				    drmach_mbox_reinit, NULL, TQ_NOSLEEP)
172011066Srafael.vanoni@sun.com 				    != NULL) {
17211708Sstevel 					drmach_mbox_ipending = 1;
17221708Sstevel 				} else {
172311066Srafael.vanoni@sun.com 					cmn_err(CE_WARN, "failed to schedule "
172411066Srafael.vanoni@sun.com 					    "mailbox reinit");
17251708Sstevel 				}
17261708Sstevel 			}
17271708Sstevel 			mutex_exit(&drmach_g_mbox_mutex);
17281708Sstevel 			cmn_err(CE_NOTE, "!Mailbox Init event received");
17291708Sstevel 		}
17301708Sstevel 
17311708Sstevel 		/* anything else will be a log_sysevent call */
17321708Sstevel 
17331708Sstevel 		if (be->board_insertion) {
17341708Sstevel 			DRMACH_PR("Board Insertion event received");
17351708Sstevel 			hint = DR_HINT_INSERT;
17361708Sstevel 			logsys++;
17371708Sstevel 	}
17381708Sstevel 		if (be->board_removal) {
17391708Sstevel 			DRMACH_PR("Board Removal event received");
17401708Sstevel 			hint = DR_HINT_REMOVE;
17411708Sstevel 			logsys++;
17421708Sstevel 		}
17431708Sstevel 		if (be->slot_assign) {
17441708Sstevel 			DRMACH_PR("Slot Assign event received");
17451708Sstevel 			logsys++;
17461708Sstevel 		}
17471708Sstevel 		if (be->slot_unassign) {
17481708Sstevel 			DRMACH_PR("Slot Unassign event received");
17491708Sstevel 			logsys++;
17501708Sstevel 		}
17511708Sstevel 		if (be->slot_avail) {
17521708Sstevel 			DRMACH_PR("Slot Available event received");
17531708Sstevel 			logsys++;
17541708Sstevel 		}
17551708Sstevel 		if (be->slot_unavail) {
17561708Sstevel 			DRMACH_PR("Slot Unavailable event received");
17571708Sstevel 			logsys++;
17581708Sstevel 		}
17591708Sstevel 		if (be->power_on) {
17601708Sstevel 			DRMACH_PR("Power ON event received");
17611708Sstevel 			logsys++;
17621708Sstevel 		}
17631708Sstevel 		if (be->power_off) {
17641708Sstevel 			DRMACH_PR("Power OFF event received");
17651708Sstevel 			logsys++;
17661708Sstevel 		}
17671708Sstevel 
17681708Sstevel 		if (logsys)
176911311SSurya.Prakki@Sun.COM 			(void) drmach_log_sysevent(
177011066Srafael.vanoni@sun.com 			    DRMACH_EXPSLOT2BNUM(msg->p_hdr.expbrd,
177111066Srafael.vanoni@sun.com 			    msg->p_hdr.slot), hint, SE_NOSLEEP, 1);
17721708Sstevel 	}
17731708Sstevel }
17741708Sstevel 
17751708Sstevel static uint32_t
drmach_get_msgid()17761708Sstevel drmach_get_msgid()
17771708Sstevel {
17781708Sstevel 	uint32_t	rv;
17791708Sstevel 	mutex_enter(&drmach_msglist_mutex);
17801708Sstevel 	if (!(++drmach_msgid))
17811708Sstevel 		++drmach_msgid;
17821708Sstevel 	rv = drmach_msgid;
17831708Sstevel 	mutex_exit(&drmach_msglist_mutex);
17841708Sstevel 	return (rv);
17851708Sstevel }
17861708Sstevel 
17871708Sstevel /*
17881708Sstevel  *	unlink an entry from the message transaction list
17891708Sstevel  *
17901708Sstevel  *	caller must hold drmach_msglist_mutex
17911708Sstevel  */
17921708Sstevel void
drmach_msglist_unlink(drmach_msglist_t * entry)17931708Sstevel drmach_msglist_unlink(drmach_msglist_t *entry)
17941708Sstevel {
17951708Sstevel 	ASSERT(mutex_owned(&drmach_msglist_mutex));
17961708Sstevel 	if (entry->prev) {
17971708Sstevel 		entry->prev->next = entry->next;
17981708Sstevel 		if (entry->next)
17991708Sstevel 			entry->next->prev = entry->prev;
18001708Sstevel 	} else {
18011708Sstevel 		drmach_msglist_first = entry->next;
18021708Sstevel 		if (entry->next)
18031708Sstevel 			entry->next->prev = NULL;
18041708Sstevel 	}
18051708Sstevel 	if (entry == drmach_msglist_last) {
18061708Sstevel 		drmach_msglist_last = entry->prev;
18071708Sstevel 	}
18081708Sstevel }
18091708Sstevel 
18101708Sstevel void
drmach_msglist_link(drmach_msglist_t * entry)18111708Sstevel drmach_msglist_link(drmach_msglist_t *entry)
18121708Sstevel {
18131708Sstevel 	mutex_enter(&drmach_msglist_mutex);
18141708Sstevel 	if (drmach_msglist_last) {
18151708Sstevel 		entry->prev = drmach_msglist_last;
18161708Sstevel 		drmach_msglist_last->next = entry;
18171708Sstevel 		drmach_msglist_last = entry;
18181708Sstevel 	} else {
18191708Sstevel 		drmach_msglist_last = drmach_msglist_first = entry;
18201708Sstevel 	}
18211708Sstevel 	mutex_exit(&drmach_msglist_mutex);
18221708Sstevel }
18231708Sstevel 
18241708Sstevel void
drmach_mbox_getmsg()18251708Sstevel drmach_mbox_getmsg()
18261708Sstevel {
18271708Sstevel 	int			err;
18281708Sstevel 	register int		msgid;
18291708Sstevel 	static uint8_t		buf[DRMACH_MAX_MBOX_MSG_SIZE];
18301708Sstevel 	dr_mbox_msg_t		*msg = (dr_mbox_msg_t *)buf;
18311708Sstevel 	dr_proto_hdr_t		*php;
18321708Sstevel 	drmach_msglist_t	*found, *entry;
18331708Sstevel 	uint32_t		type = MBOXSC_MSG_REPLY;
18341708Sstevel 	uint32_t		command;
18351708Sstevel 	uint64_t		transid;
18361708Sstevel 	uint32_t		length;
18371708Sstevel 
18381708Sstevel 	php = &msg->p_hdr;
18391708Sstevel 
18401708Sstevel 	while (drmach_getmsg_thread_run != 0) {
18411708Sstevel 		/* get a reply message */
18421708Sstevel 		command = 0;
18431708Sstevel 		transid = 0;
18441708Sstevel 		length = DRMACH_MAX_MBOX_MSG_SIZE;
184511066Srafael.vanoni@sun.com 		err = mboxsc_getmsg(KEY_SCDR, &type, &command, &transid,
184611066Srafael.vanoni@sun.com 		    &length, (void *)msg, drmach_to_getmsg);
18471708Sstevel 
18481708Sstevel 		if (err) {
18491708Sstevel 			/*
18501708Sstevel 			 * If mboxsc_getmsg returns ETIMEDOUT or EAGAIN, then
18511708Sstevel 			 * the "error" is really just a normal, transient
18521708Sstevel 			 * condition and we can retry the operation right away.
18531708Sstevel 			 * Any other error suggests a more serious problem,
18541708Sstevel 			 * ranging from a message being too big for our buffer
18551708Sstevel 			 * (EMSGSIZE) to total failure of the mailbox layer.
18561708Sstevel 			 * This second class of errors is much less "transient",
18571708Sstevel 			 * so rather than retrying over and over (and getting
18581708Sstevel 			 * the same error over and over) as fast as we can,
18591708Sstevel 			 * we'll sleep for a while before retrying.
18601708Sstevel 			 */
18611708Sstevel 			if ((err != ETIMEDOUT) && (err != EAGAIN)) {
18621708Sstevel 				cmn_err(CE_WARN,
186311066Srafael.vanoni@sun.com 				    "mboxsc_getmsg failed, err=0x%x", err);
18641708Sstevel 				delay(drmach_mbxerr_delay * hz);
18651708Sstevel 			}
18661708Sstevel 			continue;
18671708Sstevel 		}
18681708Sstevel 
18691708Sstevel 		drmach_mbox_prmsg(msg, 0);
18701708Sstevel 
18711708Sstevel 		if (php->drproto_version != DRMBX_VERSION) {
18721708Sstevel 			cmn_err(CE_WARN,
187311066Srafael.vanoni@sun.com 			    "mailbox version mismatch 0x%x vs 0x%x",
187411066Srafael.vanoni@sun.com 			    php->drproto_version, DRMBX_VERSION);
18751708Sstevel 
18761708Sstevel 			mutex_enter(&drmach_g_mbox_mutex);
18771708Sstevel 			drmach_mbox_iflag = 0;
18781708Sstevel 			/* schedule a reinit handshake if one isn't pending */
18791708Sstevel 			if (!drmach_mbox_ipending) {
18801708Sstevel 				if (taskq_dispatch(system_taskq,
188111066Srafael.vanoni@sun.com 				    drmach_mbox_reinit, NULL, TQ_NOSLEEP)
188211066Srafael.vanoni@sun.com 				    != NULL) {
18831708Sstevel 					drmach_mbox_ipending = 1;
18841708Sstevel 				} else {
188511066Srafael.vanoni@sun.com 					cmn_err(CE_WARN, "failed to schedule "
188611066Srafael.vanoni@sun.com 					    "mailbox reinit");
18871708Sstevel 				}
18881708Sstevel 			}
18891708Sstevel 			mutex_exit(&drmach_g_mbox_mutex);
18901708Sstevel 
18911708Sstevel 			continue;
18921708Sstevel 		}
18931708Sstevel 
18941708Sstevel 		msgid = php->message_id;
18951708Sstevel 		found = NULL;
18961708Sstevel 		mutex_enter(&drmach_msglist_mutex);
18971708Sstevel 		entry = drmach_msglist_first;
18981708Sstevel 		while (entry != NULL) {
18991708Sstevel 			if (entry->msgid == msgid) {
19001708Sstevel 				found = entry;
19011708Sstevel 				drmach_msglist_unlink(entry);
19021708Sstevel 				entry = NULL;
19031708Sstevel 			} else
19041708Sstevel 				entry = entry->next;
19051708Sstevel 		}
19061708Sstevel 
19071708Sstevel 		if (found) {
19081708Sstevel 			mutex_enter(&found->g_lock);
19091708Sstevel 
19101708Sstevel 			found->e_code = php->error_code;
19111708Sstevel 			if (found->i_buflen > 0)
19121708Sstevel 				bcopy((caddr_t)&msg->msgdata, found->i_buf,
191311066Srafael.vanoni@sun.com 				    found->i_buflen);
19141708Sstevel 			found->m_reply = 1;
19151708Sstevel 
19161708Sstevel 			cv_signal(&found->g_cv);
19171708Sstevel 			mutex_exit(&found->g_lock);
19181708Sstevel 		} else {
19191708Sstevel 			cmn_err(CE_WARN, "!mbox_getmsg: no match for id 0x%x",
19201708Sstevel 			    msgid);
19211708Sstevel 			cmn_err(CE_WARN, "!    cmd = 0x%x, exb = %d, slot = %d",
19221708Sstevel 			    php->command, php->expbrd, php->slot);
19231708Sstevel 		}
19241708Sstevel 
19251708Sstevel 		mutex_exit(&drmach_msglist_mutex);
19261708Sstevel 	}
19271708Sstevel 	cmn_err(CE_WARN, "mbox_getmsg: exiting");
19281708Sstevel 	mutex_enter(&drmach_msglist_mutex);
19291708Sstevel 	entry = drmach_msglist_first;
19301708Sstevel 	while (entry != NULL) {
19311708Sstevel 		if (entry->p_flag == 1) {
19321708Sstevel 			entry->f_error = -1;
19331708Sstevel 			mutex_enter(&entry->g_lock);
19341708Sstevel 			cv_signal(&entry->g_cv);
19351708Sstevel 			mutex_exit(&entry->g_lock);
19361708Sstevel 			drmach_msglist_unlink(entry);
19371708Sstevel 		}
19381708Sstevel 		entry = entry->next;
19391708Sstevel 	}
19401708Sstevel 	mutex_exit(&drmach_msglist_mutex);
19411708Sstevel 	drmach_getmsg_thread_run = -1;
19421708Sstevel 	thread_exit();
19431708Sstevel }
19441708Sstevel 
19451708Sstevel void
drmach_mbox_sendmsg()19461708Sstevel drmach_mbox_sendmsg()
19471708Sstevel {
19481708Sstevel 	int		err, retry;
19491708Sstevel 	drmach_msglist_t *entry;
19501708Sstevel 	dr_mbox_msg_t   *mp;
19511708Sstevel 	dr_proto_hdr_t  *php;
19521708Sstevel 
19531708Sstevel 	while (drmach_sendmsg_thread_run != 0) {
19541708Sstevel 		/*
19551708Sstevel 		 * Search through the list to find entries awaiting
19561708Sstevel 		 * transmission to the SC
19571708Sstevel 		 */
19581708Sstevel 		mutex_enter(&drmach_msglist_mutex);
19591708Sstevel 		entry = drmach_msglist_first;
19601708Sstevel 		retry = 0;
19611708Sstevel 		while (entry != NULL) {
19621708Sstevel 			if (entry->p_flag == 1) {
19631708Sstevel 				entry = entry->next;
19641708Sstevel 				continue;
19651708Sstevel 			}
19661708Sstevel 
19671708Sstevel 			mutex_exit(&drmach_msglist_mutex);
19681708Sstevel 
19691708Sstevel 			if (!retry)
19701708Sstevel 				mutex_enter(&entry->s_lock);
19711708Sstevel 			mp = (dr_mbox_msg_t *)entry->o_buf;
19721708Sstevel 			php = &mp->p_hdr;
19731708Sstevel 
19741708Sstevel 			drmach_mbox_prmsg(mp, 1);
19751708Sstevel 
19761708Sstevel 			err = mboxsc_putmsg(KEY_DRSC, MBOXSC_MSG_REQUEST,
197711066Srafael.vanoni@sun.com 			    php->command, NULL, entry->o_buflen, (void *)mp,
197811066Srafael.vanoni@sun.com 			    drmach_to_putmsg);
19791708Sstevel 
19801708Sstevel 			if (err) {
19811708Sstevel 				switch (err) {
19821708Sstevel 
19831708Sstevel 				case EAGAIN:
19841708Sstevel 				case EBUSY:
19851708Sstevel 					++retry;
19861708Sstevel 					mutex_enter(&drmach_msglist_mutex);
19871708Sstevel 					continue;
19881708Sstevel 
19891708Sstevel 				case ETIMEDOUT:
19901708Sstevel 					if (--entry->o_nretry <= 0) {
19911708Sstevel 						mutex_enter(
199211066Srafael.vanoni@sun.com 						    &drmach_msglist_mutex);
19931708Sstevel 						drmach_msglist_unlink(entry);
19941708Sstevel 						mutex_exit(
199511066Srafael.vanoni@sun.com 						    &drmach_msglist_mutex);
19961708Sstevel 						entry->f_error = err;
19971708Sstevel 						entry->p_flag = 1;
19981708Sstevel 						cv_signal(&entry->s_cv);
19991708Sstevel 					} else {
20001708Sstevel 						++retry;
20011708Sstevel 						mutex_enter(
200211066Srafael.vanoni@sun.com 						    &drmach_msglist_mutex);
20031708Sstevel 						continue;
20041708Sstevel 					}
20051708Sstevel 					break;
20061708Sstevel 				default:
20071708Sstevel 					mutex_enter(&drmach_msglist_mutex);
20081708Sstevel 					drmach_msglist_unlink(entry);
20091708Sstevel 					mutex_exit(&drmach_msglist_mutex);
20101708Sstevel 					entry->f_error = err;
20111708Sstevel 					entry->p_flag = 1;
20121708Sstevel 					cv_signal(&entry->s_cv);
20131708Sstevel 					break;
20141708Sstevel 				}
20151708Sstevel 			} else {
20161708Sstevel 				entry->p_flag = 1;
20171708Sstevel 				cv_signal(&entry->s_cv);
20181708Sstevel 			}
20191708Sstevel 
20201708Sstevel 			mutex_exit(&entry->s_lock);
20211708Sstevel 			retry = 0;
20221708Sstevel 			mutex_enter(&drmach_msglist_mutex);
20231708Sstevel 			entry = drmach_msglist_first;
20241708Sstevel 		}
20251708Sstevel 		mutex_exit(&drmach_msglist_mutex);
20261708Sstevel 
20271708Sstevel 		mutex_enter(&drmach_sendmsg_mutex);
202811066Srafael.vanoni@sun.com 		(void) cv_reltimedwait(&drmach_sendmsg_cv,
202911066Srafael.vanoni@sun.com 		    &drmach_sendmsg_mutex, (5 * hz), TR_CLOCK_TICK);
20301708Sstevel 		mutex_exit(&drmach_sendmsg_mutex);
20311708Sstevel 	}
20321708Sstevel 	cmn_err(CE_WARN, "mbox_sendmsg: exiting");
20331708Sstevel 	mutex_enter(&drmach_msglist_mutex);
20341708Sstevel 	entry = drmach_msglist_first;
20351708Sstevel 	while (entry != NULL) {
20361708Sstevel 		if (entry->p_flag == 0) {
20371708Sstevel 			entry->f_error = -1;
20381708Sstevel 			mutex_enter(&entry->s_lock);
20391708Sstevel 			cv_signal(&entry->s_cv);
20401708Sstevel 			mutex_exit(&entry->s_lock);
20411708Sstevel 			drmach_msglist_unlink(entry);
20421708Sstevel 		}
20431708Sstevel 		entry = entry->next;
20441708Sstevel 	}
20451708Sstevel 	mutex_exit(&drmach_msglist_mutex);
20461708Sstevel 	cv_destroy(&drmach_sendmsg_cv);
20471708Sstevel 	mutex_destroy(&drmach_sendmsg_mutex);
20481708Sstevel 
20491708Sstevel 	drmach_sendmsg_thread_run = -1;
20501708Sstevel 	thread_exit();
20511708Sstevel }
20521708Sstevel 
20531708Sstevel void
drmach_msglist_destroy(drmach_msglist_t * listp)20541708Sstevel drmach_msglist_destroy(drmach_msglist_t *listp)
20551708Sstevel {
20561708Sstevel 	if (listp != NULL) {
20571708Sstevel 		drmach_msglist_t	*entry;
20581708Sstevel 
20591708Sstevel 		mutex_enter(&drmach_msglist_mutex);
20601708Sstevel 		entry = drmach_msglist_first;
20611708Sstevel 		while (entry) {
20621708Sstevel 			if (listp == entry) {
20631708Sstevel 				drmach_msglist_unlink(listp);
20641708Sstevel 				entry = NULL;
20651708Sstevel 			} else
20661708Sstevel 				entry = entry->next;
20671708Sstevel 		}
20681708Sstevel 
20691708Sstevel 		mutex_destroy(&listp->s_lock);
20701708Sstevel 		cv_destroy(&listp->s_cv);
20711708Sstevel 		mutex_destroy(&listp->g_lock);
20721708Sstevel 		cv_destroy(&listp->g_cv);
20731708Sstevel 		kmem_free(listp, sizeof (drmach_msglist_t));
20741708Sstevel 
20751708Sstevel 		mutex_exit(&drmach_msglist_mutex);
20761708Sstevel 	}
20771708Sstevel }
20781708Sstevel 
20791708Sstevel static drmach_msglist_t	*
drmach_msglist_new(caddr_t ibufp,uint32_t ilen,dr_proto_hdr_t * hdrp,uint32_t olen,int nrtry)20801708Sstevel drmach_msglist_new(caddr_t ibufp, uint32_t ilen, dr_proto_hdr_t *hdrp,
20811708Sstevel 	uint32_t olen, int nrtry)
20821708Sstevel {
20831708Sstevel 	drmach_msglist_t	*listp;
20841708Sstevel 
20851708Sstevel 	listp = kmem_zalloc(sizeof (drmach_msglist_t), KM_SLEEP);
20861708Sstevel 	mutex_init(&listp->s_lock, NULL, MUTEX_DRIVER, NULL);
20871708Sstevel 	cv_init(&listp->s_cv, NULL, CV_DRIVER, NULL);
20881708Sstevel 	mutex_init(&listp->g_lock, NULL, MUTEX_DRIVER, NULL);
20891708Sstevel 	cv_init(&listp->g_cv, NULL, CV_DRIVER, NULL);
20901708Sstevel 	listp->o_buf = (caddr_t)hdrp;
20911708Sstevel 	listp->o_buflen = olen;
20921708Sstevel 	listp->i_buf = ibufp;
20931708Sstevel 	listp->i_buflen = ilen;
20941708Sstevel 	listp->o_nretry = nrtry;
20951708Sstevel 	listp->msgid = hdrp->message_id;
20961708Sstevel 
20971708Sstevel 	return (listp);
20981708Sstevel }
20991708Sstevel 
21001708Sstevel 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)21011708Sstevel drmach_mbox_req_rply(dr_proto_hdr_t *hdrp, uint32_t olen, caddr_t ibufp,
21021708Sstevel 	uint32_t ilen, int timeout, int nrtry, int nosig,
21031708Sstevel 	drmach_msglist_t *link)
21041708Sstevel {
21051708Sstevel 	int		crv;
21061708Sstevel 	drmach_msglist_t *listp;
21071708Sstevel 	clock_t		to_val;
21081708Sstevel 	dr_proto_hdr_t	*php;
21091708Sstevel 
21101708Sstevel 	/* setup transaction list entry */
21111708Sstevel 	listp = drmach_msglist_new(ibufp, ilen, hdrp, olen, nrtry);
21121708Sstevel 
21131708Sstevel 	/* send mailbox message, await reply */
21141708Sstevel 	mutex_enter(&listp->s_lock);
21151708Sstevel 	mutex_enter(&listp->g_lock);
21161708Sstevel 
21171708Sstevel 	listp->link = link;
21181708Sstevel 	drmach_msglist_link(listp);
21191708Sstevel 
21201708Sstevel 	mutex_enter(&drmach_sendmsg_mutex);
21211708Sstevel 	cv_signal(&drmach_sendmsg_cv);
21221708Sstevel 	mutex_exit(&drmach_sendmsg_mutex);
21231708Sstevel 
21241708Sstevel 	while (listp->p_flag == 0) {
21251708Sstevel 		cv_wait(&listp->s_cv, &listp->s_lock);
21261708Sstevel 	}
21271708Sstevel 
212811066Srafael.vanoni@sun.com 	to_val = ddi_get_lbolt() + (timeout * hz);
21291708Sstevel 
21301708Sstevel 	if (listp->f_error) {
21311708Sstevel 		listp->p_flag = 0;
213211066Srafael.vanoni@sun.com 		cmn_err(CE_WARN, "!mboxsc_putmsg failed: 0x%x", listp->f_error);
21331708Sstevel 		php = (dr_proto_hdr_t *)listp->o_buf;
21341708Sstevel 		cmn_err(CE_WARN, "!    cmd = 0x%x, exb = %d, slot = %d",
21351708Sstevel 		    php->command, php->expbrd, php->slot);
21361708Sstevel 	} else {
21371708Sstevel 		while (listp->m_reply == 0 && listp->f_error == 0) {
21381708Sstevel 			if (nosig)
21391708Sstevel 				crv = cv_timedwait(&listp->g_cv, &listp->g_lock,
214011066Srafael.vanoni@sun.com 				    to_val);
21411708Sstevel 			else
21421708Sstevel 				crv = cv_timedwait_sig(&listp->g_cv,
214311066Srafael.vanoni@sun.com 				    &listp->g_lock, to_val);
21441708Sstevel 			switch (crv) {
21451708Sstevel 				case -1: /* timed out */
21461708Sstevel 					cmn_err(CE_WARN,
21471708Sstevel 					    "!msgid=0x%x reply timed out",
21481708Sstevel 					    hdrp->message_id);
21491708Sstevel 					php = (dr_proto_hdr_t *)listp->o_buf;
21501708Sstevel 					cmn_err(CE_WARN, "!    cmd = 0x%x, "
21511708Sstevel 					    "exb = %d, slot = %d", php->command,
21521708Sstevel 					    php->expbrd, php->slot);
21531708Sstevel 					listp->f_error = ETIMEDOUT;
21541708Sstevel 					break;
21551708Sstevel 				case 0: /* signal received */
21561708Sstevel 					cmn_err(CE_WARN,
21571708Sstevel 					    "operation interrupted by signal");
21581708Sstevel 					listp->f_error = EINTR;
21591708Sstevel 					break;
21601708Sstevel 				default:
21611708Sstevel 					break;
21621708Sstevel 				}
21631708Sstevel 		}
21641708Sstevel 
21651708Sstevel 		/*
21661708Sstevel 		 * If link is set for this entry, check to see if
21671708Sstevel 		 * the linked entry has been replied to.  If not,
21681708Sstevel 		 * wait for the response.
21691708Sstevel 		 * Currently, this is only used for ABORT_TEST functionality,
21701708Sstevel 		 * wherein a check is made for the TESTBOARD reply when
21711708Sstevel 		 * the ABORT_TEST reply is received.
21721708Sstevel 		 */
21731708Sstevel 
21741708Sstevel 		if (link) {
21751708Sstevel 			mutex_enter(&link->g_lock);
21761708Sstevel 			/*
21771708Sstevel 			 * If the reply to the linked entry hasn't been
21781708Sstevel 			 * received, clear the existing link->f_error,
21791708Sstevel 			 * and await the reply.
21801708Sstevel 			 */
21811708Sstevel 			if (link->m_reply == 0) {
21821708Sstevel 				link->f_error = 0;
21831708Sstevel 			}
21841708Sstevel 			to_val =  ddi_get_lbolt() + (timeout * hz);
21851708Sstevel 			while (link->m_reply == 0 && link->f_error == 0) {
21861708Sstevel 				crv = cv_timedwait(&link->g_cv, &link->g_lock,
218711066Srafael.vanoni@sun.com 				    to_val);
21881708Sstevel 				switch (crv) {
21891708Sstevel 				case -1: /* timed out */
21901708Sstevel 					cmn_err(CE_NOTE,
21911708Sstevel 					    "!link msgid=0x%x reply timed out",
21921708Sstevel 					    link->msgid);
21931708Sstevel 					link->f_error = ETIMEDOUT;
21941708Sstevel 					break;
21951708Sstevel 				default:
21961708Sstevel 					break;
21971708Sstevel 				}
21981708Sstevel 			}
21991708Sstevel 			mutex_exit(&link->g_lock);
22001708Sstevel 		}
22011708Sstevel 	}
22021708Sstevel 	mutex_exit(&listp->g_lock);
22031708Sstevel 	mutex_exit(&listp->s_lock);
22041708Sstevel 	return (listp);
22051708Sstevel }
22061708Sstevel 
22071708Sstevel static sbd_error_t *
drmach_mbx2sbderr(drmach_msglist_t * mlp)22081708Sstevel drmach_mbx2sbderr(drmach_msglist_t *mlp)
22091708Sstevel {
22101708Sstevel 	char		a_pnt[MAXNAMELEN];
22111708Sstevel 	dr_proto_hdr_t	*php;
22121708Sstevel 	int		bnum;
22131708Sstevel 
22141708Sstevel 	if (mlp->f_error) {
22151708Sstevel 		/*
22161708Sstevel 		 * If framework failure is due to signal, return "no error"
22171708Sstevel 		 * error.
22181708Sstevel 		 */
22191708Sstevel 		if (mlp->f_error == EINTR)
22201708Sstevel 			return (drerr_new(0, ESTC_NONE, NULL));
22211708Sstevel 
22221708Sstevel 		mutex_enter(&drmach_g_mbox_mutex);
22231708Sstevel 		drmach_mbox_iflag = 0;
22241708Sstevel 		mutex_exit(&drmach_g_mbox_mutex);
22251708Sstevel 		if (!mlp->p_flag)
22261708Sstevel 			return (drerr_new(1, ESTC_MBXRQST, NULL));
22271708Sstevel 		else
22281708Sstevel 			return (drerr_new(1, ESTC_MBXRPLY, NULL));
22291708Sstevel 	}
22301708Sstevel 	php = (dr_proto_hdr_t *)mlp->o_buf;
22311708Sstevel 	bnum = 2 * php->expbrd + php->slot;
22321708Sstevel 	a_pnt[0] = '\0';
22331708Sstevel 	(void) drmach_board_name(bnum, a_pnt, MAXNAMELEN);
22341708Sstevel 
22351708Sstevel 	switch (mlp->e_code) {
22361708Sstevel 		case 0:
22371708Sstevel 			return (NULL);
22381708Sstevel 		case DRERR_NOACL:
22391708Sstevel 			return (drerr_new(0, ESTC_NOACL, "%s", a_pnt));
22401708Sstevel 		case DRERR_NOT_ASSIGNED:
22411708Sstevel 			return (drerr_new(0, ESTC_NOT_ASSIGNED, "%s", a_pnt));
22421708Sstevel 		case DRERR_NOT_ACTIVE:
22431708Sstevel 			return (drerr_new(0, ESTC_NOT_ACTIVE, "%s", a_pnt));
22441708Sstevel 		case DRERR_EMPTY_SLOT:
22451708Sstevel 			return (drerr_new(0, ESTC_EMPTY_SLOT, "%s", a_pnt));
22461708Sstevel 		case DRERR_POWER_OFF:
22471708Sstevel 			return (drerr_new(0, ESTC_POWER_OFF, "%s", a_pnt));
22481708Sstevel 		case DRERR_TEST_IN_PROGRESS:
224911066Srafael.vanoni@sun.com 			return (drerr_new(0, ESTC_TEST_IN_PROGRESS, "%s",
225011066Srafael.vanoni@sun.com 			    a_pnt));
22511708Sstevel 		case DRERR_TESTING_BUSY:
22521708Sstevel 			return (drerr_new(0, ESTC_TESTING_BUSY, "%s", a_pnt));
22531708Sstevel 		case DRERR_TEST_REQUIRED:
22541708Sstevel 			return (drerr_new(0, ESTC_TEST_REQUIRED, "%s", a_pnt));
22551708Sstevel 		case DRERR_UNAVAILABLE:
22561708Sstevel 			return (drerr_new(0, ESTC_UNAVAILABLE, "%s", a_pnt));
22571708Sstevel 		case DRERR_RECOVERABLE:
225811066Srafael.vanoni@sun.com 			return (drerr_new(0, ESTC_SMS_ERR_RECOVERABLE, "%s",
225911066Srafael.vanoni@sun.com 			    a_pnt));
22601708Sstevel 		case DRERR_UNRECOVERABLE:
226111066Srafael.vanoni@sun.com 			return (drerr_new(1, ESTC_SMS_ERR_UNRECOVERABLE, "%s",
226211066Srafael.vanoni@sun.com 			    a_pnt));
22631708Sstevel 		default:
22641708Sstevel 			return (drerr_new(1, ESTC_MBOX_UNKNOWN, NULL));
22651708Sstevel 	}
22661708Sstevel }
22671708Sstevel 
22681708Sstevel static sbd_error_t *
drmach_mbox_trans(uint8_t msgtype,int bnum,caddr_t obufp,int olen,caddr_t ibufp,int ilen)22691708Sstevel drmach_mbox_trans(uint8_t msgtype, int bnum, caddr_t obufp, int olen,
22701708Sstevel 	caddr_t ibufp, int ilen)
22711708Sstevel {
22721708Sstevel 	int			timeout = 0;
22731708Sstevel 	int			ntries = 0;
22741708Sstevel 	int			nosignals = 0;
22751708Sstevel 	dr_proto_hdr_t 		*hdrp;
22761708Sstevel 	drmach_msglist_t 	*mlp;
22771708Sstevel 	sbd_error_t		*err = NULL;
22781708Sstevel 
22791708Sstevel 	if (msgtype != DRMSG_MBOX_INIT) {
22801708Sstevel 		mutex_enter(&drmach_ri_mbox_mutex);
22811708Sstevel 		mutex_enter(&drmach_g_mbox_mutex);
22821708Sstevel 		if (drmach_mbox_iflag == 0) {
22831708Sstevel 			/* need to initialize the mailbox */
22841708Sstevel 			dr_proto_hdr_t	imsg;
22851708Sstevel 
22861708Sstevel 			mutex_exit(&drmach_g_mbox_mutex);
22871708Sstevel 
22881708Sstevel 			imsg.command = DRMSG_MBOX_INIT;
22891708Sstevel 
22901708Sstevel 			imsg.message_id = drmach_get_msgid();
22911708Sstevel 			imsg.drproto_version = DRMBX_VERSION;
22921708Sstevel 			imsg.expbrd = 0;
22931708Sstevel 			imsg.slot = 0;
22941708Sstevel 
229511066Srafael.vanoni@sun.com 			cmn_err(CE_WARN, "!reinitializing DR mailbox");
22961708Sstevel 			mlp = drmach_mbox_req_rply(&imsg, sizeof (imsg), 0, 0,
229711066Srafael.vanoni@sun.com 			    10, 5, 0, NULL);
22981708Sstevel 			err = drmach_mbx2sbderr(mlp);
22991708Sstevel 			/*
23001708Sstevel 			 * If framework failure incoming is encountered on
23011708Sstevel 			 * the MBOX_INIT [timeout on SMS reply], the error
23021708Sstevel 			 * type must be changed before returning to caller.
23031708Sstevel 			 * This is to prevent drmach_board_connect() and
23041708Sstevel 			 * drmach_board_disconnect() from marking boards
23051708Sstevel 			 * UNUSABLE based on MBOX_INIT failures.
23061708Sstevel 			 */
23071708Sstevel 			if ((err != NULL) && (err->e_code == ESTC_MBXRPLY)) {
23081708Sstevel 				cmn_err(CE_WARN,
23091708Sstevel 				    "!Changed mbox incoming to outgoing"
23101708Sstevel 				    " failure on reinit");
23111708Sstevel 				sbd_err_clear(&err);
23121708Sstevel 				err = drerr_new(0, ESTC_MBXRQST, NULL);
23131708Sstevel 			}
23141708Sstevel 			drmach_msglist_destroy(mlp);
23151708Sstevel 			if (err) {
23161708Sstevel 				mutex_exit(&drmach_ri_mbox_mutex);
23171708Sstevel 				return (err);
23181708Sstevel 			}
23191708Sstevel 			mutex_enter(&drmach_g_mbox_mutex);
23201708Sstevel 			drmach_mbox_iflag = 1;
23211708Sstevel 		}
23221708Sstevel 		mutex_exit(&drmach_g_mbox_mutex);
23231708Sstevel 		mutex_exit(&drmach_ri_mbox_mutex);
23241708Sstevel 	}
23251708Sstevel 
23261708Sstevel 	hdrp = (dr_proto_hdr_t *)obufp;
23271708Sstevel 
23281708Sstevel 	/* setup outgoing mailbox header */
23291708Sstevel 	hdrp->command = msgtype;
23301708Sstevel 	hdrp->message_id = drmach_get_msgid();
23311708Sstevel 	hdrp->drproto_version = DRMBX_VERSION;
23321708Sstevel 	hdrp->expbrd = DRMACH_BNUM2EXP(bnum);
23331708Sstevel 	hdrp->slot = DRMACH_BNUM2SLOT(bnum);
23341708Sstevel 
23351708Sstevel 	switch (msgtype) {
23361708Sstevel 
23371708Sstevel 		case DRMSG_MBOX_INIT:
23381708Sstevel 			timeout = drmach_to_mbxinit;
23391708Sstevel 			ntries = 1;
23401708Sstevel 			nosignals = 0;
23411708Sstevel 			break;
23421708Sstevel 
23431708Sstevel 		case DRMSG_ASSIGN:
23441708Sstevel 			timeout = drmach_to_assign;
23451708Sstevel 			ntries = 1;
23461708Sstevel 			nosignals = 0;
23471708Sstevel 			break;
23481708Sstevel 
23491708Sstevel 		case DRMSG_UNASSIGN:
23501708Sstevel 			timeout = drmach_to_unassign;
23511708Sstevel 			ntries = 1;
23521708Sstevel 			nosignals = 0;
23531708Sstevel 			break;
23541708Sstevel 
23551708Sstevel 		case DRMSG_POWERON:
23561708Sstevel 			timeout = drmach_to_poweron;
23571708Sstevel 			ntries = 1;
23581708Sstevel 			nosignals = 0;
23591708Sstevel 			break;
23601708Sstevel 
23611708Sstevel 		case DRMSG_POWEROFF:
23621708Sstevel 			timeout = drmach_to_poweroff;
23631708Sstevel 			ntries = 1;
23641708Sstevel 			nosignals = 0;
23651708Sstevel 			break;
23661708Sstevel 
23671708Sstevel 		case DRMSG_SHOWBOARD:
23681708Sstevel 			timeout = drmach_to_showboard;
23691708Sstevel 			ntries = 1;
23701708Sstevel 			nosignals = 0;
23711708Sstevel 			break;
23721708Sstevel 
23731708Sstevel 		case DRMSG_CLAIM:
23741708Sstevel 			timeout = drmach_to_claim;
23751708Sstevel 			ntries = 1;
23761708Sstevel 			nosignals = 1;
23771708Sstevel 			break;
23781708Sstevel 
23791708Sstevel 		case DRMSG_UNCLAIM:
23801708Sstevel 			timeout = drmach_to_unclaim;
23811708Sstevel 			ntries = 1;
23821708Sstevel 			nosignals = 1;
23831708Sstevel 			break;
23841708Sstevel 
23851708Sstevel 		case DRMSG_UNCONFIG:
23861708Sstevel 			timeout = drmach_to_unconfig;
23871708Sstevel 			ntries = 1;
23881708Sstevel 			nosignals = 0;
23891708Sstevel 			break;
23901708Sstevel 
23911708Sstevel 		case DRMSG_TESTBOARD:
23921708Sstevel 			timeout = drmach_to_testboard;
23931708Sstevel 			ntries = 1;
23941708Sstevel 			nosignals = 0;
23951708Sstevel 			break;
23961708Sstevel 
23971708Sstevel 		default:
239811066Srafael.vanoni@sun.com 			cmn_err(CE_WARN, "Unknown outgoing message type 0x%x",
239911066Srafael.vanoni@sun.com 			    msgtype);
24001708Sstevel 			err = DRMACH_INTERNAL_ERROR();
24011708Sstevel 			break;
24021708Sstevel 	}
24031708Sstevel 
24041708Sstevel 	if (err == NULL) {
240511066Srafael.vanoni@sun.com 		mlp = drmach_mbox_req_rply(hdrp, olen, ibufp, ilen, timeout,
240611066Srafael.vanoni@sun.com 		    ntries, nosignals, NULL);
24071708Sstevel 		err = drmach_mbx2sbderr(mlp);
24081708Sstevel 
24091708Sstevel 		/*
24101708Sstevel 		 * For DRMSG_TESTBOARD attempts which have timed out, or
24111708Sstevel 		 * been aborted due to a signal received after mboxsc_putmsg()
24121708Sstevel 		 * has succeeded in sending the message, a DRMSG_ABORT_TEST
24131708Sstevel 		 * must be sent.
24141708Sstevel 		 */
24151708Sstevel 		if ((msgtype == DRMSG_TESTBOARD) && (err != NULL) &&
24161708Sstevel 		    ((mlp->f_error == EINTR) || ((mlp->f_error == ETIMEDOUT) &&
24171708Sstevel 		    (mlp->p_flag != 0)))) {
24181708Sstevel 			drmach_msglist_t	*abmlp;
24191708Sstevel 			dr_abort_test_t		abibuf;
24201708Sstevel 
24211708Sstevel 			hdrp->command = DRMSG_ABORT_TEST;
24221708Sstevel 			hdrp->message_id = drmach_get_msgid();
24231708Sstevel 			abmlp = drmach_mbox_req_rply(hdrp,
24241708Sstevel 			    sizeof (dr_abort_test_t), (caddr_t)&abibuf,
24251708Sstevel 			    sizeof (abibuf), drmach_to_aborttest, 5, 1, mlp);
24261708Sstevel 			cmn_err(CE_WARN, "test aborted");
24271708Sstevel 			drmach_msglist_destroy(abmlp);
24281708Sstevel 		}
24291708Sstevel 
24301708Sstevel 		drmach_msglist_destroy(mlp);
24311708Sstevel 	}
24321708Sstevel 
24331708Sstevel 	return (err);
24341708Sstevel }
24351708Sstevel 
24361708Sstevel static int
drmach_mbox_init()24371708Sstevel drmach_mbox_init()
24381708Sstevel {
24391708Sstevel 	int			err;
24401708Sstevel 	caddr_t			obufp;
24411708Sstevel 	sbd_error_t		*serr = NULL;
24421708Sstevel 	mboxsc_timeout_range_t	mbxtoz;
24431708Sstevel 
24441708Sstevel 	drmach_mbox_istate = 0;
24451708Sstevel 	/* register the outgoing mailbox */
24461708Sstevel 	if ((err = mboxsc_init(KEY_DRSC, MBOXSC_MBOX_OUT,
244711066Srafael.vanoni@sun.com 	    NULL)) != 0) {
24481708Sstevel 		cmn_err(CE_WARN, "DR - SC mboxsc_init failed: 0x%x", err);
24491708Sstevel 		return (-1);
24501708Sstevel 	}
24511708Sstevel 	drmach_mbox_istate = 1;
24521708Sstevel 
24531708Sstevel 	/* setup the mboxsc_putmsg timeout value */
24541708Sstevel 	if (drmach_use_tuned_putmsg_to) {
24551708Sstevel 		cmn_err(CE_NOTE, "!using tuned drmach_to_putmsg = 0x%lx\n",
24561708Sstevel 		    drmach_to_putmsg);
24571708Sstevel 	} else {
24581708Sstevel 		if ((err = mboxsc_ctrl(KEY_DRSC,
24591708Sstevel 		    MBOXSC_CMD_PUTMSG_TIMEOUT_RANGE, &mbxtoz)) != 0) {
24601708Sstevel 			cmn_err(CE_WARN, "mboxsc_ctrl failed: 0x%x", err);
24611708Sstevel 			drmach_to_putmsg = 60000;
24621708Sstevel 		} else {
24631708Sstevel 			drmach_to_putmsg = mboxsc_putmsg_def_timeout() * 6;
24641708Sstevel 			DRMACH_PR("putmsg range is 0x%lx - 0x%lx value"
24651708Sstevel 			    " is 0x%lx\n", mbxtoz.min_timeout,
24661708Sstevel 			    mbxtoz.max_timeout, drmach_to_putmsg);
24671708Sstevel 		}
24681708Sstevel 	}
24691708Sstevel 
24701708Sstevel 	/* register the incoming mailbox */
24711708Sstevel 	if ((err = mboxsc_init(KEY_SCDR, MBOXSC_MBOX_IN,
247211066Srafael.vanoni@sun.com 	    drmach_mbox_event)) != 0) {
24731708Sstevel 		cmn_err(CE_WARN, "SC - DR mboxsc_init failed: 0x%x", err);
24741708Sstevel 		return (-1);
24751708Sstevel 	}
24761708Sstevel 	drmach_mbox_istate = 2;
24771708Sstevel 
24781708Sstevel 	/* initialize mutex for mailbox globals */
24791708Sstevel 	mutex_init(&drmach_g_mbox_mutex, NULL, MUTEX_DRIVER, NULL);
24801708Sstevel 
24811708Sstevel 	/* initialize mutex for mailbox re-init */
24821708Sstevel 	mutex_init(&drmach_ri_mbox_mutex, NULL, MUTEX_DRIVER, NULL);
24831708Sstevel 
24841708Sstevel 	/* initialize mailbox message list elements */
24851708Sstevel 	drmach_msglist_first = drmach_msglist_last = NULL;
24861708Sstevel 	mutex_init(&drmach_msglist_mutex, NULL, MUTEX_DRIVER, NULL);
24871708Sstevel 
24881708Sstevel 	mutex_init(&drmach_sendmsg_mutex, NULL, MUTEX_DRIVER, NULL);
24891708Sstevel 	cv_init(&drmach_sendmsg_cv, NULL, CV_DRIVER, NULL);
24901708Sstevel 
24911708Sstevel 	drmach_mbox_istate = 3;
24921708Sstevel 
24931708Sstevel 	/* start mailbox sendmsg thread */
24941708Sstevel 	drmach_sendmsg_thread_run = 1;
24951708Sstevel 	if (drmach_sendmsg_thread == NULL)
24961708Sstevel 		drmach_sendmsg_thread = thread_create(NULL, 0,
24971708Sstevel 		    (void (*)())drmach_mbox_sendmsg, NULL, 0, &p0,
24981708Sstevel 		    TS_RUN, minclsyspri);
24991708Sstevel 
25001708Sstevel 	/* start mailbox getmsg thread */
25011708Sstevel 	drmach_getmsg_thread_run = 1;
25021708Sstevel 	if (drmach_getmsg_thread == NULL)
25031708Sstevel 		drmach_getmsg_thread = thread_create(NULL, 0,
25041708Sstevel 		    (void (*)())drmach_mbox_getmsg, NULL, 0, &p0,
25051708Sstevel 		    TS_RUN, minclsyspri);
25061708Sstevel 
25071708Sstevel 	obufp = kmem_zalloc(sizeof (dr_proto_hdr_t), KM_SLEEP);
25081708Sstevel 	serr = drmach_mbox_trans(DRMSG_MBOX_INIT, 0, obufp,
250911066Srafael.vanoni@sun.com 	    sizeof (dr_proto_hdr_t), (caddr_t)NULL, 0);
25101708Sstevel 	kmem_free(obufp, sizeof (dr_proto_hdr_t));
25111708Sstevel 	if (serr) {
25121708Sstevel 		cmn_err(CE_WARN, "mbox_init: MBOX_INIT failed ecode=0x%x",
251311066Srafael.vanoni@sun.com 		    serr->e_code);
25141708Sstevel 		sbd_err_clear(&serr);
25151708Sstevel 		return (-1);
25161708Sstevel 	}
25171708Sstevel 	mutex_enter(&drmach_g_mbox_mutex);
25181708Sstevel 	drmach_mbox_iflag = 1;
25191708Sstevel 	drmach_mbox_ipending = 0;
25201708Sstevel 	mutex_exit(&drmach_g_mbox_mutex);
25211708Sstevel 
25221708Sstevel 	return (0);
25231708Sstevel }
25241708Sstevel 
25251708Sstevel static int
drmach_mbox_fini()25261708Sstevel drmach_mbox_fini()
25271708Sstevel {
25281708Sstevel 	int err, rv = 0;
25291708Sstevel 
25301708Sstevel 	if (drmach_mbox_istate > 2) {
25311708Sstevel 		drmach_getmsg_thread_run = 0;
25321708Sstevel 		drmach_sendmsg_thread_run = 0;
25331708Sstevel 		cmn_err(CE_WARN,
253411066Srafael.vanoni@sun.com 		    "drmach_mbox_fini: waiting for mbox threads...");
25351708Sstevel 		while ((drmach_getmsg_thread_run == 0) ||
253611066Srafael.vanoni@sun.com 		    (drmach_sendmsg_thread_run == 0)) {
25371708Sstevel 			continue;
25381708Sstevel 		}
253911066Srafael.vanoni@sun.com 		cmn_err(CE_WARN, "drmach_mbox_fini: mbox threads done.");
25401708Sstevel 		mutex_destroy(&drmach_msglist_mutex);
25411708Sstevel 
25421708Sstevel 	}
25431708Sstevel 	if (drmach_mbox_istate) {
25441708Sstevel 		/* de-register the outgoing mailbox */
25451708Sstevel 		if ((err = mboxsc_fini(KEY_DRSC)) != 0) {
25461708Sstevel 			cmn_err(CE_WARN, "DR - SC mboxsc_fini failed: 0x%x",
254711066Srafael.vanoni@sun.com 			    err);
25481708Sstevel 			rv = -1;
25491708Sstevel 		}
25501708Sstevel 	}
25511708Sstevel 	if (drmach_mbox_istate > 1) {
25521708Sstevel 		/* de-register the incoming mailbox */
25531708Sstevel 		if ((err = mboxsc_fini(KEY_SCDR)) != 0) {
25541708Sstevel 			cmn_err(CE_WARN, "SC - DR mboxsc_fini failed: 0x%x",
255511066Srafael.vanoni@sun.com 			    err);
25561708Sstevel 			rv = -1;
25571708Sstevel 		}
25581708Sstevel 	}
25591708Sstevel 	mutex_destroy(&drmach_g_mbox_mutex);
25601708Sstevel 	mutex_destroy(&drmach_ri_mbox_mutex);
25611708Sstevel 	return (rv);
25621708Sstevel }
25631708Sstevel 
25641708Sstevel static int
drmach_portid2bnum(int portid)25651708Sstevel drmach_portid2bnum(int portid)
25661708Sstevel {
25671708Sstevel 	int slot;
25681708Sstevel 
25691708Sstevel 	switch (portid & 0x1f) {
25701708Sstevel 	case 0: case 1: case 2: case 3:	/* cpu/wci devices */
25711708Sstevel 	case 0x1e:			/* slot 0 axq registers */
25721708Sstevel 		slot = 0;
25731708Sstevel 		break;
25741708Sstevel 
25751708Sstevel 	case 8: case 9:			/* cpu devices */
25761708Sstevel 	case 0x1c: case 0x1d:		/* schizo/wci devices */
25771708Sstevel 	case 0x1f:			/* slot 1 axq registers */
25781708Sstevel 		slot = 1;
25791708Sstevel 		break;
25801708Sstevel 
25811708Sstevel 	default:
25821708Sstevel 		ASSERT(0);		/* catch in debug kernels */
25831708Sstevel 	}
25841708Sstevel 
25851708Sstevel 	return (((portid >> 4) & 0x7e) | slot);
25861708Sstevel }
25871708Sstevel 
25881708Sstevel extern int axq_suspend_iopause;
25891708Sstevel 
25901708Sstevel static int
hold_rele_branch(dev_info_t * rdip,void * arg)25911708Sstevel hold_rele_branch(dev_info_t *rdip, void *arg)
25921708Sstevel {
25931708Sstevel 	int	i;
25941708Sstevel 	int	*holdp	= (int *)arg;
25951708Sstevel 	char	*name = ddi_node_name(rdip);
25961708Sstevel 
25971708Sstevel 	/*
25981708Sstevel 	 * For Starcat, we must be children of the root devinfo node
25991708Sstevel 	 */
26001708Sstevel 	ASSERT(ddi_get_parent(rdip) == ddi_root_node());
26011708Sstevel 
26021708Sstevel 	i = drmach_name2type_idx(name);
26031708Sstevel 
26041708Sstevel 	/*
26051708Sstevel 	 * Only children of the root devinfo node need to be
26061708Sstevel 	 * held/released since they are the only valid targets
26071708Sstevel 	 * of tree operations. This corresponds to the node types
26081708Sstevel 	 * listed in the drmach_name2type array.
26091708Sstevel 	 */
26101708Sstevel 	if (i < 0) {
26111708Sstevel 		/* Not of interest to us */
26121708Sstevel 		return (DDI_WALK_PRUNECHILD);
26131708Sstevel 	}
26141708Sstevel 
26151708Sstevel 	if (*holdp) {
26161708Sstevel 		ASSERT(!e_ddi_branch_held(rdip));
26171708Sstevel 		e_ddi_branch_hold(rdip);
26181708Sstevel 	} else {
26191708Sstevel 		ASSERT(e_ddi_branch_held(rdip));
26201708Sstevel 		e_ddi_branch_rele(rdip);
26211708Sstevel 	}
26221708Sstevel 
26231708Sstevel 	return (DDI_WALK_PRUNECHILD);
26241708Sstevel }
26251708Sstevel 
26261708Sstevel static int
drmach_init(void)26271708Sstevel drmach_init(void)
26281708Sstevel {
26291708Sstevel 	pnode_t 	nodeid;
26301708Sstevel 	gdcd_t		*gdcd;
26311708Sstevel 	int		bnum;
26321708Sstevel 	dev_info_t	*rdip;
26331708Sstevel 	int		hold, circ;
26341708Sstevel 
26351708Sstevel 	mutex_enter(&drmach_i_lock);
26361708Sstevel 	if (drmach_initialized) {
26371708Sstevel 		mutex_exit(&drmach_i_lock);
26381708Sstevel 		return (0);
26391708Sstevel 	}
26401708Sstevel 
26411708Sstevel 	gdcd = drmach_gdcd_new();
26421708Sstevel 	if (gdcd == NULL) {
26431708Sstevel 		mutex_exit(&drmach_i_lock);
26441708Sstevel 		cmn_err(CE_WARN, "drmach_init: failed to access GDCD\n");
26451708Sstevel 		return (-1);
26461708Sstevel 	}
26471708Sstevel 
26481708Sstevel 	drmach_boards = drmach_array_new(0, MAX_BOARDS - 1);
26491708Sstevel 
26501708Sstevel 	nodeid = prom_childnode(prom_rootnode());
26511708Sstevel 	do {
26521708Sstevel 		int		 len;
26531708Sstevel 		int		 portid;
26541708Sstevel 		drmachid_t	 id;
26551708Sstevel 
26561708Sstevel 		len = prom_getproplen(nodeid, "portid");
26571708Sstevel 		if (len != sizeof (portid))
26581708Sstevel 			continue;
26591708Sstevel 
26601708Sstevel 		portid = -1;
26611708Sstevel 		(void) prom_getprop(nodeid, "portid", (caddr_t)&portid);
26621708Sstevel 		if (portid == -1)
26631708Sstevel 			continue;
26641708Sstevel 
26651708Sstevel 		bnum = drmach_portid2bnum(portid);
26661708Sstevel 
26671708Sstevel 		if (drmach_array_get(drmach_boards, bnum, &id) == -1) {
26681708Sstevel 			/* portid translated to an invalid board number */
26691708Sstevel 			cmn_err(CE_WARN, "OBP node 0x%x has"
267011066Srafael.vanoni@sun.com 			    " invalid property value, %s=%u",
267111066Srafael.vanoni@sun.com 			    nodeid, "portid", portid);
26721708Sstevel 
26731708Sstevel 			/* clean up */
26741708Sstevel 			drmach_array_dispose(drmach_boards,
26751708Sstevel 			    drmach_board_dispose);
26761708Sstevel 			drmach_gdcd_dispose(gdcd);
26771708Sstevel 			mutex_exit(&drmach_i_lock);
26781708Sstevel 			return (-1);
26791708Sstevel 		} else if (id == NULL) {
26801708Sstevel 			drmach_board_t	*bp;
26811708Sstevel 			l1_slot_stat_t	*dcd;
26821708Sstevel 			int		exp, slot;
26831708Sstevel 
26841708Sstevel 			bp = drmach_board_new(bnum);
26851708Sstevel 			bp->assigned = !drmach_initialized;
26861708Sstevel 			bp->powered = !drmach_initialized;
26871708Sstevel 
26881708Sstevel 			exp = DRMACH_BNUM2EXP(bnum);
26891708Sstevel 			slot = DRMACH_BNUM2SLOT(bnum);
26901708Sstevel 			dcd = &gdcd->dcd_slot[exp][slot];
26911708Sstevel 			bp->stardrb_offset =
26921708Sstevel 			    dcd->l1ss_cpu_drblock_xwd_offset << 3;
26931708Sstevel 			DRMACH_PR("%s: stardrb_offset=0x%lx\n", bp->cm.name,
26941708Sstevel 			    bp->stardrb_offset);
26951708Sstevel 
26961708Sstevel 			if (gdcd->dcd_slot[exp][slot].l1ss_flags &
26971708Sstevel 			    L1SSFLG_THIS_L1_NULL_PROC_LPA) {
26981708Sstevel 				bp->flags |= DRMACH_NULL_PROC_LPA;
26991708Sstevel 				DRMACH_PR("%s: NULL proc LPA\n", bp->cm.name);
27001708Sstevel 			}
27011708Sstevel 		}
27021708Sstevel 	} while ((nodeid = prom_nextnode(nodeid)) != OBP_NONODE);
27031708Sstevel 
27041708Sstevel 	drmach_cpu_sram_va = vmem_alloc(heap_arena, PAGESIZE, VM_SLEEP);
27051708Sstevel 
27061708Sstevel 	if (gdcd->dcd_testcage_log2_mbytes_size != DCD_DR_TESTCAGE_DISABLED) {
27071708Sstevel 		ASSERT(gdcd->dcd_testcage_log2_mbytes_size ==
270811066Srafael.vanoni@sun.com 		    gdcd->dcd_testcage_log2_mbytes_align);
27091708Sstevel 		drmach_iocage_paddr =
271011066Srafael.vanoni@sun.com 		    (uint64_t)gdcd->dcd_testcage_mbyte_PA << 20;
27111708Sstevel 		drmach_iocage_size =
271211066Srafael.vanoni@sun.com 		    1 << (gdcd->dcd_testcage_log2_mbytes_size + 20);
27131708Sstevel 
27141708Sstevel 		drmach_iocage_vaddr = (caddr_t)vmem_alloc(heap_arena,
271511066Srafael.vanoni@sun.com 		    drmach_iocage_size, VM_SLEEP);
27161708Sstevel 		hat_devload(kas.a_hat, drmach_iocage_vaddr, drmach_iocage_size,
271711066Srafael.vanoni@sun.com 		    mmu_btop(drmach_iocage_paddr),
271811066Srafael.vanoni@sun.com 		    PROT_READ | PROT_WRITE,
271911066Srafael.vanoni@sun.com 		    HAT_LOAD_LOCK | HAT_LOAD_NOCONSIST);
27201708Sstevel 
27211708Sstevel 		DRMACH_PR("gdcd size=0x%x align=0x%x PA=0x%x\n",
272211066Srafael.vanoni@sun.com 		    gdcd->dcd_testcage_log2_mbytes_size,
272311066Srafael.vanoni@sun.com 		    gdcd->dcd_testcage_log2_mbytes_align,
272411066Srafael.vanoni@sun.com 		    gdcd->dcd_testcage_mbyte_PA);
27251708Sstevel 		DRMACH_PR("drmach size=0x%x PA=0x%lx VA=0x%p\n",
272611066Srafael.vanoni@sun.com 		    drmach_iocage_size, drmach_iocage_paddr,
272711311SSurya.Prakki@Sun.COM 		    (void *)drmach_iocage_vaddr);
27281708Sstevel 	}
27291708Sstevel 
27301708Sstevel 	if (drmach_iocage_size == 0) {
27311708Sstevel 		drmach_array_dispose(drmach_boards, drmach_board_dispose);
27321708Sstevel 		drmach_boards = NULL;
27331708Sstevel 		vmem_free(heap_arena, drmach_cpu_sram_va, PAGESIZE);
27341708Sstevel 		drmach_gdcd_dispose(gdcd);
27351708Sstevel 		mutex_exit(&drmach_i_lock);
27361708Sstevel 		cmn_err(CE_WARN, "drmach_init: iocage not available\n");
27371708Sstevel 		return (-1);
27381708Sstevel 	}
27391708Sstevel 
27401708Sstevel 	drmach_gdcd_dispose(gdcd);
27411708Sstevel 
27421708Sstevel 	mutex_init(&drmach_iocage_lock, NULL, MUTEX_DRIVER, NULL);
27431708Sstevel 	cv_init(&drmach_iocage_cv, NULL, CV_DRIVER, NULL);
27441708Sstevel 	mutex_init(&drmach_xt_mb_lock, NULL, MUTEX_DRIVER, NULL);
27451708Sstevel 	mutex_init(&drmach_bus_sync_lock, NULL, MUTEX_DRIVER, NULL);
27461708Sstevel 	mutex_init(&drmach_slice_table_lock, NULL, MUTEX_DRIVER, NULL);
27471708Sstevel 
27481708Sstevel 	mutex_enter(&cpu_lock);
27491708Sstevel 	mutex_enter(&drmach_iocage_lock);
27501708Sstevel 	ASSERT(drmach_iocage_is_busy == 0);
27511708Sstevel 	drmach_iocage_is_busy = 1;
27521708Sstevel 	drmach_iocage_mem_scrub(drmach_iocage_size);
27531708Sstevel 	drmach_iocage_is_busy = 0;
27541708Sstevel 	cv_signal(&drmach_iocage_cv);
27551708Sstevel 	mutex_exit(&drmach_iocage_lock);
27561708Sstevel 	mutex_exit(&cpu_lock);
27571708Sstevel 
27581708Sstevel 
27591708Sstevel 	if (drmach_mbox_init() == -1) {
27601708Sstevel 		cmn_err(CE_WARN, "DR - SC mailbox initialization Failed");
27611708Sstevel 	}
27621708Sstevel 
27631708Sstevel 	/*
27641708Sstevel 	 * Walk immediate children of devinfo root node and hold
27651708Sstevel 	 * all devinfo branches of interest.
27661708Sstevel 	 */
27671708Sstevel 	hold = 1;
27681708Sstevel 	rdip = ddi_root_node();
27691708Sstevel 
27701708Sstevel 	ndi_devi_enter(rdip, &circ);
27711708Sstevel 	ddi_walk_devs(ddi_get_child(rdip), hold_rele_branch, &hold);
27721708Sstevel 	ndi_devi_exit(rdip, circ);
27731708Sstevel 
27741708Sstevel 	drmach_initialized = 1;
27751708Sstevel 
27761708Sstevel 	/*
27771708Sstevel 	 * To avoid a circular patch dependency between DR and AXQ, the AXQ
27781708Sstevel 	 * rev introducing the axq_iopause_*_all interfaces should not regress
27791708Sstevel 	 * when installed without the DR rev using those interfaces. The default
27801708Sstevel 	 * is for iopause to be enabled/disabled during axq suspend/resume. By
27811708Sstevel 	 * setting the following axq flag to zero, axq will not enable iopause
27821708Sstevel 	 * during suspend/resume, instead DR will call the axq_iopause_*_all
27831708Sstevel 	 * interfaces during drmach_copy_rename.
27841708Sstevel 	 */
27851708Sstevel 	axq_suspend_iopause = 0;
27861708Sstevel 
27871708Sstevel 	mutex_exit(&drmach_i_lock);
27881708Sstevel 
27891708Sstevel 	return (0);
27901708Sstevel }
27911708Sstevel 
27921708Sstevel static void
drmach_fini(void)27931708Sstevel drmach_fini(void)
27941708Sstevel {
27951708Sstevel 	dev_info_t	*rdip;
27961708Sstevel 	int		hold, circ;
27971708Sstevel 
27981708Sstevel 	if (drmach_initialized) {
27991708Sstevel 		rw_enter(&drmach_boards_rwlock, RW_WRITER);
28001708Sstevel 		drmach_array_dispose(drmach_boards, drmach_board_dispose);
28011708Sstevel 		drmach_boards = NULL;
28021708Sstevel 		rw_exit(&drmach_boards_rwlock);
28031708Sstevel 
28041708Sstevel 		mutex_destroy(&drmach_slice_table_lock);
28051708Sstevel 		mutex_destroy(&drmach_xt_mb_lock);
28061708Sstevel 		mutex_destroy(&drmach_bus_sync_lock);
28071708Sstevel 		cv_destroy(&drmach_iocage_cv);
28081708Sstevel 		mutex_destroy(&drmach_iocage_lock);
28091708Sstevel 
28101708Sstevel 		vmem_free(heap_arena, drmach_cpu_sram_va, PAGESIZE);
28111708Sstevel 
28121708Sstevel 		/*
28131708Sstevel 		 * Walk immediate children of the root devinfo node
28141708Sstevel 		 * releasing holds acquired on branches in drmach_init()
28151708Sstevel 		 */
28161708Sstevel 		hold = 0;
28171708Sstevel 		rdip = ddi_root_node();
28181708Sstevel 
28191708Sstevel 		ndi_devi_enter(rdip, &circ);
28201708Sstevel 		ddi_walk_devs(ddi_get_child(rdip), hold_rele_branch, &hold);
28211708Sstevel 		ndi_devi_exit(rdip, circ);
28221708Sstevel 
28231708Sstevel 		drmach_initialized = 0;
28241708Sstevel 	}
28251708Sstevel 
282611311SSurya.Prakki@Sun.COM 	(void) drmach_mbox_fini();
28271708Sstevel 	if (drmach_xt_mb != NULL) {
28281708Sstevel 		vmem_free(static_alloc_arena, (void *)drmach_xt_mb,
28291708Sstevel 		    drmach_xt_mb_size);
28301708Sstevel 	}
28311708Sstevel 	rw_destroy(&drmach_boards_rwlock);
28321708Sstevel 	mutex_destroy(&drmach_i_lock);
28331708Sstevel }
28341708Sstevel 
28351708Sstevel static void
drmach_mem_read_madr(drmach_mem_t * mp,int bank,uint64_t * madr)28361708Sstevel drmach_mem_read_madr(drmach_mem_t *mp, int bank, uint64_t *madr)
28371708Sstevel {
28381708Sstevel 	kpreempt_disable();
28391708Sstevel 
28401708Sstevel 	/* get register address, read madr value */
28411708Sstevel 	if (STARCAT_CPUID_TO_PORTID(CPU->cpu_id) == mp->dev.portid) {
28421708Sstevel 		*madr = lddmcdecode(DRMACH_MC_ASI_ADDR(mp, bank));
28431708Sstevel 	} else {
28441708Sstevel 		*madr = lddphysio(DRMACH_MC_ADDR(mp, bank));
28451708Sstevel 	}
28461708Sstevel 
28471708Sstevel 	kpreempt_enable();
28481708Sstevel }
28491708Sstevel 
28501708Sstevel 
28511708Sstevel static uint64_t *
drmach_prep_mc_rename(uint64_t * p,int local,drmach_mem_t * mp,uint64_t current_basepa,uint64_t new_basepa)28521708Sstevel drmach_prep_mc_rename(uint64_t *p, int local,
28531708Sstevel 	drmach_mem_t *mp, uint64_t current_basepa, uint64_t new_basepa)
28541708Sstevel {
28551708Sstevel 	int bank;
28561708Sstevel 
28571708Sstevel 	for (bank = 0; bank < DRMACH_MC_NBANKS; bank++) {
28581708Sstevel 		uint64_t madr, bank_offset;
28591708Sstevel 
28601708Sstevel 		/* fetch mc's bank madr register value */
28611708Sstevel 		drmach_mem_read_madr(mp, bank, &madr);
28621708Sstevel 		if (madr & DRMACH_MC_VALID_MASK) {
28631708Sstevel 			uint64_t bankpa;
28641708Sstevel 
28651708Sstevel 			bank_offset = (DRMACH_MC_UM_TO_PA(madr) |
28661708Sstevel 			    DRMACH_MC_LM_TO_PA(madr)) - current_basepa;
28671708Sstevel 			bankpa = new_basepa + bank_offset;
28681708Sstevel 
28691708Sstevel 			/* encode new base pa into madr */
28701708Sstevel 			madr &= ~DRMACH_MC_UM_MASK;
28711708Sstevel 			madr |= DRMACH_MC_PA_TO_UM(bankpa);
28721708Sstevel 			madr &= ~DRMACH_MC_LM_MASK;
28731708Sstevel 			madr |= DRMACH_MC_PA_TO_LM(bankpa);
28741708Sstevel 
28751708Sstevel 			if (local)
28761708Sstevel 				*p++ = DRMACH_MC_ASI_ADDR(mp, bank);
28771708Sstevel 			else
28781708Sstevel 				*p++ = DRMACH_MC_ADDR(mp, bank);
28791708Sstevel 
28801708Sstevel 			*p++ = madr;
28811708Sstevel 		}
28821708Sstevel 	}
28831708Sstevel 
28841708Sstevel 	return (p);
28851708Sstevel }
28861708Sstevel 
28871708Sstevel static uint64_t *
drmach_prep_schizo_script(uint64_t * p,drmach_mem_t * mp,uint64_t new_basepa)28881708Sstevel drmach_prep_schizo_script(uint64_t *p, drmach_mem_t *mp, uint64_t new_basepa)
28891708Sstevel {
28901708Sstevel 	drmach_board_t	*bp;
28911708Sstevel 	int		 rv;
28921708Sstevel 	int		 idx;
28931708Sstevel 	drmachid_t	 id;
28941708Sstevel 	uint64_t	 last_scsr_pa = 0;
28951708Sstevel 
28961708Sstevel 	/* memory is always in slot 0 */
28971708Sstevel 	ASSERT(DRMACH_BNUM2SLOT(mp->dev.bp->bnum) == 0);
28981708Sstevel 
28991708Sstevel 	/* look up slot 1 board on same expander */
29001708Sstevel 	idx = DRMACH_EXPSLOT2BNUM(DRMACH_BNUM2EXP(mp->dev.bp->bnum), 1);
29011708Sstevel 	rv = drmach_array_get(drmach_boards, idx, &id);
29021708Sstevel 	bp = id; /* bp will be NULL if board not found */
29031708Sstevel 
29041708Sstevel 	/* look up should never be out of bounds */
29051708Sstevel 	ASSERT(rv == 0);
29061708Sstevel 
29071708Sstevel 	/* nothing to do when board is not found or has no devices */
29081708Sstevel 	if (rv == -1 || bp == NULL || bp->devices == NULL)
29091708Sstevel 		return (p);
29101708Sstevel 
29111708Sstevel 	rv = drmach_array_first(bp->devices, &idx, &id);
29121708Sstevel 	while (rv == 0) {
29131708Sstevel 		if (DRMACH_IS_IO_ID(id)) {
29141708Sstevel 			drmach_io_t *io = id;
29151708Sstevel 
29161708Sstevel 			/*
29171708Sstevel 			 * Skip all non-Schizo IO devices (only IO nodes
29181708Sstevel 			 * that are Schizo devices have non-zero scsr_pa).
29191708Sstevel 			 * Filter out "other" leaf to avoid writing to the
29201708Sstevel 			 * same Schizo Control/Status Register twice.
29211708Sstevel 			 */
29221708Sstevel 			if (io->scsr_pa && io->scsr_pa != last_scsr_pa) {
29231708Sstevel 				uint64_t scsr;
29241708Sstevel 
29251708Sstevel 				scsr  = lddphysio(io->scsr_pa);
29261708Sstevel 				scsr &= ~(DRMACH_LPA_BASE_MASK |
292711066Srafael.vanoni@sun.com 				    DRMACH_LPA_BND_MASK);
29281708Sstevel 				scsr |= DRMACH_PA_TO_LPA_BASE(new_basepa);
29291708Sstevel 				scsr |= DRMACH_PA_TO_LPA_BND(
293011066Srafael.vanoni@sun.com 				    new_basepa + DRMACH_MEM_SLICE_SIZE);
29311708Sstevel 
29321708Sstevel 				*p++ = io->scsr_pa;
29331708Sstevel 				*p++ = scsr;
29341708Sstevel 
29351708Sstevel 				last_scsr_pa = io->scsr_pa;
29361708Sstevel 			}
29371708Sstevel 		}
29381708Sstevel 		rv = drmach_array_next(bp->devices, &idx, &id);
29391708Sstevel 	}
29401708Sstevel 
29411708Sstevel 	return (p);
29421708Sstevel }
29431708Sstevel 
29441708Sstevel /*
29451708Sstevel  * For Panther MCs, append the MC idle reg address and drmach_mem_t pointer.
29461708Sstevel  * The latter is returned when drmach_rename fails to idle a Panther MC and
29471708Sstevel  * is used to identify the MC for error reporting.
29481708Sstevel  */
29491708Sstevel static uint64_t *
drmach_prep_pn_mc_idle(uint64_t * p,drmach_mem_t * mp,int local)29501708Sstevel drmach_prep_pn_mc_idle(uint64_t *p, drmach_mem_t *mp, int local)
29511708Sstevel {
29521708Sstevel 	/* only slot 0 has memory */
29531708Sstevel 	ASSERT(DRMACH_BNUM2SLOT(mp->dev.bp->bnum) == 0);
29541708Sstevel 	ASSERT(IS_PANTHER(mp->dev.bp->cpu_impl));
29551708Sstevel 
29561708Sstevel 	for (mp = mp->dev.bp->mem; mp != NULL; mp = mp->next) {
29571708Sstevel 		ASSERT(DRMACH_IS_MEM_ID(mp));
29581708Sstevel 
29591708Sstevel 		if (mp->dev.portid == STARCAT_CPUID_TO_PORTID(CPU->cpu_id)) {
29601708Sstevel 			if (local) {
29611708Sstevel 				*p++ = ASI_EMU_ACT_STATUS_VA;	/* local ASI */
29621708Sstevel 				*p++ = (uintptr_t)mp;
29631708Sstevel 			}
29641708Sstevel 		} else if (!local) {
29651708Sstevel 			*p++ = DRMACH_EMU_ACT_STATUS_ADDR(mp);	/* PIO */
29661708Sstevel 			*p++ = (uintptr_t)mp;
29671708Sstevel 		}
29681708Sstevel 	}
29691708Sstevel 
29701708Sstevel 	return (p);
29711708Sstevel }
29721708Sstevel 
29731708Sstevel 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)29741708Sstevel drmach_prep_rename_script(drmach_mem_t *s_mp, drmach_mem_t *t_mp,
29751708Sstevel 	uint64_t t_slice_offset, caddr_t buf, int buflen)
29761708Sstevel {
29771708Sstevel 	_NOTE(ARGUNUSED(buflen))
29781708Sstevel 
29791708Sstevel 	uint64_t		*p = (uint64_t *)buf, *q;
29801708Sstevel 	sbd_error_t		*err;
29811708Sstevel 	int			 rv;
29821708Sstevel 	drmach_mem_t		*mp, *skip_mp;
29831708Sstevel 	uint64_t		 s_basepa, t_basepa;
29841708Sstevel 	uint64_t		 s_new_basepa, t_new_basepa;
29851708Sstevel 
29861708Sstevel 	/* verify supplied buffer space is adequate */
29871708Sstevel 	ASSERT(buflen >=
298811066Srafael.vanoni@sun.com 	    /* addr for all possible MC banks */
298911066Srafael.vanoni@sun.com 	    (sizeof (uint64_t) * 4 * 4 * 18) +
299011066Srafael.vanoni@sun.com 	    /* list section terminator */
299111066Srafael.vanoni@sun.com 	    (sizeof (uint64_t) * 1) +
299211066Srafael.vanoni@sun.com 	    /* addr/id tuple for local Panther MC idle reg */
299311066Srafael.vanoni@sun.com 	    (sizeof (uint64_t) * 2) +
299411066Srafael.vanoni@sun.com 	    /* list section terminator */
299511066Srafael.vanoni@sun.com 	    (sizeof (uint64_t) * 1) +
299611066Srafael.vanoni@sun.com 	    /* addr/id tuple for 2 boards with 4 Panther MC idle regs */
299711066Srafael.vanoni@sun.com 	    (sizeof (uint64_t) * 2 * 2 * 4) +
299811066Srafael.vanoni@sun.com 	    /* list section terminator */
299911066Srafael.vanoni@sun.com 	    (sizeof (uint64_t) * 1) +
300011066Srafael.vanoni@sun.com 	    /* addr/val tuple for 1 proc with 4 MC banks */
300111066Srafael.vanoni@sun.com 	    (sizeof (uint64_t) * 2 * 4) +
300211066Srafael.vanoni@sun.com 	    /* list section terminator */
300311066Srafael.vanoni@sun.com 	    (sizeof (uint64_t) * 1) +
300411066Srafael.vanoni@sun.com 	    /* addr/val tuple for 2 boards w/ 2 schizos each */
300511066Srafael.vanoni@sun.com 	    (sizeof (uint64_t) * 2 * 2 * 2) +
300611066Srafael.vanoni@sun.com 	    /* addr/val tuple for 2 boards w/ 16 MC banks each */
300711066Srafael.vanoni@sun.com 	    (sizeof (uint64_t) * 2 * 2 * 16) +
300811066Srafael.vanoni@sun.com 	    /* list section terminator */
300911066Srafael.vanoni@sun.com 	    (sizeof (uint64_t) * 1) +
301011066Srafael.vanoni@sun.com 	    /* addr/val tuple for 18 AXQs w/ two slots each */
301111066Srafael.vanoni@sun.com 	    (sizeof (uint64_t) * 2 * 2 * 18) +
301211066Srafael.vanoni@sun.com 	    /* list section terminator */
301311066Srafael.vanoni@sun.com 	    (sizeof (uint64_t) * 1) +
301411066Srafael.vanoni@sun.com 	    /* list terminator */
301511066Srafael.vanoni@sun.com 	    (sizeof (uint64_t) * 1));
30161708Sstevel 
30171708Sstevel 	/* copy bank list to rename script */
30181708Sstevel 	mutex_enter(&drmach_bus_sync_lock);
30191708Sstevel 	for (q = drmach_bus_sync_list; *q; q++, p++)
30201708Sstevel 		*p = *q;
30211708Sstevel 	mutex_exit(&drmach_bus_sync_lock);
30221708Sstevel 
30231708Sstevel 	/* list section terminator */
30241708Sstevel 	*p++ = 0;
30251708Sstevel 
30261708Sstevel 	/*
30271708Sstevel 	 * Write idle script for MC on this processor.  A script will be
30281708Sstevel 	 * produced only if this is a Panther processor on the source or
30291708Sstevel 	 * target board.
30301708Sstevel 	 */
30311708Sstevel 	if (IS_PANTHER(s_mp->dev.bp->cpu_impl))
30321708Sstevel 		p = drmach_prep_pn_mc_idle(p, s_mp, 1);
30331708Sstevel 
30341708Sstevel 	if (IS_PANTHER(t_mp->dev.bp->cpu_impl))
30351708Sstevel 		p = drmach_prep_pn_mc_idle(p, t_mp, 1);
30361708Sstevel 
30371708Sstevel 	/* list section terminator */
30381708Sstevel 	*p++ = 0;
30391708Sstevel 
30401708Sstevel 	/*
30411708Sstevel 	 * Write idle script for all other MCs on source and target
30421708Sstevel 	 * Panther boards.
30431708Sstevel 	 */
30441708Sstevel 	if (IS_PANTHER(s_mp->dev.bp->cpu_impl))
30451708Sstevel 		p = drmach_prep_pn_mc_idle(p, s_mp, 0);
30461708Sstevel 
30471708Sstevel 	if (IS_PANTHER(t_mp->dev.bp->cpu_impl))
30481708Sstevel 		p = drmach_prep_pn_mc_idle(p, t_mp, 0);
30491708Sstevel 
30501708Sstevel 	/* list section terminator */
30511708Sstevel 	*p++ = 0;
30521708Sstevel 
30531708Sstevel 	/*
30541708Sstevel 	 * Step 1:	Write source base address to target MC
30551708Sstevel 	 *		with present bit off.
30561708Sstevel 	 * Step 2:	Now rewrite target reg with present bit on.
30571708Sstevel 	 */
30581708Sstevel 	err = drmach_mem_get_base_physaddr(s_mp, &s_basepa);
30591708Sstevel 	ASSERT(err == NULL);
30601708Sstevel 	err = drmach_mem_get_base_physaddr(t_mp, &t_basepa);
30611708Sstevel 	ASSERT(err == NULL);
30621708Sstevel 
30631708Sstevel 	/* exchange base pa. include slice offset in new target base pa */
30641708Sstevel 	s_new_basepa = t_basepa & ~ (DRMACH_MEM_SLICE_SIZE - 1);
30651708Sstevel 	t_new_basepa = (s_basepa & ~ (DRMACH_MEM_SLICE_SIZE - 1)) +
306611066Srafael.vanoni@sun.com 	    t_slice_offset;
30671708Sstevel 
30681708Sstevel 	DRMACH_PR("s_new_basepa 0x%lx\n", s_new_basepa);
30691708Sstevel 	DRMACH_PR("t_new_basepa 0x%lx\n", t_new_basepa);
30701708Sstevel 
30711708Sstevel 	DRMACH_PR("preparing MC MADR rename script (master is CPU%d):\n",
307211066Srafael.vanoni@sun.com 	    CPU->cpu_id);
30731708Sstevel 
30741708Sstevel 	/*
30751708Sstevel 	 * Write rename script for MC on this processor.  A script will
30761708Sstevel 	 * be produced only if this processor is on the source or target
30771708Sstevel 	 * board.
30781708Sstevel 	 */
30791708Sstevel 
30801708Sstevel 	skip_mp = NULL;
30811708Sstevel 	mp = s_mp->dev.bp->mem;
30821708Sstevel 	while (mp != NULL && skip_mp == NULL) {
30831708Sstevel 		if (mp->dev.portid == STARCAT_CPUID_TO_PORTID(CPU->cpu_id)) {
30841708Sstevel 			skip_mp = mp;
30851708Sstevel 			p = drmach_prep_mc_rename(p, 1, mp, s_basepa,
30861708Sstevel 			    s_new_basepa);
30871708Sstevel 		}
30881708Sstevel 
30891708Sstevel 		mp = mp->next;
30901708Sstevel 	}
30911708Sstevel 
30921708Sstevel 	mp = t_mp->dev.bp->mem;
30931708Sstevel 	while (mp != NULL && skip_mp == NULL) {
30941708Sstevel 		if (mp->dev.portid == STARCAT_CPUID_TO_PORTID(CPU->cpu_id)) {
30951708Sstevel 			skip_mp = mp;
30961708Sstevel 			p = drmach_prep_mc_rename(p, 1, mp, t_basepa,
30971708Sstevel 			    t_new_basepa);
30981708Sstevel 		}
30991708Sstevel 
31001708Sstevel 		mp = mp->next;
31011708Sstevel 	}
31021708Sstevel 
31031708Sstevel 	/* list section terminator */
31041708Sstevel 	*p++ = 0;
31051708Sstevel 
31061708Sstevel 	/*
31071708Sstevel 	 * Write rename script for all other MCs on source and target
31081708Sstevel 	 * boards.
31091708Sstevel 	 */
31101708Sstevel 
31111708Sstevel 	for (mp = s_mp->dev.bp->mem; mp; mp = mp->next) {
31121708Sstevel 		if (mp == skip_mp)
31131708Sstevel 			continue;
31141708Sstevel 		p = drmach_prep_mc_rename(p, 0, mp, s_basepa, s_new_basepa);
31151708Sstevel 	}
31161708Sstevel 
31171708Sstevel 	for (mp = t_mp->dev.bp->mem; mp; mp = mp->next) {
31181708Sstevel 		if (mp == skip_mp)
31191708Sstevel 			continue;
31201708Sstevel 		p = drmach_prep_mc_rename(p, 0, mp, t_basepa, t_new_basepa);
31211708Sstevel 	}
31221708Sstevel 
31231708Sstevel 	/* Write rename script for Schizo LPA_BASE/LPA_BND */
31241708Sstevel 	p = drmach_prep_schizo_script(p, s_mp, s_new_basepa);
31251708Sstevel 	p = drmach_prep_schizo_script(p, t_mp, t_new_basepa);
31261708Sstevel 
31271708Sstevel 	/* list section terminator */
31281708Sstevel 	*p++ = 0;
31291708Sstevel 
31301708Sstevel 	DRMACH_PR("preparing AXQ CASM rename script (EXP%d <> EXP%d):\n",
313111066Srafael.vanoni@sun.com 	    DRMACH_BNUM2EXP(s_mp->dev.bp->bnum),
313211066Srafael.vanoni@sun.com 	    DRMACH_BNUM2EXP(t_mp->dev.bp->bnum));
31331708Sstevel 
31341708Sstevel 	rv = axq_do_casm_rename_script(&p,
313511066Srafael.vanoni@sun.com 	    DRMACH_PA_TO_SLICE(s_new_basepa),
313611066Srafael.vanoni@sun.com 	    DRMACH_PA_TO_SLICE(t_new_basepa));
31371708Sstevel 	if (rv == DDI_FAILURE)
31381708Sstevel 		return (DRMACH_INTERNAL_ERROR());
31391708Sstevel 
31401708Sstevel 	/* list section & final terminator */
31411708Sstevel 	*p++ = 0;
31421708Sstevel 	*p++ = 0;
31431708Sstevel 
31441708Sstevel #ifdef DEBUG
31451708Sstevel 	{
31461708Sstevel 		uint64_t *q = (uint64_t *)buf;
31471708Sstevel 
31481708Sstevel 		/* paranoia */
31491708Sstevel 		ASSERT((caddr_t)p <= buf + buflen);
31501708Sstevel 
31511708Sstevel 		DRMACH_PR("MC bank base pa list:\n");
31521708Sstevel 		while (*q) {
31531708Sstevel 			uint64_t a = *q++;
31541708Sstevel 
31551708Sstevel 			DRMACH_PR("0x%lx\n", a);
31561708Sstevel 		}
31571708Sstevel 
31581708Sstevel 		/* skip terminator */
31591708Sstevel 		q += 1;
31601708Sstevel 
31611708Sstevel 		DRMACH_PR("local Panther MC idle reg (via ASI 0x4a):\n");
31621708Sstevel 		while (*q) {
31631708Sstevel 			DRMACH_PR("addr=0x%lx, mp=0x%lx\n", *q, *(q + 1));
31641708Sstevel 			q += 2;
31651708Sstevel 		}
31661708Sstevel 
31671708Sstevel 		/* skip terminator */
31681708Sstevel 		q += 1;
31691708Sstevel 
31701708Sstevel 		DRMACH_PR("non-local Panther MC idle reg (via ASI 0x15):\n");
31711708Sstevel 		while (*q) {
31721708Sstevel 			DRMACH_PR("addr=0x%lx, mp=0x%lx\n", *q, *(q + 1));
31731708Sstevel 			q += 2;
31741708Sstevel 		}
31751708Sstevel 
31761708Sstevel 		/* skip terminator */
31771708Sstevel 		q += 1;
31781708Sstevel 
31791708Sstevel 		DRMACH_PR("MC reprogramming script (via ASI 0x72):\n");
31801708Sstevel 		while (*q) {
31811708Sstevel 			uint64_t r = *q++;	/* register address */
31821708Sstevel 			uint64_t v = *q++;	/* new register value */
31831708Sstevel 
31841708Sstevel 			DRMACH_PR("0x%lx = 0x%lx, basepa 0x%lx\n",
318511311SSurya.Prakki@Sun.COM 			    r, v, (long)(DRMACH_MC_UM_TO_PA(v)|
318611311SSurya.Prakki@Sun.COM 			    DRMACH_MC_LM_TO_PA(v)));
31871708Sstevel 		}
31881708Sstevel 
31891708Sstevel 		/* skip terminator */
31901708Sstevel 		q += 1;
31911708Sstevel 
31921708Sstevel 		DRMACH_PR("MC/SCHIZO reprogramming script:\n");
31931708Sstevel 		while (*q) {
31941708Sstevel 			DRMACH_PR("0x%lx = 0x%lx\n", *q, *(q + 1));
31951708Sstevel 			q += 2;
31961708Sstevel 		}
31971708Sstevel 
31981708Sstevel 		/* skip terminator */
31991708Sstevel 		q += 1;
32001708Sstevel 
32011708Sstevel 		DRMACH_PR("AXQ reprogramming script:\n");
32021708Sstevel 		while (*q) {
32031708Sstevel 			DRMACH_PR("0x%lx = 0x%lx\n", *q, *(q + 1));
32041708Sstevel 			q += 2;
32051708Sstevel 		}
32061708Sstevel 
32071708Sstevel 		/* verify final terminator is present */
32081708Sstevel 		ASSERT(*(q + 1) == 0);
32091708Sstevel 
321011311SSurya.Prakki@Sun.COM 		DRMACH_PR("copy-rename script 0x%p, len %d\n",
321111311SSurya.Prakki@Sun.COM 		    (void *)buf, (int)((intptr_t)p - (intptr_t)buf));
32121708Sstevel 
32131708Sstevel 		if (drmach_debug)
32141708Sstevel 			DELAY(10000000);
32151708Sstevel 	}
32161708Sstevel #endif
32171708Sstevel 
32181708Sstevel 	return (NULL);
32191708Sstevel }
32201708Sstevel 
32211708Sstevel static void
drmach_prep_xt_mb_for_slice_update(drmach_board_t * bp,uchar_t slice)32221708Sstevel drmach_prep_xt_mb_for_slice_update(drmach_board_t *bp, uchar_t slice)
32231708Sstevel {
32241708Sstevel 	int		 rv;
32251708Sstevel 
32261708Sstevel 	ASSERT(MUTEX_HELD(&drmach_xt_mb_lock));
32271708Sstevel 
32281708Sstevel 	if (bp->devices) {
32291708Sstevel 		int		 d_idx;
32301708Sstevel 		drmachid_t	 d_id;
32311708Sstevel 
32321708Sstevel 		rv = drmach_array_first(bp->devices, &d_idx, &d_id);
32331708Sstevel 		while (rv == 0) {
32341708Sstevel 			if (DRMACH_IS_CPU_ID(d_id)) {
32351708Sstevel 				drmach_cpu_t	*cp = d_id;
32361708Sstevel 				processorid_t	 cpuid = cp->cpuid;
32371708Sstevel 
32381708Sstevel 				mutex_enter(&cpu_lock);
32391708Sstevel 				if (cpu[cpuid] && cpu[cpuid]->cpu_flags)
32401708Sstevel 					drmach_xt_mb[cpuid] = 0x80 | slice;
32411708Sstevel 				mutex_exit(&cpu_lock);
32421708Sstevel 			}
32431708Sstevel 			rv = drmach_array_next(bp->devices, &d_idx, &d_id);
32441708Sstevel 		}
32451708Sstevel 	}
32461708Sstevel 	if (DRMACH_BNUM2SLOT(bp->bnum) == 0) {
32471708Sstevel 		drmach_board_t	*s1bp = NULL;
32481708Sstevel 
32491708Sstevel 		rv = drmach_array_get(drmach_boards, bp->bnum + 1,
32501708Sstevel 		    (void *) &s1bp);
32511708Sstevel 		if (rv == 0 && s1bp != NULL) {
32521708Sstevel 			ASSERT(DRMACH_IS_BOARD_ID(s1bp));
32531708Sstevel 			ASSERT(DRMACH_BNUM2SLOT(s1bp->bnum) == 1);
32541708Sstevel 			drmach_prep_xt_mb_for_slice_update(s1bp, slice);
32551708Sstevel 		}
32561708Sstevel 	}
32571708Sstevel }
32581708Sstevel 
32591708Sstevel 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)32601708Sstevel drmach_copy_rename_init(drmachid_t t_id, uint64_t t_slice_offset,
32611708Sstevel 	drmachid_t s_id, struct memlist *c_ml, drmachid_t *cr_id)
32621708Sstevel {
32631708Sstevel 	extern void drmach_rename(uint64_t *, uint_t *, uint64_t *);
32641708Sstevel 	extern void drmach_rename_end(void);
32651708Sstevel 
32661708Sstevel 	drmach_mem_t	*s_mp, *t_mp;
32671708Sstevel 	struct memlist	*x_ml;
32681708Sstevel 	uint64_t	 off_mask, s_copybasepa, t_copybasepa, t_basepa;
32691708Sstevel 	int		 len;
32701708Sstevel 	caddr_t		 bp, wp;
32711708Sstevel 	uint_t		*p, *q;
32721708Sstevel 	sbd_error_t	*err;
32731708Sstevel 	tte_t		*tte;
32741708Sstevel 	drmach_copy_rename_t *cr;
32751708Sstevel 
32761708Sstevel 	if (!DRMACH_IS_MEM_ID(s_id))
32771708Sstevel 		return (drerr_new(0, ESTC_INAPPROP, NULL));
32781708Sstevel 	if (!DRMACH_IS_MEM_ID(t_id))
32791708Sstevel 		return (drerr_new(0, ESTC_INAPPROP, NULL));
32801708Sstevel 	s_mp = s_id;
32811708Sstevel 	t_mp = t_id;
32821708Sstevel 
32831708Sstevel 	/* get starting physical address of target memory */
32841708Sstevel 	err = drmach_mem_get_base_physaddr(t_id, &t_basepa);
32851708Sstevel 	if (err)
32861708Sstevel 		return (err);
32871708Sstevel 
32881708Sstevel 	/* calculate slice offset mask from slice size */
32891708Sstevel 	off_mask = DRMACH_MEM_SLICE_SIZE - 1;
32901708Sstevel 
32911708Sstevel 	/* calculate source and target base pa */
3292*11474SJonathan.Adams@Sun.COM 	s_copybasepa = c_ml->ml_address;
3293*11474SJonathan.Adams@Sun.COM 	t_copybasepa =
3294*11474SJonathan.Adams@Sun.COM 	    t_basepa + ((c_ml->ml_address & off_mask) - t_slice_offset);
32951708Sstevel 
32961708Sstevel 	/* paranoia */
3297*11474SJonathan.Adams@Sun.COM 	ASSERT((c_ml->ml_address & off_mask) >= t_slice_offset);
32981708Sstevel 
32991708Sstevel 	/* adjust copy memlist addresses to be relative to copy base pa */
33001708Sstevel 	x_ml = c_ml;
33011708Sstevel 	while (x_ml != NULL) {
3302*11474SJonathan.Adams@Sun.COM 		x_ml->ml_address -= s_copybasepa;
3303*11474SJonathan.Adams@Sun.COM 		x_ml = x_ml->ml_next;
33041708Sstevel 	}
33051708Sstevel 
33061708Sstevel #ifdef DEBUG
33071708Sstevel 	{
33081708Sstevel 	uint64_t s_basepa, s_size, t_size;
33091708Sstevel 
33101708Sstevel 	x_ml = c_ml;
3311*11474SJonathan.Adams@Sun.COM 	while (x_ml->ml_next != NULL)
3312*11474SJonathan.Adams@Sun.COM 		x_ml = x_ml->ml_next;
33131708Sstevel 
33141708Sstevel 	DRMACH_PR("source copy span: base pa 0x%lx, end pa 0x%lx\n",
331511066Srafael.vanoni@sun.com 	    s_copybasepa,
3316*11474SJonathan.Adams@Sun.COM 	    s_copybasepa + x_ml->ml_address + x_ml->ml_size);
33171708Sstevel 
33181708Sstevel 	DRMACH_PR("target copy span: base pa 0x%lx, end pa 0x%lx\n",
331911066Srafael.vanoni@sun.com 	    t_copybasepa,
3320*11474SJonathan.Adams@Sun.COM 	    t_copybasepa + x_ml->ml_address + x_ml->ml_size);
33211708Sstevel 
33221708Sstevel 	DRMACH_PR("copy memlist (relative to copy base pa):\n");
33231708Sstevel 	DRMACH_MEMLIST_DUMP(c_ml);
33241708Sstevel 
33251708Sstevel 	err = drmach_mem_get_base_physaddr(s_id, &s_basepa);
33261708Sstevel 	ASSERT(err == NULL);
33271708Sstevel 
33281708Sstevel 	err = drmach_mem_get_size(s_id, &s_size);
33291708Sstevel 	ASSERT(err == NULL);
33301708Sstevel 
33311708Sstevel 	err = drmach_mem_get_size(t_id, &t_size);
33321708Sstevel 	ASSERT(err == NULL);
33331708Sstevel 
33341708Sstevel 	DRMACH_PR("current source base pa 0x%lx, size 0x%lx\n",
333511066Srafael.vanoni@sun.com 	    s_basepa, s_size);
33361708Sstevel 	DRMACH_PR("current target base pa 0x%lx, size 0x%lx\n",
333711066Srafael.vanoni@sun.com 	    t_basepa, t_size);
33381708Sstevel 	}
33391708Sstevel #endif /* DEBUG */
33401708Sstevel 
33411708Sstevel 	/* Map in appropriate cpu sram page */
33421708Sstevel 	tte = &drmach_cpu_sram_tte[CPU->cpu_id];
33431708Sstevel 	ASSERT(TTE_IS_VALID(tte) && TTE_IS_8K(tte) &&
33441708Sstevel 	    TTE_IS_PRIVILEGED(tte) && TTE_IS_LOCKED(tte));
33452241Shuah 	sfmmu_dtlb_ld_kva(drmach_cpu_sram_va, tte);
33462241Shuah 	sfmmu_itlb_ld_kva(drmach_cpu_sram_va, tte);
33471708Sstevel 
33481708Sstevel 	bp = wp = drmach_cpu_sram_va;
33491708Sstevel 
33501708Sstevel 	/* Make sure the rename routine will fit */
33511708Sstevel 	len = (ptrdiff_t)drmach_rename_end - (ptrdiff_t)drmach_rename;
33521708Sstevel 	ASSERT(wp + len < bp + PAGESIZE);
33531708Sstevel 
33541708Sstevel 	/* copy text. standard bcopy not designed to work in nc space */
33551708Sstevel 	p = (uint_t *)wp;
33561708Sstevel 	q = (uint_t *)drmach_rename;
33571708Sstevel 	while (q < (uint_t *)drmach_rename_end)
33581708Sstevel 		*p++ = *q++;
33591708Sstevel 
33601708Sstevel 	/* zero remainder. standard bzero not designed to work in nc space */
33611708Sstevel 	while (p < (uint_t *)(bp + PAGESIZE))
33621708Sstevel 		*p++ = 0;
33631708Sstevel 
336411311SSurya.Prakki@Sun.COM 	DRMACH_PR("drmach_rename function 0x%p, len %d\n", (void *)wp, len);
33651708Sstevel 	wp += (len + 15) & ~15;
33661708Sstevel 
336711066Srafael.vanoni@sun.com 	err = drmach_prep_rename_script(s_mp, t_mp, t_slice_offset, wp,
336811066Srafael.vanoni@sun.com 	    PAGESIZE - (wp - bp));
33691708Sstevel 	if (err) {
33701708Sstevel cleanup:
33711708Sstevel 		xt_one(CPU->cpu_id, vtag_flushpage_tl1,
337211066Srafael.vanoni@sun.com 		    (uint64_t)drmach_cpu_sram_va, (uint64_t)ksfmmup);
33731708Sstevel 		return (err);
33741708Sstevel 	}
33751708Sstevel 
33761708Sstevel 	/* disable and flush CDC */
33771708Sstevel 	if (axq_cdc_disable_flush_all() != DDI_SUCCESS) {
33781708Sstevel 		axq_cdc_enable_all();	/* paranoia */
33791708Sstevel 		err = DRMACH_INTERNAL_ERROR();
33801708Sstevel 		goto cleanup;
33811708Sstevel 	}
33821708Sstevel 
33831708Sstevel 	/* mark both memory units busy */
33841708Sstevel 	t_mp->dev.busy++;
33851708Sstevel 	s_mp->dev.busy++;
33861708Sstevel 
33871708Sstevel 	cr = vmem_alloc(static_alloc_arena, sizeof (drmach_copy_rename_t),
33881708Sstevel 	    VM_SLEEP);
33891708Sstevel 	cr->isa = (void *)drmach_copy_rename_init;
33901708Sstevel 	cr->data = wp;
33911708Sstevel 	cr->c_ml = c_ml;
33921708Sstevel 	cr->s_mp = s_mp;
33931708Sstevel 	cr->t_mp = t_mp;
33941708Sstevel 	cr->s_copybasepa = s_copybasepa;
33951708Sstevel 	cr->t_copybasepa = t_copybasepa;
33961708Sstevel 	cr->ecode = DRMACH_CR_OK;
33971708Sstevel 
33981708Sstevel 	mutex_enter(&drmach_slice_table_lock);
33991708Sstevel 
34001708Sstevel 	mutex_enter(&drmach_xt_mb_lock);
34011708Sstevel 	bzero((void *)drmach_xt_mb, drmach_xt_mb_size);
34021708Sstevel 
34031708Sstevel 	if (DRMACH_L1_SET_LPA(s_mp->dev.bp) && drmach_reprogram_lpa) {
34041708Sstevel 		drmach_prep_xt_mb_for_slice_update(s_mp->dev.bp,
340511066Srafael.vanoni@sun.com 		    DRMACH_PA_TO_SLICE(t_copybasepa));
34061708Sstevel 	}
34071708Sstevel 	if (DRMACH_L1_SET_LPA(t_mp->dev.bp) && drmach_reprogram_lpa) {
34081708Sstevel 		drmach_prep_xt_mb_for_slice_update(t_mp->dev.bp,
340911066Srafael.vanoni@sun.com 		    DRMACH_PA_TO_SLICE(s_copybasepa));
34101708Sstevel 	}
34111708Sstevel 
34121708Sstevel 	*cr_id = cr;
34131708Sstevel 	return (NULL);
34141708Sstevel }
34151708Sstevel 
34161708Sstevel int drmach_rename_count;
34171708Sstevel int drmach_rename_ntries;
34181708Sstevel 
34191708Sstevel sbd_error_t *
drmach_copy_rename_fini(drmachid_t id)34201708Sstevel drmach_copy_rename_fini(drmachid_t id)
34211708Sstevel {
34221708Sstevel 	drmach_copy_rename_t	*cr = id;
34231708Sstevel 	sbd_error_t		*err = NULL;
34241708Sstevel 	dr_mbox_msg_t		*obufp;
34251708Sstevel 
34261708Sstevel 	ASSERT(cr->isa == (void *)drmach_copy_rename_init);
34271708Sstevel 
34281708Sstevel 	axq_cdc_enable_all();
34291708Sstevel 
34301708Sstevel 	xt_one(CPU->cpu_id, vtag_flushpage_tl1,
343111066Srafael.vanoni@sun.com 	    (uint64_t)drmach_cpu_sram_va, (uint64_t)ksfmmup);
34321708Sstevel 
34331708Sstevel 	switch (cr->ecode) {
34341708Sstevel 	case DRMACH_CR_OK:
34351708Sstevel 		break;
34361708Sstevel 	case DRMACH_CR_MC_IDLE_ERR: {
34371708Sstevel 		dev_info_t	*dip = NULL;
34381708Sstevel 		drmach_mem_t	*mp = (drmach_mem_t *)cr->earg;
34391708Sstevel 		char *path = kmem_alloc(MAXPATHLEN, KM_SLEEP);
34401708Sstevel 
34411708Sstevel 		ASSERT(DRMACH_IS_MEM_ID(mp));
34421708Sstevel 
34431708Sstevel 		err = drmach_get_dip(mp, &dip);
34441708Sstevel 
34451708Sstevel 		ASSERT(err == NULL);
34461708Sstevel 		ASSERT(dip != NULL);
34471708Sstevel 
34481708Sstevel 		err = drerr_new(0, ESBD_MEMFAIL, NULL);
34491708Sstevel 		(void) ddi_pathname(dip, path);
34501708Sstevel 		cmn_err(CE_WARN, "failed to idle memory controller %s on %s: "
34511708Sstevel 		    "copy-rename aborted", path, mp->dev.bp->cm.name);
34521708Sstevel 		kmem_free(path, MAXPATHLEN);
34531708Sstevel 		break;
34541708Sstevel 	}
34551708Sstevel 	case DRMACH_CR_IOPAUSE_ERR:
34561708Sstevel 		ASSERT((uintptr_t)cr->earg >= 0 &&
34571708Sstevel 		    (uintptr_t)cr->earg < AXQ_MAX_EXP);
34581708Sstevel 
34591708Sstevel 		err = drerr_new(0,  ESBD_SUSPEND, "EX%d", (uintptr_t)cr->earg);
34601708Sstevel 		cmn_err(CE_WARN, "failed to idle EX%ld AXQ slot1 activity prior"
34611708Sstevel 		    " to copy-rename", (uintptr_t)cr->earg);
34621708Sstevel 		break;
34631708Sstevel 	case DRMACH_CR_ONTRAP_ERR:
34641708Sstevel 		err = drerr_new(0, ESBD_MEMFAIL, NULL);
34651708Sstevel 		cmn_err(CE_WARN, "copy-rename aborted due to uncorrectable "
34661708Sstevel 		    "memory error");
34671708Sstevel 		break;
34681708Sstevel 	default:
34691708Sstevel 		err = DRMACH_INTERNAL_ERROR();
34701708Sstevel 		cmn_err(CE_WARN, "unknown copy-rename error code (%d)\n",
34711708Sstevel 		    cr->ecode);
34721708Sstevel 		break;
34731708Sstevel 	}
34741708Sstevel 
34751708Sstevel #ifdef DEBUG
34761708Sstevel 	if ((DRMACH_L1_SET_LPA(cr->s_mp->dev.bp) ||
34771708Sstevel 	    DRMACH_L1_SET_LPA(cr->t_mp->dev.bp)) && drmach_reprogram_lpa) {
34781708Sstevel 		int	i;
34791708Sstevel 		for (i = 0; i < NCPU; i++) {
34801708Sstevel 			if (drmach_xt_mb[i])
34811708Sstevel 				DRMACH_PR("cpu%d ignored drmach_xt_mb", i);
34821708Sstevel 		}
34831708Sstevel 	}
34841708Sstevel #endif
34851708Sstevel 	mutex_exit(&drmach_xt_mb_lock);
34861708Sstevel 
34871708Sstevel 	if (cr->c_ml != NULL)
34881708Sstevel 		memlist_delete(cr->c_ml);
34891708Sstevel 
34901708Sstevel 	cr->t_mp->dev.busy--;
34911708Sstevel 	cr->s_mp->dev.busy--;
34921708Sstevel 
34931708Sstevel 	if (err) {
34941708Sstevel 		mutex_exit(&drmach_slice_table_lock);
34951708Sstevel 		goto done;
34961708Sstevel 	}
34971708Sstevel 
34981708Sstevel 	/* update casm shadow for target and source board */
34991708Sstevel 	drmach_slice_table_update(cr->t_mp->dev.bp, 0);
35001708Sstevel 	drmach_slice_table_update(cr->s_mp->dev.bp, 0);
35011708Sstevel 	mutex_exit(&drmach_slice_table_lock);
35021708Sstevel 
35031708Sstevel 	mutex_enter(&drmach_bus_sync_lock);
35041708Sstevel 	drmach_bus_sync_list_update();
35051708Sstevel 	mutex_exit(&drmach_bus_sync_lock);
35061708Sstevel 
35071708Sstevel 	/*
35081708Sstevel 	 * Make a good-faith effort to notify the SC about the copy-rename, but
35091708Sstevel 	 * don't worry if it fails, since a subsequent claim/unconfig/unclaim
35101708Sstevel 	 * will duplicate the update.
35111708Sstevel 	 */
35121708Sstevel 	obufp = kmem_zalloc(sizeof (dr_mbox_msg_t), KM_SLEEP);
35131708Sstevel 	mutex_enter(&drmach_slice_table_lock);
35141708Sstevel 	drmach_msg_memslice_init(obufp->msgdata.dm_uc.mem_slice);
35151708Sstevel 	drmach_msg_memregs_init(obufp->msgdata.dm_uc.mem_regs);
35161708Sstevel 	mutex_exit(&drmach_slice_table_lock);
35171708Sstevel 	(void) drmach_mbox_trans(DRMSG_UNCONFIG, cr->s_mp->dev.bp->bnum,
351811066Srafael.vanoni@sun.com 	    (caddr_t)obufp, sizeof (dr_mbox_msg_t), (caddr_t)NULL, 0);
35191708Sstevel 	kmem_free(obufp, sizeof (dr_mbox_msg_t));
35201708Sstevel 
35211708Sstevel done:
35221708Sstevel 	vmem_free(static_alloc_arena, cr, sizeof (drmach_copy_rename_t));
35231708Sstevel 
35241708Sstevel 	DRMACH_PR("waited %d out of %d tries for drmach_rename_wait on %d cpus",
352511066Srafael.vanoni@sun.com 	    drmach_rename_ntries, drmach_cpu_ntries, drmach_rename_count);
35261708Sstevel 
35271708Sstevel 	return (err);
35281708Sstevel }
35291708Sstevel 
35301708Sstevel int drmach_slow_copy = 0;
35311708Sstevel 
35321708Sstevel void
drmach_copy_rename(drmachid_t id)35331708Sstevel drmach_copy_rename(drmachid_t id)
35341708Sstevel {
35351708Sstevel 	extern uint_t		 getpstate(void);
35361708Sstevel 	extern void		 setpstate(uint_t);
35371708Sstevel 
35381708Sstevel 	extern xcfunc_t		 drmach_rename_wait;
35391708Sstevel 	extern xcfunc_t		 drmach_rename_done;
35401708Sstevel 	extern xcfunc_t		 drmach_rename_abort;
35411708Sstevel 
35421708Sstevel 	drmach_copy_rename_t	*cr = id;
35431708Sstevel 	uint64_t		 neer;
35441708Sstevel 	struct memlist		*ml;
35451708Sstevel 	int			 i, count;
35461708Sstevel 	int			 csize, lnsize;
35471708Sstevel 	uint64_t		 caddr;
35481708Sstevel 	cpuset_t		 cpuset;
35491708Sstevel 	uint_t			 pstate;
35501708Sstevel 	uint32_t		 exp = 0;
35511708Sstevel 	on_trap_data_t		 otd;
35521708Sstevel 	xcfunc_t		*drmach_end_wait_xcall = drmach_rename_done;
35531708Sstevel 
35541708Sstevel 	ASSERT(cr->isa == (void *)drmach_copy_rename_init);
35551708Sstevel 	ASSERT(MUTEX_HELD(&cpu_lock));
35561708Sstevel 	ASSERT(cr->ecode == DRMACH_CR_OK);
35571708Sstevel 
35581708Sstevel 	/*
35591708Sstevel 	 * Prevent slot1 IO from accessing Safari memory bus.
35601708Sstevel 	 */
35611708Sstevel 	if (axq_iopause_enable_all(&exp) != DDI_SUCCESS) {
35621708Sstevel 		ASSERT(exp >= 0 && exp < AXQ_MAX_EXP);
35631708Sstevel 		cr->ecode = DRMACH_CR_IOPAUSE_ERR;
35641708Sstevel 		cr->earg = (void *)(uintptr_t)exp;
35651708Sstevel 		return;
35661708Sstevel 	}
35671708Sstevel 
35681708Sstevel 	cpuset = cpu_ready_set;
35691708Sstevel 	CPUSET_DEL(cpuset, CPU->cpu_id);
35701708Sstevel 	count = ncpus - 1;
35711708Sstevel 	drmach_rename_count = count;	/* for debug */
35721708Sstevel 
35731708Sstevel 	drmach_xt_ready = 0;
35741708Sstevel 	xt_some(cpuset, drmach_rename_wait, NULL, NULL);
35751708Sstevel 
35761708Sstevel 	for (i = 0; i < drmach_cpu_ntries; i++) {
35771708Sstevel 		if (drmach_xt_ready == count)
35781708Sstevel 			break;
35791708Sstevel 		DELAY(drmach_cpu_delay);
35801708Sstevel 	}
35811708Sstevel 
35821708Sstevel 	drmach_rename_ntries = i;	/* for debug */
35831708Sstevel 
35841708Sstevel 	drmach_xt_ready = 0;		/* steal the line back */
35851708Sstevel 	for (i = 0; i < NCPU; i++)	/* steal the line back, preserve data */
35861708Sstevel 		drmach_xt_mb[i] = drmach_xt_mb[i];
35871708Sstevel 
35881708Sstevel 	caddr = drmach_iocage_paddr;
35891708Sstevel 	csize = cpunodes[CPU->cpu_id].ecache_size;
35901708Sstevel 	lnsize = cpunodes[CPU->cpu_id].ecache_linesize;
35911708Sstevel 
35921708Sstevel 	/* disable CE reporting */
35931708Sstevel 	neer = get_error_enable();
35941708Sstevel 	set_error_enable(neer & ~EN_REG_CEEN);
35951708Sstevel 
35961708Sstevel 	/* disable interrupts (paranoia) */
35971708Sstevel 	pstate = getpstate();
35981708Sstevel 	setpstate(pstate & ~PSTATE_IE);
35991708Sstevel 
36001708Sstevel 	/*
36011708Sstevel 	 * Execute copy-rename under on_trap to protect against a panic due
36021708Sstevel 	 * to an uncorrectable error. Instead, DR will abort the copy-rename
36031708Sstevel 	 * operation and rely on the OS to do the error reporting.
36041708Sstevel 	 *
36051708Sstevel 	 * In general, trap handling on any cpu once the copy begins
36061708Sstevel 	 * can result in an inconsistent memory image on the target.
36071708Sstevel 	 */
36081708Sstevel 	if (on_trap(&otd, OT_DATA_EC)) {
36091708Sstevel 		cr->ecode = DRMACH_CR_ONTRAP_ERR;
36101708Sstevel 		goto copy_rename_end;
36111708Sstevel 	}
36121708Sstevel 
36131708Sstevel 	/*
36141708Sstevel 	 * DO COPY.
36151708Sstevel 	 */
3616*11474SJonathan.Adams@Sun.COM 	for (ml = cr->c_ml; ml; ml = ml->ml_next) {
36171708Sstevel 		uint64_t	s_pa, t_pa;
36181708Sstevel 		uint64_t	nbytes;
36191708Sstevel 
3620*11474SJonathan.Adams@Sun.COM 		s_pa = cr->s_copybasepa + ml->ml_address;
3621*11474SJonathan.Adams@Sun.COM 		t_pa = cr->t_copybasepa + ml->ml_address;
3622*11474SJonathan.Adams@Sun.COM 		nbytes = ml->ml_size;
36231708Sstevel 
36241708Sstevel 		while (nbytes != 0ull) {
36251708Sstevel 			/* copy 32 bytes at src_pa to dst_pa */
36261708Sstevel 			bcopy32_il(s_pa, t_pa);
36271708Sstevel 
36281708Sstevel 			/* increment by 32 bytes */
36291708Sstevel 			s_pa += (4 * sizeof (uint64_t));
36301708Sstevel 			t_pa += (4 * sizeof (uint64_t));
36311708Sstevel 
36321708Sstevel 			/* decrement by 32 bytes */
36331708Sstevel 			nbytes -= (4 * sizeof (uint64_t));
36341708Sstevel 
36351708Sstevel 			if (drmach_slow_copy) {	/* for debug */
36361708Sstevel 				uint64_t i = 13 * 50;
36377799SRichard.Bean@Sun.COM 				while (i--)
36387799SRichard.Bean@Sun.COM 					;
36391708Sstevel 			}
36401708Sstevel 		}
36411708Sstevel 	}
36421708Sstevel 
36431708Sstevel 	/*
36441708Sstevel 	 * XXX CHEETAH SUPPORT
36451708Sstevel 	 * For cheetah, we need to grab the iocage lock since iocage
36461708Sstevel 	 * memory is used for e$ flush.
36471708Sstevel 	 *
36481708Sstevel 	 * NOTE: This code block is dangerous at this point in the
36491708Sstevel 	 * copy-rename operation. It modifies memory after the copy
36501708Sstevel 	 * has taken place which means that any persistent state will
36511708Sstevel 	 * be abandoned after the rename operation. The code is also
36521708Sstevel 	 * performing thread synchronization at a time when all but
36531708Sstevel 	 * one processors are paused. This is a potential deadlock
36541708Sstevel 	 * situation.
36551708Sstevel 	 *
36561708Sstevel 	 * This code block must be moved to drmach_copy_rename_init.
36571708Sstevel 	 */
36581708Sstevel 	if (drmach_is_cheetah) {
36591708Sstevel 		mutex_enter(&drmach_iocage_lock);
36601708Sstevel 		while (drmach_iocage_is_busy)
36611708Sstevel 			cv_wait(&drmach_iocage_cv, &drmach_iocage_lock);
36621708Sstevel 		drmach_iocage_is_busy = 1;
36631708Sstevel 		drmach_iocage_mem_scrub(ecache_size * 2);
36641708Sstevel 		mutex_exit(&drmach_iocage_lock);
36651708Sstevel 	}
36661708Sstevel 
36671708Sstevel 	/*
36681708Sstevel 	 * bcopy32_il is implemented as a series of ldxa/stxa via
36691708Sstevel 	 * ASI_MEM instructions. Following the copy loop, the E$
36701708Sstevel 	 * of the master (this) processor will have lines in state
36711708Sstevel 	 * O that correspond to lines of home memory in state gI.
36721708Sstevel 	 * An E$ flush is necessary to commit these lines before
36731708Sstevel 	 * proceeding with the rename operation.
36741708Sstevel 	 *
36751708Sstevel 	 * Flushing the E$ will automatically flush the W$, but
36761708Sstevel 	 * the D$ and I$ must be flushed separately and explicitly.
36771708Sstevel 	 */
36781708Sstevel 	flush_ecache_il(caddr, csize, lnsize);	/* inline version */
36791708Sstevel 
36801708Sstevel 	/*
36811708Sstevel 	 * Each line of home memory is now in state gM, except in
36821708Sstevel 	 * the case of a cheetah processor when the E$ flush area
36831708Sstevel 	 * is included within the copied region. In such a case,
36841708Sstevel 	 * the lines of home memory for the upper half of the
36851708Sstevel 	 * flush area are in state gS.
36861708Sstevel 	 *
36871708Sstevel 	 * Each line of target memory is in state gM.
36881708Sstevel 	 *
36891708Sstevel 	 * Each line of this processor's E$ is in state I, except
36901708Sstevel 	 * those of a cheetah processor. All lines of a cheetah
36911708Sstevel 	 * processor's E$ are in state S and correspond to the lines
36921708Sstevel 	 * in upper half of the E$ flush area.
36931708Sstevel 	 *
36941708Sstevel 	 * It is vital at this point that none of the lines in the
36951708Sstevel 	 * home or target memories are in state gI and that none
36961708Sstevel 	 * of the lines in this processor's E$ are in state O or Os.
36971708Sstevel 	 * A single instance of such a condition will cause loss of
36981708Sstevel 	 * coherency following the rename operation.
36991708Sstevel 	 */
37001708Sstevel 
37011708Sstevel 	/*
37021708Sstevel 	 * Rename
37031708Sstevel 	 */
37041708Sstevel 	(*(void(*)())drmach_cpu_sram_va)(cr->data, &cr->ecode, &cr->earg);
37051708Sstevel 
37061708Sstevel 	/*
37071708Sstevel 	 * Rename operation complete. The physical address space
37081708Sstevel 	 * of the home and target memories have been swapped, the
37091708Sstevel 	 * routing data in the respective CASM entries have been
37101708Sstevel 	 * swapped, and LPA settings in the processor and schizo
37111708Sstevel 	 * devices have been reprogrammed accordingly.
37121708Sstevel 	 *
37131708Sstevel 	 * In the case of a cheetah processor, the E$ remains
37141708Sstevel 	 * populated with lines in state S that correspond to the
37151708Sstevel 	 * lines in the former home memory. Now that the physical
37161708Sstevel 	 * addresses have been swapped, these E$ lines correspond
37171708Sstevel 	 * to lines in the new home memory which are in state gM.
37181708Sstevel 	 * This combination is invalid. An additional E$ flush is
37191708Sstevel 	 * necessary to restore coherency. The E$ flush will cause
37201708Sstevel 	 * the lines of the new home memory for the flush region
37211708Sstevel 	 * to transition from state gM to gS. The former home memory
37221708Sstevel 	 * remains unmodified. This additional E$ flush has no effect
37231708Sstevel 	 * on a cheetah+ processor.
37241708Sstevel 	 */
37251708Sstevel 	flush_ecache_il(caddr, csize, lnsize);	/* inline version */
37261708Sstevel 
37271708Sstevel 	/*
37281708Sstevel 	 * The D$ and I$ must be flushed to ensure that coherency is
37291708Sstevel 	 * maintained. Any line in a cache that is in the valid
37301708Sstevel 	 * state has its corresponding line of the new home memory
37311708Sstevel 	 * in the gM state. This is an invalid condition. When the
37321708Sstevel 	 * flushes are complete the cache line states will be
37331708Sstevel 	 * resynchronized with those in the new home memory.
37341708Sstevel 	 */
37351708Sstevel 	flush_icache_il();			/* inline version */
37361708Sstevel 	flush_dcache_il();			/* inline version */
37371708Sstevel 	flush_pcache_il();			/* inline version */
37381708Sstevel 
37391708Sstevel copy_rename_end:
37401708Sstevel 
37411708Sstevel 	no_trap();
37421708Sstevel 
37431708Sstevel 	/* enable interrupts */
37441708Sstevel 	setpstate(pstate);
37451708Sstevel 
37461708Sstevel 	/* enable CE reporting */
37471708Sstevel 	set_error_enable(neer);
37481708Sstevel 
37491708Sstevel 	if (cr->ecode != DRMACH_CR_OK)
37501708Sstevel 		drmach_end_wait_xcall = drmach_rename_abort;
37511708Sstevel 
37521708Sstevel 	/*
37531708Sstevel 	 * XXX CHEETAH SUPPORT
37541708Sstevel 	 */
37551708Sstevel 	if (drmach_is_cheetah) {
37561708Sstevel 		mutex_enter(&drmach_iocage_lock);
37571708Sstevel 		drmach_iocage_mem_scrub(ecache_size * 2);
37581708Sstevel 		drmach_iocage_is_busy = 0;
37591708Sstevel 		cv_signal(&drmach_iocage_cv);
37601708Sstevel 		mutex_exit(&drmach_iocage_lock);
37611708Sstevel 	}
37621708Sstevel 
37631708Sstevel 	axq_iopause_disable_all();
37641708Sstevel 
37651708Sstevel 	xt_some(cpuset, drmach_end_wait_xcall, NULL, NULL);
37661708Sstevel }
37671708Sstevel 
37681708Sstevel static void drmach_io_dispose(drmachid_t);
37691708Sstevel static sbd_error_t *drmach_io_release(drmachid_t);
37701708Sstevel static sbd_error_t *drmach_io_status(drmachid_t, drmach_status_t *);
37711708Sstevel 
37721708Sstevel static sbd_error_t *
drmach_pci_new(drmach_device_t * proto,drmachid_t * idp)37731708Sstevel drmach_pci_new(drmach_device_t *proto, drmachid_t *idp)
37741708Sstevel {
37751708Sstevel 	drmach_node_t	*node = proto->node;
37761708Sstevel 	sbd_error_t	*err;
37771708Sstevel 	drmach_reg_t	 regs[3];
37781708Sstevel 	int		 rv;
37791708Sstevel 	int		 len = 0;
37801708Sstevel 
37811708Sstevel 	rv = node->n_getproplen(node, "reg", &len);
37821708Sstevel 	if (rv != 0 || len != sizeof (regs)) {
37831708Sstevel 		sbd_error_t *err;
37841708Sstevel 
37851708Sstevel 		/* pci nodes are expected to have regs */
37861708Sstevel 		err = drerr_new(1, ESTC_GETPROP,
378711066Srafael.vanoni@sun.com 		    "Device Node 0x%x: property %s",
378811066Srafael.vanoni@sun.com 		    (uint_t)node->get_dnode(node), "reg");
37891708Sstevel 		return (err);
37901708Sstevel 	}
37911708Sstevel 
37921708Sstevel 	rv = node->n_getprop(node, "reg", (void *)regs, sizeof (regs));
37931708Sstevel 	if (rv) {
37941708Sstevel 		sbd_error_t *err;
37951708Sstevel 
37961708Sstevel 		err = drerr_new(1, ESTC_GETPROP,
379711066Srafael.vanoni@sun.com 		    "Device Node 0x%x: property %s",
379811066Srafael.vanoni@sun.com 		    (uint_t)node->get_dnode(node), "reg");
37991708Sstevel 
38001708Sstevel 		return (err);
38011708Sstevel 	}
38021708Sstevel 
38031708Sstevel 	/*
38041708Sstevel 	 * Fix up unit number so that Leaf A has a lower unit number
38051708Sstevel 	 * than Leaf B.
38061708Sstevel 	 */
38071708Sstevel 	if ((proto->portid % 2) != 0) {
38081708Sstevel 		if ((regs[0].reg_addr_lo & 0x700000) == 0x700000)
38091708Sstevel 			proto->unum = 0;
38101708Sstevel 		else
38111708Sstevel 			proto->unum = 1;
38121708Sstevel 	} else {
38131708Sstevel 		if ((regs[0].reg_addr_lo & 0x700000) == 0x700000)
38141708Sstevel 			proto->unum = 2;
38151708Sstevel 		else
38161708Sstevel 			proto->unum = 3;
38171708Sstevel 	}
38181708Sstevel 
38191708Sstevel 	err = drmach_io_new(proto, idp);
38201708Sstevel 	if (err == NULL) {
38211708Sstevel 		drmach_io_t *self = *idp;
38221708Sstevel 
38231708Sstevel 		/* reassemble 64-bit base address */
38241708Sstevel 		self->scsr_pa  = (uint64_t)regs[1].reg_addr_hi << 32;
38251708Sstevel 		self->scsr_pa |= (uint64_t)regs[1].reg_addr_lo;
38261708Sstevel 	}
38271708Sstevel 
38281708Sstevel 	return (err);
38291708Sstevel }
38301708Sstevel 
38311708Sstevel static sbd_error_t *
drmach_io_new(drmach_device_t * proto,drmachid_t * idp)38321708Sstevel drmach_io_new(drmach_device_t *proto, drmachid_t *idp)
38331708Sstevel {
38341708Sstevel 	drmach_io_t	*ip;
38351708Sstevel 
38361708Sstevel 	ip = kmem_zalloc(sizeof (drmach_io_t), KM_SLEEP);
38371708Sstevel 	bcopy(proto, &ip->dev, sizeof (ip->dev));
38381708Sstevel 	ip->dev.node = drmach_node_dup(proto->node);
38391708Sstevel 	ip->dev.cm.isa = (void *)drmach_io_new;
38401708Sstevel 	ip->dev.cm.dispose = drmach_io_dispose;
38411708Sstevel 	ip->dev.cm.release = drmach_io_release;
38421708Sstevel 	ip->dev.cm.status = drmach_io_status;
38431708Sstevel 
384411311SSurya.Prakki@Sun.COM 	(void) snprintf(ip->dev.cm.name, sizeof (ip->dev.cm.name), "%s%d",
384511066Srafael.vanoni@sun.com 	    ip->dev.type, ip->dev.unum);
38461708Sstevel 
38471708Sstevel 	*idp = (drmachid_t)ip;
38481708Sstevel 	return (NULL);
38491708Sstevel }
38501708Sstevel 
38511708Sstevel static void
drmach_io_dispose(drmachid_t id)38521708Sstevel drmach_io_dispose(drmachid_t id)
38531708Sstevel {
38541708Sstevel 	drmach_io_t *self;
38551708Sstevel 
38561708Sstevel 	ASSERT(DRMACH_IS_IO_ID(id));
38571708Sstevel 
38581708Sstevel 	self = id;
38591708Sstevel 	if (self->dev.node)
38601708Sstevel 		drmach_node_dispose(self->dev.node);
38611708Sstevel 
38621708Sstevel 	kmem_free(self, sizeof (*self));
38631708Sstevel }
38641708Sstevel 
38651708Sstevel /*ARGSUSED*/
38661708Sstevel sbd_error_t *
drmach_pre_op(int cmd,drmachid_t id,drmach_opts_t * opts)38671708Sstevel drmach_pre_op(int cmd, drmachid_t id, drmach_opts_t *opts)
38681708Sstevel {
38691708Sstevel 	drmach_board_t	*bp = (drmach_board_t *)id;
38701708Sstevel 	sbd_error_t	*err = NULL;
38711708Sstevel 
38721708Sstevel 	if (id && DRMACH_IS_BOARD_ID(id)) {
38731708Sstevel 		switch (cmd) {
38741708Sstevel 			case SBD_CMD_TEST:
38751708Sstevel 			case SBD_CMD_STATUS:
38761708Sstevel 			case SBD_CMD_GETNCM:
38771708Sstevel 				break;
38781708Sstevel 			case SBD_CMD_CONNECT:
38791708Sstevel 				if (bp->connected)
38801708Sstevel 					err = drerr_new(0, ESBD_STATE, NULL);
38811708Sstevel 
38821708Sstevel 				if (bp->cond == SBD_COND_UNUSABLE)
38831708Sstevel 					err = drerr_new(0,
388411066Srafael.vanoni@sun.com 					    ESBD_FATAL_STATE, NULL);
38851708Sstevel 				break;
38861708Sstevel 			case SBD_CMD_DISCONNECT:
38871708Sstevel 				if (!bp->connected)
38881708Sstevel 					err = drerr_new(0, ESBD_STATE, NULL);
38891708Sstevel 
38901708Sstevel 				if (bp->cond == SBD_COND_UNUSABLE)
38911708Sstevel 					err = drerr_new(0,
389211066Srafael.vanoni@sun.com 					    ESBD_FATAL_STATE, NULL);
38931708Sstevel 				break;
38941708Sstevel 			default:
38951708Sstevel 				if (bp->cond == SBD_COND_UNUSABLE)
38961708Sstevel 					err = drerr_new(0,
389711066Srafael.vanoni@sun.com 					    ESBD_FATAL_STATE, NULL);
38981708Sstevel 				break;
38991708Sstevel 
39001708Sstevel 		}
39011708Sstevel 	}
39021708Sstevel 
39031708Sstevel 	return (err);
39041708Sstevel }
39051708Sstevel 
39061708Sstevel /*ARGSUSED*/
39071708Sstevel sbd_error_t *
drmach_post_op(int cmd,drmachid_t id,drmach_opts_t * opts)39081708Sstevel drmach_post_op(int cmd, drmachid_t id, drmach_opts_t *opts)
39091708Sstevel {
39101708Sstevel 	return (NULL);
39111708Sstevel }
39121708Sstevel 
39131708Sstevel sbd_error_t *
drmach_board_assign(int bnum,drmachid_t * id)39141708Sstevel drmach_board_assign(int bnum, drmachid_t *id)
39151708Sstevel {
39161708Sstevel 	sbd_error_t	*err = NULL;
39171708Sstevel 	caddr_t		obufp;
39181708Sstevel 
39191708Sstevel 	if (!drmach_initialized && drmach_init() == -1) {
39201708Sstevel 		err = DRMACH_INTERNAL_ERROR();
39211708Sstevel 	}
39221708Sstevel 
39231708Sstevel 	rw_enter(&drmach_boards_rwlock, RW_WRITER);
39241708Sstevel 
39251708Sstevel 	if (!err) {
39261708Sstevel 		if (drmach_array_get(drmach_boards, bnum, id) == -1) {
39271708Sstevel 			err = drerr_new(0, ESTC_BNUM, "%d", bnum);
39281708Sstevel 		} else {
39291708Sstevel 			drmach_board_t	*bp;
39301708Sstevel 
39311708Sstevel 			if (*id)
39321708Sstevel 				rw_downgrade(&drmach_boards_rwlock);
39331708Sstevel 
39341708Sstevel 			obufp = kmem_zalloc(sizeof (dr_proto_hdr_t), KM_SLEEP);
39351708Sstevel 			err = drmach_mbox_trans(DRMSG_ASSIGN, bnum, obufp,
393611066Srafael.vanoni@sun.com 			    sizeof (dr_proto_hdr_t), (caddr_t)NULL, 0);
39371708Sstevel 			kmem_free(obufp, sizeof (dr_proto_hdr_t));
39381708Sstevel 
39391708Sstevel 			if (!err) {
39401708Sstevel 				bp = *id;
39411708Sstevel 				if (!*id)
39421708Sstevel 					bp = *id  =
39431708Sstevel 					    (drmachid_t)drmach_board_new(bnum);
39441708Sstevel 				bp->assigned = 1;
39451708Sstevel 			}
39461708Sstevel 		}
39471708Sstevel 	}
39481708Sstevel 	rw_exit(&drmach_boards_rwlock);
39491708Sstevel 	return (err);
39501708Sstevel }
39511708Sstevel 
39521708Sstevel static uint_t
drmach_board_non_panther_cpus(gdcd_t * gdcd,uint_t exp,uint_t slot)39531708Sstevel drmach_board_non_panther_cpus(gdcd_t *gdcd, uint_t exp, uint_t slot)
39541708Sstevel {
39551708Sstevel 	uint_t	port, port_start, port_end;
39561708Sstevel 	uint_t	non_panther_cpus = 0;
39571708Sstevel 	uint_t	impl;
39581708Sstevel 
39591708Sstevel 	ASSERT(gdcd != NULL);
39601708Sstevel 
39611708Sstevel 	/*
39621708Sstevel 	 * Determine PRD port indices based on slot location.
39631708Sstevel 	 */
39641708Sstevel 	switch (slot) {
39651708Sstevel 	case 0:
39661708Sstevel 		port_start = 0;
39671708Sstevel 		port_end = 3;
39681708Sstevel 		break;
39691708Sstevel 	case 1:
39701708Sstevel 		port_start = 4;
39711708Sstevel 		port_end = 5;
39721708Sstevel 		break;
39731708Sstevel 	default:
39741708Sstevel 		ASSERT(0);
39751708Sstevel 		/* check all */
39761708Sstevel 		port_start = 0;
39771708Sstevel 		port_end = 5;
39781708Sstevel 		break;
39791708Sstevel 	}
39801708Sstevel 
39811708Sstevel 	for (port = port_start; port <= port_end; port++) {
39821708Sstevel 		if (gdcd->dcd_prd[exp][port].prd_ptype == SAFPTYPE_CPU &&
39831708Sstevel 		    RSV_GOOD(gdcd->dcd_prd[exp][port].prd_prsv)) {
39841708Sstevel 			/*
39851708Sstevel 			 * This Safari port passed POST and represents a
39861708Sstevel 			 * cpu, so check the implementation.
39871708Sstevel 			 */
39881708Sstevel 			impl = (gdcd->dcd_prd[exp][port].prd_ver_reg >> 32)
39891708Sstevel 			    & 0xffff;
39901708Sstevel 
39911708Sstevel 			switch (impl) {
39921708Sstevel 			case CHEETAH_IMPL:
39931708Sstevel 			case CHEETAH_PLUS_IMPL:
39941708Sstevel 			case JAGUAR_IMPL:
39951708Sstevel 				non_panther_cpus++;
39961708Sstevel 				break;
39971708Sstevel 			case PANTHER_IMPL:
39981708Sstevel 				break;
39991708Sstevel 			default:
40001708Sstevel 				ASSERT(0);
40011708Sstevel 				non_panther_cpus++;
40021708Sstevel 				break;
40031708Sstevel 			}
40041708Sstevel 		}
40051708Sstevel 	}
40061708Sstevel 
40071708Sstevel 	DRMACH_PR("drmach_board_non_panther_cpus: exp=%d, slot=%d, "
40081708Sstevel 	    "non_panther_cpus=%d", exp, slot, non_panther_cpus);
40091708Sstevel 
40101708Sstevel 	return (non_panther_cpus);
40111708Sstevel }
40121708Sstevel 
40131708Sstevel sbd_error_t *
drmach_board_connect(drmachid_t id,drmach_opts_t * opts)40141708Sstevel drmach_board_connect(drmachid_t id, drmach_opts_t *opts)
40151708Sstevel {
40161708Sstevel 	_NOTE(ARGUNUSED(opts))
40171708Sstevel 
40181708Sstevel 	drmach_board_t		*bp = (drmach_board_t *)id;
40191708Sstevel 	sbd_error_t		*err;
40201708Sstevel 	dr_mbox_msg_t		*obufp;
40211708Sstevel 	gdcd_t			*gdcd = NULL;
40221708Sstevel 	uint_t			exp, slot;
40231708Sstevel 	sc_gptwocfg_cookie_t	scc;
40241708Sstevel 	int			panther_pages_enabled;
40251708Sstevel 
40261708Sstevel 	if (!DRMACH_IS_BOARD_ID(id))
40271708Sstevel 		return (drerr_new(0, ESTC_INAPPROP, NULL));
40281708Sstevel 
40291708Sstevel 	/*
40301708Sstevel 	 * Build the casm info portion of the CLAIM message.
40311708Sstevel 	 */
40321708Sstevel 	obufp = kmem_zalloc(sizeof (dr_mbox_msg_t), KM_SLEEP);
40331708Sstevel 	mutex_enter(&drmach_slice_table_lock);
40341708Sstevel 	drmach_msg_memslice_init(obufp->msgdata.dm_cr.mem_slice);
40351708Sstevel 	drmach_msg_memregs_init(obufp->msgdata.dm_cr.mem_regs);
40361708Sstevel 	mutex_exit(&drmach_slice_table_lock);
40371708Sstevel 	err = drmach_mbox_trans(DRMSG_CLAIM, bp->bnum, (caddr_t)obufp,
403811066Srafael.vanoni@sun.com 	    sizeof (dr_mbox_msg_t), (caddr_t)NULL, 0);
40391708Sstevel 	kmem_free(obufp, sizeof (dr_mbox_msg_t));
40401708Sstevel 
40411708Sstevel 	if (err) {
40421708Sstevel 		/*
40431708Sstevel 		 * if mailbox timeout or unrecoverable error from SC,
40441708Sstevel 		 * board cannot be touched.  Mark the status as
40451708Sstevel 		 * unusable.
40461708Sstevel 		 */
40471708Sstevel 		if ((err->e_code == ESTC_SMS_ERR_UNRECOVERABLE) ||
404811066Srafael.vanoni@sun.com 		    (err->e_code == ESTC_MBXRPLY))
404911066Srafael.vanoni@sun.com 			bp->cond = SBD_COND_UNUSABLE;
40501708Sstevel 		return (err);
40511708Sstevel 	}
40521708Sstevel 
40531708Sstevel 	gdcd = drmach_gdcd_new();
40541708Sstevel 	if (gdcd == NULL) {
40551708Sstevel 		cmn_err(CE_WARN, "failed to read GDCD info for %s\n",
40561708Sstevel 		    bp->cm.name);
40571708Sstevel 		return (DRMACH_INTERNAL_ERROR());
40581708Sstevel 	}
40591708Sstevel 
40601708Sstevel 	/*
40611708Sstevel 	 * Read CPU SRAM DR buffer offset from GDCD.
40621708Sstevel 	 */
40631708Sstevel 	exp = DRMACH_BNUM2EXP(bp->bnum);
40641708Sstevel 	slot = DRMACH_BNUM2SLOT(bp->bnum);
40651708Sstevel 	bp->stardrb_offset =
40661708Sstevel 	    gdcd->dcd_slot[exp][slot].l1ss_cpu_drblock_xwd_offset << 3;
40671708Sstevel 	DRMACH_PR("%s: stardrb_offset=0x%lx\n", bp->cm.name,
40681708Sstevel 	    bp->stardrb_offset);
40691708Sstevel 
40701708Sstevel 	/*
40711708Sstevel 	 * Read board LPA setting from GDCD.
40721708Sstevel 	 */
40731708Sstevel 	bp->flags &= ~DRMACH_NULL_PROC_LPA;
40741708Sstevel 	if (gdcd->dcd_slot[exp][slot].l1ss_flags &
40751708Sstevel 	    L1SSFLG_THIS_L1_NULL_PROC_LPA) {
40761708Sstevel 		bp->flags |= DRMACH_NULL_PROC_LPA;
40771708Sstevel 		DRMACH_PR("%s: NULL proc LPA\n", bp->cm.name);
40781708Sstevel 	}
40791708Sstevel 
40801708Sstevel 	/*
40811708Sstevel 	 * XXX Until the Solaris large pages support heterogeneous cpu
40821708Sstevel 	 * domains, DR needs to prevent the addition of non-Panther cpus
40831708Sstevel 	 * to an all-Panther domain with large pages enabled.
40841708Sstevel 	 */
40851708Sstevel 	panther_pages_enabled = (page_num_pagesizes() > DEFAULT_MMU_PAGE_SIZES);
40861708Sstevel 	if (drmach_board_non_panther_cpus(gdcd, exp, slot) > 0 &&
40871708Sstevel 	    panther_pages_enabled && drmach_large_page_restriction) {
40881708Sstevel 		cmn_err(CE_WARN, "Domain shutdown is required to add a non-"
40891708Sstevel 		    "UltraSPARC-IV+ board into an all UltraSPARC-IV+ domain");
40901708Sstevel 		err = drerr_new(0, ESTC_SUPPORT, NULL);
40911708Sstevel 	}
40921708Sstevel 
40931708Sstevel 	if (err == NULL) {
40941708Sstevel 		/* do saf configurator stuff */
40951708Sstevel 		DRMACH_PR("calling sc_probe_board for bnum=%d\n", bp->bnum);
40961708Sstevel 		scc = sc_probe_board(bp->bnum);
40971708Sstevel 		if (scc == NULL)
40981708Sstevel 			err = drerr_new(0, ESTC_PROBE, bp->cm.name);
40991708Sstevel 	}
41001708Sstevel 
41011708Sstevel 	if (err) {
41021708Sstevel 		/* flush CDC srams */
41031708Sstevel 		if (axq_cdc_flush_all() != DDI_SUCCESS) {
41041708Sstevel 			goto out;
41051708Sstevel 		}
41061708Sstevel 
41071708Sstevel 		/*
41081708Sstevel 		 * Build the casm info portion of the UNCLAIM message.
41091708Sstevel 		 */
41101708Sstevel 		obufp = kmem_zalloc(sizeof (dr_mbox_msg_t), KM_SLEEP);
41111708Sstevel 		mutex_enter(&drmach_slice_table_lock);
41121708Sstevel 		drmach_msg_memslice_init(obufp->msgdata.dm_ur.mem_slice);
41131708Sstevel 		drmach_msg_memregs_init(obufp->msgdata.dm_ur.mem_regs);
41141708Sstevel 		mutex_exit(&drmach_slice_table_lock);
41151708Sstevel 		(void) drmach_mbox_trans(DRMSG_UNCLAIM, bp->bnum,
411611066Srafael.vanoni@sun.com 		    (caddr_t)obufp, sizeof (dr_mbox_msg_t),
411711066Srafael.vanoni@sun.com 		    (caddr_t)NULL, 0);
41181708Sstevel 
41191708Sstevel 		kmem_free(obufp, sizeof (dr_mbox_msg_t));
41201708Sstevel 
41211708Sstevel 		/*
41221708Sstevel 		 * we clear the connected flag just in case it would have
41231708Sstevel 		 * been set by a concurrent drmach_board_status() thread
41241708Sstevel 		 * before the UNCLAIM completed.
41251708Sstevel 		 */
41261708Sstevel 		bp->connected = 0;
41271708Sstevel 		goto out;
41281708Sstevel 	}
41291708Sstevel 
41301708Sstevel 	/*
41311708Sstevel 	 * Now that the board has been successfully attached, obtain
41321708Sstevel 	 * platform-specific DIMM serial id information for the board.
41331708Sstevel 	 */
41341708Sstevel 	if ((DRMACH_BNUM2SLOT(bp->bnum) == 0) &&
41351708Sstevel 	    plat_ecc_capability_sc_get(PLAT_ECC_DIMM_SID_MESSAGE)) {
41361708Sstevel 		(void) plat_request_mem_sids(DRMACH_BNUM2EXP(bp->bnum));
41371708Sstevel 	}
41381708Sstevel 
41391708Sstevel out:
41401708Sstevel 	if (gdcd != NULL)
41411708Sstevel 		drmach_gdcd_dispose(gdcd);
41421708Sstevel 
41431708Sstevel 	return (err);
41441708Sstevel }
41451708Sstevel 
41461708Sstevel static void
drmach_slice_table_update(drmach_board_t * bp,int invalidate)41471708Sstevel drmach_slice_table_update(drmach_board_t *bp, int invalidate)
41481708Sstevel {
41491708Sstevel 	static char		*axq_name = "address-extender-queue";
41501708Sstevel 	static dev_info_t	*axq_dip = NULL;
41511708Sstevel 	static int		 axq_exp = -1;
41521708Sstevel 	static int		 axq_slot;
41531708Sstevel 	int			 e, s, slice;
41541708Sstevel 
41551708Sstevel 	ASSERT(MUTEX_HELD(&drmach_slice_table_lock));
41561708Sstevel 
41571708Sstevel 	e = DRMACH_BNUM2EXP(bp->bnum);
41581708Sstevel 	if (invalidate) {
41591708Sstevel 		ASSERT(DRMACH_BNUM2SLOT(bp->bnum) == 0);
41601708Sstevel 
41611708Sstevel 		/* invalidate cached casm value */
41621708Sstevel 		drmach_slice_table[e] = 0;
41631708Sstevel 
41641708Sstevel 		/* invalidate cached axq info if for same exp */
41651708Sstevel 		if (e == axq_exp && axq_dip) {
41661708Sstevel 			ndi_rele_devi(axq_dip);
41671708Sstevel 			axq_dip = NULL;
41681708Sstevel 		}
41691708Sstevel 	}
41701708Sstevel 
41711708Sstevel 	if (axq_dip == NULL || !i_ddi_devi_attached(axq_dip)) {
41721708Sstevel 		int i, portid;
41731708Sstevel 
41741708Sstevel 		/* search for an attached slot0 axq instance */
41751708Sstevel 		for (i = 0; i < AXQ_MAX_EXP * AXQ_MAX_SLOT_PER_EXP; i++) {
41761708Sstevel 			if (axq_dip)
41771708Sstevel 				ndi_rele_devi(axq_dip);
41781708Sstevel 			axq_dip = ddi_find_devinfo(axq_name, i, 0);
41791708Sstevel 			if (axq_dip && DDI_CF2(axq_dip)) {
41801708Sstevel 				portid = ddi_getprop(DDI_DEV_T_ANY, axq_dip,
41811708Sstevel 				    DDI_PROP_DONTPASS, "portid", -1);
41821708Sstevel 				if (portid == -1) {
41831708Sstevel 					DRMACH_PR("cant get portid of axq "
41841708Sstevel 					    "instance %d\n", i);
41851708Sstevel 					continue;
41861708Sstevel 				}
41871708Sstevel 
41881708Sstevel 				axq_exp = (portid >> 5) & 0x1f;
41891708Sstevel 				axq_slot = portid & 1;
41901708Sstevel 
41911708Sstevel 				if (invalidate && axq_exp == e)
41921708Sstevel 					continue;
41931708Sstevel 
41941708Sstevel 				if (axq_slot == 0)
41951708Sstevel 					break;	/* found */
41961708Sstevel 			}
41971708Sstevel 		}
41981708Sstevel 
41991708Sstevel 		if (i == AXQ_MAX_EXP * AXQ_MAX_SLOT_PER_EXP) {
42001708Sstevel 			if (axq_dip) {
42011708Sstevel 				ndi_rele_devi(axq_dip);
42021708Sstevel 				axq_dip = NULL;
42031708Sstevel 			}
42041708Sstevel 			DRMACH_PR("drmach_slice_table_update: failed to "
42051708Sstevel 			    "update axq dip\n");
42061708Sstevel 			return;
42071708Sstevel 		}
42081708Sstevel 
42091708Sstevel 	}
42101708Sstevel 
42111708Sstevel 	ASSERT(axq_dip);
42121708Sstevel 	ASSERT(axq_slot == 0);
42131708Sstevel 
42141708Sstevel 	if (invalidate)
42151708Sstevel 		return;
42161708Sstevel 
42171708Sstevel 	s = DRMACH_BNUM2SLOT(bp->bnum);
421811066Srafael.vanoni@sun.com 	DRMACH_PR("using AXQ casm %d.%d for slot%d.%d\n", axq_exp, axq_slot,
421911066Srafael.vanoni@sun.com 	    e, s);
42201708Sstevel 
42211708Sstevel 	/* invalidate entry */
42221708Sstevel 	drmach_slice_table[e] &= ~0x20;
42231708Sstevel 
42241708Sstevel 	/*
42251708Sstevel 	 * find a slice that routes to expander e. If no match
42261708Sstevel 	 * is found, drmach_slice_table[e] will remain invalid.
42271708Sstevel 	 *
42281708Sstevel 	 * The CASM is a routing table indexed by slice number.
42291708Sstevel 	 * Each element in the table contains permission bits,
42301708Sstevel 	 * a destination expander number and a valid bit. The
42311708Sstevel 	 * valid bit must true for the element to be meaningful.
42321708Sstevel 	 *
42331708Sstevel 	 * CASM entry structure
42341708Sstevel 	 *   Bits 15..6 ignored
42351708Sstevel 	 *   Bit  5	valid
42361708Sstevel 	 *   Bits 0..4	expander number
42371708Sstevel 	 *
42381708Sstevel 	 * NOTE: the for loop is really enumerating the range of slices,
42391708Sstevel 	 * which is ALWAYS equal to the range of expanders. Hence,
42401708Sstevel 	 * AXQ_MAX_EXP is okay to use in this loop.
42411708Sstevel 	 */
42421708Sstevel 	for (slice = 0; slice < AXQ_MAX_EXP; slice++) {
42431708Sstevel 		uint32_t casm = axq_casm_read(axq_exp, axq_slot, slice);
42441708Sstevel 
42451708Sstevel 		if ((casm & 0x20) && (casm & 0x1f) == e)
42461708Sstevel 			drmach_slice_table[e] = 0x20 | slice;
42471708Sstevel 	}
42481708Sstevel }
42491708Sstevel 
42501708Sstevel /*
42511708Sstevel  * Get base and bound PAs for slot 1 board lpa programming
42521708Sstevel  * If a cpu/mem board is present in the same expander, use slice
42531708Sstevel  * information corresponding to the CASM.  Otherwise, set base and
42541708Sstevel  * bound PAs to 0.
42551708Sstevel  */
42561708Sstevel static void
drmach_lpa_bb_get(drmach_board_t * s1bp,uint64_t * basep,uint64_t * boundp)42571708Sstevel drmach_lpa_bb_get(drmach_board_t *s1bp, uint64_t *basep, uint64_t *boundp)
42581708Sstevel {
42591708Sstevel 	drmachid_t s0id;
42601708Sstevel 
42611708Sstevel 	ASSERT(mutex_owned(&drmach_slice_table_lock));
42621708Sstevel 	ASSERT(DRMACH_BNUM2SLOT(s1bp->bnum) == 1);
42631708Sstevel 
42641708Sstevel 	*basep = *boundp = 0;
42651708Sstevel 	if (drmach_array_get(drmach_boards, s1bp->bnum - 1, &s0id) == 0 &&
426611066Srafael.vanoni@sun.com 	    s0id != 0) {
42671708Sstevel 
42681708Sstevel 		uint32_t slice;
426911066Srafael.vanoni@sun.com 		if ((slice = drmach_slice_table[DRMACH_BNUM2EXP(s1bp->bnum)])
427011066Srafael.vanoni@sun.com 		    & 0x20) {
42711708Sstevel 			*basep = DRMACH_SLICE_TO_PA(slice & DRMACH_SLICE_MASK);
42721708Sstevel 			*boundp = *basep + DRMACH_MEM_SLICE_SIZE;
42731708Sstevel 		}
42741708Sstevel 	}
42751708Sstevel }
42761708Sstevel 
42771708Sstevel 
42781708Sstevel /*
42791708Sstevel  * Reprogram slot 1 lpa's as required.
42801708Sstevel  * The purpose of this routine is maintain the LPA settings of the devices
42811708Sstevel  * in slot 1. To date we know Schizo and Cheetah are the only devices that
42821708Sstevel  * require this attention. The LPA setting must match the slice field in the
42831708Sstevel  * CASM element for the local expander. This field is guaranteed to be
42841708Sstevel  * programmed in accordance with the cacheable address space on the slot 0
42851708Sstevel  * board of the local expander. If no memory is present on the slot 0 board,
42861708Sstevel  * there is no cacheable address space and, hence, the CASM slice field will
42871708Sstevel  * be zero or its valid bit will be false (or both).
42881708Sstevel  */
42891708Sstevel 
42901708Sstevel static void
drmach_slot1_lpa_set(drmach_board_t * bp)42911708Sstevel drmach_slot1_lpa_set(drmach_board_t *bp)
42921708Sstevel {
42931708Sstevel 	drmachid_t	id;
42941708Sstevel 	drmach_board_t	*s1bp = NULL;
42951708Sstevel 	int		rv, idx, is_maxcat = 1;
42961708Sstevel 	uint64_t	last_scsr_pa = 0;
42971708Sstevel 	uint64_t	new_basepa, new_boundpa;
42981708Sstevel 
42991708Sstevel 	if (DRMACH_BNUM2SLOT(bp->bnum)) {
43001708Sstevel 		s1bp = bp;
43011708Sstevel 		if (s1bp->devices == NULL) {
43021708Sstevel 			DRMACH_PR("drmach...lpa_set: slot1=%d not present",
430311066Srafael.vanoni@sun.com 			    bp->bnum);
43041708Sstevel 			return;
43051708Sstevel 		}
43061708Sstevel 	} else {
43071708Sstevel 		rv = drmach_array_get(drmach_boards, bp->bnum + 1, &id);
43081708Sstevel 		/* nothing to do when board is not found or has no devices */
43091708Sstevel 		s1bp = id;
43101708Sstevel 		if (rv == -1 || s1bp == NULL || s1bp->devices == NULL) {
43111708Sstevel 			DRMACH_PR("drmach...lpa_set: slot1=%d not present",
431211066Srafael.vanoni@sun.com 			    bp->bnum + 1);
43131708Sstevel 			return;
43141708Sstevel 		}
43151708Sstevel 		ASSERT(DRMACH_IS_BOARD_ID(id));
43161708Sstevel 	}
43171708Sstevel 	mutex_enter(&drmach_slice_table_lock);
43181708Sstevel 	drmach_lpa_bb_get(s1bp, &new_basepa, &new_boundpa);
43191708Sstevel 	DRMACH_PR("drmach_...lpa_set: bnum=%d base=0x%lx bound=0x%lx\n",
432011066Srafael.vanoni@sun.com 	    s1bp->bnum, new_basepa, new_boundpa);
43211708Sstevel 
43221708Sstevel 	rv = drmach_array_first(s1bp->devices, &idx, &id);
43231708Sstevel 	while (rv == 0) {
43241708Sstevel 		if (DRMACH_IS_IO_ID(id)) {
43251708Sstevel 			drmach_io_t *io = id;
43261708Sstevel 
43271708Sstevel 			is_maxcat = 0;
43281708Sstevel 
43291708Sstevel 			/*
43301708Sstevel 			 * Skip all non-Schizo IO devices (only IO nodes
43311708Sstevel 			 * that are Schizo devices have non-zero scsr_pa).
43321708Sstevel 			 * Filter out "other" leaf to avoid writing to the
43331708Sstevel 			 * same Schizo Control/Status Register twice.
43341708Sstevel 			 */
43351708Sstevel 			if (io->scsr_pa && io->scsr_pa != last_scsr_pa) {
43361708Sstevel 				uint64_t scsr;
43371708Sstevel 
43381708Sstevel 				scsr  = lddphysio(io->scsr_pa);
43391708Sstevel 				DRMACH_PR("drmach...lpa_set: old scsr=0x%lx\n",
434011066Srafael.vanoni@sun.com 				    scsr);
43411708Sstevel 				scsr &= ~(DRMACH_LPA_BASE_MASK |
434211066Srafael.vanoni@sun.com 				    DRMACH_LPA_BND_MASK);
43431708Sstevel 				scsr |= DRMACH_PA_TO_LPA_BASE(new_basepa);
43441708Sstevel 				scsr |= DRMACH_PA_TO_LPA_BND(new_boundpa);
43451708Sstevel 
43461708Sstevel 				stdphysio(io->scsr_pa, scsr);
43471708Sstevel 				DRMACH_PR("drmach...lpa_set: new scsr=0x%lx\n",
434811066Srafael.vanoni@sun.com 				    scsr);
43491708Sstevel 
43501708Sstevel 				last_scsr_pa = io->scsr_pa;
43511708Sstevel 			}
43521708Sstevel 		}
43531708Sstevel 		rv = drmach_array_next(s1bp->devices, &idx, &id);
43541708Sstevel 	}
43551708Sstevel 
43561708Sstevel 	if (is_maxcat && DRMACH_L1_SET_LPA(s1bp) && drmach_reprogram_lpa) {
43571708Sstevel 		extern xcfunc_t	drmach_set_lpa;
43581708Sstevel 
43591708Sstevel 		DRMACH_PR("reprogramming maxcat lpa's");
43601708Sstevel 
43611708Sstevel 		mutex_enter(&cpu_lock);
43621708Sstevel 		rv = drmach_array_first(s1bp->devices, &idx, &id);
43631708Sstevel 		while (rv == 0 && id != NULL) {
43641708Sstevel 			if (DRMACH_IS_CPU_ID(id)) {
43651708Sstevel 				int ntries;
43661708Sstevel 				processorid_t cpuid;
43671708Sstevel 
43681708Sstevel 				cpuid = ((drmach_cpu_t *)id)->cpuid;
43691708Sstevel 
43701708Sstevel 				/*
43711708Sstevel 				 * Check for unconfigured or powered-off
43721708Sstevel 				 * MCPUs.  If CPU_READY flag is clear, the
43731708Sstevel 				 * MCPU cannot be xcalled.
43741708Sstevel 				 */
43751708Sstevel 				if ((cpu[cpuid] == NULL) ||
437611066Srafael.vanoni@sun.com 				    (cpu[cpuid]->cpu_flags &
437711066Srafael.vanoni@sun.com 				    CPU_READY) == 0) {
43781708Sstevel 
43791708Sstevel 					rv = drmach_array_next(s1bp->devices,
438011066Srafael.vanoni@sun.com 					    &idx, &id);
43811708Sstevel 					continue;
43821708Sstevel 				}
43831708Sstevel 
43841708Sstevel 				/*
43851708Sstevel 				 * XXX CHEETAH SUPPORT
43861708Sstevel 				 * for cheetah, we need to clear iocage
43871708Sstevel 				 * memory since it will be used for e$ flush
43881708Sstevel 				 * in drmach_set_lpa.
43891708Sstevel 				 */
43901708Sstevel 				if (drmach_is_cheetah) {
43911708Sstevel 					mutex_enter(&drmach_iocage_lock);
43921708Sstevel 					while (drmach_iocage_is_busy)
43931708Sstevel 						cv_wait(&drmach_iocage_cv,
439411066Srafael.vanoni@sun.com 						    &drmach_iocage_lock);
43951708Sstevel 					drmach_iocage_is_busy = 1;
439611066Srafael.vanoni@sun.com 					drmach_iocage_mem_scrub(ecache_size *
439711066Srafael.vanoni@sun.com 					    2);
43981708Sstevel 					mutex_exit(&drmach_iocage_lock);
43991708Sstevel 				}
44001708Sstevel 
44011708Sstevel 				/*
44021708Sstevel 				 * drmach_slice_table[*]
44031708Sstevel 				 *	bit 5	valid
44041708Sstevel 				 *	bit 0:4	slice number
44051708Sstevel 				 *
44061708Sstevel 				 * drmach_xt_mb[*] format for drmach_set_lpa
44071708Sstevel 				 *	bit 7	valid
44081708Sstevel 				 *	bit 6	set null LPA
44091708Sstevel 				 *			(overrides bits 0:4)
44101708Sstevel 				 *	bit 0:4	slice number
44111708Sstevel 				 *
44121708Sstevel 				 * drmach_set_lpa derives processor CBASE and
44131708Sstevel 				 * CBND from bits 6 and 0:4 of drmach_xt_mb.
44141708Sstevel 				 * If bit 6 is set, then CBASE = CBND = 0.
44151708Sstevel 				 * Otherwise, CBASE = slice number;
44161708Sstevel 				 * CBND = slice number + 1.
44171708Sstevel 				 * No action is taken if bit 7 is zero.
44181708Sstevel 				 */
44191708Sstevel 
44201708Sstevel 				mutex_enter(&drmach_xt_mb_lock);
44211708Sstevel 				bzero((void *)drmach_xt_mb,
44221708Sstevel 				    drmach_xt_mb_size);
44231708Sstevel 
44241708Sstevel 				if (new_basepa == 0 && new_boundpa == 0)
44251708Sstevel 					drmach_xt_mb[cpuid] = 0x80 | 0x40;
44261708Sstevel 				else
44271708Sstevel 					drmach_xt_mb[cpuid] = 0x80 |
442811066Srafael.vanoni@sun.com 					    DRMACH_PA_TO_SLICE(new_basepa);
44291708Sstevel 
44301708Sstevel 				drmach_xt_ready = 0;
44311708Sstevel 
44321708Sstevel 				xt_one(cpuid, drmach_set_lpa, NULL, NULL);
44331708Sstevel 
44341708Sstevel 				ntries = drmach_cpu_ntries;
44351708Sstevel 				while (!drmach_xt_ready && ntries) {
44361708Sstevel 					DELAY(drmach_cpu_delay);
44371708Sstevel 					ntries--;
44381708Sstevel 				}
44391708Sstevel 				mutex_exit(&drmach_xt_mb_lock);
44401708Sstevel 				drmach_xt_ready = 0;
44411708Sstevel 
44421708Sstevel 				/*
44431708Sstevel 				 * XXX CHEETAH SUPPORT
44441708Sstevel 				 * for cheetah, we need to clear iocage
44451708Sstevel 				 * memory since it was used for e$ flush
44461708Sstevel 				 * in performed drmach_set_lpa.
44471708Sstevel 				 */
44481708Sstevel 				if (drmach_is_cheetah) {
44491708Sstevel 					mutex_enter(&drmach_iocage_lock);
445011066Srafael.vanoni@sun.com 					drmach_iocage_mem_scrub(ecache_size *
445111066Srafael.vanoni@sun.com 					    2);
44521708Sstevel 					drmach_iocage_is_busy = 0;
44531708Sstevel 					cv_signal(&drmach_iocage_cv);
44541708Sstevel 					mutex_exit(&drmach_iocage_lock);
44551708Sstevel 				}
44561708Sstevel 			}
44571708Sstevel 			rv = drmach_array_next(s1bp->devices, &idx, &id);
44581708Sstevel 		}
44591708Sstevel 		mutex_exit(&cpu_lock);
44601708Sstevel 	}
44611708Sstevel 	mutex_exit(&drmach_slice_table_lock);
44621708Sstevel }
44631708Sstevel 
44641708Sstevel /*
44651708Sstevel  * Return the number of connected Panther boards in the domain.
44661708Sstevel  */
44671708Sstevel static int
drmach_panther_boards(void)44681708Sstevel drmach_panther_boards(void)
44691708Sstevel {
44701708Sstevel 	int		rv;
44711708Sstevel 	int		b_idx;
44721708Sstevel 	drmachid_t	b_id;
44731708Sstevel 	drmach_board_t	*bp;
44741708Sstevel 	int		npanther = 0;
44751708Sstevel 
44761708Sstevel 	rv = drmach_array_first(drmach_boards, &b_idx, &b_id);
44771708Sstevel 	while (rv == 0) {
44781708Sstevel 		ASSERT(DRMACH_IS_BOARD_ID(b_id));
44791708Sstevel 		bp = b_id;
44801708Sstevel 
44811708Sstevel 		if (IS_PANTHER(bp->cpu_impl))
44821708Sstevel 			npanther++;
44831708Sstevel 
44841708Sstevel 		rv = drmach_array_next(drmach_boards, &b_idx, &b_id);
44851708Sstevel 	}
44861708Sstevel 
44871708Sstevel 	return (npanther);
44881708Sstevel }
44891708Sstevel 
44901708Sstevel /*ARGSUSED*/
44911708Sstevel sbd_error_t *
drmach_board_disconnect(drmachid_t id,drmach_opts_t * opts)44921708Sstevel drmach_board_disconnect(drmachid_t id, drmach_opts_t *opts)
44931708Sstevel {
44941708Sstevel 	drmach_board_t	*bp;
44951708Sstevel 	dr_mbox_msg_t	*obufp;
44961708Sstevel 	sbd_error_t	*err = NULL;
44971708Sstevel 
44981708Sstevel 	sc_gptwocfg_cookie_t	scc;
44991708Sstevel 
45001708Sstevel 	if (!DRMACH_IS_BOARD_ID(id))
45011708Sstevel 		return (drerr_new(0, ESTC_INAPPROP, NULL));
45021708Sstevel 	bp = id;
45031708Sstevel 
45041708Sstevel 	/*
45051708Sstevel 	 * Build the casm info portion of the UNCLAIM message.
45061708Sstevel 	 * This must be done prior to calling for saf configurator
45071708Sstevel 	 * deprobe, to ensure that the associated axq instance
45081708Sstevel 	 * is not detached.
45091708Sstevel 	 */
45101708Sstevel 	obufp = kmem_zalloc(sizeof (dr_mbox_msg_t), KM_SLEEP);
45111708Sstevel 	mutex_enter(&drmach_slice_table_lock);
45121708Sstevel 	drmach_msg_memslice_init(obufp->msgdata.dm_ur.mem_slice);
45131708Sstevel 
45141708Sstevel 	/*
45151708Sstevel 	 * If disconnecting slot 0 board, update the casm slice table
45161708Sstevel 	 * info now, for use by drmach_slot1_lpa_set()
45171708Sstevel 	 */
45181708Sstevel 	if (DRMACH_BNUM2SLOT(bp->bnum) == 0)
451911066Srafael.vanoni@sun.com 		drmach_slice_table_update(bp, 1);
45201708Sstevel 
45211708Sstevel 	drmach_msg_memregs_init(obufp->msgdata.dm_ur.mem_regs);
45221708Sstevel 	mutex_exit(&drmach_slice_table_lock);
45231708Sstevel 
45241708Sstevel 	/*
45251708Sstevel 	 * Update LPA information for slot1 board
45261708Sstevel 	 */
45271708Sstevel 	drmach_slot1_lpa_set(bp);
45281708Sstevel 
45291708Sstevel 	/* disable and flush CDC */
45301708Sstevel 	if (axq_cdc_disable_flush_all() != DDI_SUCCESS) {
45311708Sstevel 		axq_cdc_enable_all();	/* paranoia */
45321708Sstevel 		err = DRMACH_INTERNAL_ERROR();
45331708Sstevel 	}
45341708Sstevel 
45351708Sstevel 	/*
45361708Sstevel 	 * call saf configurator for deprobe
45371708Sstevel 	 * It's done now before sending an UNCLAIM message because
45381708Sstevel 	 * IKP will probe boards it doesn't know about <present at boot>
45391708Sstevel 	 * prior to unprobing them.  If this happens after sending the
45401708Sstevel 	 * UNCLAIM, it will cause a dstop for domain transgression error.
45411708Sstevel 	 */
45421708Sstevel 
45431708Sstevel 	if (!err) {
45441708Sstevel 		scc = sc_unprobe_board(bp->bnum);
45451708Sstevel 		axq_cdc_enable_all();
45461708Sstevel 		if (scc != NULL) {
45471708Sstevel 			err = drerr_new(0, ESTC_DEPROBE, bp->cm.name);
45481708Sstevel 		}
45491708Sstevel 	}
45501708Sstevel 
45511708Sstevel 	/*
45521708Sstevel 	 * If disconnecting a board from a Panther domain, wait a fixed-
45531708Sstevel 	 * time delay for pending Safari transactions to complete on the
45541708Sstevel 	 * disconnecting board's processors.  The bus sync list read used
45551708Sstevel 	 * in drmach_shutdown_asm to synchronize with outstanding Safari
45561708Sstevel 	 * transactions assumes no read-bypass-write mode for all memory
45571708Sstevel 	 * controllers.  Since Panther supports read-bypass-write, a
45581708Sstevel 	 * delay is used that is slightly larger than the maximum Safari
45591708Sstevel 	 * timeout value in the Safari/Fireplane Config Reg.
45601708Sstevel 	 */
45611708Sstevel 	if (drmach_panther_boards() > 0 || drmach_unclaim_delay_all) {
456211066Srafael.vanoni@sun.com 		clock_t	stime = ddi_get_lbolt();
45631708Sstevel 
45641708Sstevel 		delay(drv_usectohz(drmach_unclaim_usec_delay));
45651708Sstevel 
456611066Srafael.vanoni@sun.com 		stime = ddi_get_lbolt() - stime;
45671708Sstevel 		DRMACH_PR("delayed %ld ticks (%ld secs) before disconnecting "
45681708Sstevel 		    "board %s from domain\n", stime, stime / hz, bp->cm.name);
45691708Sstevel 	}
45701708Sstevel 
45711708Sstevel 	if (!err) {
45721708Sstevel 		obufp->msgdata.dm_ur.mem_clear = 0;
45731708Sstevel 
45741708Sstevel 		err = drmach_mbox_trans(DRMSG_UNCLAIM, bp->bnum, (caddr_t)obufp,
457511066Srafael.vanoni@sun.com 		    sizeof (dr_mbox_msg_t), (caddr_t)NULL, 0);
45761708Sstevel 
45771708Sstevel 		if (err) {
45781708Sstevel 			/*
45791708Sstevel 			 * if mailbox timeout or unrecoverable error from SC,
45801708Sstevel 			 * board cannot be touched.  Mark the status as
45811708Sstevel 			 * unusable.
45821708Sstevel 			 */
45831708Sstevel 			if ((err->e_code == ESTC_SMS_ERR_UNRECOVERABLE) ||
458411066Srafael.vanoni@sun.com 			    (err->e_code == ESTC_MBXRPLY))
458511066Srafael.vanoni@sun.com 				bp->cond = SBD_COND_UNUSABLE;
45861708Sstevel 			else {
45871708Sstevel 				DRMACH_PR("UNCLAIM failed for bnum=%d\n",
458811066Srafael.vanoni@sun.com 				    bp->bnum);
45891708Sstevel 				DRMACH_PR("calling sc_probe_board: bnum=%d\n",
459011066Srafael.vanoni@sun.com 				    bp->bnum);
45911708Sstevel 				scc = sc_probe_board(bp->bnum);
45921708Sstevel 				if (scc == NULL) {
45931708Sstevel 					cmn_err(CE_WARN,
45941708Sstevel 					"sc_probe_board failed for bnum=%d",
459511066Srafael.vanoni@sun.com 					    bp->bnum);
45961708Sstevel 				} else {
45971708Sstevel 					if (DRMACH_BNUM2SLOT(bp->bnum) == 0) {
45981708Sstevel 						mutex_enter(
45991708Sstevel 						    &drmach_slice_table_lock);
46001708Sstevel 						drmach_slice_table_update(bp,
46011708Sstevel 						    0);
46021708Sstevel 						mutex_exit(
46031708Sstevel 						    &drmach_slice_table_lock);
46041708Sstevel 					}
46051708Sstevel 					drmach_slot1_lpa_set(bp);
46061708Sstevel 				}
46071708Sstevel 			}
46081708Sstevel 		} else {
46091708Sstevel 			bp->connected = 0;
46101708Sstevel 			/*
46111708Sstevel 			 * Now that the board has been successfully detached,
46121708Sstevel 			 * discard platform-specific DIMM serial id information
46131708Sstevel 			 * for the board.
46141708Sstevel 			 */
46151708Sstevel 			if ((DRMACH_BNUM2SLOT(bp->bnum) == 0) &&
46161708Sstevel 			    plat_ecc_capability_sc_get(
46171708Sstevel 			    PLAT_ECC_DIMM_SID_MESSAGE)) {
46181708Sstevel 				(void) plat_discard_mem_sids(
46191708Sstevel 				    DRMACH_BNUM2EXP(bp->bnum));
46201708Sstevel 			}
46211708Sstevel 		}
46221708Sstevel 	}
46231708Sstevel 	kmem_free(obufp, sizeof (dr_mbox_msg_t));
46241708Sstevel 
46251708Sstevel 	return (err);
46261708Sstevel }
46271708Sstevel 
46281708Sstevel static int
drmach_get_portid(drmach_node_t * np)46291708Sstevel drmach_get_portid(drmach_node_t *np)
46301708Sstevel {
46311708Sstevel 	drmach_node_t	pp;
46321708Sstevel 	int		portid;
46331708Sstevel 	char		type[OBP_MAXPROPNAME];
46341708Sstevel 
46351708Sstevel 	if (np->n_getprop(np, "portid", &portid, sizeof (portid)) == 0)
46361708Sstevel 		return (portid);
46371708Sstevel 
46381708Sstevel 	/*
46391708Sstevel 	 * Get the device_type property to see if we should
46401708Sstevel 	 * continue processing this node.
46411708Sstevel 	 */
46421708Sstevel 	if (np->n_getprop(np, "device_type", &type, sizeof (type)) != 0)
46431708Sstevel 		return (-1);
46441708Sstevel 
46451708Sstevel 	/*
46461708Sstevel 	 * If the device is a CPU without a 'portid' property,
46471708Sstevel 	 * it is a CMP core. For such cases, the parent node
46481708Sstevel 	 * has the portid.
46491708Sstevel 	 */
46501708Sstevel 	if (strcmp(type, DRMACH_CPU_NAMEPROP) == 0) {
46511708Sstevel 		if (np->get_parent(np, &pp) != 0)
46521708Sstevel 			return (-1);
46531708Sstevel 
46541708Sstevel 		if (pp.n_getprop(&pp, "portid", &portid, sizeof (portid)) == 0)
46551708Sstevel 			return (portid);
46561708Sstevel 	}
46571708Sstevel 
46581708Sstevel 	return (-1);
46591708Sstevel }
46601708Sstevel 
46611708Sstevel /*
46621708Sstevel  * This is a helper function to determine if a given
46631708Sstevel  * node should be considered for a dr operation according
46641708Sstevel  * to predefined dr type nodes and the node's name.
46651708Sstevel  * Formal Parameter : The name of a device node.
46661708Sstevel  * Return Value: -1, name does not map to a valid dr type.
46671708Sstevel  *		 A value greater or equal to 0, name is a valid dr type.
46681708Sstevel  */
46691708Sstevel static int
drmach_name2type_idx(char * name)46701708Sstevel drmach_name2type_idx(char *name)
46711708Sstevel {
46721708Sstevel 	int 	index, ntypes;
46731708Sstevel 
46741708Sstevel 	if (name == NULL)
46751708Sstevel 		return (-1);
46761708Sstevel 
46771708Sstevel 	/*
46781708Sstevel 	 * Determine how many possible types are currently supported
46791708Sstevel 	 * for dr.
46801708Sstevel 	 */
46811708Sstevel 	ntypes = sizeof (drmach_name2type) / sizeof (drmach_name2type[0]);
46821708Sstevel 
46831708Sstevel 	/* Determine if the node's name correspond to a predefined type. */
46841708Sstevel 	for (index = 0; index < ntypes; index++) {
46851708Sstevel 		if (strcmp(drmach_name2type[index].name, name) == 0)
46861708Sstevel 			/* The node is an allowed type for dr. */
46871708Sstevel 			return (index);
46881708Sstevel 	}
46891708Sstevel 
46901708Sstevel 	/*
46911708Sstevel 	 * If the name of the node does not map to any of the
46921708Sstevel 	 * types in the array drmach_name2type then the node is not of
46931708Sstevel 	 * interest to dr.
46941708Sstevel 	 */
46951708Sstevel 	return (-1);
46961708Sstevel }
46971708Sstevel 
46981708Sstevel static int
drmach_board_find_devices_cb(drmach_node_walk_args_t * args)46991708Sstevel drmach_board_find_devices_cb(drmach_node_walk_args_t *args)
47001708Sstevel {
47011708Sstevel 	drmach_node_t			*node = args->node;
47021708Sstevel 	drmach_board_cb_data_t		*data = args->data;
47031708Sstevel 	drmach_board_t			*obj = data->obj;
47041708Sstevel 
47051708Sstevel 	int		rv, portid;
47061708Sstevel 	drmachid_t	id;
47071708Sstevel 	drmach_device_t	*device;
47081708Sstevel 	char	name[OBP_MAXDRVNAME];
47091708Sstevel 
47101708Sstevel 	portid = drmach_get_portid(node);
47111708Sstevel 	if (portid == -1) {
47121708Sstevel 		/*
47131708Sstevel 		 * if the node does not have a portid property, then
47141708Sstevel 		 * by that information alone it is known that drmach
47151708Sstevel 		 * is not interested in it.
47161708Sstevel 		 */
47171708Sstevel 		return (0);
47181708Sstevel 	}
47191708Sstevel 	rv = node->n_getprop(node, "name", name, OBP_MAXDRVNAME);
47201708Sstevel 
47211708Sstevel 	/* The node must have a name */
47221708Sstevel 	if (rv)
47231708Sstevel 		return (0);
47241708Sstevel 
47251708Sstevel 	/*
47261708Sstevel 	 * Ignore devices whose portid do not map to this board,
47271708Sstevel 	 * or that their name property is not mapped to a valid
47281708Sstevel 	 * dr device name.
47291708Sstevel 	 */
47301708Sstevel 	if ((drmach_portid2bnum(portid) != obj->bnum) ||
47311708Sstevel 	    (drmach_name2type_idx(name) < 0))
47321708Sstevel 		return (0);
47331708Sstevel 
47341708Sstevel 	/*
47351708Sstevel 	 * Create a device data structure from this node data.
47361708Sstevel 	 * The call may yield nothing if the node is not of interest
47371708Sstevel 	 * to drmach.
47381708Sstevel 	 */
47391708Sstevel 	data->err = drmach_device_new(node, obj, portid, &id);
47401708Sstevel 	if (data->err)
47411708Sstevel 		return (-1);
47421708Sstevel 	else if (!id) {
47431708Sstevel 		/*
47441708Sstevel 		 * drmach_device_new examined the node we passed in
47451708Sstevel 		 * and determined that it was either one not of
47461708Sstevel 		 * interest to drmach or the PIM dr layer.
47471708Sstevel 		 * So, it is skipped.
47481708Sstevel 		 */
47491708Sstevel 		return (0);
47501708Sstevel 	}
47511708Sstevel 
47521708Sstevel 	rv = drmach_array_set(obj->devices, data->ndevs++, id);
47531708Sstevel 	if (rv) {
47541708Sstevel 		data->err = DRMACH_INTERNAL_ERROR();
47551708Sstevel 		return (-1);
47561708Sstevel 	}
47571708Sstevel 
47581708Sstevel 	device = id;
47591708Sstevel 
47601708Sstevel #ifdef DEBUG
47611708Sstevel 	DRMACH_PR("%d %s %d %p\n", portid, device->type, device->unum, id);
47621708Sstevel 	if (DRMACH_IS_IO_ID(id))
47631708Sstevel 		DRMACH_PR("ndevs = %d dip/node = %p", data->ndevs, node->here);
47641708Sstevel #endif
47651708Sstevel 
47661708Sstevel 	data->err = (*data->found)(data->a, device->type, device->unum, id);
47671708Sstevel 	return (data->err == NULL ? 0 : -1);
47681708Sstevel }
47691708Sstevel 
47701708Sstevel sbd_error_t *
drmach_board_find_devices(drmachid_t id,void * a,sbd_error_t * (* found)(void * a,const char *,int,drmachid_t))47711708Sstevel drmach_board_find_devices(drmachid_t id, void *a,
47721708Sstevel 	sbd_error_t *(*found)(void *a, const char *, int, drmachid_t))
47731708Sstevel {
47741708Sstevel 	drmach_board_t		*bp = (drmach_board_t *)id;
47751708Sstevel 	sbd_error_t		*err;
47761708Sstevel 	int			 max_devices;
47771708Sstevel 	int			 rv;
47781708Sstevel 	drmach_board_cb_data_t	data;
47791708Sstevel 
47801708Sstevel 	if (!DRMACH_IS_BOARD_ID(id))
47811708Sstevel 		return (drerr_new(0, ESTC_INAPPROP, NULL));
47821708Sstevel 
47831708Sstevel 	max_devices  = plat_max_cpu_units_per_board();
47841708Sstevel 	max_devices += plat_max_mem_units_per_board();
47851708Sstevel 	max_devices += plat_max_io_units_per_board();
47861708Sstevel 
47871708Sstevel 	bp->devices = drmach_array_new(0, max_devices);
47881708Sstevel 
47891708Sstevel 	if (bp->tree == NULL)
47901708Sstevel 		bp->tree = drmach_node_new();
47911708Sstevel 
47921708Sstevel 	data.obj = bp;
47931708Sstevel 	data.ndevs = 0;
47941708Sstevel 	data.found = found;
47951708Sstevel 	data.a = a;
47961708Sstevel 	data.err = NULL;
47971708Sstevel 
47981708Sstevel 	mutex_enter(&drmach_slice_table_lock);
47991708Sstevel 	mutex_enter(&drmach_bus_sync_lock);
48001708Sstevel 
48011708Sstevel 	rv = drmach_node_walk(bp->tree, &data, drmach_board_find_devices_cb);
48021708Sstevel 
48031708Sstevel 	drmach_slice_table_update(bp, 0);
48041708Sstevel 	drmach_bus_sync_list_update();
48051708Sstevel 
48061708Sstevel 	mutex_exit(&drmach_bus_sync_lock);
48071708Sstevel 	mutex_exit(&drmach_slice_table_lock);
48081708Sstevel 
48091708Sstevel 	if (rv == 0) {
48101708Sstevel 		err = NULL;
48111708Sstevel 		drmach_slot1_lpa_set(bp);
48121708Sstevel 	} else {
48131708Sstevel 		drmach_array_dispose(bp->devices, drmach_device_dispose);
48141708Sstevel 		bp->devices = NULL;
48151708Sstevel 
48161708Sstevel 		if (data.err)
48171708Sstevel 			err = data.err;
48181708Sstevel 		else
48191708Sstevel 			err = DRMACH_INTERNAL_ERROR();
48201708Sstevel 	}
48211708Sstevel 
48221708Sstevel 	return (err);
48231708Sstevel }
48241708Sstevel 
48251708Sstevel int
drmach_board_lookup(int bnum,drmachid_t * id)48261708Sstevel drmach_board_lookup(int bnum, drmachid_t *id)
48271708Sstevel {
48281708Sstevel 	int	rv = 0;
48291708Sstevel 
48301708Sstevel 	if (!drmach_initialized && drmach_init() == -1) {
48311708Sstevel 		*id = 0;
48321708Sstevel 		return (-1);
48331708Sstevel 	}
48341708Sstevel 	rw_enter(&drmach_boards_rwlock, RW_WRITER);
48351708Sstevel 	if (drmach_array_get(drmach_boards, bnum, id)) {
48361708Sstevel 		*id = 0;
48371708Sstevel 		rv = -1;
48381708Sstevel 	} else {
48391708Sstevel 		caddr_t		obufp;
48401708Sstevel 		dr_showboard_t	shb;
48411708Sstevel 		sbd_error_t	*err = NULL;
48421708Sstevel 		drmach_board_t	*bp;
48431708Sstevel 
48441708Sstevel 		bp = *id;
48451708Sstevel 
48461708Sstevel 		if (bp)
48471708Sstevel 			rw_downgrade(&drmach_boards_rwlock);
48481708Sstevel 
48491708Sstevel 		obufp = kmem_zalloc(sizeof (dr_proto_hdr_t), KM_SLEEP);
48501708Sstevel 		err = drmach_mbox_trans(DRMSG_SHOWBOARD, bnum, obufp,
485111066Srafael.vanoni@sun.com 		    sizeof (dr_proto_hdr_t), (caddr_t)&shb,
485211066Srafael.vanoni@sun.com 		    sizeof (dr_showboard_t));
48531708Sstevel 		kmem_free(obufp, sizeof (dr_proto_hdr_t));
48541708Sstevel 
48551708Sstevel 		if (err) {
48561708Sstevel 			if (err->e_code == ESTC_UNAVAILABLE) {
48571708Sstevel 				*id = 0;
48581708Sstevel 				rv = -1;
48591708Sstevel 			}
48601708Sstevel 			sbd_err_clear(&err);
48611708Sstevel 		} else {
48621708Sstevel 			if (!bp)
48631708Sstevel 				bp = *id  = (drmachid_t)drmach_board_new(bnum);
48641708Sstevel 			bp->connected = (shb.bd_assigned && shb.bd_active);
48651708Sstevel 			bp->empty = shb.slot_empty;
48661708Sstevel 
48671708Sstevel 			switch (shb.test_status) {
48681708Sstevel 				case DR_TEST_STATUS_UNKNOWN:
48691708Sstevel 				case DR_TEST_STATUS_IPOST:
48701708Sstevel 				case DR_TEST_STATUS_ABORTED:
48711708Sstevel 					bp->cond = SBD_COND_UNKNOWN;
48721708Sstevel 					break;
48731708Sstevel 				case DR_TEST_STATUS_PASSED:
48741708Sstevel 					bp->cond = SBD_COND_OK;
48751708Sstevel 					break;
48761708Sstevel 				case DR_TEST_STATUS_FAILED:
48771708Sstevel 					bp->cond = SBD_COND_FAILED;
48781708Sstevel 					break;
48791708Sstevel 				default:
48801708Sstevel 					bp->cond = SBD_COND_UNKNOWN;
48811708Sstevel 				DRMACH_PR("Unknown test status=0x%x from SC\n",
488211066Srafael.vanoni@sun.com 				    shb.test_status);
48831708Sstevel 					break;
48841708Sstevel 			}
488511311SSurya.Prakki@Sun.COM 			(void) strncpy(bp->type, shb.board_type,
488611311SSurya.Prakki@Sun.COM 			    sizeof (bp->type));
48871708Sstevel 			bp->assigned = shb.bd_assigned;
48881708Sstevel 			bp->powered = shb.power_on;
48891708Sstevel 		}
48901708Sstevel 	}
48911708Sstevel 	rw_exit(&drmach_boards_rwlock);
48921708Sstevel 	return (rv);
48931708Sstevel }
48941708Sstevel 
48951708Sstevel sbd_error_t *
drmach_board_name(int bnum,char * buf,int buflen)48961708Sstevel drmach_board_name(int bnum, char *buf, int buflen)
48971708Sstevel {
489811311SSurya.Prakki@Sun.COM 	(void) snprintf(buf, buflen, "%s%d", DRMACH_BNUM2SLOT(bnum) ?
48991708Sstevel 	    "IO" : "SB", DRMACH_BNUM2EXP(bnum));
49001708Sstevel 
49011708Sstevel 	return (NULL);
49021708Sstevel }
49031708Sstevel 
49041708Sstevel sbd_error_t *
drmach_board_poweroff(drmachid_t id)49051708Sstevel drmach_board_poweroff(drmachid_t id)
49061708Sstevel {
49071708Sstevel 	drmach_board_t	*bp;
49081708Sstevel 	sbd_error_t	*err;
49091708Sstevel 	drmach_status_t	 stat;
49101708Sstevel 
49111708Sstevel 	if (!DRMACH_IS_BOARD_ID(id))
49121708Sstevel 		return (drerr_new(0, ESTC_INAPPROP, NULL));
49131708Sstevel 	bp = id;
49141708Sstevel 
49151708Sstevel 	err = drmach_board_status(id, &stat);
49161708Sstevel 	if (!err) {
49171708Sstevel 		if (stat.configured || stat.busy)
49181708Sstevel 			err = drerr_new(0, ESTC_CONFIGBUSY, bp->cm.name);
49191708Sstevel 		else {
49201708Sstevel 			caddr_t	obufp;
49211708Sstevel 
49221708Sstevel 			obufp = kmem_zalloc(sizeof (dr_proto_hdr_t), KM_SLEEP);
49231708Sstevel 			err = drmach_mbox_trans(DRMSG_POWEROFF, bp->bnum, obufp,
492411066Srafael.vanoni@sun.com 			    sizeof (dr_proto_hdr_t), (caddr_t)NULL, 0);
49251708Sstevel 			kmem_free(obufp, sizeof (dr_proto_hdr_t));
49261708Sstevel 			if (!err)
49271708Sstevel 				bp->powered = 0;
49281708Sstevel 		}
49291708Sstevel 	}
49301708Sstevel 	return (err);
49311708Sstevel }
49321708Sstevel 
49331708Sstevel sbd_error_t *
drmach_board_poweron(drmachid_t id)49341708Sstevel drmach_board_poweron(drmachid_t id)
49351708Sstevel {
49361708Sstevel 	drmach_board_t	*bp;
49371708Sstevel 	caddr_t		obufp;
49381708Sstevel 	sbd_error_t	*err;
49391708Sstevel 
49401708Sstevel 	if (!DRMACH_IS_BOARD_ID(id))
49411708Sstevel 		return (drerr_new(0, ESTC_INAPPROP, NULL));
49421708Sstevel 	bp = id;
49431708Sstevel 
49441708Sstevel 	obufp = kmem_zalloc(sizeof (dr_proto_hdr_t), KM_SLEEP);
49451708Sstevel 	err = drmach_mbox_trans(DRMSG_POWERON, bp->bnum, obufp,
494611066Srafael.vanoni@sun.com 	    sizeof (dr_proto_hdr_t), (caddr_t)NULL, 0);
49471708Sstevel 	if (!err)
49481708Sstevel 		bp->powered = 1;
49491708Sstevel 
49501708Sstevel 	kmem_free(obufp, sizeof (dr_proto_hdr_t));
49511708Sstevel 
49521708Sstevel 	return (err);
49531708Sstevel }
49541708Sstevel 
49551708Sstevel static sbd_error_t *
drmach_board_release(drmachid_t id)49561708Sstevel drmach_board_release(drmachid_t id)
49571708Sstevel {
49581708Sstevel 	if (!DRMACH_IS_BOARD_ID(id))
49591708Sstevel 		return (drerr_new(0, ESTC_INAPPROP, NULL));
49601708Sstevel 	return (NULL);
49611708Sstevel }
49621708Sstevel 
49631708Sstevel sbd_error_t *
drmach_board_test(drmachid_t id,drmach_opts_t * opts,int force)49641708Sstevel drmach_board_test(drmachid_t id, drmach_opts_t *opts, int force)
49651708Sstevel {
49661708Sstevel 	drmach_board_t		*bp;
49671772Sjl139090 	drmach_device_t		*dp[MAX_CORES_PER_CMP];
49681708Sstevel 	dr_mbox_msg_t		*obufp;
49691708Sstevel 	sbd_error_t		*err;
49701708Sstevel 	dr_testboard_reply_t	tbr;
49711708Sstevel 	int			cpylen;
49721708Sstevel 	char			*copts;
49731708Sstevel 	int			is_io;
49741772Sjl139090 	cpu_flag_t		oflags[MAX_CORES_PER_CMP];
49751708Sstevel 
49761708Sstevel 	if (!DRMACH_IS_BOARD_ID(id))
49771708Sstevel 		return (drerr_new(0, ESTC_INAPPROP, NULL));
49781708Sstevel 	bp = id;
49791708Sstevel 
49801708Sstevel 	/*
49811708Sstevel 	 * If the board is an I/O or MAXCAT board, setup I/O cage for
49821708Sstevel 	 * testing. Slot 1 indicates I/O or MAXCAT board.
49831708Sstevel 	 */
49841708Sstevel 
49851708Sstevel 	is_io = DRMACH_BNUM2SLOT(bp->bnum);
49861708Sstevel 
49871708Sstevel 	obufp = kmem_zalloc(sizeof (dr_mbox_msg_t), KM_SLEEP);
49881708Sstevel 
49891708Sstevel 	if (force)
49901708Sstevel 		obufp->msgdata.dm_tb.force = 1;
49911708Sstevel 
49921708Sstevel 	obufp->msgdata.dm_tb.immediate = 1;
49931708Sstevel 
49941708Sstevel 	if ((opts->size > 0) && ((copts = opts->copts) != NULL)) {
49951708Sstevel 		cpylen = (opts->size > DR_HPOPTLEN ? DR_HPOPTLEN : opts->size);
49961708Sstevel 		bcopy(copts, obufp->msgdata.dm_tb.hpost_opts, cpylen);
49971708Sstevel 	}
49981708Sstevel 
49991708Sstevel 	if (is_io) {
50001708Sstevel 		err = drmach_iocage_setup(&obufp->msgdata.dm_tb, dp, oflags);
50011708Sstevel 
50021708Sstevel 		if (err) {
50031708Sstevel 			kmem_free(obufp, sizeof (dr_mbox_msg_t));
50041708Sstevel 			return (err);
50051708Sstevel 		}
50061708Sstevel 	}
50071708Sstevel 
50081708Sstevel 	err = drmach_mbox_trans(DRMSG_TESTBOARD, bp->bnum, (caddr_t)obufp,
500911066Srafael.vanoni@sun.com 	    sizeof (dr_mbox_msg_t), (caddr_t)&tbr, sizeof (tbr));
50101708Sstevel 
50111708Sstevel 	if (!err)
50121708Sstevel 		bp->cond = SBD_COND_OK;
50131708Sstevel 	else
50141708Sstevel 		bp->cond = SBD_COND_UNKNOWN;
50151708Sstevel 
50161708Sstevel 	if ((!err) && (tbr.test_status != DR_TEST_STATUS_PASSED)) {
50171708Sstevel 		/* examine test status */
50181708Sstevel 		switch (tbr.test_status) {
50191708Sstevel 			case DR_TEST_STATUS_IPOST:
50201708Sstevel 				bp->cond = SBD_COND_UNKNOWN;
502111066Srafael.vanoni@sun.com 				err = drerr_new(0, ESTC_TEST_IN_PROGRESS, NULL);
50221708Sstevel 				break;
50231708Sstevel 			case DR_TEST_STATUS_UNKNOWN:
50241708Sstevel 				bp->cond = SBD_COND_UNKNOWN;
50251708Sstevel 				err = drerr_new(1,
502611066Srafael.vanoni@sun.com 				    ESTC_TEST_STATUS_UNKNOWN, NULL);
50271708Sstevel 				break;
50281708Sstevel 			case DR_TEST_STATUS_FAILED:
50291708Sstevel 				bp->cond = SBD_COND_FAILED;
503011066Srafael.vanoni@sun.com 				err = drerr_new(1, ESTC_TEST_FAILED, NULL);
50311708Sstevel 				break;
50321708Sstevel 			case DR_TEST_STATUS_ABORTED:
50331708Sstevel 				bp->cond = SBD_COND_UNKNOWN;
503411066Srafael.vanoni@sun.com 				err = drerr_new(1, ESTC_TEST_ABORTED, NULL);
50351708Sstevel 				break;
50361708Sstevel 			default:
50371708Sstevel 				bp->cond = SBD_COND_UNKNOWN;
503811066Srafael.vanoni@sun.com 				err = drerr_new(1, ESTC_TEST_RESULT_UNKNOWN,
503911066Srafael.vanoni@sun.com 				    NULL);
50401708Sstevel 				break;
50411708Sstevel 		}
50421708Sstevel 	}
50431708Sstevel 
50441708Sstevel 	/*
50451708Sstevel 	 * If I/O cage test was performed, check for availability of the
50461708Sstevel 	 * cpu used.  If cpu has been returned, it's OK to proceed with
50471708Sstevel 	 * reconfiguring it for use.
50481708Sstevel 	 */
50491708Sstevel 	if (is_io) {
50501708Sstevel 		DRMACH_PR("drmach_board_test: tbr.cpu_recovered: %d",
505111066Srafael.vanoni@sun.com 		    tbr.cpu_recovered);
50521708Sstevel 		DRMACH_PR("drmach_board_test: port id: %d",
505311066Srafael.vanoni@sun.com 		    tbr.cpu_portid);
50541708Sstevel 
50551708Sstevel 		/*
50561708Sstevel 		 * Check the cpu_recovered flag in the testboard reply, or
50571708Sstevel 		 * if the testboard request message was not sent to SMS due
50581708Sstevel 		 * to an mboxsc_putmsg() failure, it's OK to recover the
50591708Sstevel 		 * cpu since hpost hasn't touched it.
50601708Sstevel 		 */
50611708Sstevel 		if ((tbr.cpu_recovered && tbr.cpu_portid ==
50621708Sstevel 		    obufp->msgdata.dm_tb.cpu_portid) ||
50631708Sstevel 		    ((err) && (err->e_code == ESTC_MBXRQST))) {
50641708Sstevel 
50651708Sstevel 			int i;
50661708Sstevel 
50671708Sstevel 			mutex_enter(&cpu_lock);
50681772Sjl139090 			for (i = 0; i < MAX_CORES_PER_CMP; i++) {
50691708Sstevel 				if (dp[i] != NULL) {
50701708Sstevel 					(void) drmach_iocage_cpu_return(dp[i],
50711708Sstevel 					    oflags[i]);
50721708Sstevel 				}
50731708Sstevel 			}
50741708Sstevel 			mutex_exit(&cpu_lock);
50751708Sstevel 		} else {
50761708Sstevel 			cmn_err(CE_WARN, "Unable to recover port id %d "
50771708Sstevel 			    "after I/O cage test: cpu_recovered=%d, "
50781708Sstevel 			    "returned portid=%d",
50791708Sstevel 			    obufp->msgdata.dm_tb.cpu_portid,
50801708Sstevel 			    tbr.cpu_recovered, tbr.cpu_portid);
50811708Sstevel 		}
508211311SSurya.Prakki@Sun.COM 		(void) drmach_iocage_mem_return(&tbr);
50831708Sstevel 	}
50841708Sstevel 	kmem_free(obufp, sizeof (dr_mbox_msg_t));
50851708Sstevel 
50861708Sstevel 	return (err);
50871708Sstevel }
50881708Sstevel 
50891708Sstevel sbd_error_t *
drmach_board_unassign(drmachid_t id)50901708Sstevel drmach_board_unassign(drmachid_t id)
50911708Sstevel {
50921708Sstevel 	drmach_board_t	*bp;
50931708Sstevel 	sbd_error_t	*err;
50941708Sstevel 	drmach_status_t	 stat;
50951708Sstevel 	caddr_t		obufp;
50961708Sstevel 
50971708Sstevel 	rw_enter(&drmach_boards_rwlock, RW_WRITER);
50981708Sstevel 
50991708Sstevel 	if (!DRMACH_IS_BOARD_ID(id)) {
51001708Sstevel 		rw_exit(&drmach_boards_rwlock);
51011708Sstevel 		return (drerr_new(0, ESTC_INAPPROP, NULL));
51021708Sstevel 	}
51031708Sstevel 	bp = id;
51041708Sstevel 
51051708Sstevel 	err = drmach_board_status(id, &stat);
51061708Sstevel 	if (err) {
51071708Sstevel 		rw_exit(&drmach_boards_rwlock);
51081708Sstevel 		return (err);
51091708Sstevel 	}
51101708Sstevel 
51111708Sstevel 	if (stat.configured || stat.busy) {
51121708Sstevel 		err = drerr_new(0, ESTC_CONFIGBUSY, bp->cm.name);
51131708Sstevel 	} else {
51141708Sstevel 
51151708Sstevel 		obufp = kmem_zalloc(sizeof (dr_proto_hdr_t), KM_SLEEP);
51161708Sstevel 		err = drmach_mbox_trans(DRMSG_UNASSIGN, bp->bnum, obufp,
511711066Srafael.vanoni@sun.com 		    sizeof (dr_proto_hdr_t), (caddr_t)NULL, 0);
51181708Sstevel 		kmem_free(obufp, sizeof (dr_proto_hdr_t));
51191708Sstevel 		if (!err) {
51201708Sstevel 			if (drmach_array_set(drmach_boards, bp->bnum, 0) != 0)
51211708Sstevel 				err = DRMACH_INTERNAL_ERROR();
51221708Sstevel 			else
51231708Sstevel 				drmach_board_dispose(bp);
51241708Sstevel 		}
51251708Sstevel 	}
51261708Sstevel 	rw_exit(&drmach_boards_rwlock);
51271708Sstevel 	return (err);
51281708Sstevel }
51291708Sstevel 
51301708Sstevel static sbd_error_t *
drmach_read_reg_addr(drmach_device_t * dp,uint64_t * p)51311708Sstevel drmach_read_reg_addr(drmach_device_t *dp, uint64_t *p)
51321708Sstevel {
51331708Sstevel 	int		len;
51341708Sstevel 	drmach_reg_t	reg;
51351708Sstevel 	drmach_node_t	pp;
51361708Sstevel 	drmach_node_t	*np = dp->node;
51371708Sstevel 
51381708Sstevel 	/*
51391708Sstevel 	 * If the node does not have a portid property,
51401708Sstevel 	 * it represents a CMP device. For a CMP, the reg
51411708Sstevel 	 * property of the parent holds the information of
51421708Sstevel 	 * interest.
51431708Sstevel 	 */
51441708Sstevel 	if (dp->node->n_getproplen(dp->node, "portid", &len) != 0) {
51451708Sstevel 
51461708Sstevel 		if (dp->node->get_parent(dp->node, &pp) != 0) {
51471708Sstevel 			return (DRMACH_INTERNAL_ERROR());
51481708Sstevel 		}
51491708Sstevel 		np = &pp;
51501708Sstevel 	}
51511708Sstevel 
51521708Sstevel 	if (np->n_getproplen(np, "reg", &len) != 0)
51531708Sstevel 		return (DRMACH_INTERNAL_ERROR());
51541708Sstevel 
51551708Sstevel 	if (len != sizeof (reg))
51561708Sstevel 		return (DRMACH_INTERNAL_ERROR());
51571708Sstevel 
51581708Sstevel 	if (np->n_getprop(np, "reg", &reg, sizeof (reg)) != 0)
51591708Sstevel 		return (DRMACH_INTERNAL_ERROR());
51601708Sstevel 
51611708Sstevel 	/* reassemble 64-bit base address */
51621708Sstevel 	*p = ((uint64_t)reg.reg_addr_hi << 32) | reg.reg_addr_lo;
51631708Sstevel 
51641708Sstevel 	return (NULL);
51651708Sstevel }
51661708Sstevel 
51671708Sstevel static void
drmach_cpu_read(uint64_t arg1,uint64_t arg2)51681708Sstevel drmach_cpu_read(uint64_t arg1, uint64_t arg2)
51691708Sstevel {
51701708Sstevel 	uint64_t	*saf_config_reg = (uint64_t *)arg1;
51711708Sstevel 	uint_t		*reg_read = (uint_t *)arg2;
51721708Sstevel 
51731708Sstevel 	*saf_config_reg = lddsafconfig();
51741708Sstevel 	*reg_read = 0x1;
51751708Sstevel }
51761708Sstevel 
51771708Sstevel /*
51781708Sstevel  * A return value of 1 indicates success and 0 indicates a failure
51791708Sstevel  */
51801708Sstevel static int
drmach_cpu_read_scr(drmach_cpu_t * cp,uint64_t * scr)51811708Sstevel drmach_cpu_read_scr(drmach_cpu_t *cp, uint64_t *scr)
51821708Sstevel {
51831708Sstevel 
51841708Sstevel 	int 	rv = 0x0;
51851708Sstevel 
51861708Sstevel 	*scr = 0x0;
51871708Sstevel 
51881708Sstevel 	/*
51891708Sstevel 	 * Confirm cpu was in ready set when xc was issued.
51901708Sstevel 	 * This is done by verifying rv which is
51911708Sstevel 	 * set to 0x1 when xc_one is successful.
51921708Sstevel 	 */
51931708Sstevel 	xc_one(cp->dev.portid, (xcfunc_t *)drmach_cpu_read,
51941708Sstevel 	    (uint64_t)scr, (uint64_t)&rv);
51951708Sstevel 
51961708Sstevel 	return (rv);
51971708Sstevel 
51981708Sstevel }
51991708Sstevel 
52001708Sstevel static sbd_error_t *
drmach_cpu_read_cpuid(drmach_cpu_t * cp,processorid_t * cpuid)52011708Sstevel drmach_cpu_read_cpuid(drmach_cpu_t *cp, processorid_t *cpuid)
52021708Sstevel {
52031708Sstevel 	drmach_node_t	*np;
52041708Sstevel 
52051708Sstevel 	np = cp->dev.node;
52061708Sstevel 
52071708Sstevel 	/*
52081708Sstevel 	 * If a CPU does not have a portid property, it must
52091708Sstevel 	 * be a CMP device with a cpuid property.
52101708Sstevel 	 */
52111708Sstevel 	if (np->n_getprop(np, "portid", cpuid, sizeof (*cpuid)) != 0) {
52121708Sstevel 
52131708Sstevel 		if (np->n_getprop(np, "cpuid", cpuid, sizeof (*cpuid)) != 0) {
52141708Sstevel 			return (DRMACH_INTERNAL_ERROR());
52151708Sstevel 		}
52161708Sstevel 	}
52171708Sstevel 
52181708Sstevel 	return (NULL);
52191708Sstevel }
52201708Sstevel 
52211708Sstevel /* Starcat CMP core id is bit 2 of the cpuid */
52221708Sstevel #define	DRMACH_COREID_MASK	(1u << 2)
52231708Sstevel #define	DRMACH_CPUID2SRAM_IDX(id) \
52241708Sstevel 		((id & DRMACH_COREID_MASK) >> 1 | (id & 0x1))
52251708Sstevel 
52261708Sstevel static sbd_error_t *
drmach_cpu_new(drmach_device_t * proto,drmachid_t * idp)52271708Sstevel drmach_cpu_new(drmach_device_t *proto, drmachid_t *idp)
52281708Sstevel {
52291708Sstevel 	static void drmach_cpu_dispose(drmachid_t);
52301708Sstevel 	static sbd_error_t *drmach_cpu_release(drmachid_t);
52311708Sstevel 	static sbd_error_t *drmach_cpu_status(drmachid_t, drmach_status_t *);
52321708Sstevel 
52331708Sstevel 	sbd_error_t	*err;
52341708Sstevel 	uint64_t	scr_pa;
52351708Sstevel 	drmach_cpu_t	*cp = NULL;
52361708Sstevel 	pfn_t		pfn;
52371708Sstevel 	uint64_t	cpu_stardrb_offset, cpu_sram_pa;
52381708Sstevel 	int		idx;
52391708Sstevel 	int		impl;
52401708Sstevel 	processorid_t	cpuid;
52411708Sstevel 
52421708Sstevel 	err = drmach_read_reg_addr(proto, &scr_pa);
52431708Sstevel 	if (err) {
52441708Sstevel 		goto fail;
52451708Sstevel 	}
52461708Sstevel 
52471708Sstevel 	cp = kmem_zalloc(sizeof (drmach_cpu_t), KM_SLEEP);
52481708Sstevel 	bcopy(proto, &cp->dev, sizeof (cp->dev));
52491708Sstevel 	cp->dev.node = drmach_node_dup(proto->node);
52501708Sstevel 	cp->dev.cm.isa = (void *)drmach_cpu_new;
52511708Sstevel 	cp->dev.cm.dispose = drmach_cpu_dispose;
52521708Sstevel 	cp->dev.cm.release = drmach_cpu_release;
52531708Sstevel 	cp->dev.cm.status = drmach_cpu_status;
52541708Sstevel 	cp->scr_pa = scr_pa;
52551708Sstevel 
52561708Sstevel 	err = drmach_cpu_read_cpuid(cp, &cpuid);
52571708Sstevel 	if (err) {
52581708Sstevel 		goto fail;
52591708Sstevel 	}
52601708Sstevel 
52611708Sstevel 	err = drmach_cpu_get_impl(cp, &impl);
52621708Sstevel 	if (err) {
52631708Sstevel 		goto fail;
52641708Sstevel 	}
52651708Sstevel 
52661708Sstevel 	cp->cpuid = cpuid;
52671708Sstevel 	cp->coreid = STARCAT_CPUID_TO_COREID(cp->cpuid);
52681708Sstevel 	cp->dev.unum = STARCAT_CPUID_TO_AGENT(cp->cpuid);
52691708Sstevel 
52701708Sstevel 	/*
52711708Sstevel 	 * Init the board cpu type.  Assumes all board cpus are the same type.
52721708Sstevel 	 */
52731708Sstevel 	if (cp->dev.bp->cpu_impl == 0) {
52741708Sstevel 		cp->dev.bp->cpu_impl = impl;
52751708Sstevel 	}
52761708Sstevel 	ASSERT(cp->dev.bp->cpu_impl == impl);
52771708Sstevel 
52781708Sstevel 	/*
52791708Sstevel 	 * XXX CHEETAH SUPPORT
52801708Sstevel 	 * determine if the domain uses Cheetah procs
52811708Sstevel 	 */
52821708Sstevel 	if (drmach_is_cheetah < 0) {
52831708Sstevel 		drmach_is_cheetah = IS_CHEETAH(impl);
52841708Sstevel 	}
52851708Sstevel 
52861708Sstevel 	/*
52871708Sstevel 	 * Initialize TTE for mapping CPU SRAM STARDRB buffer.
52881708Sstevel 	 * The STARDRB buffer (16KB on Cheetah+ boards, 32KB on
52891708Sstevel 	 * Jaguar/Panther boards) is shared by all cpus in a Safari port
52901708Sstevel 	 * pair. Each cpu uses 8KB according to the following layout:
52911708Sstevel 	 *
52921708Sstevel 	 * Page 0:	even numbered Cheetah+'s and Panther/Jaguar core 0's
52931708Sstevel 	 * Page 1:	odd numbered Cheetah+'s and Panther/Jaguar core 0's
52941708Sstevel 	 * Page 2:	even numbered Panther/Jaguar core 1's
52951708Sstevel 	 * Page 3:	odd numbered Panther/Jaguar core 1's
52961708Sstevel 	 */
52971708Sstevel 	idx = DRMACH_CPUID2SRAM_IDX(cp->cpuid);
52981708Sstevel 	cpu_stardrb_offset = cp->dev.bp->stardrb_offset + (PAGESIZE * idx);
52991708Sstevel 	cpu_sram_pa = DRMACH_CPU_SRAM_ADDR + cpu_stardrb_offset;
53001708Sstevel 	pfn = cpu_sram_pa >> PAGESHIFT;
53011708Sstevel 
53021708Sstevel 	ASSERT(drmach_cpu_sram_tte[cp->cpuid].tte_inthi == 0 &&
53031708Sstevel 	    drmach_cpu_sram_tte[cp->cpuid].tte_intlo == 0);
53041708Sstevel 	drmach_cpu_sram_tte[cp->cpuid].tte_inthi = TTE_PFN_INTHI(pfn) |
530511066Srafael.vanoni@sun.com 	    TTE_VALID_INT | TTE_SZ_INT(TTE8K);
53061708Sstevel 	drmach_cpu_sram_tte[cp->cpuid].tte_intlo = TTE_PFN_INTLO(pfn) |
530711066Srafael.vanoni@sun.com 	    TTE_HWWR_INT | TTE_PRIV_INT | TTE_LCK_INT;
53081708Sstevel 
53091708Sstevel 	DRMACH_PR("drmach_cpu_new: cpuid=%d, coreid=%d, stardrb_offset=0x%lx, "
53101708Sstevel 	    "cpu_sram_offset=0x%lx, idx=%d\n", cp->cpuid, cp->coreid,
53111708Sstevel 	    cp->dev.bp->stardrb_offset, cpu_stardrb_offset, idx);
53121708Sstevel 
531311311SSurya.Prakki@Sun.COM 	(void) snprintf(cp->dev.cm.name, sizeof (cp->dev.cm.name), "%s%d",
53141708Sstevel 	    cp->dev.type, cp->dev.unum);
53151708Sstevel 
53161708Sstevel 	*idp = (drmachid_t)cp;
53171708Sstevel 	return (NULL);
53181708Sstevel 
53191708Sstevel fail:
53201708Sstevel 	if (cp) {
53211708Sstevel 		drmach_node_dispose(cp->dev.node);
53221708Sstevel 		kmem_free(cp, sizeof (*cp));
53231708Sstevel 	}
53241708Sstevel 
53251708Sstevel 	*idp = (drmachid_t)0;
53261708Sstevel 	return (err);
53271708Sstevel }
53281708Sstevel 
53291708Sstevel static void
drmach_cpu_dispose(drmachid_t id)53301708Sstevel drmach_cpu_dispose(drmachid_t id)
53311708Sstevel {
53321708Sstevel 	drmach_cpu_t	*self;
53331708Sstevel 	processorid_t	cpuid;
53341708Sstevel 
53351708Sstevel 	ASSERT(DRMACH_IS_CPU_ID(id));
53361708Sstevel 
53371708Sstevel 	self = id;
53381708Sstevel 	if (self->dev.node)
53391708Sstevel 		drmach_node_dispose(self->dev.node);
53401708Sstevel 
53411708Sstevel 	cpuid = self->cpuid;
53421708Sstevel 	ASSERT(TTE_IS_VALID(&drmach_cpu_sram_tte[cpuid]) &&
53431708Sstevel 	    TTE_IS_8K(&drmach_cpu_sram_tte[cpuid]) &&
53441708Sstevel 	    TTE_IS_PRIVILEGED(&drmach_cpu_sram_tte[cpuid]) &&
53451708Sstevel 	    TTE_IS_LOCKED(&drmach_cpu_sram_tte[cpuid]));
53461708Sstevel 	drmach_cpu_sram_tte[cpuid].tte_inthi = 0;
53471708Sstevel 	drmach_cpu_sram_tte[cpuid].tte_intlo = 0;
53481708Sstevel 
53491708Sstevel 	kmem_free(self, sizeof (*self));
53501708Sstevel }
53511708Sstevel 
53521708Sstevel static int
drmach_cpu_start(struct cpu * cp)53531708Sstevel drmach_cpu_start(struct cpu *cp)
53541708Sstevel {
53551708Sstevel 	extern xcfunc_t	drmach_set_lpa;
53561708Sstevel 	extern void	restart_other_cpu(int);
53571708Sstevel 	int		cpuid = cp->cpu_id;
53581708Sstevel 	int		rv, bnum;
53591708Sstevel 	drmach_board_t	*bp;
53601708Sstevel 
53611708Sstevel 	ASSERT(MUTEX_HELD(&cpu_lock));
53621708Sstevel 	ASSERT(cpunodes[cpuid].nodeid != (pnode_t)0);
53631708Sstevel 
53641708Sstevel 	cp->cpu_flags &= ~CPU_POWEROFF;
53651708Sstevel 
53661708Sstevel 	/*
53671708Sstevel 	 * NOTE: restart_other_cpu pauses cpus during the
53681708Sstevel 	 *	 slave cpu start.  This helps to quiesce the
53691708Sstevel 	 *	 bus traffic a bit which makes the tick sync
53701708Sstevel 	 *	 routine in the prom more robust.
53711708Sstevel 	 */
53721708Sstevel 	DRMACH_PR("COLD START for cpu (%d)\n", cpuid);
53731708Sstevel 
53741708Sstevel 	if (prom_hotaddcpu(cpuid) != 0) {
53751708Sstevel 		cmn_err(CE_PANIC, "prom_hotaddcpu() for cpuid=%d failed.",
537611066Srafael.vanoni@sun.com 		    cpuid);
53771708Sstevel 	}
53781708Sstevel 
53791708Sstevel 	restart_other_cpu(cpuid);
53801708Sstevel 
53811708Sstevel 	bnum = drmach_portid2bnum(cpunodes[cpuid].portid);
53821708Sstevel 	rv = drmach_array_get(drmach_boards, bnum, (drmachid_t)&bp);
53831708Sstevel 	if (rv == -1 || bp == NULL) {
53841708Sstevel 		DRMACH_PR("drmach_cpu_start: cannot read board info for "
538511311SSurya.Prakki@Sun.COM 		    "cpuid=%d: rv=%d, bp=%p\n", cpuid, rv, (void *)bp);
53861708Sstevel 	} else if (DRMACH_L1_SET_LPA(bp) && drmach_reprogram_lpa) {
53871708Sstevel 		int exp;
53881708Sstevel 		int ntries;
53891708Sstevel 
53901708Sstevel 		mutex_enter(&drmach_xt_mb_lock);
53911708Sstevel 		mutex_enter(&drmach_slice_table_lock);
53921708Sstevel 		bzero((void *)drmach_xt_mb, drmach_xt_mb_size);
53931708Sstevel 
53941708Sstevel 		/*
53951708Sstevel 		 * drmach_slice_table[*]
53961708Sstevel 		 *	bit 5	valid
53971708Sstevel 		 *	bit 0:4	slice number
53981708Sstevel 		 *
53991708Sstevel 		 * drmach_xt_mb[*] format for drmach_set_lpa
54001708Sstevel 		 *	bit 7	valid
54011708Sstevel 		 *	bit 6	set null LPA (overrides bits 0:4)
54021708Sstevel 		 *	bit 0:4	slice number
54031708Sstevel 		 *
54041708Sstevel 		 * drmach_set_lpa derives processor CBASE and CBND
54051708Sstevel 		 * from bits 6 and 0:4 of drmach_xt_mb.  If bit 6 is
54061708Sstevel 		 * set, then CBASE = CBND = 0. Otherwise, CBASE = slice
54071708Sstevel 		 * number; CBND = slice number + 1.
54081708Sstevel 		 * No action is taken if bit 7 is zero.
54091708Sstevel 		 */
54101708Sstevel 		exp = (cpuid >> 5) & 0x1f;
54111708Sstevel 		if (drmach_slice_table[exp] & 0x20) {
54121708Sstevel 			drmach_xt_mb[cpuid] = 0x80 |
541311066Srafael.vanoni@sun.com 			    (drmach_slice_table[exp] & 0x1f);
54141708Sstevel 		} else {
54151708Sstevel 			drmach_xt_mb[cpuid] = 0x80 | 0x40;
54161708Sstevel 		}
54171708Sstevel 
54181708Sstevel 		drmach_xt_ready = 0;
54191708Sstevel 
54201708Sstevel 		xt_one(cpuid, drmach_set_lpa, NULL, NULL);
54211708Sstevel 
54221708Sstevel 		ntries = drmach_cpu_ntries;
54231708Sstevel 		while (!drmach_xt_ready && ntries) {
54241708Sstevel 			DELAY(drmach_cpu_delay);
54251708Sstevel 			ntries--;
54261708Sstevel 		}
54271708Sstevel 
54281708Sstevel 		mutex_exit(&drmach_slice_table_lock);
54291708Sstevel 		mutex_exit(&drmach_xt_mb_lock);
54301708Sstevel 
54311708Sstevel 		DRMACH_PR(
543211066Srafael.vanoni@sun.com 		    "waited %d out of %d tries for drmach_set_lpa on cpu%d",
543311066Srafael.vanoni@sun.com 		    drmach_cpu_ntries - ntries, drmach_cpu_ntries,
543411066Srafael.vanoni@sun.com 		    cp->cpu_id);
543511066Srafael.vanoni@sun.com 	}
543611066Srafael.vanoni@sun.com 
543711066Srafael.vanoni@sun.com 	xt_one(cpuid, vtag_flushpage_tl1, (uint64_t)drmach_cpu_sram_va,
543811066Srafael.vanoni@sun.com 	    (uint64_t)ksfmmup);
54391708Sstevel 
54401708Sstevel 	return (0);
54411708Sstevel }
54421708Sstevel 
54431708Sstevel /*
54441708Sstevel  * A detaching CPU is xcalled with an xtrap to drmach_cpu_stop_self() after
54451708Sstevel  * it has been offlined. The function of this routine is to get the cpu
54461708Sstevel  * spinning in a safe place. The requirement is that the system will not
54471708Sstevel  * reference anything on the detaching board (memory and i/o is detached
54481708Sstevel  * elsewhere) and that the CPU not reference anything on any other board
54491708Sstevel  * in the system.  This isolation is required during and after the writes
54501708Sstevel  * to the domain masks to remove the board from the domain.
54511708Sstevel  *
54521708Sstevel  * To accomplish this isolation the following is done:
54531708Sstevel  *	1) Create a locked mapping to the STARDRB data buffer located
54541708Sstevel  *	   in this cpu's sram. There is one TTE per cpu, initialized in
54551708Sstevel  *	   drmach_cpu_new(). The cpuid is used to select which TTE to use.
54561708Sstevel  *	   Each Safari port pair shares the CPU SRAM on a Serengeti CPU/MEM
54571708Sstevel  *	   board. The STARDRB buffer is 16KB on Cheetah+ boards, 32KB on Jaguar
54581708Sstevel  *	   boards. Each STARDRB buffer is logically divided by DR into one
54591708Sstevel  *	   8KB page per cpu (or Jaguar core).
54601708Sstevel  *	2) Copy the target function (drmach_shutdown_asm) into buffer.
54611708Sstevel  *	3) Jump to function now in the cpu sram.
54621708Sstevel  *	   Function will:
54631708Sstevel  *	   3.1) Flush its Ecache (displacement).
54641708Sstevel  *	   3.2) Flush its Dcache with HW mechanism.
54651708Sstevel  *	   3.3) Flush its Icache with HW mechanism.
54661708Sstevel  *	   3.4) Flush all valid and _unlocked_ D-TLB and I-TLB entries.
54671708Sstevel  *	   3.5) Set LPA to NULL
54681708Sstevel  *	   3.6) Clear xt_mb to signal completion. Note: cache line is
54691708Sstevel  *	        recovered by drmach_cpu_poweroff().
54701708Sstevel  *	4) Jump into an infinite loop.
54711708Sstevel  */
54721708Sstevel 
54731708Sstevel static void
drmach_cpu_stop_self(void)54741708Sstevel drmach_cpu_stop_self(void)
54751708Sstevel {
547611066Srafael.vanoni@sun.com 	extern void drmach_shutdown_asm(uint64_t, uint64_t, int, int, uint64_t);
547711066Srafael.vanoni@sun.com 	extern void drmach_shutdown_asm_end(void);
54781708Sstevel 
54791708Sstevel 	tte_t		*tte;
54801708Sstevel 	uint_t		*p, *q;
54811708Sstevel 	uint64_t	 stack_pointer;
54821708Sstevel 
54831708Sstevel 	ASSERT(((ptrdiff_t)drmach_shutdown_asm_end -
548411066Srafael.vanoni@sun.com 	    (ptrdiff_t)drmach_shutdown_asm) < PAGESIZE);
54851708Sstevel 
54861708Sstevel 	tte = &drmach_cpu_sram_tte[CPU->cpu_id];
548711066Srafael.vanoni@sun.com 	ASSERT(TTE_IS_VALID(tte) && TTE_IS_8K(tte) && TTE_IS_PRIVILEGED(tte) &&
548811066Srafael.vanoni@sun.com 	    TTE_IS_LOCKED(tte));
54892241Shuah 	sfmmu_dtlb_ld_kva(drmach_cpu_sram_va, tte);
54902241Shuah 	sfmmu_itlb_ld_kva(drmach_cpu_sram_va, tte);
54911708Sstevel 
54921708Sstevel 	/* copy text. standard bcopy not designed to work in nc space */
54931708Sstevel 	p = (uint_t *)drmach_cpu_sram_va;
54941708Sstevel 	q = (uint_t *)drmach_shutdown_asm;
54951708Sstevel 	while (q < (uint_t *)drmach_shutdown_asm_end)
54961708Sstevel 		*p++ = *q++;
54971708Sstevel 
54981708Sstevel 	/* zero to assist debug */
54991708Sstevel 	q = (uint_t *)(drmach_cpu_sram_va + PAGESIZE);
55001708Sstevel 	while (p < q)
55011708Sstevel 		*p++ = 0;
55021708Sstevel 
55031708Sstevel 	/* a parking spot for the stack pointer */
55041708Sstevel 	stack_pointer = (uint64_t)q;
55051708Sstevel 
55061708Sstevel 	/* call copy of drmach_shutdown_asm */
55071708Sstevel 	(*(void (*)())drmach_cpu_sram_va)(
550811066Srafael.vanoni@sun.com 	    stack_pointer,
550911066Srafael.vanoni@sun.com 	    drmach_iocage_paddr,
551011066Srafael.vanoni@sun.com 	    cpunodes[CPU->cpu_id].ecache_size,
551111066Srafael.vanoni@sun.com 	    cpunodes[CPU->cpu_id].ecache_linesize,
551211066Srafael.vanoni@sun.com 	    va_to_pa((void *)&drmach_xt_mb[CPU->cpu_id]));
55131708Sstevel }
55141708Sstevel 
55151708Sstevel static void
drmach_cpu_shutdown_self(void)55161708Sstevel drmach_cpu_shutdown_self(void)
55171708Sstevel {
55181708Sstevel 	cpu_t		*cp = CPU;
55191708Sstevel 	int		cpuid = cp->cpu_id;
55201708Sstevel 	extern void	flush_windows(void);
55211708Sstevel 
55221708Sstevel 	flush_windows();
55231708Sstevel 
55241708Sstevel 	(void) spl8();
55251708Sstevel 
55261708Sstevel 	ASSERT(cp->cpu_intr_actv == 0);
55271708Sstevel 	ASSERT(cp->cpu_thread == cp->cpu_idle_thread ||
55281708Sstevel 	    cp->cpu_thread == cp->cpu_startup_thread);
55291708Sstevel 
55301708Sstevel 	cp->cpu_flags = CPU_OFFLINE | CPU_QUIESCED | CPU_POWEROFF;
55311708Sstevel 
55321708Sstevel 	drmach_cpu_stop_self();
55331708Sstevel 
55341708Sstevel 	cmn_err(CE_PANIC, "CPU %d FAILED TO SHUTDOWN", cpuid);
55351708Sstevel }
55361708Sstevel 
55371708Sstevel static sbd_error_t *
drmach_cpu_release(drmachid_t id)55381708Sstevel drmach_cpu_release(drmachid_t id)
55391708Sstevel {
55401708Sstevel 	drmach_cpu_t	*cp;
55411708Sstevel 	struct cpu	*cpu;
55421708Sstevel 	sbd_error_t	*err;
55431708Sstevel 
55441708Sstevel 	if (!DRMACH_IS_CPU_ID(id))
55451708Sstevel 		return (drerr_new(0, ESTC_INAPPROP, NULL));
55461708Sstevel 	cp = id;
55471708Sstevel 
55481708Sstevel 	ASSERT(MUTEX_HELD(&cpu_lock));
55491708Sstevel 
55501708Sstevel 	cpu = cpu_get(cp->cpuid);
55511708Sstevel 	if (cpu == NULL)
55521708Sstevel 		err = DRMACH_INTERNAL_ERROR();
55531708Sstevel 	else
55541708Sstevel 		err = NULL;
55551708Sstevel 
55561708Sstevel 	return (err);
55571708Sstevel }
55581708Sstevel 
55591708Sstevel static sbd_error_t *
drmach_cpu_status(drmachid_t id,drmach_status_t * stat)55601708Sstevel drmach_cpu_status(drmachid_t id, drmach_status_t *stat)
55611708Sstevel {
55621708Sstevel 	drmach_cpu_t	*cp;
55631708Sstevel 	drmach_device_t	*dp;
55641708Sstevel 
55651708Sstevel 	ASSERT(DRMACH_IS_CPU_ID(id));
55661708Sstevel 	cp = id;
55671708Sstevel 	dp = &cp->dev;
55681708Sstevel 
55691708Sstevel 	stat->assigned = dp->bp->assigned;
55701708Sstevel 	stat->powered = dp->bp->powered;
55711708Sstevel 	mutex_enter(&cpu_lock);
55721708Sstevel 	stat->configured = (cpu_get(cp->cpuid) != NULL);
55731708Sstevel 	mutex_exit(&cpu_lock);
55741708Sstevel 	stat->busy = dp->busy;
557511311SSurya.Prakki@Sun.COM 	(void) strncpy(stat->type, dp->type, sizeof (stat->type));
55761708Sstevel 	stat->info[0] = '\0';
55771708Sstevel 
55781708Sstevel 	return (NULL);
55791708Sstevel }
55801708Sstevel 
55811708Sstevel sbd_error_t *
drmach_cpu_disconnect(drmachid_t id)55821708Sstevel drmach_cpu_disconnect(drmachid_t id)
55831708Sstevel {
55841708Sstevel 	if (!DRMACH_IS_CPU_ID(id))
55851708Sstevel 		return (drerr_new(0, ESTC_INAPPROP, NULL));
55861708Sstevel 
55871708Sstevel 	return (NULL);
55881708Sstevel }
55891708Sstevel 
55901708Sstevel sbd_error_t *
drmach_cpu_get_id(drmachid_t id,processorid_t * cpuid)55911708Sstevel drmach_cpu_get_id(drmachid_t id, processorid_t *cpuid)
55921708Sstevel {
55931708Sstevel 	drmach_cpu_t	*cpu;
55941708Sstevel 
55951708Sstevel 	if (!DRMACH_IS_CPU_ID(id))
55961708Sstevel 		return (drerr_new(0, ESTC_INAPPROP, NULL));
55971708Sstevel 	cpu = id;
55981708Sstevel 
55991708Sstevel 	*cpuid = cpu->cpuid;
56001708Sstevel 	return (NULL);
56011708Sstevel }
56021708Sstevel 
56031708Sstevel sbd_error_t *
drmach_cpu_get_impl(drmachid_t id,int * ip)56041708Sstevel drmach_cpu_get_impl(drmachid_t id, int *ip)
56051708Sstevel {
56061708Sstevel 	drmach_node_t	*np;
56071708Sstevel 	int		impl;
56081708Sstevel 
56091708Sstevel 	if (!DRMACH_IS_CPU_ID(id))
56101708Sstevel 		return (drerr_new(0, ESTC_INAPPROP, NULL));
56111708Sstevel 
56121708Sstevel 	np = ((drmach_device_t *)id)->node;
56131708Sstevel 
56141708Sstevel 	if (np->n_getprop(np, "implementation#", &impl, sizeof (impl)) == -1) {
56151708Sstevel 		return (DRMACH_INTERNAL_ERROR());
56161708Sstevel 	}
56171708Sstevel 
56181708Sstevel 	*ip = impl;
56191708Sstevel 
56201708Sstevel 	return (NULL);
56211708Sstevel }
56221708Sstevel 
56231708Sstevel /*
56241708Sstevel  * Flush this cpu's ecache, then ensure all outstanding safari
56251708Sstevel  * transactions have retired.
56261708Sstevel  */
56271708Sstevel void
drmach_cpu_flush_ecache_sync(void)56281708Sstevel drmach_cpu_flush_ecache_sync(void)
56291708Sstevel {
56301708Sstevel 	uint64_t *p;
56311708Sstevel 
56321708Sstevel 	ASSERT(curthread->t_bound_cpu == CPU);
56331708Sstevel 
56341708Sstevel 	cpu_flush_ecache();
56351708Sstevel 
56361708Sstevel 	mutex_enter(&drmach_bus_sync_lock);
56371708Sstevel 	for (p = drmach_bus_sync_list; *p; p++)
56381708Sstevel 		(void) ldphys(*p);
56391708Sstevel 	mutex_exit(&drmach_bus_sync_lock);
56401708Sstevel 
56411708Sstevel 	cpu_flush_ecache();
56421708Sstevel }
56431708Sstevel 
56441708Sstevel sbd_error_t *
drmach_get_dip(drmachid_t id,dev_info_t ** dip)56451708Sstevel drmach_get_dip(drmachid_t id, dev_info_t **dip)
56461708Sstevel {
56471708Sstevel 	drmach_device_t	*dp;
56481708Sstevel 
56491708Sstevel 	if (!DRMACH_IS_DEVICE_ID(id))
56501708Sstevel 		return (drerr_new(0, ESTC_INAPPROP, NULL));
56511708Sstevel 	dp = id;
56521708Sstevel 
56531708Sstevel 	*dip = dp->node->n_getdip(dp->node);
56541708Sstevel 	return (NULL);
56551708Sstevel }
56561708Sstevel 
56571708Sstevel sbd_error_t *
drmach_io_is_attached(drmachid_t id,int * yes)56581708Sstevel drmach_io_is_attached(drmachid_t id, int *yes)
56591708Sstevel {
56601708Sstevel 	drmach_device_t *dp;
56611708Sstevel 	dev_info_t	*dip;
56621708Sstevel 	int state;
56631708Sstevel 
56641708Sstevel 	if (!DRMACH_IS_IO_ID(id))
56651708Sstevel 		return (drerr_new(0, ESTC_INAPPROP, NULL));
56661708Sstevel 	dp = id;
56671708Sstevel 
56681708Sstevel 	dip = dp->node->n_getdip(dp->node);
56691708Sstevel 	if (dip == NULL) {
56701708Sstevel 		*yes = 0;
56711708Sstevel 		return (NULL);
56721708Sstevel 	}
56731708Sstevel 
56741708Sstevel 	state = ddi_get_devstate(dip);
56751708Sstevel 	*yes = i_ddi_devi_attached(dip) || (state == DDI_DEVSTATE_UP);
56761708Sstevel 
56771708Sstevel 	return (NULL);
56781708Sstevel }
56791708Sstevel 
56801708Sstevel static int
drmach_dip_is_schizo_xmits_0_pci_b(dev_info_t * dip)56811708Sstevel drmach_dip_is_schizo_xmits_0_pci_b(dev_info_t *dip)
56821708Sstevel {
56831708Sstevel 	char			dtype[OBP_MAXPROPNAME];
56841708Sstevel 	int			portid;
56851708Sstevel 	uint_t			pci_csr_base;
56861708Sstevel 	struct pci_phys_spec	*regbuf = NULL;
56871708Sstevel 	int			rv, len;
56881708Sstevel 
56891708Sstevel 	ASSERT(dip != NULL);
56901708Sstevel 	rv = ddi_getproplen(DDI_DEV_T_ANY, dip, 0, "device_type", &len);
56911708Sstevel 	if ((rv != DDI_PROP_SUCCESS) || (len > sizeof (dtype)))
56921708Sstevel 		return (0);
56931708Sstevel 
56941708Sstevel 	if (ddi_getlongprop_buf(DDI_DEV_T_ANY, dip, 0, "device_type",
569511066Srafael.vanoni@sun.com 	    (caddr_t)dtype, &len) == DDI_PROP_SUCCESS) {
56961708Sstevel 
56971708Sstevel 		if (strncmp(dtype, "pci", 3) == 0) {
56981708Sstevel 
56991708Sstevel 			/*
57001708Sstevel 			 * Get safari portid. All schizo/xmits 0
57011708Sstevel 			 * safari IDs end in 0x1C.
57021708Sstevel 			 */
570311066Srafael.vanoni@sun.com 			rv = ddi_getproplen(DDI_DEV_T_ANY, dip, 0, "portid",
570411066Srafael.vanoni@sun.com 			    &len);
57051708Sstevel 
57061708Sstevel 			if ((rv != DDI_PROP_SUCCESS) ||
570711066Srafael.vanoni@sun.com 			    (len > sizeof (portid)))
570811066Srafael.vanoni@sun.com 				return (0);
57091708Sstevel 
57101708Sstevel 			rv = ddi_getlongprop_buf(DDI_DEV_T_ANY, dip, 0,
571111066Srafael.vanoni@sun.com 			    "portid", (caddr_t)&portid, &len);
57121708Sstevel 
57131708Sstevel 			if (rv != DDI_PROP_SUCCESS)
57141708Sstevel 				return (0);
57151708Sstevel 
57161708Sstevel 			if ((portid & 0x1F) != 0x1C)
57171708Sstevel 				return (0);
57181708Sstevel 
57191708Sstevel 			if (ddi_getlongprop(DDI_DEV_T_ANY, dip,
572011066Srafael.vanoni@sun.com 			    DDI_PROP_DONTPASS, "reg", (caddr_t)&regbuf,
572111066Srafael.vanoni@sun.com 			    &len) == DDI_PROP_SUCCESS) {
57221708Sstevel 
57231708Sstevel 				pci_csr_base = regbuf[0].pci_phys_mid &
572411066Srafael.vanoni@sun.com 				    PCI_CONF_ADDR_MASK;
57251708Sstevel 				kmem_free(regbuf, len);
57261708Sstevel 				/*
57271708Sstevel 				 * All PCI B-Leafs are at configspace 0x70.0000.
57281708Sstevel 				 */
57291708Sstevel 				if (pci_csr_base == 0x700000)
57301708Sstevel 					return (1);
57311708Sstevel 			}
57321708Sstevel 		}
57331708Sstevel 	}
57341708Sstevel 	return (0);
57351708Sstevel }
57361708Sstevel 
57371708Sstevel #define	SCHIZO_BINDING_NAME		"pci108e,8001"
57381708Sstevel #define	XMITS_BINDING_NAME		"pci108e,8002"
57391708Sstevel 
57401708Sstevel /*
57411708Sstevel  * Verify if the dip is an instance of MAN 'eri'.
57421708Sstevel  */
57431708Sstevel static int
drmach_dip_is_man_eri(dev_info_t * dip)57441708Sstevel drmach_dip_is_man_eri(dev_info_t *dip)
57451708Sstevel {
57461708Sstevel 	struct pci_phys_spec	*regbuf = NULL;
57471708Sstevel 	dev_info_t		*parent_dip;
57481708Sstevel 	char			*name;
57491708Sstevel 	uint_t			pci_device;
57501708Sstevel 	uint_t			pci_function;
57511708Sstevel 	int			len;
57521708Sstevel 
57531708Sstevel 	if (dip == NULL)
57541708Sstevel 		return (0);
57551708Sstevel 	/*
57561708Sstevel 	 * Verify if the parent is schizo(xmits)0 and pci B leaf.
57571708Sstevel 	 */
57581708Sstevel 	if (((parent_dip = ddi_get_parent(dip)) == NULL) ||
575911066Srafael.vanoni@sun.com 	    ((name = ddi_binding_name(parent_dip)) == NULL))
57601708Sstevel 		return (0);
57611708Sstevel 	if (strcmp(name, SCHIZO_BINDING_NAME) != 0) {
57621708Sstevel 		/*
57631708Sstevel 		 * This RIO could be on XMITS, so get the dip to
57641708Sstevel 		 * XMITS PCI Leaf.
57651708Sstevel 		 */
57661708Sstevel 		if ((parent_dip = ddi_get_parent(parent_dip)) == NULL)
57671708Sstevel 			return (0);
57681708Sstevel 		if (((name = ddi_binding_name(parent_dip)) == NULL) ||
576911066Srafael.vanoni@sun.com 		    (strcmp(name, XMITS_BINDING_NAME) != 0)) {
57701708Sstevel 			return (0);
57711708Sstevel 		}
57721708Sstevel 	}
57731708Sstevel 	if (!drmach_dip_is_schizo_xmits_0_pci_b(parent_dip))
57741708Sstevel 		return (0);
57751708Sstevel 	/*
57761708Sstevel 	 * Finally make sure it is the MAN eri.
57771708Sstevel 	 */
57781708Sstevel 	if (ddi_getlongprop(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
577911066Srafael.vanoni@sun.com 	    "reg", (caddr_t)&regbuf, &len) == DDI_PROP_SUCCESS) {
57801708Sstevel 
57811708Sstevel 		pci_device = PCI_REG_DEV_G(regbuf->pci_phys_hi);
57821708Sstevel 		pci_function = PCI_REG_FUNC_G(regbuf->pci_phys_hi);
57831708Sstevel 		kmem_free(regbuf, len);
57841708Sstevel 
57851708Sstevel 		/*
57861708Sstevel 		 * The network function of the RIO ASIC will always be
57871708Sstevel 		 * device 3 and function 1 ("network@3,1").
57881708Sstevel 		 */
57891708Sstevel 		if ((pci_device == 3) && (pci_function == 1))
57901708Sstevel 			return (1);
57911708Sstevel 	}
57921708Sstevel 	return (0);
57931708Sstevel }
57941708Sstevel 
57951708Sstevel typedef struct {
57961708Sstevel 	int		iosram_inst;
57971708Sstevel 	dev_info_t	*eri_dip;
57981708Sstevel 	int		bnum;
57991708Sstevel } drmach_io_inst_t;
58001708Sstevel 
58011708Sstevel int
drmach_board_find_io_insts(dev_info_t * dip,void * args)58021708Sstevel drmach_board_find_io_insts(dev_info_t *dip, void *args)
58031708Sstevel {
58041708Sstevel 	drmach_io_inst_t	*ios = (drmach_io_inst_t *)args;
58051708Sstevel 
58061708Sstevel 	int	rv;
58071708Sstevel 	int	len;
58081708Sstevel 	int	portid;
58091708Sstevel 	char	name[OBP_MAXDRVNAME];
58101708Sstevel 
58111708Sstevel 	rv = ddi_getproplen(DDI_DEV_T_ANY, dip, 0, "portid", &len);
58121708Sstevel 
58131708Sstevel 	if ((rv != DDI_PROP_SUCCESS) || (len > sizeof (portid))) {
58141708Sstevel 		return (DDI_WALK_CONTINUE);
58151708Sstevel 	}
58161708Sstevel 
58171708Sstevel 	rv = ddi_getlongprop_buf(DDI_DEV_T_ANY, dip, 0,
581811066Srafael.vanoni@sun.com 	    "portid", (caddr_t)&portid, &len);
58191708Sstevel 	if (rv != DDI_PROP_SUCCESS)
58201708Sstevel 		return (DDI_WALK_CONTINUE);
58211708Sstevel 
58221708Sstevel 	/* ignore devices that are not on this board */
58231708Sstevel 	if (drmach_portid2bnum(portid) != ios->bnum)
58241708Sstevel 		return (DDI_WALK_CONTINUE);
58251708Sstevel 
58261708Sstevel 	if ((ios->iosram_inst < 0) || (ios->eri_dip == NULL)) {
582711066Srafael.vanoni@sun.com 		rv = ddi_getproplen(DDI_DEV_T_ANY, dip, 0, "name", &len);
58281708Sstevel 		if (rv == DDI_PROP_SUCCESS) {
58291708Sstevel 
58301708Sstevel 			rv = ddi_getlongprop_buf(DDI_DEV_T_ANY, dip,
583111066Srafael.vanoni@sun.com 			    0, "name",
583211066Srafael.vanoni@sun.com 			    (caddr_t)name, &len);
58331708Sstevel 			if (rv != DDI_PROP_SUCCESS)
58341708Sstevel 				return (DDI_WALK_CONTINUE);
58351708Sstevel 
58361708Sstevel 			if (strncmp("iosram", name, 6) == 0) {
58371708Sstevel 				ios->iosram_inst = ddi_get_instance(dip);
58381708Sstevel 				if (ios->eri_dip == NULL)
58391708Sstevel 					return (DDI_WALK_CONTINUE);
58401708Sstevel 				else
58411708Sstevel 					return (DDI_WALK_TERMINATE);
58421708Sstevel 			} else {
58431708Sstevel 				if (drmach_dip_is_man_eri(dip)) {
58441708Sstevel 					ASSERT(ios->eri_dip == NULL);
58451708Sstevel 					ndi_hold_devi(dip);
58461708Sstevel 					ios->eri_dip = dip;
58471708Sstevel 					if (ios->iosram_inst < 0)
58481708Sstevel 						return (DDI_WALK_CONTINUE);
58491708Sstevel 					else
58501708Sstevel 						return (DDI_WALK_TERMINATE);
58511708Sstevel 				}
58521708Sstevel 			}
58531708Sstevel 		}
58541708Sstevel 	}
58551708Sstevel 	return (DDI_WALK_CONTINUE);
58561708Sstevel }
58571708Sstevel 
58581708Sstevel sbd_error_t *
drmach_io_pre_release(drmachid_t id)58591708Sstevel drmach_io_pre_release(drmachid_t id)
58601708Sstevel {
58611708Sstevel 	drmach_io_inst_t	ios;
58621708Sstevel 	drmach_board_t		*bp;
58631708Sstevel 	int			rv = 0;
58641708Sstevel 	sbd_error_t		*err = NULL;
58651708Sstevel 	drmach_device_t		*dp;
58661708Sstevel 	dev_info_t		*rdip;
58671708Sstevel 	int			circ;
58681708Sstevel 
58691708Sstevel 	if (!DRMACH_IS_IO_ID(id))
58701708Sstevel 		return (drerr_new(0, ESTC_INAPPROP, NULL));
58711708Sstevel 	dp = id;
58721708Sstevel 	bp = dp->bp;
58731708Sstevel 
58741708Sstevel 	rdip = dp->node->n_getdip(dp->node);
58751708Sstevel 
58761708Sstevel 	/* walk device tree to find iosram instance for the board */
58771708Sstevel 	ios.iosram_inst = -1;
58781708Sstevel 	ios.eri_dip = NULL;
58791708Sstevel 	ios.bnum = bp->bnum;
58801708Sstevel 
58811708Sstevel 	ndi_devi_enter(rdip, &circ);
58821708Sstevel 	ddi_walk_devs(ddi_get_child(rdip), drmach_board_find_io_insts,
588311066Srafael.vanoni@sun.com 	    (void *)&ios);
58841708Sstevel 
58851708Sstevel 	DRMACH_PR("drmach_io_pre_release: bnum=%d iosram=%d eri=0x%p\n",
588611311SSurya.Prakki@Sun.COM 	    ios.bnum, ios.iosram_inst, (void *)ios.eri_dip);
58871708Sstevel 	ndi_devi_exit(rdip, circ);
58881708Sstevel 
58891708Sstevel 	if (ios.eri_dip) {
58901708Sstevel 		/*
58911708Sstevel 		 * Release hold acquired in drmach_board_find_io_insts()
58921708Sstevel 		 */
58931708Sstevel 		ndi_rele_devi(ios.eri_dip);
58941708Sstevel 	}
58951708Sstevel 	if (ios.iosram_inst >= 0) {
58961708Sstevel 		/* call for tunnel switch */
58971708Sstevel 		do {
58981708Sstevel 			DRMACH_PR("calling iosram_switchfrom(%d)\n",
589911066Srafael.vanoni@sun.com 			    ios.iosram_inst);
59001708Sstevel 			rv = iosram_switchfrom(ios.iosram_inst);
59011708Sstevel 			if (rv)
59021708Sstevel 				DRMACH_PR("iosram_switchfrom returned %d\n",
590311066Srafael.vanoni@sun.com 				    rv);
59041708Sstevel 		} while (rv == EAGAIN);
59051708Sstevel 
59061708Sstevel 		if (rv)
59071708Sstevel 			err = drerr_new(0, ESTC_IOSWITCH, NULL);
59081708Sstevel 	}
59091708Sstevel 	return (err);
59101708Sstevel }
59111708Sstevel 
59121708Sstevel sbd_error_t *
drmach_io_unrelease(drmachid_t id)59131708Sstevel drmach_io_unrelease(drmachid_t id)
59141708Sstevel {
59151708Sstevel 	dev_info_t	*dip;
59161708Sstevel 	sbd_error_t	*err = NULL;
59171708Sstevel 	drmach_device_t	*dp;
59181708Sstevel 
59191708Sstevel 	if (!DRMACH_IS_IO_ID(id))
59201708Sstevel 		return (drerr_new(0, ESTC_INAPPROP, NULL));
59211708Sstevel 	dp = id;
59221708Sstevel 
59231708Sstevel 	dip = dp->node->n_getdip(dp->node);
59241708Sstevel 
59251708Sstevel 	if (dip == NULL)
59261708Sstevel 		err = DRMACH_INTERNAL_ERROR();
59271708Sstevel 	else {
59281708Sstevel 		int (*func)(dev_info_t *dip);
59291708Sstevel 
59301708Sstevel 		func = (int (*)(dev_info_t *))kobj_getsymvalue("man_dr_attach",
593111066Srafael.vanoni@sun.com 		    0);
59321708Sstevel 
59331708Sstevel 		if (func) {
59341708Sstevel 			drmach_io_inst_t ios;
59351708Sstevel 			dev_info_t	*pdip;
59361708Sstevel 			int		circ;
59371708Sstevel 
59381708Sstevel 			/*
59391708Sstevel 			 * Walk device tree to find rio dip for the board
59401708Sstevel 			 * Since we are not interested in iosram instance here,
59411708Sstevel 			 * initialize it to 0, so that the walk terminates as
59421708Sstevel 			 * soon as eri dip is found.
59431708Sstevel 			 */
59441708Sstevel 			ios.iosram_inst = 0;
59451708Sstevel 			ios.eri_dip = NULL;
59461708Sstevel 			ios.bnum = dp->bp->bnum;
59471708Sstevel 
59481708Sstevel 			if (pdip = ddi_get_parent(dip)) {
59491708Sstevel 				ndi_hold_devi(pdip);
59501708Sstevel 				ndi_devi_enter(pdip, &circ);
59511708Sstevel 			}
59521708Sstevel 			/*
59531708Sstevel 			 * Root node doesn't have to be held in any way.
59541708Sstevel 			 */
595511066Srafael.vanoni@sun.com 			ddi_walk_devs(dip, drmach_board_find_io_insts,
595611066Srafael.vanoni@sun.com 			    (void *)&ios);
59571708Sstevel 
59581708Sstevel 			if (pdip) {
59591708Sstevel 				ndi_devi_exit(pdip, circ);
59601708Sstevel 				ndi_rele_devi(pdip);
59611708Sstevel 			}
59621708Sstevel 
59631708Sstevel 			DRMACH_PR("drmach_io_unrelease: bnum=%d eri=0x%p\n",
596411311SSurya.Prakki@Sun.COM 			    ios.bnum, (void *)ios.eri_dip);
59651708Sstevel 
59661708Sstevel 			if (ios.eri_dip) {
59671708Sstevel 				DRMACH_PR("calling man_dr_attach\n");
59681708Sstevel 				if ((*func)(ios.eri_dip))
596911066Srafael.vanoni@sun.com 					err = drerr_new(0, ESTC_NWSWITCH, NULL);
59701708Sstevel 				/*
59711708Sstevel 				 * Release hold acquired in
59721708Sstevel 				 * drmach_board_find_io_insts()
59731708Sstevel 				 */
59741708Sstevel 				ndi_rele_devi(ios.eri_dip);
59751708Sstevel 			}
59761708Sstevel 		} else
59771708Sstevel 			DRMACH_PR("man_dr_attach NOT present\n");
59781708Sstevel 	}
59791708Sstevel 	return (err);
59801708Sstevel }
59811708Sstevel 
59821708Sstevel static sbd_error_t *
drmach_io_release(drmachid_t id)59831708Sstevel drmach_io_release(drmachid_t id)
59841708Sstevel {
59851708Sstevel 	dev_info_t	*dip;
59861708Sstevel 	sbd_error_t	*err = NULL;
59871708Sstevel 	drmach_device_t	*dp;
59881708Sstevel 
59891708Sstevel 	if (!DRMACH_IS_IO_ID(id))
59901708Sstevel 		return (drerr_new(0, ESTC_INAPPROP, NULL));
59911708Sstevel 	dp = id;
59921708Sstevel 
59931708Sstevel 	dip = dp->node->n_getdip(dp->node);
59941708Sstevel 
59951708Sstevel 	if (dip == NULL)
59961708Sstevel 		err = DRMACH_INTERNAL_ERROR();
59971708Sstevel 	else {
59981708Sstevel 		int (*func)(dev_info_t *dip);
59991708Sstevel 
60001708Sstevel 		func = (int (*)(dev_info_t *))kobj_getsymvalue("man_dr_detach",
600111066Srafael.vanoni@sun.com 		    0);
60021708Sstevel 
60031708Sstevel 		if (func) {
60041708Sstevel 			drmach_io_inst_t ios;
60051708Sstevel 			dev_info_t	*pdip;
60061708Sstevel 			int		circ;
60071708Sstevel 
60081708Sstevel 			/*
60091708Sstevel 			 * Walk device tree to find rio dip for the board
60101708Sstevel 			 * Since we are not interested in iosram instance here,
60111708Sstevel 			 * initialize it to 0, so that the walk terminates as
60121708Sstevel 			 * soon as eri dip is found.
60131708Sstevel 			 */
60141708Sstevel 			ios.iosram_inst = 0;
60151708Sstevel 			ios.eri_dip = NULL;
60161708Sstevel 			ios.bnum = dp->bp->bnum;
60171708Sstevel 
60181708Sstevel 			if (pdip = ddi_get_parent(dip)) {
60191708Sstevel 				ndi_hold_devi(pdip);
60201708Sstevel 				ndi_devi_enter(pdip, &circ);
60211708Sstevel 			}
60221708Sstevel 			/*
60231708Sstevel 			 * Root node doesn't have to be held in any way.
60241708Sstevel 			 */
602511066Srafael.vanoni@sun.com 			ddi_walk_devs(dip, drmach_board_find_io_insts,
602611066Srafael.vanoni@sun.com 			    (void *)&ios);
60271708Sstevel 
60281708Sstevel 			if (pdip) {
60291708Sstevel 				ndi_devi_exit(pdip, circ);
60301708Sstevel 				ndi_rele_devi(pdip);
60311708Sstevel 			}
60321708Sstevel 
60331708Sstevel 			DRMACH_PR("drmach_io_release: bnum=%d eri=0x%p\n",
603411311SSurya.Prakki@Sun.COM 			    ios.bnum, (void *)ios.eri_dip);
60351708Sstevel 
60361708Sstevel 			if (ios.eri_dip) {
60371708Sstevel 				DRMACH_PR("calling man_dr_detach\n");
60381708Sstevel 				if ((*func)(ios.eri_dip))
603911066Srafael.vanoni@sun.com 					err = drerr_new(0, ESTC_NWSWITCH, NULL);
60401708Sstevel 				/*
60411708Sstevel 				 * Release hold acquired in
60421708Sstevel 				 * drmach_board_find_io_insts()
60431708Sstevel 				 */
60441708Sstevel 				ndi_rele_devi(ios.eri_dip);
60451708Sstevel 			}
60461708Sstevel 		} else
60471708Sstevel 			DRMACH_PR("man_dr_detach NOT present\n");
60481708Sstevel 	}
60491708Sstevel 	return (err);
60501708Sstevel }
60511708Sstevel 
60521708Sstevel sbd_error_t *
drmach_io_post_release(drmachid_t id)60531708Sstevel drmach_io_post_release(drmachid_t id)
60541708Sstevel {
60551708Sstevel 	char 		*path;
60561708Sstevel 	dev_info_t	*rdip;
60571708Sstevel 	drmach_device_t	*dp;
60581708Sstevel 
60591708Sstevel 	if (!DRMACH_IS_DEVICE_ID(id))
60601708Sstevel 		return (drerr_new(0, ESTC_INAPPROP, NULL));
60611708Sstevel 	dp = id;
60621708Sstevel 
60631708Sstevel 	rdip = dp->node->n_getdip(dp->node);
60641708Sstevel 
60651708Sstevel 	/*
60661708Sstevel 	 * Always called after drmach_unconfigure() which on Starcat
60671708Sstevel 	 * unconfigures the branch but doesn't remove it so the
60681708Sstevel 	 * dip must always exist.
60691708Sstevel 	 */
60701708Sstevel 	ASSERT(rdip);
60711708Sstevel 
60721708Sstevel 	ASSERT(e_ddi_branch_held(rdip));
60731708Sstevel #ifdef DEBUG
60741708Sstevel 	path = kmem_alloc(MAXPATHLEN, KM_SLEEP);
60751708Sstevel 	(void) ddi_pathname(rdip, path);
60761708Sstevel 	DRMACH_PR("post_release dip path is: %s\n", path);
60771708Sstevel 	kmem_free(path, MAXPATHLEN);
60781708Sstevel #endif
60791708Sstevel 
60801708Sstevel 	if (strcmp(dp->type, DRMACH_DEVTYPE_PCI) == 0) {
60811708Sstevel 		if (schpc_remove_pci(rdip)) {
60821708Sstevel 			DRMACH_PR("schpc_remove_pci failed\n");
60831708Sstevel 			return (drerr_new(0, ESBD_OFFLINE, NULL));
60841708Sstevel 		} else {
60851708Sstevel 			DRMACH_PR("schpc_remove_pci succeeded\n");
60861708Sstevel 		}
60871708Sstevel 	}
60881708Sstevel 
60891708Sstevel 	return (NULL);
60901708Sstevel }
60911708Sstevel 
60921708Sstevel sbd_error_t *
drmach_io_post_attach(drmachid_t id)60931708Sstevel drmach_io_post_attach(drmachid_t id)
60941708Sstevel {
60951708Sstevel 	int		circ;
60961708Sstevel 	dev_info_t	*dip;
60971708Sstevel 	dev_info_t	*pdip;
60981708Sstevel 	drmach_device_t	*dp;
60991708Sstevel 	drmach_io_inst_t ios;
61001708Sstevel 
61011708Sstevel 	if (!DRMACH_IS_DEVICE_ID(id))
61021708Sstevel 		return (drerr_new(0, ESTC_INAPPROP, NULL));
61031708Sstevel 	dp = id;
61041708Sstevel 
61051708Sstevel 	dip = dp->node->n_getdip(dp->node);
61061708Sstevel 
61071708Sstevel 	/*
61081708Sstevel 	 * We held the branch rooted at dip earlier, so at a minimum the
61091708Sstevel 	 * root i.e. dip must be present in the device tree.
61101708Sstevel 	 */
61111708Sstevel 	ASSERT(dip);
61121708Sstevel 
61131708Sstevel 	if (strcmp(dp->type, DRMACH_DEVTYPE_PCI) == 0) {
61141708Sstevel 		if (schpc_add_pci(dip)) {
61151708Sstevel 			DRMACH_PR("schpc_add_pci failed\n");
61161708Sstevel 		} else {
61171708Sstevel 			DRMACH_PR("schpc_add_pci succeeded\n");
61181708Sstevel 		}
61191708Sstevel 	}
61201708Sstevel 
61211708Sstevel 	/*
61221708Sstevel 	 * Walk device tree to find rio dip for the board
61231708Sstevel 	 * Since we are not interested in iosram instance here,
61241708Sstevel 	 * initialize it to 0, so that the walk terminates as
61251708Sstevel 	 * soon as eri dip is found.
61261708Sstevel 	 */
61271708Sstevel 	ios.iosram_inst = 0;
61281708Sstevel 	ios.eri_dip = NULL;
61291708Sstevel 	ios.bnum = dp->bp->bnum;
61301708Sstevel 
61311708Sstevel 	if (pdip = ddi_get_parent(dip)) {
61321708Sstevel 		ndi_hold_devi(pdip);
61331708Sstevel 		ndi_devi_enter(pdip, &circ);
61341708Sstevel 	}
61351708Sstevel 	/*
61361708Sstevel 	 * Root node doesn't have to be held in any way.
61371708Sstevel 	 */
613811066Srafael.vanoni@sun.com 	ddi_walk_devs(dip, drmach_board_find_io_insts, (void *)&ios);
61391708Sstevel 	if (pdip) {
61401708Sstevel 		ndi_devi_exit(pdip, circ);
61411708Sstevel 		ndi_rele_devi(pdip);
61421708Sstevel 	}
61431708Sstevel 
614411311SSurya.Prakki@Sun.COM 	DRMACH_PR("drmach_io_post_attach: bnum=%d eri=0x%p\n",
614511311SSurya.Prakki@Sun.COM 	    ios.bnum, (void *)ios.eri_dip);
61461708Sstevel 
61471708Sstevel 	if (ios.eri_dip) {
61481708Sstevel 		int (*func)(dev_info_t *dip);
61491708Sstevel 
61501708Sstevel 		func =
615111066Srafael.vanoni@sun.com 		    (int (*)(dev_info_t *))kobj_getsymvalue("man_dr_attach", 0);
61521708Sstevel 
61531708Sstevel 		if (func) {
61541708Sstevel 			DRMACH_PR("calling man_dr_attach\n");
61551708Sstevel 			(void) (*func)(ios.eri_dip);
61561708Sstevel 		} else {
61571708Sstevel 			DRMACH_PR("man_dr_attach NOT present\n");
61581708Sstevel 		}
61591708Sstevel 
61601708Sstevel 		/*
61611708Sstevel 		 * Release hold acquired in drmach_board_find_io_insts()
61621708Sstevel 		 */
61631708Sstevel 		ndi_rele_devi(ios.eri_dip);
61641708Sstevel 
61651708Sstevel 	}
61661708Sstevel 
61671708Sstevel 	return (NULL);
61681708Sstevel }
61691708Sstevel 
61701708Sstevel static sbd_error_t *
drmach_io_status(drmachid_t id,drmach_status_t * stat)61711708Sstevel drmach_io_status(drmachid_t id, drmach_status_t *stat)
61721708Sstevel {
61731708Sstevel 	drmach_device_t *dp;
61741708Sstevel 	sbd_error_t	*err;
61751708Sstevel 	int		 configured;
61761708Sstevel 
61771708Sstevel 	ASSERT(DRMACH_IS_IO_ID(id));
61781708Sstevel 	dp = id;
61791708Sstevel 
61801708Sstevel 	err = drmach_io_is_attached(id, &configured);
61811708Sstevel 	if (err)
61821708Sstevel 		return (err);
61831708Sstevel 
61841708Sstevel 	stat->assigned = dp->bp->assigned;
61851708Sstevel 	stat->powered = dp->bp->powered;
61861708Sstevel 	stat->configured = (configured != 0);
61871708Sstevel 	stat->busy = dp->busy;
618811311SSurya.Prakki@Sun.COM 	(void) strncpy(stat->type, dp->type, sizeof (stat->type));
61891708Sstevel 	stat->info[0] = '\0';
61901708Sstevel 
61911708Sstevel 	return (NULL);
61921708Sstevel }
61931708Sstevel 
61941708Sstevel sbd_error_t *
drmach_mem_init_size(drmachid_t id)61951708Sstevel drmach_mem_init_size(drmachid_t id)
61961708Sstevel {
61971708Sstevel 	drmach_mem_t	*mp;
61981708Sstevel 	sbd_error_t	*err;
61991708Sstevel 	gdcd_t		*gdcd;
62001708Sstevel 	mem_chunk_t	*chunk;
62011708Sstevel 	uint64_t	 chunks, pa, mask, sz;
62021708Sstevel 
62031708Sstevel 	if (!DRMACH_IS_MEM_ID(id))
62041708Sstevel 		return (drerr_new(0, ESTC_INAPPROP, NULL));
62051708Sstevel 	mp = id;
62061708Sstevel 
62071708Sstevel 	err = drmach_mem_get_base_physaddr(id, &pa);
62081708Sstevel 	if (err)
62091708Sstevel 		return (err);
62101708Sstevel 
62111708Sstevel 	mask = ~ (DRMACH_MEM_SLICE_SIZE - 1);
62121708Sstevel 	pa &= mask;
62131708Sstevel 
62141708Sstevel 	gdcd = drmach_gdcd_new();
62151708Sstevel 	if (gdcd == NULL)
62161708Sstevel 		return (DRMACH_INTERNAL_ERROR());
62171708Sstevel 
62181708Sstevel 	sz = 0;
62191708Sstevel 	chunk = gdcd->dcd_chunk_list.dcl_chunk;
62201708Sstevel 	chunks = gdcd->dcd_chunk_list.dcl_chunks;
62211708Sstevel 	while (chunks-- != 0) {
62221708Sstevel 		if ((chunk->mc_base_pa & mask) == pa) {
62231708Sstevel 			sz += chunk->mc_mbytes * 1048576;
62241708Sstevel 		}
62251708Sstevel 
62261708Sstevel 		++chunk;
62271708Sstevel 	}
62281708Sstevel 	mp->nbytes = sz;
62291708Sstevel 
62301708Sstevel 	drmach_gdcd_dispose(gdcd);
62311708Sstevel 	return (NULL);
62321708Sstevel }
62331708Sstevel 
62341708Sstevel /*
62351708Sstevel  * Hardware registers are organized into consecutively
62361708Sstevel  * addressed registers.  The reg property's hi and lo fields
62371708Sstevel  * together describe the base address of the register set for
62381708Sstevel  * this memory-controller.  Register descriptions and offsets
62391708Sstevel  * (from the base address) are as follows:
62401708Sstevel  *
62411708Sstevel  * Description				Offset	Size (bytes)
62421708Sstevel  * Memory Timing Control Register I	0x00	8
62431708Sstevel  * Memory Timing Control Register II	0x08	8
62441708Sstevel  * Memory Address Decoding Register I	0x10	8
62451708Sstevel  * Memory Address Decoding Register II	0x18	8
62461708Sstevel  * Memory Address Decoding Register III	0x20	8
62471708Sstevel  * Memory Address Decoding Register IV	0x28	8
62481708Sstevel  * Memory Address Control Register	0x30	8
62491708Sstevel  * Memory Timing Control Register III	0x38	8
62501708Sstevel  * Memory Timing Control Register IV	0x40	8
62511708Sstevel  * Memory Timing Control Register V  	0x48	8 (Jaguar, Panther only)
62521708Sstevel  * EMU Activity Status Register		0x50	8 (Panther only)
62531708Sstevel  *
62541708Sstevel  * Only the Memory Address Decoding Register and EMU Activity Status
62551708Sstevel  * Register addresses are needed for DRMACH.
62561708Sstevel  */
62571708Sstevel static sbd_error_t *
drmach_mem_new(drmach_device_t * proto,drmachid_t * idp)62581708Sstevel drmach_mem_new(drmach_device_t *proto, drmachid_t *idp)
62591708Sstevel {
62601708Sstevel 	static void drmach_mem_dispose(drmachid_t);
62611708Sstevel 	static sbd_error_t *drmach_mem_release(drmachid_t);
62621708Sstevel 	static sbd_error_t *drmach_mem_status(drmachid_t, drmach_status_t *);
62631708Sstevel 
62641708Sstevel 	sbd_error_t	*err;
62651708Sstevel 	uint64_t	 madr_pa;
62661708Sstevel 	drmach_mem_t	*mp;
62671708Sstevel 	int		 bank, count;
62681708Sstevel 
62691708Sstevel 	err = drmach_read_reg_addr(proto, &madr_pa);
62701708Sstevel 	if (err)
62711708Sstevel 		return (err);
62721708Sstevel 
62731708Sstevel 	mp = kmem_zalloc(sizeof (drmach_mem_t), KM_SLEEP);
62741708Sstevel 	bcopy(proto, &mp->dev, sizeof (mp->dev));
62751708Sstevel 	mp->dev.node = drmach_node_dup(proto->node);
62761708Sstevel 	mp->dev.cm.isa = (void *)drmach_mem_new;
62771708Sstevel 	mp->dev.cm.dispose = drmach_mem_dispose;
62781708Sstevel 	mp->dev.cm.release = drmach_mem_release;
62791708Sstevel 	mp->dev.cm.status = drmach_mem_status;
62801708Sstevel 	mp->madr_pa = madr_pa;
62811708Sstevel 
628211311SSurya.Prakki@Sun.COM 	(void) snprintf(mp->dev.cm.name,
628311311SSurya.Prakki@Sun.COM 	    sizeof (mp->dev.cm.name), "%s", mp->dev.type);
62841708Sstevel 
62851708Sstevel 	for (count = bank = 0; bank < DRMACH_MC_NBANKS; bank++) {
62861708Sstevel 		uint64_t madr;
62871708Sstevel 
62881708Sstevel 		drmach_mem_read_madr(mp, bank, &madr);
62891708Sstevel 		if (madr & DRMACH_MC_VALID_MASK) {
62901708Sstevel 			count += 1;
62911708Sstevel 			break;
62921708Sstevel 		}
62931708Sstevel 	}
62941708Sstevel 
62951708Sstevel 	/*
62961708Sstevel 	 * If none of the banks had their valid bit set, that means
62971708Sstevel 	 * post did not configure this MC to participate in the
62981708Sstevel 	 * domain.  So, pretend this node does not exist by returning
62991708Sstevel 	 * a drmachid of zero.
63001708Sstevel 	 */
63011708Sstevel 	if (count == 0) {
63021708Sstevel 		/* drmach_mem_dispose frees board mem list */
63031708Sstevel 		drmach_node_dispose(mp->dev.node);
63041708Sstevel 		kmem_free(mp, sizeof (*mp));
63051708Sstevel 		*idp = (drmachid_t)0;
63061708Sstevel 		return (NULL);
63071708Sstevel 	}
63081708Sstevel 
63091708Sstevel 	/*
63101708Sstevel 	 * Only one mem unit per board is exposed to the
63111708Sstevel 	 * PIM layer.  The first mem unit encountered during
63121708Sstevel 	 * tree walk is used to represent all mem units on
63131708Sstevel 	 * the same board.
63141708Sstevel 	 */
63151708Sstevel 	if (mp->dev.bp->mem == NULL) {
63161708Sstevel 		/* start list of mem units on this board */
63171708Sstevel 		mp->dev.bp->mem = mp;
63181708Sstevel 
63191708Sstevel 		/*
63201708Sstevel 		 * force unum to zero since this is the only mem unit
63211708Sstevel 		 * that will be visible to the PIM layer.
63221708Sstevel 		 */
63231708Sstevel 		mp->dev.unum = 0;
63241708Sstevel 
63251708Sstevel 		/*
63261708Sstevel 		 * board memory size kept in this mem unit only
63271708Sstevel 		 */
63281708Sstevel 		err = drmach_mem_init_size(mp);
63291708Sstevel 		if (err) {
63301708Sstevel 			mp->dev.bp->mem = NULL;
63311708Sstevel 			/* drmach_mem_dispose frees board mem list */
63321708Sstevel 			drmach_node_dispose(mp->dev.node);
63331708Sstevel 			kmem_free(mp, sizeof (*mp));
63341708Sstevel 			*idp = (drmachid_t)0;
63351708Sstevel 			return (NULL);
63361708Sstevel 		}
63371708Sstevel 
63381708Sstevel 		/*
63391708Sstevel 		 * allow this instance (the first encountered on this board)
63401708Sstevel 		 * to be visible to the PIM layer.
63411708Sstevel 		 */
63421708Sstevel 		*idp = (drmachid_t)mp;
63431708Sstevel 	} else {
63441708Sstevel 		drmach_mem_t *lp;
63451708Sstevel 
63461708Sstevel 		/* hide this mem instance behind the first. */
63471708Sstevel 		for (lp = mp->dev.bp->mem; lp->next; lp = lp->next)
63481708Sstevel 			;
63491708Sstevel 		lp->next = mp;
63501708Sstevel 
63511708Sstevel 		/*
63521708Sstevel 		 * hide this instance from the caller.
63531708Sstevel 		 * See drmach_board_find_devices_cb() for details.
63541708Sstevel 		 */
63551708Sstevel 		*idp = (drmachid_t)0;
63561708Sstevel 	}
63571708Sstevel 
63581708Sstevel 	return (NULL);
63591708Sstevel }
63601708Sstevel 
63611708Sstevel static void
drmach_mem_dispose(drmachid_t id)63621708Sstevel drmach_mem_dispose(drmachid_t id)
63631708Sstevel {
63641708Sstevel 	drmach_mem_t *mp, *next;
63651708Sstevel 	drmach_board_t *bp;
63661708Sstevel 
63671708Sstevel 	ASSERT(DRMACH_IS_MEM_ID(id));
63681708Sstevel 
63691708Sstevel 	mutex_enter(&drmach_bus_sync_lock);
63701708Sstevel 
63711708Sstevel 	mp = id;
63721708Sstevel 	bp = mp->dev.bp;
63731708Sstevel 
63741708Sstevel 	do {
63751708Sstevel 		if (mp->dev.node)
63761708Sstevel 			drmach_node_dispose(mp->dev.node);
63771708Sstevel 
63781708Sstevel 		next = mp->next;
63791708Sstevel 		kmem_free(mp, sizeof (*mp));
63801708Sstevel 		mp = next;
63811708Sstevel 	} while (mp);
63821708Sstevel 
63831708Sstevel 	bp->mem = NULL;
63841708Sstevel 
63851708Sstevel 	drmach_bus_sync_list_update();
63861708Sstevel 	mutex_exit(&drmach_bus_sync_lock);
63871708Sstevel }
63881708Sstevel 
63891708Sstevel sbd_error_t *
drmach_mem_add_span(drmachid_t id,uint64_t basepa,uint64_t size)63901708Sstevel drmach_mem_add_span(drmachid_t id, uint64_t basepa, uint64_t size)
63911708Sstevel {
63921708Sstevel 	pfn_t		basepfn = (pfn_t)(basepa >> PAGESHIFT);
63931708Sstevel 	pgcnt_t		npages = (pgcnt_t)(size >> PAGESHIFT);
63941708Sstevel 	int		rv;
63951708Sstevel 
63961708Sstevel 	ASSERT(size != 0);
63971708Sstevel 
63981708Sstevel 	if (!DRMACH_IS_MEM_ID(id))
63991708Sstevel 		return (drerr_new(0, ESTC_INAPPROP, NULL));
64001708Sstevel 
64014266Sdp78419 	rv = kcage_range_add(basepfn, npages, KCAGE_DOWN);
64021708Sstevel 	if (rv == ENOMEM) {
64031708Sstevel 		cmn_err(CE_WARN, "%lu megabytes not available"
640411066Srafael.vanoni@sun.com 		    " to kernel cage", size >> 20);
64051708Sstevel 	} else if (rv != 0) {
64061708Sstevel 		/* catch this in debug kernels */
64071708Sstevel 		ASSERT(0);
64081708Sstevel 
64091708Sstevel 		cmn_err(CE_WARN, "unexpected kcage_range_add"
641011066Srafael.vanoni@sun.com 		    " return value %d", rv);
64111708Sstevel 	}
64121708Sstevel 
64131708Sstevel 	return (NULL);
64141708Sstevel }
64151708Sstevel 
64161708Sstevel sbd_error_t *
drmach_mem_del_span(drmachid_t id,uint64_t basepa,uint64_t size)64171708Sstevel drmach_mem_del_span(drmachid_t id, uint64_t basepa, uint64_t size)
64181708Sstevel {
64191708Sstevel 	pfn_t		 basepfn = (pfn_t)(basepa >> PAGESHIFT);
64201708Sstevel 	pgcnt_t		 npages = (pgcnt_t)(size >> PAGESHIFT);
64211708Sstevel 	int		 rv;
64221708Sstevel 
64231708Sstevel 	if (!DRMACH_IS_MEM_ID(id))
64241708Sstevel 		return (drerr_new(0, ESTC_INAPPROP, NULL));
64251708Sstevel 
64261708Sstevel 	if (size > 0) {
64271708Sstevel 		rv = kcage_range_delete_post_mem_del(basepfn, npages);
64281708Sstevel 		if (rv != 0) {
64291708Sstevel 			cmn_err(CE_WARN,
64301708Sstevel 			    "unexpected kcage_range_delete_post_mem_del"
64311708Sstevel 			    " return value %d", rv);
64321708Sstevel 			return (DRMACH_INTERNAL_ERROR());
64331708Sstevel 		}
64341708Sstevel 	}
64351708Sstevel 
64361708Sstevel 	return (NULL);
64371708Sstevel }
64381708Sstevel 
64391708Sstevel sbd_error_t *
drmach_mem_disable(drmachid_t id)64401708Sstevel drmach_mem_disable(drmachid_t id)
64411708Sstevel {
64421708Sstevel 	if (!DRMACH_IS_MEM_ID(id))
64431708Sstevel 		return (drerr_new(0, ESTC_INAPPROP, NULL));
64441708Sstevel 	else
64451708Sstevel 		return (NULL);
64461708Sstevel }
64471708Sstevel 
64481708Sstevel sbd_error_t *
drmach_mem_enable(drmachid_t id)64491708Sstevel drmach_mem_enable(drmachid_t id)
64501708Sstevel {
64511708Sstevel 	if (!DRMACH_IS_MEM_ID(id))
64521708Sstevel 		return (drerr_new(0, ESTC_INAPPROP, NULL));
64531708Sstevel 	else
64541708Sstevel 		return (NULL);
64551708Sstevel }
64561708Sstevel 
64571708Sstevel sbd_error_t *
drmach_mem_get_alignment(drmachid_t id,uint64_t * mask)64581708Sstevel drmach_mem_get_alignment(drmachid_t id, uint64_t *mask)
64591708Sstevel {
64601708Sstevel #define	MB(mb) ((mb) * 1048576ull)
64611708Sstevel 
64621708Sstevel 	static struct {
64631708Sstevel 		uint_t		uk;
64641708Sstevel 		uint64_t	segsz;
64651708Sstevel 	}  uk2segsz[] = {
64661708Sstevel 		{ 0x003,	MB(256)	  },
64671708Sstevel 		{ 0x007,	MB(512)	  },
64681708Sstevel 		{ 0x00f,	MB(1024)  },
64691708Sstevel 		{ 0x01f,	MB(2048)  },
64701708Sstevel 		{ 0x03f,	MB(4096)  },
64711708Sstevel 		{ 0x07f,	MB(8192)  },
64721708Sstevel 		{ 0x0ff,	MB(16384) },
64731708Sstevel 		{ 0x1ff,	MB(32768) },
64741708Sstevel 		{ 0x3ff,	MB(65536) },
64751708Sstevel 		{ 0x7ff,	MB(131072) }
64761708Sstevel 	};
64771708Sstevel 	static int len = sizeof (uk2segsz) / sizeof (uk2segsz[0]);
64781708Sstevel 
64791708Sstevel #undef MB
64801708Sstevel 
64811708Sstevel 	uint64_t	 largest_sz = 0;
64821708Sstevel 	drmach_mem_t	*mp;
64831708Sstevel 
64841708Sstevel 	if (!DRMACH_IS_MEM_ID(id))
64851708Sstevel 		return (drerr_new(0, ESTC_INAPPROP, NULL));
64861708Sstevel 
64871708Sstevel 	/* prime the result with a default value */
64881708Sstevel 	*mask = (DRMACH_MEM_SLICE_SIZE - 1);
64891708Sstevel 
64901708Sstevel 	for (mp = id; mp; mp = mp->next) {
64911708Sstevel 		int bank;
64921708Sstevel 
64931708Sstevel 		for (bank = 0; bank < DRMACH_MC_NBANKS; bank++) {
64941708Sstevel 			int		i;
64951708Sstevel 			uint_t		uk;
64961708Sstevel 			uint64_t	madr;
64971708Sstevel 
64981708Sstevel 			/* get register value, extract uk and normalize */
64991708Sstevel 			drmach_mem_read_madr(mp, bank, &madr);
65001708Sstevel 
65011708Sstevel 			if (!(madr & DRMACH_MC_VALID_MASK))
65021708Sstevel 				continue;
65031708Sstevel 
65041708Sstevel 			uk = DRMACH_MC_UK(madr);
65051708Sstevel 
65061708Sstevel 			/* match uk value */
65071708Sstevel 			for (i = 0; i < len; i++)
65081708Sstevel 				if (uk == uk2segsz[i].uk)
65091708Sstevel 					break;
65101708Sstevel 
65111708Sstevel 			if (i < len) {
65121708Sstevel 				uint64_t sz = uk2segsz[i].segsz;
65131708Sstevel 
65141708Sstevel 				/*
65151708Sstevel 				 * remember largest segment size,
65161708Sstevel 				 * update mask result
65171708Sstevel 				 */
65181708Sstevel 				if (sz > largest_sz) {
65191708Sstevel 					largest_sz = sz;
65201708Sstevel 					*mask = sz - 1;
65211708Sstevel 				}
65221708Sstevel 			} else {
65231708Sstevel 				/*
65241708Sstevel 				 * uk not in table, punt using
65251708Sstevel 				 * entire slice size. no longer any
65261708Sstevel 				 * reason to check other banks.
65271708Sstevel 				 */
65281708Sstevel 				*mask = (DRMACH_MEM_SLICE_SIZE - 1);
65291708Sstevel 				return (NULL);
65301708Sstevel 			}
65311708Sstevel 		}
65321708Sstevel 	}
65331708Sstevel 
65341708Sstevel 	return (NULL);
65351708Sstevel }
65361708Sstevel 
65371708Sstevel sbd_error_t *
drmach_mem_get_base_physaddr(drmachid_t id,uint64_t * base_addr)65381708Sstevel drmach_mem_get_base_physaddr(drmachid_t id, uint64_t *base_addr)
65391708Sstevel {
65401708Sstevel 	drmach_mem_t *mp;
65411708Sstevel 
65421708Sstevel 	if (!DRMACH_IS_MEM_ID(id))
65431708Sstevel 		return (drerr_new(0, ESTC_INAPPROP, NULL));
65441708Sstevel 
65451708Sstevel 	*base_addr = (uint64_t)-1;
65461708Sstevel 	for (mp = id; mp; mp = mp->next) {
65471708Sstevel 		int bank;
65481708Sstevel 
65491708Sstevel 		for (bank = 0; bank < DRMACH_MC_NBANKS; bank++) {
65501708Sstevel 			uint64_t addr, madr;
65511708Sstevel 
65521708Sstevel 			drmach_mem_read_madr(mp, bank, &madr);
65531708Sstevel 			if (madr & DRMACH_MC_VALID_MASK) {
65541708Sstevel 				addr = DRMACH_MC_UM_TO_PA(madr) |
655511066Srafael.vanoni@sun.com 				    DRMACH_MC_LM_TO_PA(madr);
65561708Sstevel 
65571708Sstevel 				if (addr < *base_addr)
65581708Sstevel 					*base_addr = addr;
65591708Sstevel 			}
65601708Sstevel 		}
65611708Sstevel 	}
65621708Sstevel 
65631708Sstevel 	/* should not happen, but ... */
65641708Sstevel 	if (*base_addr == (uint64_t)-1)
65651708Sstevel 		return (DRMACH_INTERNAL_ERROR());
65661708Sstevel 
65671708Sstevel 	return (NULL);
65681708Sstevel }
65691708Sstevel 
65701708Sstevel void
drmach_bus_sync_list_update(void)65711708Sstevel drmach_bus_sync_list_update(void)
65721708Sstevel {
65731708Sstevel 	int		rv, idx, cnt = 0;
65741708Sstevel 	drmachid_t	id;
65751708Sstevel 
65761708Sstevel 	ASSERT(MUTEX_HELD(&drmach_bus_sync_lock));
65771708Sstevel 
65781708Sstevel 	rv = drmach_array_first(drmach_boards, &idx, &id);
65791708Sstevel 	while (rv == 0) {
65801708Sstevel 		drmach_board_t		*bp = id;
65811708Sstevel 		drmach_mem_t		*mp = bp->mem;
65821708Sstevel 
65831708Sstevel 		while (mp) {
65841708Sstevel 			int bank;
65851708Sstevel 
65861708Sstevel 			for (bank = 0; bank < DRMACH_MC_NBANKS; bank++) {
65871708Sstevel 				uint64_t madr;
65881708Sstevel 
65891708Sstevel 				drmach_mem_read_madr(mp, bank, &madr);
65901708Sstevel 				if (madr & DRMACH_MC_VALID_MASK) {
65911708Sstevel 					uint64_t pa;
65921708Sstevel 
65931708Sstevel 					pa  = DRMACH_MC_UM_TO_PA(madr);
65941708Sstevel 					pa |= DRMACH_MC_LM_TO_PA(madr);
65951708Sstevel 
65961708Sstevel 					/*
65971708Sstevel 					 * The list is zero terminated.
65981708Sstevel 					 * Offset the pa by a doubleword
65991708Sstevel 					 * to avoid confusing a pa value of
66001708Sstevel 					 * of zero with the terminator.
66011708Sstevel 					 */
66021708Sstevel 					pa += sizeof (uint64_t);
66031708Sstevel 
66041708Sstevel 					drmach_bus_sync_list[cnt++] = pa;
66051708Sstevel 				}
66061708Sstevel 			}
66071708Sstevel 
66081708Sstevel 			mp = mp->next;
66091708Sstevel 		}
66101708Sstevel 
66111708Sstevel 		rv = drmach_array_next(drmach_boards, &idx, &id);
66121708Sstevel 	}
66131708Sstevel 
66141708Sstevel 	drmach_bus_sync_list[cnt] = 0;
66151708Sstevel }
66161708Sstevel 
66171708Sstevel sbd_error_t *
drmach_mem_get_memlist(drmachid_t id,struct memlist ** ml)66181708Sstevel drmach_mem_get_memlist(drmachid_t id, struct memlist **ml)
66191708Sstevel {
66201708Sstevel 	sbd_error_t	*err;
66211708Sstevel 	struct memlist	*mlist;
66221708Sstevel 	gdcd_t		*gdcd;
66231708Sstevel 	mem_chunk_t	*chunk;
66241708Sstevel 	uint64_t	 chunks, pa, mask;
66251708Sstevel 
66261708Sstevel 	err = drmach_mem_get_base_physaddr(id, &pa);
66271708Sstevel 	if (err)
66281708Sstevel 		return (err);
66291708Sstevel 
66301708Sstevel 	gdcd = drmach_gdcd_new();
66311708Sstevel 	if (gdcd == NULL)
66321708Sstevel 		return (DRMACH_INTERNAL_ERROR());
66331708Sstevel 
66341708Sstevel 	mask = ~ (DRMACH_MEM_SLICE_SIZE - 1);
66351708Sstevel 	pa &= mask;
66361708Sstevel 
66371708Sstevel 	mlist = NULL;
66381708Sstevel 	chunk = gdcd->dcd_chunk_list.dcl_chunk;
66391708Sstevel 	chunks = gdcd->dcd_chunk_list.dcl_chunks;
66401708Sstevel 	while (chunks-- != 0) {
66411708Sstevel 		if ((chunk->mc_base_pa & mask) == pa) {
664211066Srafael.vanoni@sun.com 			mlist = memlist_add_span(mlist, chunk->mc_base_pa,
664311066Srafael.vanoni@sun.com 			    chunk->mc_mbytes * 1048576);
66441708Sstevel 		}
66451708Sstevel 
66461708Sstevel 		++chunk;
66471708Sstevel 	}
66481708Sstevel 
66491708Sstevel 	drmach_gdcd_dispose(gdcd);
66501708Sstevel 
66511708Sstevel #ifdef DEBUG
66521708Sstevel 	DRMACH_PR("GDCD derived memlist:");
66531708Sstevel 	memlist_dump(mlist);
66541708Sstevel #endif
66551708Sstevel 
66561708Sstevel 	*ml = mlist;
66571708Sstevel 	return (NULL);
66581708Sstevel }
66591708Sstevel 
66601708Sstevel sbd_error_t *
drmach_mem_get_size(drmachid_t id,uint64_t * bytes)66611708Sstevel drmach_mem_get_size(drmachid_t id, uint64_t *bytes)
66621708Sstevel {
66631708Sstevel 	drmach_mem_t	*mp;
66641708Sstevel 
66651708Sstevel 	if (!DRMACH_IS_MEM_ID(id))
66661708Sstevel 		return (drerr_new(0, ESTC_INAPPROP, NULL));
66671708Sstevel 	mp = id;
66681708Sstevel 
66691708Sstevel 	ASSERT(mp->nbytes != 0);
66701708Sstevel 	*bytes = mp->nbytes;
66711708Sstevel 
66721708Sstevel 	return (NULL);
66731708Sstevel }
66741708Sstevel 
66751708Sstevel sbd_error_t *
drmach_mem_get_slice_size(drmachid_t id,uint64_t * bytes)66761708Sstevel drmach_mem_get_slice_size(drmachid_t id, uint64_t *bytes)
66771708Sstevel {
66781708Sstevel 	sbd_error_t	*err;
66791708Sstevel 	drmach_device_t	*mp;
66801708Sstevel 
66811708Sstevel 	if (!DRMACH_IS_MEM_ID(id))
66821708Sstevel 		return (drerr_new(0, ESTC_INAPPROP, NULL));
66831708Sstevel 	mp = id;
66841708Sstevel 
66851708Sstevel 	switch (DRMACH_BNUM2SLOT(mp->bp->bnum)) {
66861708Sstevel 		case 0:	*bytes = DRMACH_MEM_USABLE_SLICE_SIZE;
66871708Sstevel 			err = NULL;
66881708Sstevel 			break;
66891708Sstevel 
66901708Sstevel 		case 1: *bytes = 0;
66911708Sstevel 			err = NULL;
66921708Sstevel 			break;
66931708Sstevel 
66941708Sstevel 		default:
66951708Sstevel 			err = DRMACH_INTERNAL_ERROR();
66961708Sstevel 			break;
66971708Sstevel 	}
66981708Sstevel 
66991708Sstevel 	return (err);
67001708Sstevel }
67011708Sstevel 
67021708Sstevel processorid_t drmach_mem_cpu_affinity_nail;
67031708Sstevel 
67041708Sstevel processorid_t
drmach_mem_cpu_affinity(drmachid_t id)67051708Sstevel drmach_mem_cpu_affinity(drmachid_t id)
67061708Sstevel {
67071708Sstevel 	drmach_device_t	*mp;
67081708Sstevel 	drmach_board_t	*bp;
67091708Sstevel 	processorid_t	 cpuid;
67101708Sstevel 
67111708Sstevel 	if (!DRMACH_IS_MEM_ID(id))
67121708Sstevel 		return (CPU_CURRENT);
67131708Sstevel 
67141708Sstevel 	if (drmach_mem_cpu_affinity_nail) {
67151708Sstevel 		cpuid = drmach_mem_cpu_affinity_nail;
67161708Sstevel 
67171708Sstevel 		if (cpuid < 0 || cpuid > NCPU)
67181708Sstevel 			return (CPU_CURRENT);
67191708Sstevel 
67201708Sstevel 		mutex_enter(&cpu_lock);
67211708Sstevel 		if (cpu[cpuid] == NULL || !CPU_ACTIVE(cpu[cpuid]))
67221708Sstevel 			cpuid = CPU_CURRENT;
67231708Sstevel 		mutex_exit(&cpu_lock);
67241708Sstevel 
67251708Sstevel 		return (cpuid);
67261708Sstevel 	}
67271708Sstevel 
67281708Sstevel 	/* try to choose a proc on the target board */
67291708Sstevel 	mp = id;
67301708Sstevel 	bp = mp->bp;
67311708Sstevel 	if (bp->devices) {
67321708Sstevel 		int		 rv;
67331708Sstevel 		int		 d_idx;
67341708Sstevel 		drmachid_t	 d_id;
67351708Sstevel 
67361708Sstevel 		rv = drmach_array_first(bp->devices, &d_idx, &d_id);
67371708Sstevel 		while (rv == 0) {
67381708Sstevel 			if (DRMACH_IS_CPU_ID(d_id)) {
67391708Sstevel 				drmach_cpu_t	*cp = d_id;
67401708Sstevel 
67411708Sstevel 				mutex_enter(&cpu_lock);
67421708Sstevel 				cpuid = cp->cpuid;
67431708Sstevel 				if (cpu[cpuid] && CPU_ACTIVE(cpu[cpuid])) {
67441708Sstevel 					mutex_exit(&cpu_lock);
67451708Sstevel 					return (cpuid);
67461708Sstevel 				} else {
67471708Sstevel 					mutex_exit(&cpu_lock);
67481708Sstevel 				}
67491708Sstevel 			}
67501708Sstevel 
67511708Sstevel 			rv = drmach_array_next(bp->devices, &d_idx, &d_id);
67521708Sstevel 		}
67531708Sstevel 	}
67541708Sstevel 
67551708Sstevel 	/* otherwise, this proc, wherever it is */
67561708Sstevel 	return (CPU_CURRENT);
67571708Sstevel }
67581708Sstevel 
67591708Sstevel static sbd_error_t *
drmach_mem_release(drmachid_t id)67601708Sstevel drmach_mem_release(drmachid_t id)
67611708Sstevel {
67621708Sstevel 	if (!DRMACH_IS_MEM_ID(id))
67631708Sstevel 		return (drerr_new(0, ESTC_INAPPROP, NULL));
67641708Sstevel 	return (NULL);
67651708Sstevel }
67661708Sstevel 
67671708Sstevel static sbd_error_t *
drmach_mem_status(drmachid_t id,drmach_status_t * stat)67681708Sstevel drmach_mem_status(drmachid_t id, drmach_status_t *stat)
67691708Sstevel {
67701708Sstevel 	drmach_mem_t	*mp;
67711708Sstevel 	sbd_error_t	*err;
67721708Sstevel 	uint64_t	 pa, slice_size;
67731708Sstevel 	struct memlist	*ml;
67741708Sstevel 
67751708Sstevel 	ASSERT(DRMACH_IS_MEM_ID(id));
67761708Sstevel 	mp = id;
67771708Sstevel 
67781708Sstevel 	/* get starting physical address of target memory */
67791708Sstevel 	err = drmach_mem_get_base_physaddr(id, &pa);
67801708Sstevel 	if (err)
67811708Sstevel 		return (err);
67821708Sstevel 
67831708Sstevel 	/* round down to slice boundary */
67841708Sstevel 	slice_size = DRMACH_MEM_SLICE_SIZE;
67851708Sstevel 	pa &= ~ (slice_size - 1);
67861708Sstevel 
67871708Sstevel 	/* stop at first span that is in slice */
67881708Sstevel 	memlist_read_lock();
6789*11474SJonathan.Adams@Sun.COM 	for (ml = phys_install; ml; ml = ml->ml_next)
6790*11474SJonathan.Adams@Sun.COM 		if (ml->ml_address >= pa && ml->ml_address < pa + slice_size)
67911708Sstevel 			break;
67921708Sstevel 	memlist_read_unlock();
67931708Sstevel 
67941708Sstevel 	stat->assigned = mp->dev.bp->assigned;
67951708Sstevel 	stat->powered = mp->dev.bp->powered;
67961708Sstevel 	stat->configured = (ml != NULL);
67971708Sstevel 	stat->busy = mp->dev.busy;
679811311SSurya.Prakki@Sun.COM 	(void) strncpy(stat->type, mp->dev.type, sizeof (stat->type));
67991708Sstevel 	stat->info[0] = '\0';
68001708Sstevel 
68011708Sstevel 	return (NULL);
68021708Sstevel }
68031708Sstevel 
68041708Sstevel sbd_error_t *
drmach_board_deprobe(drmachid_t id)68051708Sstevel drmach_board_deprobe(drmachid_t id)
68061708Sstevel {
68071708Sstevel 	drmach_board_t	*bp;
68081708Sstevel 	sbd_error_t	*err = NULL;
68091708Sstevel 
68101708Sstevel 	if (!DRMACH_IS_BOARD_ID(id))
68111708Sstevel 		return (drerr_new(0, ESTC_INAPPROP, NULL));
68121708Sstevel 	bp = id;
68131708Sstevel 
68141708Sstevel 	if (bp->tree) {
68151708Sstevel 		drmach_node_dispose(bp->tree);
68161708Sstevel 		bp->tree = NULL;
68171708Sstevel 	}
68181708Sstevel 	if (bp->devices) {
68191708Sstevel 		drmach_array_dispose(bp->devices, drmach_device_dispose);
68201708Sstevel 		bp->devices = NULL;
68211708Sstevel 		bp->mem = NULL;  /* TODO: still needed? */
68221708Sstevel 	}
68231708Sstevel 	return (err);
68241708Sstevel }
68251708Sstevel 
68261708Sstevel /*ARGSUSED1*/
68271708Sstevel static sbd_error_t *
drmach_pt_showlpa(drmachid_t id,drmach_opts_t * opts)68281708Sstevel drmach_pt_showlpa(drmachid_t id, drmach_opts_t *opts)
68291708Sstevel {
68301708Sstevel 	drmach_device_t	*dp;
68311708Sstevel 	uint64_t	val;
68321708Sstevel 	int		err = 1;
68331708Sstevel 
68341708Sstevel 	if (DRMACH_IS_CPU_ID(id)) {
68351708Sstevel 		drmach_cpu_t *cp = id;
68361708Sstevel 		if (drmach_cpu_read_scr(cp, &val))
68371708Sstevel 			err = 0;
68381708Sstevel 	} else if (DRMACH_IS_IO_ID(id) && ((drmach_io_t *)id)->scsr_pa != 0) {
68391708Sstevel 		drmach_io_t *io = id;
68401708Sstevel 		val = lddphysio(io->scsr_pa);
68411708Sstevel 		err = 0;
68421708Sstevel 	}
68431708Sstevel 	if (err)
68441708Sstevel 		return (drerr_new(0, ESTC_INAPPROP, NULL));
68451708Sstevel 
68461708Sstevel 	dp = id;
68471708Sstevel 	uprintf("showlpa %s::%s portid %d, base pa %lx, bound pa %lx\n",
684811066Srafael.vanoni@sun.com 	    dp->bp->cm.name,
684911066Srafael.vanoni@sun.com 	    dp->cm.name,
685011066Srafael.vanoni@sun.com 	    dp->portid,
685111311SSurya.Prakki@Sun.COM 	    (long)(DRMACH_LPA_BASE_TO_PA(val)),
685211311SSurya.Prakki@Sun.COM 	    (long)(DRMACH_LPA_BND_TO_PA(val)));
68531708Sstevel 
68541708Sstevel 	return (NULL);
68551708Sstevel }
68561708Sstevel 
68571708Sstevel /*ARGSUSED*/
68581708Sstevel static sbd_error_t *
drmach_pt_ikprobe(drmachid_t id,drmach_opts_t * opts)68591708Sstevel drmach_pt_ikprobe(drmachid_t id, drmach_opts_t *opts)
68601708Sstevel {
68611708Sstevel 	drmach_board_t		*bp = (drmach_board_t *)id;
68621708Sstevel 	sbd_error_t		*err;
68631708Sstevel 	sc_gptwocfg_cookie_t	scc;
68641708Sstevel 
68651708Sstevel 	if (!DRMACH_IS_BOARD_ID(id))
68661708Sstevel 		return (drerr_new(0, ESTC_INAPPROP, NULL));
68671708Sstevel 
68681708Sstevel 	/* do saf configurator stuff */
68691708Sstevel 	DRMACH_PR("calling sc_probe_board for bnum=%d\n", bp->bnum);
68701708Sstevel 	scc = sc_probe_board(bp->bnum);
68711708Sstevel 	if (scc == NULL) {
68721708Sstevel 		err = drerr_new(0, ESTC_PROBE, bp->cm.name);
68731708Sstevel 		return (err);
68741708Sstevel 	}
68751708Sstevel 
68761708Sstevel 	return (err);
68771708Sstevel }
68781708Sstevel 
68791708Sstevel /*ARGSUSED*/
68801708Sstevel static sbd_error_t *
drmach_pt_ikdeprobe(drmachid_t id,drmach_opts_t * opts)68811708Sstevel drmach_pt_ikdeprobe(drmachid_t id, drmach_opts_t *opts)
68821708Sstevel {
68831708Sstevel 	drmach_board_t	*bp;
68841708Sstevel 	sbd_error_t	*err = NULL;
68851708Sstevel 	sc_gptwocfg_cookie_t	scc;
68861708Sstevel 
68871708Sstevel 	if (!DRMACH_IS_BOARD_ID(id))
68881708Sstevel 		return (drerr_new(0, ESTC_INAPPROP, NULL));
68891708Sstevel 	bp = id;
68901708Sstevel 
68911708Sstevel 	cmn_err(CE_CONT, "DR: in-kernel unprobe board %d\n", bp->bnum);
68921708Sstevel 	scc = sc_unprobe_board(bp->bnum);
68931708Sstevel 	if (scc != NULL) {
68941708Sstevel 		err = drerr_new(0, ESTC_DEPROBE, bp->cm.name);
68951708Sstevel 	}
68961708Sstevel 
68971708Sstevel 	if (err == NULL)
68981708Sstevel 		err = drmach_board_deprobe(id);
68991708Sstevel 
69001708Sstevel 	return (err);
69011708Sstevel }
69021708Sstevel 
69031708Sstevel static sbd_error_t *
drmach_pt_readmem(drmachid_t id,drmach_opts_t * opts)69041708Sstevel drmach_pt_readmem(drmachid_t id, drmach_opts_t *opts)
69051708Sstevel {
69061708Sstevel 	_NOTE(ARGUNUSED(id))
69071708Sstevel 	_NOTE(ARGUNUSED(opts))
69081708Sstevel 
69091708Sstevel 	struct memlist	*ml;
69101708Sstevel 	uint64_t	src_pa;
69111708Sstevel 	uint64_t	dst_pa;
69121708Sstevel 	uint64_t	dst;
69131708Sstevel 
69141708Sstevel 	dst_pa = va_to_pa(&dst);
69151708Sstevel 
69161708Sstevel 	memlist_read_lock();
6917*11474SJonathan.Adams@Sun.COM 	for (ml = phys_install; ml; ml = ml->ml_next) {
69181708Sstevel 		uint64_t	nbytes;
69191708Sstevel 
6920*11474SJonathan.Adams@Sun.COM 		src_pa = ml->ml_address;
6921*11474SJonathan.Adams@Sun.COM 		nbytes = ml->ml_size;
69221708Sstevel 
69231708Sstevel 		while (nbytes != 0ull) {
69241708Sstevel 
69251708Sstevel 			/* copy 32 bytes at src_pa to dst_pa */
69261708Sstevel 			bcopy32_il(src_pa, dst_pa);
69271708Sstevel 
69281708Sstevel 			/* increment by 32 bytes */
69291708Sstevel 			src_pa += (4 * sizeof (uint64_t));
69301708Sstevel 
69311708Sstevel 			/* decrement by 32 bytes */
69321708Sstevel 			nbytes -= (4 * sizeof (uint64_t));
69331708Sstevel 		}
69341708Sstevel 	}
69351708Sstevel 	memlist_read_unlock();
69361708Sstevel 
69371708Sstevel 	return (NULL);
69381708Sstevel }
69391708Sstevel 
69401708Sstevel static sbd_error_t *
drmach_pt_recovercpu(drmachid_t id,drmach_opts_t * opts)69411708Sstevel drmach_pt_recovercpu(drmachid_t id, drmach_opts_t *opts)
69421708Sstevel {
69431708Sstevel 	_NOTE(ARGUNUSED(opts))
69441708Sstevel 
69451708Sstevel 	drmach_cpu_t	*cp;
69461708Sstevel 
69471708Sstevel 	if (!DRMACH_IS_CPU_ID(id))
69481708Sstevel 		return (drerr_new(0, ESTC_INAPPROP, NULL));
69491708Sstevel 	cp = id;
69501708Sstevel 
69511708Sstevel 	mutex_enter(&cpu_lock);
69521708Sstevel 	(void) drmach_iocage_cpu_return(&(cp->dev),
69531708Sstevel 	    CPU_ENABLE | CPU_EXISTS | CPU_READY | CPU_RUNNING);
69541708Sstevel 	mutex_exit(&cpu_lock);
69551708Sstevel 
69561708Sstevel 	return (NULL);
69571708Sstevel }
69581708Sstevel 
69591708Sstevel /*
69601708Sstevel  * Starcat DR passthrus are for debugging purposes only.
69611708Sstevel  */
69621708Sstevel static struct {
69631708Sstevel 	const char	*name;
69641708Sstevel 	sbd_error_t	*(*handler)(drmachid_t id, drmach_opts_t *opts);
69651708Sstevel } drmach_pt_arr[] = {
69661708Sstevel 	{ "showlpa",		drmach_pt_showlpa		},
69671708Sstevel 	{ "ikprobe",		drmach_pt_ikprobe		},
69681708Sstevel 	{ "ikdeprobe",		drmach_pt_ikdeprobe		},
69691708Sstevel 	{ "readmem",		drmach_pt_readmem		},
69701708Sstevel 	{ "recovercpu",		drmach_pt_recovercpu		},
69711708Sstevel 
69721708Sstevel 	/* the following line must always be last */
69731708Sstevel 	{ NULL,			NULL				}
69741708Sstevel };
69751708Sstevel 
69761708Sstevel /*ARGSUSED*/
69771708Sstevel sbd_error_t *
drmach_passthru(drmachid_t id,drmach_opts_t * opts)69781708Sstevel drmach_passthru(drmachid_t id, drmach_opts_t *opts)
69791708Sstevel {
69801708Sstevel 	int		i;
69811708Sstevel 	sbd_error_t	*err;
69821708Sstevel 
69831708Sstevel 	i = 0;
69841708Sstevel 	while (drmach_pt_arr[i].name != NULL) {
69851708Sstevel 		int len = strlen(drmach_pt_arr[i].name);
69861708Sstevel 
69871708Sstevel 		if (strncmp(drmach_pt_arr[i].name, opts->copts, len) == 0)
69881708Sstevel 			break;
69891708Sstevel 
69901708Sstevel 		i += 1;
69911708Sstevel 	}
69921708Sstevel 
69931708Sstevel 	if (drmach_pt_arr[i].name == NULL)
69941708Sstevel 		err = drerr_new(0, ESTC_UNKPTCMD, opts->copts);
69951708Sstevel 	else
69961708Sstevel 		err = (*drmach_pt_arr[i].handler)(id, opts);
69971708Sstevel 
69981708Sstevel 	return (err);
69991708Sstevel }
70001708Sstevel 
70011708Sstevel sbd_error_t *
drmach_release(drmachid_t id)70021708Sstevel drmach_release(drmachid_t id)
70031708Sstevel {
70041708Sstevel 	drmach_common_t *cp;
70051708Sstevel 
70061708Sstevel 	if (!DRMACH_IS_DEVICE_ID(id))
70071708Sstevel 		return (drerr_new(0, ESTC_INAPPROP, NULL));
70081708Sstevel 	cp = id;
70091708Sstevel 
70101708Sstevel 	return (cp->release(id));
70111708Sstevel }
70121708Sstevel 
70131708Sstevel sbd_error_t *
drmach_status(drmachid_t id,drmach_status_t * stat)70141708Sstevel drmach_status(drmachid_t id, drmach_status_t *stat)
70151708Sstevel {
70161708Sstevel 	drmach_common_t *cp;
70171708Sstevel 	sbd_error_t	*err;
70181708Sstevel 
70191708Sstevel 	rw_enter(&drmach_boards_rwlock, RW_READER);
70201708Sstevel 
70211708Sstevel 	if (!DRMACH_IS_ID(id)) {
70221708Sstevel 		rw_exit(&drmach_boards_rwlock);
70231708Sstevel 		return (drerr_new(0, ESTC_NOTID, NULL));
70241708Sstevel 	}
70251708Sstevel 
70261708Sstevel 	cp = id;
70271708Sstevel 
70281708Sstevel 	err = cp->status(id, stat);
70291708Sstevel 	rw_exit(&drmach_boards_rwlock);
70301708Sstevel 	return (err);
70311708Sstevel }
70321708Sstevel 
70331708Sstevel static sbd_error_t *
drmach_i_status(drmachid_t id,drmach_status_t * stat)70341708Sstevel drmach_i_status(drmachid_t id, drmach_status_t *stat)
70351708Sstevel {
70361708Sstevel 	drmach_common_t *cp;
70371708Sstevel 
70381708Sstevel 	if (!DRMACH_IS_ID(id))
70391708Sstevel 		return (drerr_new(0, ESTC_NOTID, NULL));
70401708Sstevel 	cp = id;
70411708Sstevel 
70421708Sstevel 	return (cp->status(id, stat));
70431708Sstevel }
70441708Sstevel 
70451708Sstevel /*ARGSUSED*/
70461708Sstevel sbd_error_t *
drmach_unconfigure(drmachid_t id,int flags)70471708Sstevel drmach_unconfigure(drmachid_t id, int flags)
70481708Sstevel {
70491708Sstevel 	drmach_device_t	*dp;
70501708Sstevel 	dev_info_t 	*rdip;
70511708Sstevel 
70521708Sstevel 	char	name[OBP_MAXDRVNAME];
70531708Sstevel 	int rv;
70541708Sstevel 
70551708Sstevel 	/*
70561708Sstevel 	 * Since CPU nodes are not configured, it is
70571708Sstevel 	 * necessary to skip the unconfigure step as
70581708Sstevel 	 * well.
70591708Sstevel 	 */
70601708Sstevel 	if (DRMACH_IS_CPU_ID(id)) {
70611708Sstevel 		return (NULL);
70621708Sstevel 	}
70631708Sstevel 
70641708Sstevel 	for (; id; ) {
70651708Sstevel 		dev_info_t	*fdip = NULL;
70661708Sstevel 
70671708Sstevel 		if (!DRMACH_IS_DEVICE_ID(id))
70681708Sstevel 			return (drerr_new(0, ESTC_INAPPROP, NULL));
70691708Sstevel 		dp = id;
70701708Sstevel 
70711708Sstevel 		rdip = dp->node->n_getdip(dp->node);
70721708Sstevel 
70731708Sstevel 		/*
70741708Sstevel 		 * drmach_unconfigure() is always called on a configured branch.
70751708Sstevel 		 * So the root of the branch was held earlier and must exist.
70761708Sstevel 		 */
70771708Sstevel 		ASSERT(rdip);
70781708Sstevel 
70791708Sstevel 		DRMACH_PR("drmach_unconfigure: unconfiguring DDI branch");
70801708Sstevel 
70811708Sstevel 		rv = dp->node->n_getprop(dp->node,
70821708Sstevel 		    "name", name, OBP_MAXDRVNAME);
70831708Sstevel 
70841708Sstevel 		/* The node must have a name */
70851708Sstevel 		if (rv)
70861708Sstevel 			return (0);
70871708Sstevel 
70881708Sstevel 		if (drmach_name2type_idx(name) < 0) {
70891708Sstevel 			if (DRMACH_IS_MEM_ID(id)) {
70901708Sstevel 				drmach_mem_t	*mp = id;
70911708Sstevel 				id = mp->next;
70921708Sstevel 			} else {
70931708Sstevel 				id = NULL;
70941708Sstevel 			}
70951708Sstevel 			continue;
70961708Sstevel 		}
70971708Sstevel 
70981708Sstevel 		/*
70991708Sstevel 		 * NOTE: FORCE flag is no longer needed under devfs
71001708Sstevel 		 */
71011708Sstevel 		ASSERT(e_ddi_branch_held(rdip));
71021708Sstevel 		if (e_ddi_branch_unconfigure(rdip, &fdip, 0) != 0) {
710311066Srafael.vanoni@sun.com 			sbd_error_t *err = NULL;
710411066Srafael.vanoni@sun.com 			char *path = kmem_alloc(MAXPATHLEN, KM_SLEEP);
71051708Sstevel 
71061708Sstevel 			/*
71071708Sstevel 			 * If non-NULL, fdip is returned held and must be
71081708Sstevel 			 * released.
71091708Sstevel 			 */
71101708Sstevel 			if (fdip != NULL) {
71111708Sstevel 				(void) ddi_pathname(fdip, path);
71121708Sstevel 				ddi_release_devi(fdip);
71131708Sstevel 			} else {
71141708Sstevel 				(void) ddi_pathname(rdip, path);
71151708Sstevel 			}
71161708Sstevel 
71171708Sstevel 			err = drerr_new(1, ESTC_DRVFAIL, path);
71181708Sstevel 
71191708Sstevel 			kmem_free(path, MAXPATHLEN);
71201708Sstevel 
71211708Sstevel 			/*
71221708Sstevel 			 * If we were unconfiguring an IO board, a call was
71231708Sstevel 			 * made to man_dr_detach.  We now need to call
71241708Sstevel 			 * man_dr_attach to regain man use of the eri.
71251708Sstevel 			 */
71261708Sstevel 			if (DRMACH_IS_IO_ID(id)) {
71271708Sstevel 				int (*func)(dev_info_t *dip);
71281708Sstevel 
71291708Sstevel 				func = (int (*)(dev_info_t *))kobj_getsymvalue\
713011066Srafael.vanoni@sun.com 				    ("man_dr_attach", 0);
71311708Sstevel 
71321708Sstevel 				if (func) {
71331708Sstevel 					drmach_io_inst_t ios;
71341708Sstevel 					dev_info_t 	*pdip;
71351708Sstevel 					int		circ;
71361708Sstevel 
71371708Sstevel 					/*
71381708Sstevel 					 * Walk device tree to find rio dip for
71391708Sstevel 					 * the board
71401708Sstevel 					 * Since we are not interested in iosram
71411708Sstevel 					 * instance here, initialize it to 0, so
71421708Sstevel 					 * that the walk terminates as soon as
71431708Sstevel 					 * eri dip is found.
71441708Sstevel 					 */
71451708Sstevel 					ios.iosram_inst = 0;
71461708Sstevel 					ios.eri_dip = NULL;
71471708Sstevel 					ios.bnum = dp->bp->bnum;
71481708Sstevel 
71491708Sstevel 					if (pdip = ddi_get_parent(rdip)) {
71501708Sstevel 						ndi_hold_devi(pdip);
71511708Sstevel 						ndi_devi_enter(pdip, &circ);
71521708Sstevel 					}
71531708Sstevel 					/*
71541708Sstevel 					 * Root node doesn't have to be held in
71551708Sstevel 					 * any way.
71561708Sstevel 					 */
71571708Sstevel 					ASSERT(e_ddi_branch_held(rdip));
71581708Sstevel 					ddi_walk_devs(rdip,
715911066Srafael.vanoni@sun.com 					    drmach_board_find_io_insts,
716011066Srafael.vanoni@sun.com 					    (void *)&ios);
71611708Sstevel 
71621708Sstevel 					DRMACH_PR("drmach_unconfigure: bnum=%d"
716311066Srafael.vanoni@sun.com 					    " eri=0x%p\n",
716411311SSurya.Prakki@Sun.COM 					    ios.bnum, (void *)ios.eri_dip);
71651708Sstevel 
71661708Sstevel 					if (pdip) {
71671708Sstevel 						ndi_devi_exit(pdip, circ);
71681708Sstevel 						ndi_rele_devi(pdip);
71691708Sstevel 					}
71701708Sstevel 
71711708Sstevel 					if (ios.eri_dip) {
71721708Sstevel 						DRMACH_PR("calling"
717311066Srafael.vanoni@sun.com 						    " man_dr_attach\n");
71741708Sstevel 						(void) (*func)(ios.eri_dip);
71751708Sstevel 						/*
71761708Sstevel 						 * Release hold acquired in
71771708Sstevel 						 * drmach_board_find_io_insts()
71781708Sstevel 						 */
71791708Sstevel 						ndi_rele_devi(ios.eri_dip);
71801708Sstevel 					}
71811708Sstevel 				}
71821708Sstevel 			}
71831708Sstevel 			return (err);
71841708Sstevel 		}
71851708Sstevel 
71861708Sstevel 		if (DRMACH_IS_MEM_ID(id)) {
71871708Sstevel 			drmach_mem_t	*mp = id;
71881708Sstevel 			id = mp->next;
71891708Sstevel 		} else {
71901708Sstevel 			id = NULL;
71911708Sstevel 		}
71921708Sstevel 	}
71931708Sstevel 
71941708Sstevel 	return (NULL);
71951708Sstevel }
71961708Sstevel 
71971708Sstevel /*
71981708Sstevel  * drmach interfaces to legacy Starfire platmod logic
71991708Sstevel  * linkage via runtime symbol look up, called from plat_cpu_power*
72001708Sstevel  */
72011708Sstevel 
72021708Sstevel /*
72031708Sstevel  * Start up a cpu.  It is possible that we're attempting to restart
72041708Sstevel  * the cpu after an UNCONFIGURE in which case the cpu will be
72051708Sstevel  * spinning in its cache.  So, all we have to do is wakeup him up.
72061708Sstevel  * Under normal circumstances the cpu will be coming from a previous
72071708Sstevel  * CONNECT and thus will be spinning in OBP.  In both cases, the
72081708Sstevel  * startup sequence is the same.
72091708Sstevel  */
72101708Sstevel int
drmach_cpu_poweron(struct cpu * cp)72111708Sstevel drmach_cpu_poweron(struct cpu *cp)
72121708Sstevel {
72131708Sstevel 	DRMACH_PR("drmach_cpu_poweron: starting cpuid %d\n", cp->cpu_id);
72141708Sstevel 
72151708Sstevel 	ASSERT(MUTEX_HELD(&cpu_lock));
72161708Sstevel 
72171708Sstevel 	if (drmach_cpu_start(cp) != 0)
72181708Sstevel 		return (EBUSY);
72191708Sstevel 	else
72201708Sstevel 		return (0);
72211708Sstevel }
72221708Sstevel 
72231708Sstevel int
drmach_cpu_poweroff(struct cpu * cp)72241708Sstevel drmach_cpu_poweroff(struct cpu *cp)
72251708Sstevel {
72261708Sstevel 	int		ntries;
72271708Sstevel 	processorid_t	cpuid;
72281708Sstevel 	void		drmach_cpu_shutdown_self(void);
72291708Sstevel 
72301708Sstevel 	DRMACH_PR("drmach_cpu_poweroff: stopping cpuid %d\n", cp->cpu_id);
72311708Sstevel 
72321708Sstevel 	ASSERT(MUTEX_HELD(&cpu_lock));
72331708Sstevel 
72341708Sstevel 	/*
72351708Sstevel 	 * XXX CHEETAH SUPPORT
72361708Sstevel 	 * for cheetah, we need to grab the iocage lock since iocage
72371708Sstevel 	 * memory is used for e$ flush.
72381708Sstevel 	 */
72391708Sstevel 	if (drmach_is_cheetah) {
72401708Sstevel 		mutex_enter(&drmach_iocage_lock);
72411708Sstevel 		while (drmach_iocage_is_busy)
72421708Sstevel 			cv_wait(&drmach_iocage_cv, &drmach_iocage_lock);
72431708Sstevel 		drmach_iocage_is_busy = 1;
72441708Sstevel 		drmach_iocage_mem_scrub(ecache_size * 2);
72451708Sstevel 		mutex_exit(&drmach_iocage_lock);
72461708Sstevel 	}
72471708Sstevel 
72481708Sstevel 	cpuid = cp->cpu_id;
72491708Sstevel 
72501708Sstevel 	/*
72511708Sstevel 	 * Set affinity to ensure consistent reading and writing of
72521708Sstevel 	 * drmach_xt_mb[cpuid] by one "master" CPU directing
72531708Sstevel 	 * the shutdown of the target CPU.
72541708Sstevel 	 */
72551708Sstevel 	affinity_set(CPU->cpu_id);
72561708Sstevel 
72571708Sstevel 	/*
72581708Sstevel 	 * Capture all CPUs (except for detaching proc) to prevent
72591708Sstevel 	 * crosscalls to the detaching proc until it has cleared its
72601708Sstevel 	 * bit in cpu_ready_set.
72611708Sstevel 	 *
72621708Sstevel 	 * The CPUs remain paused and the prom_mutex is known to be free.
72631708Sstevel 	 * This prevents blocking when doing prom IEEE-1275 calls at a
72641708Sstevel 	 * high PIL level.
72651708Sstevel 	 */
72661708Sstevel 	promsafe_pause_cpus();
72671708Sstevel 
72681708Sstevel 	/*
72691708Sstevel 	 * Quiesce interrupts on the target CPU. We do this by setting
72701708Sstevel 	 * the CPU 'not ready'- (i.e. removing the CPU from cpu_ready_set) to
72711708Sstevel 	 * prevent it from receiving cross calls and cross traps.
72721708Sstevel 	 * This prevents the processor from receiving any new soft interrupts.
72731708Sstevel 	 */
72741708Sstevel 	mp_cpu_quiesce(cp);
72751708Sstevel 
727611311SSurya.Prakki@Sun.COM 	(void) prom_hotremovecpu(cpuid);
72771708Sstevel 
72781708Sstevel 	start_cpus();
72791708Sstevel 
72801708Sstevel 	/* setup xt_mb, will be cleared by drmach_shutdown_asm when ready */
72811708Sstevel 	drmach_xt_mb[cpuid] = 0x80;
72821708Sstevel 
72831708Sstevel 	xt_one_unchecked(cp->cpu_id, (xcfunc_t *)idle_stop_xcall,
728411066Srafael.vanoni@sun.com 	    (uint64_t)drmach_cpu_shutdown_self, NULL);
72851708Sstevel 
72861708Sstevel 	ntries = drmach_cpu_ntries;
72871708Sstevel 	while (drmach_xt_mb[cpuid] && ntries) {
72881708Sstevel 		DELAY(drmach_cpu_delay);
72891708Sstevel 		ntries--;
72901708Sstevel 	}
72911708Sstevel 
72921708Sstevel 	drmach_xt_mb[cpuid] = 0;	/* steal the cache line back */
72931708Sstevel 
72941708Sstevel 	membar_sync();			/* make sure copy-back retires */
72951708Sstevel 
72961708Sstevel 	affinity_clear();
72971708Sstevel 
72981708Sstevel 	/*
72991708Sstevel 	 * XXX CHEETAH SUPPORT
73001708Sstevel 	 */
73011708Sstevel 	if (drmach_is_cheetah) {
73021708Sstevel 		mutex_enter(&drmach_iocage_lock);
73031708Sstevel 		drmach_iocage_mem_scrub(ecache_size * 2);
73041708Sstevel 		drmach_iocage_is_busy = 0;
73051708Sstevel 		cv_signal(&drmach_iocage_cv);
73061708Sstevel 		mutex_exit(&drmach_iocage_lock);
73071708Sstevel 	}
73081708Sstevel 
73091708Sstevel 	DRMACH_PR("waited %d out of %d tries for "
731011066Srafael.vanoni@sun.com 	    "drmach_cpu_shutdown_self on cpu%d",
731111066Srafael.vanoni@sun.com 	    drmach_cpu_ntries - ntries, drmach_cpu_ntries, cp->cpu_id);
73121708Sstevel 
73131708Sstevel 	/*
73141708Sstevel 	 * Do this here instead of drmach_cpu_shutdown_self() to
73151708Sstevel 	 * avoid an assertion failure panic in turnstile.c.
73161708Sstevel 	 */
73171708Sstevel 	CPU_SIGNATURE(OS_SIG, SIGST_DETACHED, SIGSUBST_NULL, cpuid);
73181708Sstevel 
73191708Sstevel 	return (0);
73201708Sstevel }
73211708Sstevel 
73221708Sstevel void
drmach_iocage_mem_scrub(uint64_t nbytes)73231708Sstevel drmach_iocage_mem_scrub(uint64_t nbytes)
73241708Sstevel {
732511311SSurya.Prakki@Sun.COM 	extern uint32_t drmach_bc_bzero(void*, size_t);
732611311SSurya.Prakki@Sun.COM 	uint32_t	rv;
73271708Sstevel 
73281708Sstevel 	ASSERT(MUTEX_HELD(&cpu_lock));
73291708Sstevel 
73301708Sstevel 	affinity_set(CPU->cpu_id);
73311708Sstevel 
73321708Sstevel 	rv = drmach_bc_bzero(drmach_iocage_vaddr, nbytes);
73331708Sstevel 	if (rv != 0) {
73341708Sstevel 		DRMACH_PR(
73351708Sstevel 		"iocage scrub failed, drmach_bc_bzero returned %d\n", rv);
733611066Srafael.vanoni@sun.com 		rv = drmach_bc_bzero(drmach_iocage_vaddr, drmach_iocage_size);
73371708Sstevel 		if (rv != 0)
73381708Sstevel 			cmn_err(CE_PANIC,
73391708Sstevel 			    "iocage scrub failed, drmach_bc_bzero rv=%d\n",
73401708Sstevel 			    rv);
73411708Sstevel 	}
73421708Sstevel 
73431708Sstevel 	cpu_flush_ecache();
73441708Sstevel 
73451708Sstevel 	affinity_clear();
73461708Sstevel }
73471708Sstevel 
73481708Sstevel #define	ALIGN(x, a)	((a) == 0 ? (uintptr_t)(x) : \
73491708Sstevel 	(((uintptr_t)(x) + (uintptr_t)(a) - 1l) & ~((uintptr_t)(a) - 1l)))
73501708Sstevel 
73511708Sstevel static sbd_error_t *
drmach_iocage_mem_get(dr_testboard_req_t * tbrq)73521708Sstevel drmach_iocage_mem_get(dr_testboard_req_t *tbrq)
73531708Sstevel {
73541708Sstevel 	pfn_t		basepfn;
73551708Sstevel 	pgcnt_t		npages;
73561708Sstevel 	extern int	memscrub_delete_span(pfn_t, pgcnt_t);
73571708Sstevel 	uint64_t	drmach_iocage_paddr_mbytes;
73581708Sstevel 
73591708Sstevel 	ASSERT(drmach_iocage_paddr != -1);
73601708Sstevel 
73611708Sstevel 	basepfn = (pfn_t)(drmach_iocage_paddr >> PAGESHIFT);
73621708Sstevel 	npages = (pgcnt_t)(drmach_iocage_size >> PAGESHIFT);
73631708Sstevel 
736411311SSurya.Prakki@Sun.COM 	(void) memscrub_delete_span(basepfn, npages);
73651708Sstevel 
73661708Sstevel 	mutex_enter(&cpu_lock);
73671708Sstevel 	drmach_iocage_mem_scrub(drmach_iocage_size);
73681708Sstevel 	mutex_exit(&cpu_lock);
73691708Sstevel 
73701708Sstevel 	/*
73711708Sstevel 	 * HPOST wants the address of the cage to be 64 megabyte-aligned
73721708Sstevel 	 * and in megabyte units.
73731708Sstevel 	 * The size of the cage is also in megabyte units.
73741708Sstevel 	 */
73751708Sstevel 	ASSERT(drmach_iocage_paddr == ALIGN(drmach_iocage_paddr, 0x4000000));
73761708Sstevel 
73771708Sstevel 	drmach_iocage_paddr_mbytes = drmach_iocage_paddr / 0x100000;
73781708Sstevel 
73791708Sstevel 	tbrq->memaddrhi = (uint32_t)(drmach_iocage_paddr_mbytes >> 32);
73801708Sstevel 	tbrq->memaddrlo = (uint32_t)drmach_iocage_paddr_mbytes;
73811708Sstevel 	tbrq->memlen = drmach_iocage_size / 0x100000;
73821708Sstevel 
73831708Sstevel 	DRMACH_PR("drmach_iocage_mem_get: hi: 0x%x", tbrq->memaddrhi);
73841708Sstevel 	DRMACH_PR("drmach_iocage_mem_get: lo: 0x%x", tbrq->memaddrlo);
73851708Sstevel 	DRMACH_PR("drmach_iocage_mem_get: size: 0x%x", tbrq->memlen);
73861708Sstevel 
73871708Sstevel 	return (NULL);
73881708Sstevel }
73891708Sstevel 
73901708Sstevel static sbd_error_t *
drmach_iocage_mem_return(dr_testboard_reply_t * tbr)73911708Sstevel drmach_iocage_mem_return(dr_testboard_reply_t *tbr)
73921708Sstevel {
73931708Sstevel 	_NOTE(ARGUNUSED(tbr))
73941708Sstevel 
73951708Sstevel 	pfn_t		basepfn;
73961708Sstevel 	pgcnt_t		npages;
73971708Sstevel 	extern int	memscrub_add_span(pfn_t, pgcnt_t);
73981708Sstevel 
73991708Sstevel 	ASSERT(drmach_iocage_paddr != -1);
74001708Sstevel 
74011708Sstevel 	basepfn = (pfn_t)(drmach_iocage_paddr >> PAGESHIFT);
74021708Sstevel 	npages = (pgcnt_t)(drmach_iocage_size >> PAGESHIFT);
74031708Sstevel 
740411311SSurya.Prakki@Sun.COM 	(void) memscrub_add_span(basepfn, npages);
74051708Sstevel 
74061708Sstevel 	mutex_enter(&cpu_lock);
74071708Sstevel 	mutex_enter(&drmach_iocage_lock);
74081708Sstevel 	drmach_iocage_mem_scrub(drmach_iocage_size);
74091708Sstevel 	drmach_iocage_is_busy = 0;
74101708Sstevel 	cv_signal(&drmach_iocage_cv);
74111708Sstevel 	mutex_exit(&drmach_iocage_lock);
74121708Sstevel 	mutex_exit(&cpu_lock);
74131708Sstevel 
74141708Sstevel 	return (NULL);
74151708Sstevel }
74161708Sstevel 
74171708Sstevel static int
drmach_cpu_intr_disable(cpu_t * cp)74181708Sstevel drmach_cpu_intr_disable(cpu_t *cp)
74191708Sstevel {
74201708Sstevel 	if (cpu_intr_disable(cp) != 0)
74211708Sstevel 		return (-1);
74221708Sstevel 	return (0);
74231708Sstevel }
74241708Sstevel 
74251708Sstevel static int
drmach_iocage_cpu_acquire(drmach_device_t * dp,cpu_flag_t * oflags)74261708Sstevel drmach_iocage_cpu_acquire(drmach_device_t *dp, cpu_flag_t *oflags)
74271708Sstevel {
74281708Sstevel 	struct cpu	*cp;
74291708Sstevel 	processorid_t	cpuid;
74301708Sstevel 	static char	*fn = "drmach_iocage_cpu_acquire";
74311708Sstevel 	sbd_error_t 	*err;
74321708Sstevel 	int 		impl;
74331708Sstevel 
74341708Sstevel 	ASSERT(DRMACH_IS_CPU_ID(dp));
74351708Sstevel 	ASSERT(MUTEX_HELD(&cpu_lock));
74361708Sstevel 
74371708Sstevel 	cpuid = ((drmach_cpu_t *)dp)->cpuid;
74381708Sstevel 
74391708Sstevel 	DRMACH_PR("%s: attempting to acquire CPU id %d", fn, cpuid);
74401708Sstevel 
74411708Sstevel 	if (dp->busy)
74421708Sstevel 		return (-1);
74431708Sstevel 
74441708Sstevel 	if ((cp = cpu_get(cpuid)) == NULL) {
74451708Sstevel 		DRMACH_PR("%s: cpu_get(%d) returned NULL", fn, cpuid);
74461708Sstevel 		return (-1);
74471708Sstevel 	}
74481708Sstevel 
74491708Sstevel 	if (!CPU_ACTIVE(cp)) {
74501708Sstevel 		DRMACH_PR("%s: skipping offlined CPU id %d", fn, cpuid);
74511708Sstevel 		return (-1);
74521708Sstevel 	}
74531708Sstevel 
74541708Sstevel 	/*
74551708Sstevel 	 * There is a known HW bug where a Jaguar CPU in Safari port 0 (SBX/P0)
74561708Sstevel 	 * can fail to receive an XIR. To workaround this issue until a hardware
74571708Sstevel 	 * fix is implemented, we will exclude the selection of these CPUs.
74581708Sstevel 	 *
74591708Sstevel 	 * Once a fix is implemented in hardware, this code should be updated
74601708Sstevel 	 * to allow Jaguar CPUs that have the fix to be used. However, support
74611708Sstevel 	 * must be retained to skip revisions that do not have this fix.
74621708Sstevel 	 */
74631708Sstevel 
74641708Sstevel 	err = drmach_cpu_get_impl(dp, &impl);
74651708Sstevel 	if (err) {
74661708Sstevel 		DRMACH_PR("%s: error getting impl. of CPU id %d", fn, cpuid);
74671708Sstevel 		sbd_err_clear(&err);
74681708Sstevel 		return (-1);
74691708Sstevel 	}
74701708Sstevel 
74711708Sstevel 	if (IS_JAGUAR(impl) && (STARCAT_CPUID_TO_LPORT(cpuid) == 0) &&
74721708Sstevel 	    drmach_iocage_exclude_jaguar_port_zero) {
74731708Sstevel 		DRMACH_PR("%s: excluding CPU id %d: port 0 on jaguar",
74741708Sstevel 		    fn, cpuid);
74751708Sstevel 		return (-1);
74761708Sstevel 	}
74771708Sstevel 
74781708Sstevel 	ASSERT(oflags);
74791708Sstevel 	*oflags = cp->cpu_flags;
74801708Sstevel 
74811708Sstevel 	if (cpu_offline(cp, 0)) {
74821708Sstevel 		DRMACH_PR("%s: cpu_offline failed for CPU id %d", fn, cpuid);
74831708Sstevel 		return (-1);
74841708Sstevel 	}
74851708Sstevel 
74861708Sstevel 	if (cpu_poweroff(cp)) {
74871708Sstevel 		DRMACH_PR("%s: cpu_poweroff failed for CPU id %d", fn, cpuid);
74881708Sstevel 		if (cpu_online(cp)) {
74891708Sstevel 			cmn_err(CE_WARN, "failed to online CPU id %d "
74901708Sstevel 			    "during I/O cage test selection", cpuid);
74911708Sstevel 		}
74921708Sstevel 		if (CPU_ACTIVE(cp) && cpu_flagged_nointr(*oflags) &&
74931708Sstevel 		    drmach_cpu_intr_disable(cp) != 0) {
74941708Sstevel 			cmn_err(CE_WARN, "failed to restore CPU id %d "
74951708Sstevel 			    "no-intr during I/O cage test selection", cpuid);
74961708Sstevel 		}
74971708Sstevel 		return (-1);
74981708Sstevel 	}
74991708Sstevel 
75001708Sstevel 	if (cpu_unconfigure(cpuid)) {
75011708Sstevel 		DRMACH_PR("%s: cpu_unconfigure failed for CPU id %d", fn,
75021708Sstevel 		    cpuid);
75031708Sstevel 		(void) cpu_configure(cpuid);
75041708Sstevel 		if ((cp = cpu_get(cpuid)) == NULL) {
75051708Sstevel 			cmn_err(CE_WARN, "failed to reconfigure CPU id %d "
75061708Sstevel 			    "during I/O cage test selection", cpuid);
75071708Sstevel 			dp->busy = 1;
75081708Sstevel 			return (-1);
75091708Sstevel 		}
75101708Sstevel 		if (cpu_poweron(cp) || cpu_online(cp)) {
75111708Sstevel 			cmn_err(CE_WARN, "failed to %s CPU id %d "
75121708Sstevel 			    "during I/O cage test selection",
75131708Sstevel 			    cpu_is_poweredoff(cp) ?
75141708Sstevel 			    "poweron" : "online", cpuid);
75151708Sstevel 		}
75161708Sstevel 		if (CPU_ACTIVE(cp) && cpu_flagged_nointr(*oflags) &&
75171708Sstevel 		    drmach_cpu_intr_disable(cp) != 0) {
75181708Sstevel 			cmn_err(CE_WARN, "failed to restore CPU id %d "
75191708Sstevel 			    "no-intr during I/O cage test selection", cpuid);
75201708Sstevel 		}
75211708Sstevel 		return (-1);
75221708Sstevel 	}
75231708Sstevel 
75241708Sstevel 	dp->busy = 1;
75251708Sstevel 
75261708Sstevel 	DRMACH_PR("%s: acquired CPU id %d", fn, cpuid);
75271708Sstevel 
75281708Sstevel 	return (0);
75291708Sstevel }
75301708Sstevel 
75311708Sstevel /*
75321708Sstevel  * Attempt to acquire all the CPU devices passed in. It is
75331708Sstevel  * assumed that all the devices in the list are the cores of
75341708Sstevel  * a single CMP device. Non CMP devices can be handled as a
75351708Sstevel  * single core CMP by passing in a one element list.
75361708Sstevel  *
75371708Sstevel  * Success is only returned if *all* the devices in the list
75381708Sstevel  * can be acquired. In the failure case, none of the devices
75391708Sstevel  * in the list will be held as acquired.
75401708Sstevel  */
75411708Sstevel static int
drmach_iocage_cmp_acquire(drmach_device_t ** dpp,cpu_flag_t * oflags)75421708Sstevel drmach_iocage_cmp_acquire(drmach_device_t **dpp, cpu_flag_t *oflags)
75431708Sstevel {
75441708Sstevel 	int	curr;
75451708Sstevel 	int	i;
75461708Sstevel 	int	rv = 0;
75471708Sstevel 
75481708Sstevel 	ASSERT((dpp != NULL) && (*dpp != NULL));
75491708Sstevel 
75501708Sstevel 	/*
75511708Sstevel 	 * Walk the list of CPU devices (cores of a CMP)
75521708Sstevel 	 * and attempt to acquire them. Bail out if an
75531708Sstevel 	 * error is encountered.
75541708Sstevel 	 */
75551772Sjl139090 	for (curr = 0; curr < MAX_CORES_PER_CMP; curr++) {
75561708Sstevel 
75571708Sstevel 		/* check for the end of the list */
75581708Sstevel 		if (dpp[curr] == NULL) {
75591708Sstevel 			break;
75601708Sstevel 		}
75611708Sstevel 
75621708Sstevel 		ASSERT(DRMACH_IS_CPU_ID(dpp[curr]));
75631708Sstevel 		ASSERT(dpp[curr]->portid == (*dpp)->portid);
75641708Sstevel 
75651708Sstevel 		rv = drmach_iocage_cpu_acquire(dpp[curr], &oflags[curr]);
75661708Sstevel 		if (rv != 0) {
75671708Sstevel 			break;
75681708Sstevel 		}
75691708Sstevel 	}
75701708Sstevel 
75711708Sstevel 	/*
75721708Sstevel 	 * Check for an error.
75731708Sstevel 	 */
75741708Sstevel 	if (rv != 0) {
75751708Sstevel 		/*
75761708Sstevel 		 * Make a best effort attempt to return any cores
75771708Sstevel 		 * that were already acquired before the error was
75781708Sstevel 		 * encountered.
75791708Sstevel 		 */
75801708Sstevel 		for (i = 0; i < curr; i++) {
75811708Sstevel 			(void) drmach_iocage_cpu_return(dpp[i], oflags[i]);
75821708Sstevel 		}
75831708Sstevel 	}
75841708Sstevel 
75851708Sstevel 	return (rv);
75861708Sstevel }
75871708Sstevel 
75881708Sstevel static int
drmach_iocage_cpu_return(drmach_device_t * dp,cpu_flag_t oflags)75891708Sstevel drmach_iocage_cpu_return(drmach_device_t *dp, cpu_flag_t oflags)
75901708Sstevel {
75911708Sstevel 	processorid_t	cpuid;
75921708Sstevel 	struct cpu	*cp;
75931708Sstevel 	int		rv = 0;
75941708Sstevel 	static char	*fn = "drmach_iocage_cpu_return";
75951708Sstevel 
75961708Sstevel 	ASSERT(DRMACH_IS_CPU_ID(dp));
75971708Sstevel 	ASSERT(MUTEX_HELD(&cpu_lock));
75981708Sstevel 
75991708Sstevel 	cpuid = ((drmach_cpu_t *)dp)->cpuid;
76001708Sstevel 
76011708Sstevel 	DRMACH_PR("%s: attempting to return CPU id: %d", fn, cpuid);
76021708Sstevel 
76031708Sstevel 	if (cpu_configure(cpuid)) {
76041708Sstevel 		cmn_err(CE_WARN, "failed to reconfigure CPU id %d "
76051708Sstevel 		    "after I/O cage test", cpuid);
76061708Sstevel 		/*
76071708Sstevel 		 * The component was never set to unconfigured during the IO
76081708Sstevel 		 * cage test, so we need to leave marked as busy to prevent
76091708Sstevel 		 * further DR operations involving this component.
76101708Sstevel 		 */
76111708Sstevel 		return (-1);
76121708Sstevel 	}
76131708Sstevel 
76141708Sstevel 	if ((cp = cpu_get(cpuid)) == NULL) {
76151708Sstevel 		cmn_err(CE_WARN, "cpu_get failed on CPU id %d after "
76161708Sstevel 		    "I/O cage test", cpuid);
76171708Sstevel 		dp->busy = 0;
76181708Sstevel 		return (-1);
76191708Sstevel 	}
76201708Sstevel 
76211708Sstevel 	if (cpu_poweron(cp) || cpu_online(cp)) {
76221708Sstevel 		cmn_err(CE_WARN, "failed to %s CPU id %d after I/O "
76231708Sstevel 		    "cage test", cpu_is_poweredoff(cp) ?
76241708Sstevel 		    "poweron" : "online", cpuid);
76251708Sstevel 		rv = -1;
76261708Sstevel 	}
76271708Sstevel 
76281708Sstevel 	/*
76291708Sstevel 	 * drmach_iocage_cpu_acquire will accept cpus in state P_ONLINE or
76301708Sstevel 	 * P_NOINTR. Need to return to previous user-visible state.
76311708Sstevel 	 */
76321708Sstevel 	if (CPU_ACTIVE(cp) && cpu_flagged_nointr(oflags) &&
76331708Sstevel 	    drmach_cpu_intr_disable(cp) != 0) {
76341708Sstevel 		cmn_err(CE_WARN, "failed to restore CPU id %d "
76351708Sstevel 		    "no-intr after I/O cage test", cpuid);
76361708Sstevel 		rv = -1;
76371708Sstevel 	}
76381708Sstevel 
76391708Sstevel 	dp->busy = 0;
76401708Sstevel 
76411708Sstevel 	DRMACH_PR("%s: returned CPU id: %d", fn, cpuid);
76421708Sstevel 
76431708Sstevel 	return (rv);
76441708Sstevel }
76451708Sstevel 
76461708Sstevel static sbd_error_t *
drmach_iocage_cpu_get(dr_testboard_req_t * tbrq,drmach_device_t ** dpp,cpu_flag_t * oflags)76471708Sstevel drmach_iocage_cpu_get(dr_testboard_req_t *tbrq, drmach_device_t **dpp,
76481708Sstevel     cpu_flag_t *oflags)
76491708Sstevel {
76501708Sstevel 	drmach_board_t	*bp;
76511708Sstevel 	int		b_rv;
76521708Sstevel 	int		b_idx;
76531708Sstevel 	drmachid_t	b_id;
76541708Sstevel 	int		found;
76551708Sstevel 
76561708Sstevel 	mutex_enter(&cpu_lock);
76571708Sstevel 
76581708Sstevel 	ASSERT(drmach_boards != NULL);
76591708Sstevel 
76601708Sstevel 	found = 0;
76611708Sstevel 
76621708Sstevel 	/*
76631708Sstevel 	 * Walk the board list.
76641708Sstevel 	 */
76651708Sstevel 	b_rv = drmach_array_first(drmach_boards, &b_idx, &b_id);
76661708Sstevel 
76671708Sstevel 	while (b_rv == 0) {
76681708Sstevel 
76691708Sstevel 		int		d_rv;
76701708Sstevel 		int		d_idx;
76711708Sstevel 		drmachid_t	d_id;
76721708Sstevel 
76731708Sstevel 		bp = b_id;
76741708Sstevel 
76751708Sstevel 		if (bp->connected == 0 || bp->devices == NULL) {
76761708Sstevel 			b_rv = drmach_array_next(drmach_boards, &b_idx, &b_id);
76771708Sstevel 			continue;
76781708Sstevel 		}
76791708Sstevel 
76801708Sstevel 		/* An AXQ restriction disqualifies MCPU's as candidates. */
76811708Sstevel 		if (DRMACH_BNUM2SLOT(bp->bnum) == 1) {
76821708Sstevel 			b_rv = drmach_array_next(drmach_boards, &b_idx, &b_id);
76831708Sstevel 			continue;
76841708Sstevel 		}
76851708Sstevel 
76861708Sstevel 		/*
76871708Sstevel 		 * Walk the device list of this board.
76881708Sstevel 		 */
76891708Sstevel 		d_rv = drmach_array_first(bp->devices, &d_idx, &d_id);
76901708Sstevel 
76911708Sstevel 		while (d_rv == 0) {
76921708Sstevel 
76931708Sstevel 			drmach_device_t	*ndp;
76941708Sstevel 
76951708Sstevel 			/* only interested in CPU devices */
76961708Sstevel 			if (!DRMACH_IS_CPU_ID(d_id)) {
76971708Sstevel 				d_rv = drmach_array_next(bp->devices, &d_idx,
76981708Sstevel 				    &d_id);
76991708Sstevel 				continue;
77001708Sstevel 			}
77011708Sstevel 
77021708Sstevel 			/*
77031708Sstevel 			 * The following code assumes two properties
77041708Sstevel 			 * of a CMP device:
77051708Sstevel 			 *
77061708Sstevel 			 *   1. All cores of a CMP are grouped together
77071708Sstevel 			 *	in the device list.
77081708Sstevel 			 *
77091708Sstevel 			 *   2. There will only be a maximum of two cores
77101708Sstevel 			 *	present in the CMP.
77111708Sstevel 			 *
77121708Sstevel 			 * If either of these two properties change,
77131708Sstevel 			 * this code will have to be revisited.
77141708Sstevel 			 */
77151708Sstevel 
77161708Sstevel 			dpp[0] = d_id;
77171708Sstevel 			dpp[1] = NULL;
77181708Sstevel 
77191708Sstevel 			/*
77201708Sstevel 			 * Get the next device. It may or may not be used.
77211708Sstevel 			 */
77221708Sstevel 			d_rv = drmach_array_next(bp->devices, &d_idx, &d_id);
77231708Sstevel 			ndp = d_id;
77241708Sstevel 
77251708Sstevel 			if ((d_rv == 0) && DRMACH_IS_CPU_ID(d_id)) {
77261708Sstevel 				/*
77271708Sstevel 				 * The second device is only interesting for
77281708Sstevel 				 * this pass if it has the same portid as the
77291708Sstevel 				 * first device. This implies that both are
77301708Sstevel 				 * cores of the same CMP.
77311708Sstevel 				 */
77321708Sstevel 				if (dpp[0]->portid == ndp->portid) {
77331708Sstevel 					dpp[1] = d_id;
77341708Sstevel 				}
77351708Sstevel 			}
77361708Sstevel 
77371708Sstevel 			/*
77381708Sstevel 			 * Attempt to acquire all cores of the CMP.
77391708Sstevel 			 */
77401708Sstevel 			if (drmach_iocage_cmp_acquire(dpp, oflags) == 0) {
77411708Sstevel 				found = 1;
77421708Sstevel 				break;
77431708Sstevel 			}
77441708Sstevel 
77451708Sstevel 			/*
77461708Sstevel 			 * Check if the search for the second core was
77471708Sstevel 			 * successful. If not, the next iteration should
77481708Sstevel 			 * use that device.
77491708Sstevel 			 */
77501708Sstevel 			if (dpp[1] == NULL) {
77511708Sstevel 				continue;
77521708Sstevel 			}
77531708Sstevel 
77541708Sstevel 			d_rv = drmach_array_next(bp->devices, &d_idx, &d_id);
77551708Sstevel 		}
77561708Sstevel 
77571708Sstevel 		if (found)
77581708Sstevel 			break;
77591708Sstevel 
77601708Sstevel 		b_rv = drmach_array_next(drmach_boards, &b_idx, &b_id);
77611708Sstevel 	}
77621708Sstevel 
77631708Sstevel 	mutex_exit(&cpu_lock);
77641708Sstevel 
77651708Sstevel 	if (!found) {
77661708Sstevel 		return (drerr_new(1, ESTC_IOCAGE_NO_CPU_AVAIL, NULL));
77671708Sstevel 	}
77681708Sstevel 
77691708Sstevel 	tbrq->cpu_portid = (*dpp)->portid;
77701708Sstevel 
77711708Sstevel 	return (NULL);
77721708Sstevel }
77731708Sstevel 
77741708Sstevel /*
77751708Sstevel  * Setup an iocage by acquiring a cpu and memory.
77761708Sstevel  */
77771708Sstevel static sbd_error_t *
drmach_iocage_setup(dr_testboard_req_t * tbrq,drmach_device_t ** dpp,cpu_flag_t * oflags)77781708Sstevel drmach_iocage_setup(dr_testboard_req_t *tbrq, drmach_device_t **dpp,
77791708Sstevel     cpu_flag_t *oflags)
77801708Sstevel {
77811708Sstevel 	sbd_error_t *err;
77821708Sstevel 
77831708Sstevel 	err = drmach_iocage_cpu_get(tbrq, dpp, oflags);
77841708Sstevel 	if (!err) {
77851708Sstevel 		mutex_enter(&drmach_iocage_lock);
77861708Sstevel 		while (drmach_iocage_is_busy)
77871708Sstevel 			cv_wait(&drmach_iocage_cv, &drmach_iocage_lock);
77881708Sstevel 		drmach_iocage_is_busy = 1;
77891708Sstevel 		mutex_exit(&drmach_iocage_lock);
77901708Sstevel 		err = drmach_iocage_mem_get(tbrq);
77911708Sstevel 		if (err) {
77921708Sstevel 			mutex_enter(&drmach_iocage_lock);
77931708Sstevel 			drmach_iocage_is_busy = 0;
77941708Sstevel 			cv_signal(&drmach_iocage_cv);
77951708Sstevel 			mutex_exit(&drmach_iocage_lock);
77961708Sstevel 		}
77971708Sstevel 	}
77981708Sstevel 	return (err);
77991708Sstevel }
78001708Sstevel 
78011708Sstevel #define	DRMACH_SCHIZO_PCI_LEAF_MAX	2
78021708Sstevel #define	DRMACH_SCHIZO_PCI_SLOT_MAX	8
78031708Sstevel #define	DRMACH_S1P_SAMPLE_MAX		2
78041708Sstevel 
78051708Sstevel typedef enum {
78061708Sstevel 	DRMACH_POST_SUSPEND = 0,
78071708Sstevel 	DRMACH_PRE_RESUME
78081708Sstevel } drmach_sr_iter_t;
78091708Sstevel 
78101708Sstevel typedef struct {
78111708Sstevel 	dev_info_t	*dip;
78121708Sstevel 	uint32_t	portid;
78131708Sstevel 	uint32_t	pcr_sel_save;
78141708Sstevel 	uint32_t	pic_l2_io_q[DRMACH_S1P_SAMPLE_MAX];
78151708Sstevel 	uint64_t	reg_basepa;
78161708Sstevel } drmach_s1p_axq_t;
78171708Sstevel 
78181708Sstevel typedef struct {
78191708Sstevel 	dev_info_t		*dip;
78201708Sstevel 	uint32_t		portid;
78211708Sstevel 	uint64_t		csr_basepa;
78221708Sstevel 	struct {
78231708Sstevel 		uint64_t 	slot_intr_state_diag;
78241708Sstevel 		uint64_t 	obio_intr_state_diag;
78251708Sstevel 		uint_t		nmap_regs;
78261708Sstevel 		uint64_t	*intr_map_regs;
78271708Sstevel 	} regs[DRMACH_S1P_SAMPLE_MAX];
78281708Sstevel } drmach_s1p_pci_t;
78291708Sstevel 
78301708Sstevel typedef struct {
78311708Sstevel 	uint64_t		csr_basepa;
78321708Sstevel 	struct {
78331708Sstevel 		uint64_t	csr;
78341708Sstevel 		uint64_t	errctrl;
78351708Sstevel 		uint64_t	errlog;
78361708Sstevel 	} regs[DRMACH_S1P_SAMPLE_MAX];
78371708Sstevel 	drmach_s1p_pci_t	pci[DRMACH_SCHIZO_PCI_LEAF_MAX];
78381708Sstevel } drmach_s1p_schizo_t;
78391708Sstevel 
78401708Sstevel typedef struct {
78411708Sstevel 	drmach_s1p_axq_t	axq;
78421708Sstevel 	drmach_s1p_schizo_t	schizo[STARCAT_SLOT1_IO_MAX];
78431708Sstevel } drmach_slot1_pause_t;
78441708Sstevel 
78451708Sstevel /*
78461708Sstevel  * Table of saved state for paused slot1 devices.
78471708Sstevel  */
78481708Sstevel static drmach_slot1_pause_t *drmach_slot1_paused[STARCAT_BDSET_MAX];
78491708Sstevel static int drmach_slot1_pause_init = 1;
78501708Sstevel 
78511708Sstevel #ifdef DEBUG
78521708Sstevel int drmach_slot1_pause_debug = 1;
78531708Sstevel #else
78541708Sstevel int drmach_slot1_pause_debug = 0;
78551708Sstevel #endif /* DEBUG */
78561708Sstevel 
78571708Sstevel static int
drmach_is_slot1_pause_axq(dev_info_t * dip,char * name,int * id,uint64_t * reg)78581708Sstevel drmach_is_slot1_pause_axq(dev_info_t *dip, char *name, int *id, uint64_t *reg)
78591708Sstevel {
78601708Sstevel 	int		portid, exp, slot, i;
78611708Sstevel 	drmach_reg_t	regs[2];
78621708Sstevel 	int		reglen = sizeof (regs);
78631708Sstevel 
78641708Sstevel 	if ((portid = ddi_getprop(DDI_DEV_T_ANY, dip,
78651708Sstevel 	    DDI_PROP_DONTPASS, "portid", -1)) == -1) {
78661708Sstevel 		return (0);
78671708Sstevel 	}
78681708Sstevel 
78691708Sstevel 	exp = (portid >> 5) & 0x1f;
78701708Sstevel 	slot = portid & 0x1;
78711708Sstevel 
78721708Sstevel 	if (slot == 0 || strncmp(name, DRMACH_AXQ_NAMEPROP,
78731708Sstevel 	    strlen(DRMACH_AXQ_NAMEPROP))) {
78741708Sstevel 		return (0);
78751708Sstevel 	}
78761708Sstevel 
78771708Sstevel 	mutex_enter(&cpu_lock);
78781708Sstevel 	for (i = 0; i < STARCAT_SLOT1_CPU_MAX; i++) {
78791708Sstevel 		if (cpu[MAKE_CPUID(exp, slot, i)]) {
78801708Sstevel 			/* maxcat cpu present */
78811708Sstevel 			mutex_exit(&cpu_lock);
78821708Sstevel 			return (0);
78831708Sstevel 		}
78841708Sstevel 	}
78851708Sstevel 	mutex_exit(&cpu_lock);
78861708Sstevel 
78871708Sstevel 	if (ddi_getlongprop_buf(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
78881708Sstevel 	    "reg", (caddr_t)regs, &reglen) != DDI_PROP_SUCCESS) {
78891708Sstevel 		DRMACH_PR("drmach_is_slot1_pause_axq: no reg prop for "
789011311SSurya.Prakki@Sun.COM 		    "axq dip=%p\n", (void *)dip);
78911708Sstevel 		return (0);
78921708Sstevel 	}
78931708Sstevel 
78941708Sstevel 	ASSERT(id && reg);
78951708Sstevel 	*reg = (uint64_t)regs[0].reg_addr_hi << 32;
78961708Sstevel 	*reg |= (uint64_t)regs[0].reg_addr_lo;
78971708Sstevel 	*id = portid;
78981708Sstevel 
78991708Sstevel 	return (1);
79001708Sstevel }
79011708Sstevel 
79021708Sstevel /*
79031708Sstevel  * Allocate an entry in the slot1_paused state table.
79041708Sstevel  */
79051708Sstevel 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)79061708Sstevel drmach_slot1_pause_add_axq(dev_info_t *axq_dip, char *axq_name, int axq_portid,
79071708Sstevel     uint64_t reg, drmach_slot1_pause_t **slot1_paused)
79081708Sstevel {
79091708Sstevel 	int	axq_exp;
79101708Sstevel 	drmach_slot1_pause_t *slot1;
79111708Sstevel 
79121708Sstevel 	axq_exp = (axq_portid >> 5) & 0x1f;
79131708Sstevel 
79141708Sstevel 	ASSERT(axq_portid & 0x1);
79151708Sstevel 	ASSERT(slot1_paused[axq_exp] == NULL);
79161708Sstevel 	ASSERT(strncmp(axq_name, DRMACH_AXQ_NAMEPROP,
79171708Sstevel 	    strlen(DRMACH_AXQ_NAMEPROP)) == 0);
79181708Sstevel 
79191708Sstevel 	slot1 = kmem_zalloc(sizeof (*slot1), KM_SLEEP);
79201708Sstevel 
79211708Sstevel 	/*
79221708Sstevel 	 * XXX This dip should really be held (via ndi_hold_devi())
79231708Sstevel 	 * before saving it in the axq pause structure. However that
79241708Sstevel 	 * would prevent DR as the pause data structures persist until
79251708Sstevel 	 * the next suspend. drmach code should be modified to free the
79261708Sstevel 	 * the slot 1 pause data structures for a boardset when its
79271708Sstevel 	 * slot 1 board is DRed out. The dip can then be released via
79281708Sstevel 	 * ndi_rele_devi() when the pause data structure is freed
79291708Sstevel 	 * allowing DR to proceed. Until this change is made, drmach
79301708Sstevel 	 * code should be careful about dereferencing the saved dip
79311708Sstevel 	 * as it may no longer exist.
79321708Sstevel 	 */
79331708Sstevel 	slot1->axq.dip = axq_dip;
79341708Sstevel 	slot1->axq.portid = axq_portid;
79351708Sstevel 	slot1->axq.reg_basepa = reg;
79361708Sstevel 	slot1_paused[axq_exp] = slot1;
79371708Sstevel }
79381708Sstevel 
79391708Sstevel static void
drmach_s1p_pci_free(drmach_s1p_pci_t * pci)79401708Sstevel drmach_s1p_pci_free(drmach_s1p_pci_t *pci)
79411708Sstevel {
79421708Sstevel 	int	i;
79431708Sstevel 
79441708Sstevel 	for (i = 0; i < DRMACH_S1P_SAMPLE_MAX; i++) {
79451708Sstevel 		if (pci->regs[i].intr_map_regs != NULL) {
79461708Sstevel 			ASSERT(pci->regs[i].nmap_regs > 0);
79471708Sstevel 			kmem_free(pci->regs[i].intr_map_regs,
79481708Sstevel 			    pci->regs[i].nmap_regs * sizeof (uint64_t));
79491708Sstevel 		}
79501708Sstevel 	}
79511708Sstevel }
79521708Sstevel 
79531708Sstevel static void
drmach_slot1_pause_free(drmach_slot1_pause_t ** slot1_paused)79541708Sstevel drmach_slot1_pause_free(drmach_slot1_pause_t **slot1_paused)
79551708Sstevel {
79561708Sstevel 	int	i, j, k;
79571708Sstevel 	drmach_slot1_pause_t *slot1;
79581708Sstevel 
79591708Sstevel 	for (i = 0; i < STARCAT_BDSET_MAX; i++) {
79601708Sstevel 		if ((slot1 = slot1_paused[i]) == NULL)
79611708Sstevel 			continue;
79621708Sstevel 
79631708Sstevel 		for (j = 0; j < STARCAT_SLOT1_IO_MAX; j++)
79641708Sstevel 			for (k = 0; k < DRMACH_SCHIZO_PCI_LEAF_MAX; k++)
79651708Sstevel 				drmach_s1p_pci_free(&slot1->schizo[j].pci[k]);
79661708Sstevel 
79671708Sstevel 		kmem_free(slot1, sizeof (*slot1));
79681708Sstevel 		slot1_paused[i] = NULL;
79691708Sstevel 	}
79701708Sstevel }
79711708Sstevel 
79721708Sstevel /*
79731708Sstevel  * Tree walk callback routine. If dip represents a Schizo PCI leaf,
79741708Sstevel  * fill in the appropriate info in the slot1_paused state table.
79751708Sstevel  */
79761708Sstevel static int
drmach_find_slot1_io(dev_info_t * dip,void * arg)79771708Sstevel drmach_find_slot1_io(dev_info_t *dip, void *arg)
79781708Sstevel {
79791708Sstevel 	int		portid, exp, ioc_unum, leaf_unum;
79801708Sstevel 	char		buf[OBP_MAXDRVNAME];
79811708Sstevel 	int		buflen = sizeof (buf);
79821708Sstevel 	drmach_reg_t	regs[3];
79831708Sstevel 	int		reglen = sizeof (regs);
79841708Sstevel 	uint32_t	leaf_offset;
79851708Sstevel 	uint64_t	schizo_csr_pa, pci_csr_pa;
79861708Sstevel 	drmach_s1p_pci_t *pci;
79871708Sstevel 	drmach_slot1_pause_t **slot1_paused = (drmach_slot1_pause_t **)arg;
79881708Sstevel 
79891708Sstevel 	if (ddi_getlongprop_buf(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
79901708Sstevel 	    "name", (caddr_t)buf, &buflen) != DDI_PROP_SUCCESS ||
79911708Sstevel 	    strncmp(buf, DRMACH_PCI_NAMEPROP, strlen(DRMACH_PCI_NAMEPROP))) {
79921708Sstevel 		return (DDI_WALK_CONTINUE);
79931708Sstevel 	}
79941708Sstevel 
79951708Sstevel 	if ((portid = ddi_getprop(DDI_DEV_T_ANY, dip,
79961708Sstevel 	    DDI_PROP_DONTPASS, "portid", -1)) == -1) {
79971708Sstevel 		return (DDI_WALK_CONTINUE);
79981708Sstevel 	}
79991708Sstevel 
80001708Sstevel 	if (ddi_getlongprop_buf(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
80011708Sstevel 	    "reg", (caddr_t)regs, &reglen) != DDI_PROP_SUCCESS) {
80021708Sstevel 		DRMACH_PR("drmach_find_slot1_io: no reg prop for pci "
800311311SSurya.Prakki@Sun.COM 		    "dip=%p\n", (void *)dip);
80041708Sstevel 		return (DDI_WALK_CONTINUE);
80051708Sstevel 	}
80061708Sstevel 
80071708Sstevel 	exp = portid >> 5;
80081708Sstevel 	ioc_unum = portid & 0x1;
80091708Sstevel 	leaf_offset = regs[0].reg_addr_lo & 0x7fffff;
80101708Sstevel 	pci_csr_pa = (uint64_t)regs[0].reg_addr_hi << 32;
80111708Sstevel 	pci_csr_pa |= (uint64_t)regs[0].reg_addr_lo;
80121708Sstevel 	schizo_csr_pa = (uint64_t)regs[1].reg_addr_hi << 32;
80131708Sstevel 	schizo_csr_pa |= (uint64_t)regs[1].reg_addr_lo;
80141708Sstevel 
80151708Sstevel 	ASSERT(exp >= 0 && exp < STARCAT_BDSET_MAX);
80161708Sstevel 	ASSERT(slot1_paused[exp] != NULL);
80171708Sstevel 	ASSERT(leaf_offset == 0x600000 || leaf_offset == 0x700000);
80181708Sstevel 	ASSERT(slot1_paused[exp]->schizo[ioc_unum].csr_basepa == 0x0UL ||
80191708Sstevel 	    slot1_paused[exp]->schizo[ioc_unum].csr_basepa == schizo_csr_pa);
80201708Sstevel 
80211708Sstevel 	leaf_unum = (leaf_offset == 0x600000) ? 0 : 1;
80221708Sstevel 	slot1_paused[exp]->schizo[ioc_unum].csr_basepa = schizo_csr_pa;
80231708Sstevel 	pci = &slot1_paused[exp]->schizo[ioc_unum].pci[leaf_unum];
80241708Sstevel 
80251708Sstevel 	/*
80261708Sstevel 	 * XXX This dip should really be held (via ndi_hold_devi())
80271708Sstevel 	 * before saving it in the pci pause structure. However that
80281708Sstevel 	 * would prevent DR as the pause data structures persist until
80291708Sstevel 	 * the next suspend. drmach code should be modified to free the
80301708Sstevel 	 * the slot 1 pause data structures for a boardset when its
80311708Sstevel 	 * slot 1 board is DRed out. The dip can then be released via
80321708Sstevel 	 * ndi_rele_devi() when the pause data structure is freed
80331708Sstevel 	 * allowing DR to proceed. Until this change is made, drmach
80341708Sstevel 	 * code should be careful about dereferencing the saved dip as
80351708Sstevel 	 * it may no longer exist.
80361708Sstevel 	 */
80371708Sstevel 	pci->dip = dip;
80381708Sstevel 	pci->portid = portid;
80391708Sstevel 	pci->csr_basepa = pci_csr_pa;
80401708Sstevel 
80411708Sstevel 	DRMACH_PR("drmach_find_slot1_io: name=%s, portid=0x%x, dip=%p\n",
804211311SSurya.Prakki@Sun.COM 	    buf, portid, (void *)dip);
80431708Sstevel 
80441708Sstevel 	return (DDI_WALK_PRUNECHILD);
80451708Sstevel }
80461708Sstevel 
80471708Sstevel static void
drmach_slot1_pause_add_io(drmach_slot1_pause_t ** slot1_paused)80481708Sstevel drmach_slot1_pause_add_io(drmach_slot1_pause_t **slot1_paused)
80491708Sstevel {
80501708Sstevel 	/*
80511708Sstevel 	 * Root node doesn't have to be held
80521708Sstevel 	 */
80531708Sstevel 	ddi_walk_devs(ddi_root_node(), drmach_find_slot1_io,
80541708Sstevel 	    (void *)slot1_paused);
80551708Sstevel }
80561708Sstevel 
80571708Sstevel /*
80581708Sstevel  * Save the interrupt mapping registers for each non-idle interrupt
80591708Sstevel  * represented by the bit pairs in the saved interrupt state
80601708Sstevel  * diagnostic registers for this PCI leaf.
80611708Sstevel  */
80621708Sstevel static void
drmach_s1p_intr_map_reg_save(drmach_s1p_pci_t * pci,drmach_sr_iter_t iter)80631708Sstevel drmach_s1p_intr_map_reg_save(drmach_s1p_pci_t *pci, drmach_sr_iter_t iter)
80641708Sstevel {
80651708Sstevel 	int	 i, cnt, ino;
80661708Sstevel 	uint64_t reg;
80671708Sstevel 	char	 *dname;
80681708Sstevel 	uchar_t	 Xmits;
80691708Sstevel 
80701708Sstevel 	dname = ddi_binding_name(pci->dip);
80711708Sstevel 	Xmits = (strcmp(dname, XMITS_BINDING_NAME) == 0)  ?  1 : 0;
80721708Sstevel 
80731708Sstevel 	/*
80741708Sstevel 	 * 1st pass allocates, 2nd pass populates.
80751708Sstevel 	 */
80761708Sstevel 	for (i = 0; i < 2; i++) {
80771708Sstevel 		cnt = ino = 0;
80781708Sstevel 
80791708Sstevel 		/*
80801708Sstevel 		 * PCI slot interrupts
80811708Sstevel 		 */
80821708Sstevel 		reg = pci->regs[iter].slot_intr_state_diag;
80831708Sstevel 		while (reg) {
80841708Sstevel 			/*
80851708Sstevel 			 * Xmits Interrupt Number Offset(ino) Assignments
80861708Sstevel 			 *   00-17 PCI Slot Interrupts
80871708Sstevel 			 *   18-1f Not Used
80881708Sstevel 			 */
80891708Sstevel 			if ((Xmits) && (ino > 0x17))
80901708Sstevel 				break;
80911708Sstevel 			if ((reg & COMMON_CLEAR_INTR_REG_MASK) !=
80921708Sstevel 			    COMMON_CLEAR_INTR_REG_IDLE) {
80931708Sstevel 				if (i) {
80941708Sstevel 					pci->regs[iter].intr_map_regs[cnt] =
80951708Sstevel 					    lddphysio(pci->csr_basepa +
80961708Sstevel 					    SCHIZO_IB_INTR_MAP_REG_OFFSET +
80971708Sstevel 					    ino * sizeof (reg));
80981708Sstevel 				}
80991708Sstevel 				++cnt;
81001708Sstevel 			}
81011708Sstevel 			++ino;
81021708Sstevel 			reg >>= 2;
81031708Sstevel 		}
81041708Sstevel 
81051708Sstevel 		/*
81061708Sstevel 		 * Xmits Interrupt Number Offset(ino) Assignments
81071708Sstevel 		 *   20-2f Not Used
81081708Sstevel 		 *   30-37 Internal interrupts
81091708Sstevel 		 *   38-3e Not Used
81101708Sstevel 		 */
81111708Sstevel 		ino = (Xmits)  ?  0x30 : 0x20;
81121708Sstevel 
81131708Sstevel 		/*
81141708Sstevel 		 * OBIO and internal schizo interrupts
81151708Sstevel 		 * Each PCI leaf has a set of mapping registers for all
81161708Sstevel 		 * possible interrupt sources except the NewLink interrupts.
81171708Sstevel 		 */
81181708Sstevel 		reg = pci->regs[iter].obio_intr_state_diag;
81191708Sstevel 		while (reg && ino <= 0x38) {
81201708Sstevel 			if ((reg & COMMON_CLEAR_INTR_REG_MASK) !=
81211708Sstevel 			    COMMON_CLEAR_INTR_REG_IDLE) {
81221708Sstevel 				if (i) {
81231708Sstevel 					pci->regs[iter].intr_map_regs[cnt] =
81241708Sstevel 					    lddphysio(pci->csr_basepa +
81251708Sstevel 					    SCHIZO_IB_INTR_MAP_REG_OFFSET +
81261708Sstevel 					    ino * sizeof (reg));
81271708Sstevel 				}
81281708Sstevel 				++cnt;
81291708Sstevel 			}
81301708Sstevel 			++ino;
81311708Sstevel 			reg >>= 2;
81321708Sstevel 		}
81331708Sstevel 
81341708Sstevel 		if (!i) {
81351708Sstevel 			pci->regs[iter].nmap_regs = cnt;
81361708Sstevel 			pci->regs[iter].intr_map_regs =
81371708Sstevel 			    kmem_zalloc(cnt * sizeof (reg), KM_SLEEP);
81381708Sstevel 		}
81391708Sstevel 	}
81401708Sstevel }
81411708Sstevel 
81421708Sstevel static void
drmach_s1p_axq_update(drmach_s1p_axq_t * axq,drmach_sr_iter_t iter)81431708Sstevel drmach_s1p_axq_update(drmach_s1p_axq_t *axq, drmach_sr_iter_t iter)
81441708Sstevel {
81451708Sstevel 	uint32_t	reg;
81461708Sstevel 
81471708Sstevel 	if (axq->reg_basepa == 0x0UL)
81481708Sstevel 		return;
81491708Sstevel 
81501708Sstevel 	if (iter == DRMACH_POST_SUSPEND) {
81511708Sstevel 		axq->pcr_sel_save = ldphysio(axq->reg_basepa +
81521708Sstevel 		    AXQ_SLOT1_PERFCNT_SEL);
81531708Sstevel 		/*
81541708Sstevel 		 * Select l2_io_queue counter by writing L2_IO_Q mux
81551708Sstevel 		 * input to bits 0-6 of perf cntr select reg.
81561708Sstevel 		 */
81571708Sstevel 		reg = axq->pcr_sel_save;
81581708Sstevel 		reg &= ~AXQ_PIC_CLEAR_MASK;
81591708Sstevel 		reg |= L2_IO_Q;
81601708Sstevel 
81611708Sstevel 		stphysio(axq->reg_basepa + AXQ_SLOT1_PERFCNT_SEL, reg);
81621708Sstevel 	}
81631708Sstevel 
81641708Sstevel 	axq->pic_l2_io_q[iter] = ldphysio(axq->reg_basepa + AXQ_SLOT1_PERFCNT0);
81651708Sstevel 
81661708Sstevel 	if (iter == DRMACH_PRE_RESUME) {
81671708Sstevel 		stphysio(axq->reg_basepa + AXQ_SLOT1_PERFCNT_SEL,
81681708Sstevel 		    axq->pcr_sel_save);
81691708Sstevel 	}
81701708Sstevel 
81711708Sstevel 	DRMACH_PR("drmach_s1p_axq_update: axq #%d pic_l2_io_q[%d]=%d\n",
81721708Sstevel 	    ddi_get_instance(axq->dip), iter, axq->pic_l2_io_q[iter]);
81731708Sstevel }
81741708Sstevel 
81751708Sstevel static void
drmach_s1p_schizo_update(drmach_s1p_schizo_t * schizo,drmach_sr_iter_t iter)81761708Sstevel drmach_s1p_schizo_update(drmach_s1p_schizo_t *schizo, drmach_sr_iter_t iter)
81771708Sstevel {
81781708Sstevel 	int	i;
81791708Sstevel 	drmach_s1p_pci_t *pci;
81801708Sstevel 
81811708Sstevel 	if (schizo->csr_basepa == 0x0UL)
81821708Sstevel 		return;
81831708Sstevel 
81841708Sstevel 	schizo->regs[iter].csr =
81851708Sstevel 	    lddphysio(schizo->csr_basepa + SCHIZO_CB_CSR_OFFSET);
81861708Sstevel 	schizo->regs[iter].errctrl =
81871708Sstevel 	    lddphysio(schizo->csr_basepa + SCHIZO_CB_ERRCTRL_OFFSET);
81881708Sstevel 	schizo->regs[iter].errlog =
81891708Sstevel 	    lddphysio(schizo->csr_basepa + SCHIZO_CB_ERRLOG_OFFSET);
81901708Sstevel 
81911708Sstevel 	for (i = 0; i < DRMACH_SCHIZO_PCI_LEAF_MAX; i++) {
81921708Sstevel 		pci = &schizo->pci[i];
81931708Sstevel 		if (pci->dip != NULL && pci->csr_basepa != 0x0UL) {
81941708Sstevel 			pci->regs[iter].slot_intr_state_diag =
81951708Sstevel 			    lddphysio(pci->csr_basepa +
81961708Sstevel 			    COMMON_IB_SLOT_INTR_STATE_DIAG_REG);
81971708Sstevel 
81981708Sstevel 			pci->regs[iter].obio_intr_state_diag =
81991708Sstevel 			    lddphysio(pci->csr_basepa +
82001708Sstevel 			    COMMON_IB_OBIO_INTR_STATE_DIAG_REG);
82011708Sstevel 
82021708Sstevel 			drmach_s1p_intr_map_reg_save(pci, iter);
82031708Sstevel 		}
82041708Sstevel 	}
82051708Sstevel }
82061708Sstevel 
82071708Sstevel /*
82081708Sstevel  * Called post-suspend and pre-resume to snapshot the suspend state
82091708Sstevel  * of slot1 AXQs and Schizos.
82101708Sstevel  */
82111708Sstevel static void
drmach_slot1_pause_update(drmach_slot1_pause_t ** slot1_paused,drmach_sr_iter_t iter)82121708Sstevel drmach_slot1_pause_update(drmach_slot1_pause_t **slot1_paused,
82131708Sstevel     drmach_sr_iter_t iter)
82141708Sstevel {
82151708Sstevel 	int	i, j;
82161708Sstevel 	drmach_slot1_pause_t *slot1;
82171708Sstevel 
82181708Sstevel 	for (i = 0; i < STARCAT_BDSET_MAX; i++) {
82191708Sstevel 		if ((slot1 = slot1_paused[i]) == NULL)
82201708Sstevel 			continue;
82211708Sstevel 
82221708Sstevel 		drmach_s1p_axq_update(&slot1->axq, iter);
82231708Sstevel 		for (j = 0; j < STARCAT_SLOT1_IO_MAX; j++)
82241708Sstevel 			drmach_s1p_schizo_update(&slot1->schizo[j], iter);
82251708Sstevel 	}
82261708Sstevel }
82271708Sstevel 
82281708Sstevel /*
82291708Sstevel  * Starcat hPCI Schizo devices.
82301708Sstevel  *
82311708Sstevel  * The name field is overloaded. NULL means the slot (interrupt concentrator
82321708Sstevel  * bus) is not used. intr_mask is a bit mask representing the 4 possible
82331708Sstevel  * interrupts per slot, on if valid (rio does not use interrupt lines 0, 1).
82341708Sstevel  */
82351708Sstevel static struct {
82361708Sstevel 	char	*name;
82371708Sstevel 	uint8_t	intr_mask;
82381708Sstevel } drmach_schz_slot_intr[][DRMACH_SCHIZO_PCI_LEAF_MAX] = {
82391708Sstevel 	/* Schizo 0 */		/* Schizo 1 */
82401708Sstevel 	{{"C3V0", 0xf},		{"C3V1", 0xf}},		/* slot 0 */
82411708Sstevel 	{{"C5V0", 0xf},		{"C5V1", 0xf}},		/* slot 1 */
82421708Sstevel 	{{"rio", 0xc},		{NULL, 0x0}},		/* slot 2 */
82431708Sstevel 	{{NULL, 0x0},		{NULL, 0x0}},		/* slot 3 */
82441708Sstevel 	{{"sbbc", 0xf},		{NULL, 0x0}},		/* slot 4 */
82451708Sstevel 	{{NULL, 0x0},		{NULL, 0x0}},		/* slot 5 */
82461708Sstevel 	{{NULL, 0x0},		{NULL, 0x0}},		/* slot 6 */
82471708Sstevel 	{{NULL, 0x0},		{NULL, 0x0}}		/* slot 7 */
82481708Sstevel };
82491708Sstevel 
82501708Sstevel /*
82511708Sstevel  * See Schizo Specification, Revision 51 (May 23, 2001), Section 22.4.4
82521708Sstevel  * "Interrupt Registers", Table 22-69, page 306.
82531708Sstevel  */
82541708Sstevel static char *
drmach_schz_internal_ino2str(int ino)82551708Sstevel drmach_schz_internal_ino2str(int ino)
82561708Sstevel {
82571708Sstevel 	int	intr;
82581708Sstevel 
82591708Sstevel 	ASSERT(ino >= 0x30 && ino <= 0x37);
82601708Sstevel 
82611708Sstevel 	intr = ino & 0x7;
82621708Sstevel 	switch (intr) {
82631708Sstevel 		case (0x0):	return ("Uncorrectable ECC error");
82641708Sstevel 		case (0x1):	return ("Correctable ECC error");
82651708Sstevel 		case (0x2):	return ("PCI Bus A Error");
82661708Sstevel 		case (0x3):	return ("PCI Bus B Error");
82671708Sstevel 		case (0x4):	return ("Safari Bus Error");
82681708Sstevel 		default:	return ("Reserved");
82691708Sstevel 	}
82701708Sstevel }
82711708Sstevel 
82721708Sstevel #define	DRMACH_INTR_MASK_SHIFT(ino)	((ino) << 1)
82731708Sstevel 
82741708Sstevel static void
drmach_s1p_decode_slot_intr(int exp,int unum,drmach_s1p_pci_t * pci,int ino,drmach_sr_iter_t iter)82751708Sstevel drmach_s1p_decode_slot_intr(int exp, int unum, drmach_s1p_pci_t *pci,
82761708Sstevel     int ino, drmach_sr_iter_t iter)
82771708Sstevel {
82781708Sstevel 	uint8_t		intr_mask;
82791708Sstevel 	char		*slot_devname;
82801708Sstevel 	char		namebuf[OBP_MAXDRVNAME];
82811708Sstevel 	int		slot, intr_line, slot_valid, intr_valid;
82821708Sstevel 
82831708Sstevel 	ASSERT(ino >= 0 && ino <= 0x1f);
82841708Sstevel 	ASSERT((pci->regs[iter].slot_intr_state_diag &
82851708Sstevel 	    (COMMON_CLEAR_INTR_REG_MASK << DRMACH_INTR_MASK_SHIFT(ino))) !=
82861708Sstevel 	    COMMON_CLEAR_INTR_REG_IDLE);
82871708Sstevel 
82881708Sstevel 	slot = (ino >> 2) & 0x7;
82891708Sstevel 	intr_line = ino & 0x3;
82901708Sstevel 
82911708Sstevel 	slot_devname = drmach_schz_slot_intr[slot][unum].name;
82921708Sstevel 	slot_valid = (slot_devname == NULL) ? 0 : 1;
82931708Sstevel 	if (!slot_valid) {
829411311SSurya.Prakki@Sun.COM 		(void) snprintf(namebuf, sizeof (namebuf), "slot %d (INVALID)",
829511311SSurya.Prakki@Sun.COM 		    slot);
82961708Sstevel 		slot_devname = namebuf;
82971708Sstevel 	}
82981708Sstevel 
82991708Sstevel 	intr_mask = drmach_schz_slot_intr[slot][unum].intr_mask;
83001708Sstevel 	intr_valid = (1 << intr_line) & intr_mask;
83011708Sstevel 
83021708Sstevel 	prom_printf("IO%d/P%d PCI slot interrupt: ino=0x%x, source device=%s, "
83031708Sstevel 	    "interrupt line=%d%s\n", exp, unum, ino, slot_devname, intr_line,
83041708Sstevel 	    (slot_valid && !intr_valid) ? " (INVALID)" : "");
83051708Sstevel }
83061708Sstevel 
83071708Sstevel /*
83081708Sstevel  * Log interrupt source device info for all valid, pending interrupts
83091708Sstevel  * on each Schizo PCI leaf. Called if Schizo has logged a Safari bus
83101708Sstevel  * error in the error ctrl reg.
83111708Sstevel  */
83121708Sstevel static void
drmach_s1p_schizo_log_intr(drmach_s1p_schizo_t * schizo,int exp,int unum,drmach_sr_iter_t iter)83131708Sstevel drmach_s1p_schizo_log_intr(drmach_s1p_schizo_t *schizo, int exp,
83141708Sstevel     int unum, drmach_sr_iter_t iter)
83151708Sstevel {
83161708Sstevel 	uint64_t	reg;
83171708Sstevel 	int		i, n, ino;
83181708Sstevel 	drmach_s1p_pci_t *pci;
83191708Sstevel 
83201708Sstevel 	ASSERT(exp >= 0 && exp < STARCAT_BDSET_MAX);
83211708Sstevel 	ASSERT(unum < STARCAT_SLOT1_IO_MAX);
83221708Sstevel 
83231708Sstevel 	/*
83241708Sstevel 	 * Check the saved interrupt mapping registers. If interrupt is valid,
83251708Sstevel 	 * map the ino to the Schizo source device and check that the pci
83261708Sstevel 	 * slot and interrupt line are valid.
83271708Sstevel 	 */
83281708Sstevel 	for (i = 0; i < DRMACH_SCHIZO_PCI_LEAF_MAX; i++) {
83291708Sstevel 		pci = &schizo->pci[i];
83301708Sstevel 		for (n = 0; n < pci->regs[iter].nmap_regs; n++) {
83311708Sstevel 			reg = pci->regs[iter].intr_map_regs[n];
83321708Sstevel 			if (reg & COMMON_INTR_MAP_REG_VALID) {
83331708Sstevel 				ino = reg & COMMON_INTR_MAP_REG_INO;
83341708Sstevel 
83351708Sstevel 				if (ino <= 0x1f) {
83361708Sstevel 					/*
83371708Sstevel 					 * PCI slot interrupt
83381708Sstevel 					 */
83391708Sstevel 					drmach_s1p_decode_slot_intr(exp, unum,
83401708Sstevel 					    pci, ino, iter);
83411708Sstevel 				} else if (ino <= 0x2f) {
83421708Sstevel 					/*
83431708Sstevel 					 * OBIO interrupt
83441708Sstevel 					 */
83451708Sstevel 					prom_printf("IO%d/P%d OBIO interrupt: "
83461708Sstevel 					    "ino=0x%x\n", exp, unum, ino);
83471708Sstevel 				} else if (ino <= 0x37) {
83481708Sstevel 					/*
83491708Sstevel 					 * Internal interrupt
83501708Sstevel 					 */
83511708Sstevel 					prom_printf("IO%d/P%d Internal "
83521708Sstevel 					    "interrupt: ino=0x%x (%s)\n",
83531708Sstevel 					    exp, unum, ino,
83541708Sstevel 					    drmach_schz_internal_ino2str(ino));
83551708Sstevel 				} else {
83561708Sstevel 					/*
83571708Sstevel 					 * NewLink interrupt
83581708Sstevel 					 */
83591708Sstevel 					prom_printf("IO%d/P%d NewLink "
83601708Sstevel 					    "interrupt: ino=0x%x\n", exp,
83611708Sstevel 					    unum, ino);
83621708Sstevel 				}
83631708Sstevel 
83641708Sstevel 				DRMACH_PR("drmach_s1p_schizo_log_intr: "
83651708Sstevel 				    "exp=%d, schizo=%d, pci_leaf=%c, "
83661708Sstevel 				    "ino=0x%x, intr_map_reg=0x%lx\n",
83671708Sstevel 				    exp, unum, (i == 0) ? 'A' : 'B', ino, reg);
83681708Sstevel 			}
83691708Sstevel 		}
83701708Sstevel 	}
83711708Sstevel }
83721708Sstevel 
83731708Sstevel /*
83741708Sstevel  * See Schizo Specification, Revision 51 (May 23, 2001), Section 22.2.4
83751708Sstevel  * "Safari Error Control/Log Registers", Table 22-11, page 248.
83761708Sstevel  */
83771708Sstevel #define	DRMACH_SCHIZO_SAFARI_UNMAPPED_ERR	(0x1ull << 4)
83781708Sstevel 
83791708Sstevel /*
83801708Sstevel  * Check for possible error indicators prior to resuming the
83811708Sstevel  * AXQ driver, which will de-assert slot1 AXQ_DOMCTRL_PAUSE.
83821708Sstevel  */
83831708Sstevel static void
drmach_slot1_pause_verify(drmach_slot1_pause_t ** slot1_paused,drmach_sr_iter_t iter)83841708Sstevel drmach_slot1_pause_verify(drmach_slot1_pause_t **slot1_paused,
83851708Sstevel     drmach_sr_iter_t iter)
83861708Sstevel {
83871708Sstevel 	int	i, j;
83881708Sstevel 	int 	errflag = 0;
83891708Sstevel 	drmach_slot1_pause_t *slot1;
83901708Sstevel 
83911708Sstevel 	/*
83921708Sstevel 	 * Check for logged schizo bus error and pending interrupts.
83931708Sstevel 	 */
83941708Sstevel 	for (i = 0; i < STARCAT_BDSET_MAX; i++) {
83951708Sstevel 		if ((slot1 = slot1_paused[i]) == NULL)
83961708Sstevel 			continue;
83971708Sstevel 
83981708Sstevel 		for (j = 0; j < STARCAT_SLOT1_IO_MAX; j++) {
83991708Sstevel 			if (slot1->schizo[j].csr_basepa == 0x0UL)
84001708Sstevel 				continue;
84011708Sstevel 
84021708Sstevel 			if (slot1->schizo[j].regs[iter].errlog &
84031708Sstevel 			    DRMACH_SCHIZO_SAFARI_UNMAPPED_ERR) {
84041708Sstevel 				if (!errflag) {
84051708Sstevel 					prom_printf("DR WARNING: interrupt "
84061708Sstevel 					    "attempt detected during "
84071708Sstevel 					    "copy-rename (%s):\n",
84081708Sstevel 					    (iter == DRMACH_POST_SUSPEND) ?
84091708Sstevel 					    "post suspend" : "pre resume");
84101708Sstevel 					++errflag;
84111708Sstevel 				}
84121708Sstevel 				drmach_s1p_schizo_log_intr(&slot1->schizo[j],
84131708Sstevel 				    i, j, iter);
84141708Sstevel 			}
84151708Sstevel 		}
84161708Sstevel 	}
84171708Sstevel 
84181708Sstevel 	/*
84191708Sstevel 	 * Check for changes in axq l2_io_q performance counters (2nd pass only)
84201708Sstevel 	 */
84211708Sstevel 	if (iter == DRMACH_PRE_RESUME) {
84221708Sstevel 		for (i = 0; i < STARCAT_BDSET_MAX; i++) {
84231708Sstevel 			if ((slot1 = slot1_paused[i]) == NULL)
84241708Sstevel 				continue;
84251708Sstevel 
84261708Sstevel 			if (slot1->axq.pic_l2_io_q[DRMACH_POST_SUSPEND] !=
84271708Sstevel 			    slot1->axq.pic_l2_io_q[DRMACH_PRE_RESUME]) {
84281708Sstevel 				prom_printf("DR WARNING: IO transactions "
84291708Sstevel 				    "detected on IO%d during copy-rename: "
84301708Sstevel 				    "AXQ l2_io_q performance counter "
84311708Sstevel 				    "start=%d, end=%d\n", i,
84321708Sstevel 				    slot1->axq.pic_l2_io_q[DRMACH_POST_SUSPEND],
84331708Sstevel 				    slot1->axq.pic_l2_io_q[DRMACH_PRE_RESUME]);
84341708Sstevel 			}
84351708Sstevel 		}
84361708Sstevel 	}
84371708Sstevel }
84381708Sstevel 
84391708Sstevel struct drmach_sr_list {
84401708Sstevel 	dev_info_t		*dip;
84411708Sstevel 	struct drmach_sr_list	*next;
84421708Sstevel 	struct drmach_sr_list	*prev;
84431708Sstevel };
84441708Sstevel 
84451708Sstevel static struct drmach_sr_ordered {
84461708Sstevel 	char			*name;
84471708Sstevel 	struct drmach_sr_list	*ring;
84481708Sstevel } drmach_sr_ordered[] = {
84491708Sstevel 	{ "iosram",			NULL },
84501708Sstevel 	{ "address-extender-queue",	NULL },
84511708Sstevel 	{ NULL,				NULL }, /* terminator -- required */
84521708Sstevel };
84531708Sstevel 
84541708Sstevel static void
drmach_sr_insert(struct drmach_sr_list ** lp,dev_info_t * dip)84551708Sstevel drmach_sr_insert(struct drmach_sr_list **lp, dev_info_t *dip)
84561708Sstevel {
84571708Sstevel 	struct drmach_sr_list *np;
84581708Sstevel 
845911311SSurya.Prakki@Sun.COM 	DRMACH_PR("drmach_sr_insert: adding dip %p\n", (void *)dip);
84601708Sstevel 
84611708Sstevel 	np = (struct drmach_sr_list *)kmem_alloc(
846211066Srafael.vanoni@sun.com 	    sizeof (struct drmach_sr_list), KM_SLEEP);
84631708Sstevel 
84641708Sstevel 	ndi_hold_devi(dip);
84651708Sstevel 	np->dip = dip;
84661708Sstevel 
84671708Sstevel 	if (*lp == NULL) {
84681708Sstevel 		/* establish list */
84691708Sstevel 		*lp = np->next = np->prev = np;
84701708Sstevel 	} else {
84711708Sstevel 		/* place new node behind head node on ring list */
84721708Sstevel 		np->prev = (*lp)->prev;
84731708Sstevel 		np->next = *lp;
84741708Sstevel 		np->prev->next = np;
84751708Sstevel 		np->next->prev = np;
84761708Sstevel 	}
84771708Sstevel }
84781708Sstevel 
84791708Sstevel static void
drmach_sr_delete(struct drmach_sr_list ** lp,dev_info_t * dip)84801708Sstevel drmach_sr_delete(struct drmach_sr_list **lp, dev_info_t *dip)
84811708Sstevel {
848211311SSurya.Prakki@Sun.COM 	DRMACH_PR("drmach_sr_delete: searching for dip %p\n", (void *)dip);
84831708Sstevel 
84841708Sstevel 	if (*lp) {
84851708Sstevel 		struct drmach_sr_list *xp;
84861708Sstevel 
84871708Sstevel 		/* start search with mostly likely node */
84881708Sstevel 		xp = (*lp)->prev;
84891708Sstevel 		do {
84901708Sstevel 			if (xp->dip == dip) {
84911708Sstevel 				xp->prev->next = xp->next;
84921708Sstevel 				xp->next->prev = xp->prev;
84931708Sstevel 
84941708Sstevel 				if (xp == *lp)
84951708Sstevel 					*lp = xp->next;
84961708Sstevel 				if (xp == *lp)
84971708Sstevel 					*lp = NULL;
84981708Sstevel 				xp->dip = NULL;
84991708Sstevel 				ndi_rele_devi(dip);
85001708Sstevel 				kmem_free(xp, sizeof (*xp));
85011708Sstevel 
85021708Sstevel 				DRMACH_PR("drmach_sr_delete:"
850311311SSurya.Prakki@Sun.COM 				    " disposed sr node for dip %p",
850411311SSurya.Prakki@Sun.COM 				    (void *)dip);
85051708Sstevel 				return;
85061708Sstevel 			}
85071708Sstevel 
85081708Sstevel 			DRMACH_PR("drmach_sr_delete: still searching\n");
85091708Sstevel 
85101708Sstevel 			xp = xp->prev;
85111708Sstevel 		} while (xp != (*lp)->prev);
85121708Sstevel 	}
85131708Sstevel 
85141708Sstevel 	/* every dip should be found during resume */
851511311SSurya.Prakki@Sun.COM 	DRMACH_PR("ERROR: drmach_sr_delete: can't find dip %p", (void *)dip);
85161708Sstevel }
85171708Sstevel 
85181708Sstevel int
drmach_verify_sr(dev_info_t * dip,int sflag)85191708Sstevel drmach_verify_sr(dev_info_t *dip, int sflag)
85201708Sstevel {
85211708Sstevel 	int	rv;
85221708Sstevel 	int	len;
85231708Sstevel 	char    name[OBP_MAXDRVNAME];
85241708Sstevel 
85251708Sstevel 	if (drmach_slot1_pause_debug) {
85261708Sstevel 		if (sflag && drmach_slot1_pause_init) {
85271708Sstevel 			drmach_slot1_pause_free(drmach_slot1_paused);
85281708Sstevel 			drmach_slot1_pause_init = 0;
85291708Sstevel 		} else if (!sflag && !drmach_slot1_pause_init) {
85301708Sstevel 			/* schedule init for next suspend */
85311708Sstevel 			drmach_slot1_pause_init = 1;
85321708Sstevel 		}
85331708Sstevel 	}
85341708Sstevel 
85351708Sstevel 	rv = ddi_getproplen(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
853611066Srafael.vanoni@sun.com 	    "name", &len);
85371708Sstevel 	if (rv == DDI_PROP_SUCCESS) {
85381708Sstevel 		int		portid;
85391708Sstevel 		uint64_t	reg;
85401708Sstevel 		struct drmach_sr_ordered *op;
85411708Sstevel 
85421708Sstevel 		rv = ddi_getlongprop_buf(DDI_DEV_T_ANY, dip,
854311066Srafael.vanoni@sun.com 		    DDI_PROP_DONTPASS, "name", (caddr_t)name, &len);
85441708Sstevel 
85451708Sstevel 		if (rv != DDI_PROP_SUCCESS)
85461708Sstevel 			return (0);
85471708Sstevel 
85481708Sstevel 		if (drmach_slot1_pause_debug && sflag &&
85491708Sstevel 		    drmach_is_slot1_pause_axq(dip, name, &portid, &reg)) {
85501708Sstevel 			drmach_slot1_pause_add_axq(dip, name, portid, reg,
85511708Sstevel 			    drmach_slot1_paused);
85521708Sstevel 		}
85531708Sstevel 
85541708Sstevel 		for (op = drmach_sr_ordered; op->name; op++) {
85551708Sstevel 			if (strncmp(op->name, name, strlen(op->name)) == 0) {
85561708Sstevel 				if (sflag)
85571708Sstevel 					drmach_sr_insert(&op->ring, dip);
85581708Sstevel 				else
85591708Sstevel 					drmach_sr_delete(&op->ring, dip);
85601708Sstevel 				return (1);
85611708Sstevel 			}
85621708Sstevel 		}
85631708Sstevel 	}
85641708Sstevel 
85651708Sstevel 	return (0);
85661708Sstevel }
85671708Sstevel 
85681708Sstevel static void
drmach_sr_dip(dev_info_t * dip,int suspend)85691708Sstevel drmach_sr_dip(dev_info_t *dip, int suspend)
85701708Sstevel {
85711708Sstevel 	int	 rv;
85721708Sstevel 	major_t	 maj;
85731708Sstevel 	char	*name, *name_addr, *aka;
85741708Sstevel 
85751708Sstevel 	if ((name = ddi_get_name(dip)) == NULL)
85761708Sstevel 		name = "<null name>";
85771708Sstevel 	else if ((maj = ddi_name_to_major(name)) != -1)
85781708Sstevel 		aka = ddi_major_to_name(maj);
85791708Sstevel 	else
85801708Sstevel 		aka = "<unknown>";
85811708Sstevel 
85821708Sstevel 	if ((name_addr = ddi_get_name_addr(dip)) == NULL)
85831708Sstevel 		name_addr = "<null>";
85841708Sstevel 
85851708Sstevel 	prom_printf("\t%s %s@%s (aka %s)\n",
858611066Srafael.vanoni@sun.com 	    suspend ? "suspending" : "resuming",
858711066Srafael.vanoni@sun.com 	    name, name_addr, aka);
85881708Sstevel 
85891708Sstevel 	if (suspend) {
85901708Sstevel 		rv = devi_detach(dip, DDI_SUSPEND);
85911708Sstevel 	} else {
85921708Sstevel 		rv = devi_attach(dip, DDI_RESUME);
85931708Sstevel 	}
85941708Sstevel 
85951708Sstevel 	if (rv != DDI_SUCCESS) {
85961708Sstevel 		prom_printf("\tFAILED to %s %s@%s\n",
859711066Srafael.vanoni@sun.com 		    suspend ? "suspend" : "resume",
859811066Srafael.vanoni@sun.com 		    name, name_addr);
85991708Sstevel 	}
86001708Sstevel }
86011708Sstevel 
86021708Sstevel void
drmach_suspend_last()86031708Sstevel drmach_suspend_last()
86041708Sstevel {
86051708Sstevel 	struct drmach_sr_ordered *op;
86061708Sstevel 
86071708Sstevel 	if (drmach_slot1_pause_debug)
86081708Sstevel 		drmach_slot1_pause_add_io(drmach_slot1_paused);
86091708Sstevel 
86101708Sstevel 	/*
86111708Sstevel 	 * The ordering array declares the strict sequence in which
86121708Sstevel 	 * the named drivers are to suspended. Each element in
86131708Sstevel 	 * the array may have a double-linked ring list of driver
86141708Sstevel 	 * instances (dip) in the order in which they were presented
86151708Sstevel 	 * to drmach_verify_sr. If present, walk the list in the
86161708Sstevel 	 * forward direction to suspend each instance.
86171708Sstevel 	 */
86181708Sstevel 	for (op = drmach_sr_ordered; op->name; op++) {
86191708Sstevel 		if (op->ring) {
86201708Sstevel 			struct drmach_sr_list *rp;
86211708Sstevel 
86221708Sstevel 			rp = op->ring;
86231708Sstevel 			do {
86241708Sstevel 				drmach_sr_dip(rp->dip, 1);
86251708Sstevel 				rp = rp->next;
86261708Sstevel 			} while (rp != op->ring);
86271708Sstevel 		}
86281708Sstevel 	}
86291708Sstevel 
86301708Sstevel 	if (drmach_slot1_pause_debug) {
86311708Sstevel 		drmach_slot1_pause_update(drmach_slot1_paused,
86321708Sstevel 		    DRMACH_POST_SUSPEND);
86331708Sstevel 		drmach_slot1_pause_verify(drmach_slot1_paused,
86341708Sstevel 		    DRMACH_POST_SUSPEND);
86351708Sstevel 	}
86361708Sstevel }
86371708Sstevel 
86381708Sstevel void
drmach_resume_first()86391708Sstevel drmach_resume_first()
86401708Sstevel {
86411708Sstevel 	struct drmach_sr_ordered *op = drmach_sr_ordered +
864211066Srafael.vanoni@sun.com 	    (sizeof (drmach_sr_ordered) / sizeof (drmach_sr_ordered[0]));
86431708Sstevel 
86441708Sstevel 	if (drmach_slot1_pause_debug) {
86451708Sstevel 		drmach_slot1_pause_update(drmach_slot1_paused,
86461708Sstevel 		    DRMACH_PRE_RESUME);
86471708Sstevel 		drmach_slot1_pause_verify(drmach_slot1_paused,
86481708Sstevel 		    DRMACH_PRE_RESUME);
86491708Sstevel 	}
86501708Sstevel 
86511708Sstevel 	op -= 1;	/* point at terminating element */
86521708Sstevel 
86531708Sstevel 	/*
86541708Sstevel 	 * walk ordering array and rings backwards to resume dips
86551708Sstevel 	 * in reverse order in which they were suspended
86561708Sstevel 	 */
86571708Sstevel 	while (--op >= drmach_sr_ordered) {
86581708Sstevel 		if (op->ring) {
86591708Sstevel 			struct drmach_sr_list *rp;
86601708Sstevel 
86611708Sstevel 			rp = op->ring->prev;
86621708Sstevel 			do {
86631708Sstevel 				drmach_sr_dip(rp->dip, 0);
86641708Sstevel 				rp = rp->prev;
86651708Sstevel 			} while (rp != op->ring->prev);
86661708Sstevel 		}
86671708Sstevel 	}
86681708Sstevel }
86691708Sstevel 
86701708Sstevel /*
86711708Sstevel  * Log a DR sysevent.
86721708Sstevel  * Return value: 0 success, non-zero failure.
86731708Sstevel  */
86741708Sstevel int
drmach_log_sysevent(int board,char * hint,int flag,int verbose)86751708Sstevel drmach_log_sysevent(int board, char *hint, int flag, int verbose)
86761708Sstevel {
86771708Sstevel 	sysevent_t			*ev;
86781708Sstevel 	sysevent_id_t			eid;
86791708Sstevel 	int				rv, km_flag;
86801708Sstevel 	sysevent_value_t		evnt_val;
86811708Sstevel 	sysevent_attr_list_t		*evnt_attr_list = NULL;
86821708Sstevel 	char				attach_pnt[MAXNAMELEN];
86831708Sstevel 
86841708Sstevel 	km_flag = (flag == SE_SLEEP) ? KM_SLEEP : KM_NOSLEEP;
86851708Sstevel 	attach_pnt[0] = '\0';
86861708Sstevel 	if (drmach_board_name(board, attach_pnt, MAXNAMELEN)) {
86871708Sstevel 		rv = -1;
86881708Sstevel 		goto logexit;
86891708Sstevel 	}
86901708Sstevel 	if (verbose)
86911708Sstevel 		DRMACH_PR("drmach_log_sysevent: %s %s, flag: %d, verbose: %d\n",
869211066Srafael.vanoni@sun.com 		    attach_pnt, hint, flag, verbose);
86931708Sstevel 
86941708Sstevel 	if ((ev = sysevent_alloc(EC_DR, ESC_DR_AP_STATE_CHANGE,
869511066Srafael.vanoni@sun.com 	    SUNW_KERN_PUB"dr", km_flag)) == NULL) {
86961708Sstevel 		rv = -2;
86971708Sstevel 		goto logexit;
86981708Sstevel 	}
86991708Sstevel 	evnt_val.value_type = SE_DATA_TYPE_STRING;
87001708Sstevel 	evnt_val.value.sv_string = attach_pnt;
87011708Sstevel 	if ((rv = sysevent_add_attr(&evnt_attr_list, DR_AP_ID,
870211066Srafael.vanoni@sun.com 	    &evnt_val, km_flag)) != 0)
87031708Sstevel 		goto logexit;
87041708Sstevel 
87051708Sstevel 	evnt_val.value_type = SE_DATA_TYPE_STRING;
87061708Sstevel 	evnt_val.value.sv_string = hint;
87071708Sstevel 	if ((rv = sysevent_add_attr(&evnt_attr_list, DR_HINT,
870811066Srafael.vanoni@sun.com 	    &evnt_val, km_flag)) != 0) {
87091708Sstevel 		sysevent_free_attr(evnt_attr_list);
87101708Sstevel 		goto logexit;
87111708Sstevel 	}
87121708Sstevel 
87131708Sstevel 	(void) sysevent_attach_attributes(ev, evnt_attr_list);
87141708Sstevel 
87151708Sstevel 	/*
87161708Sstevel 	 * Log the event but do not sleep waiting for its
87171708Sstevel 	 * delivery. This provides insulation from syseventd.
87181708Sstevel 	 */
87191708Sstevel 	rv = log_sysevent(ev, SE_NOSLEEP, &eid);
87201708Sstevel 
87211708Sstevel logexit:
87221708Sstevel 	if (ev)
87231708Sstevel 		sysevent_free(ev);
87241708Sstevel 	if ((rv != 0) && verbose)
87251708Sstevel 		cmn_err(CE_WARN,
872611066Srafael.vanoni@sun.com 		    "drmach_log_sysevent failed (rv %d) for %s  %s\n",
872711066Srafael.vanoni@sun.com 		    rv, attach_pnt, hint);
87281708Sstevel 
87291708Sstevel 	return (rv);
87301708Sstevel }
87311708Sstevel 
87321708Sstevel /*
87331708Sstevel  * Initialize the mem_slice portion of a claim/unconfig/unclaim mailbox message.
87341708Sstevel  * Only the valid entries are modified, so the array should be zeroed out
87351708Sstevel  * initially.
87361708Sstevel  */
87371708Sstevel static void
drmach_msg_memslice_init(dr_memslice_t slice_arr[])87381708Sstevel drmach_msg_memslice_init(dr_memslice_t slice_arr[]) {
87391708Sstevel 	int	i;
87401708Sstevel 	char	c;
87411708Sstevel 
87421708Sstevel 	ASSERT(mutex_owned(&drmach_slice_table_lock));
87431708Sstevel 
87441708Sstevel 	for (i = 0; i < AXQ_MAX_EXP; i++) {
87451708Sstevel 		c = drmach_slice_table[i];
87461708Sstevel 
87471708Sstevel 		if (c & 0x20) {
87481708Sstevel 			slice_arr[i].valid = 1;
87491708Sstevel 			slice_arr[i].slice = c & 0x1f;
87501708Sstevel 		}
87511708Sstevel 	}
87521708Sstevel }
87531708Sstevel 
87541708Sstevel /*
87551708Sstevel  * Initialize the mem_regs portion of a claim/unconfig/unclaim mailbox message.
87561708Sstevel  * Only the valid entries are modified, so the array should be zeroed out
87571708Sstevel  * initially.
87581708Sstevel  */
87591708Sstevel static void
drmach_msg_memregs_init(dr_memregs_t regs_arr[])87601708Sstevel drmach_msg_memregs_init(dr_memregs_t regs_arr[]) {
87611708Sstevel 	int		rv, exp, mcnum, bank;
87621708Sstevel 	uint64_t	madr;
87631708Sstevel 	drmachid_t	id;
87641708Sstevel 	drmach_board_t	*bp;
87651708Sstevel 	drmach_mem_t	*mp;
87661708Sstevel 	dr_memregs_t	*memregs;
87671708Sstevel 
87681708Sstevel 	/* CONSTCOND */
87691708Sstevel 	ASSERT(DRMACH_MC_NBANKS == (PMBANKS_PER_PORT * LMBANKS_PER_PMBANK));
87701708Sstevel 
87711708Sstevel 	for (exp = 0; exp < 18; exp++) {
87721708Sstevel 		rv = drmach_array_get(drmach_boards,
87731708Sstevel 		    DRMACH_EXPSLOT2BNUM(exp, 0), &id);
87741708Sstevel 		ASSERT(rv == 0);	/* should never be out of bounds */
87751708Sstevel 		if (id == NULL) {
87761708Sstevel 			continue;
87771708Sstevel 		}
87781708Sstevel 
87791708Sstevel 		memregs = &regs_arr[exp];
87801708Sstevel 		bp = (drmach_board_t *)id;
87811708Sstevel 		for (mp = bp->mem; mp != NULL; mp = mp->next) {
87821708Sstevel 			mcnum = mp->dev.portid & 0x3;
87831708Sstevel 			for (bank = 0; bank < DRMACH_MC_NBANKS; bank++) {
87841708Sstevel 				drmach_mem_read_madr(mp, bank, &madr);
87851708Sstevel 				if (madr & DRMACH_MC_VALID_MASK) {
87861708Sstevel 					DRMACH_PR("%d.%d.%d.madr = 0x%lx\n",
878711066Srafael.vanoni@sun.com 					    exp, mcnum, bank, madr);
87881708Sstevel 					memregs->madr[mcnum][bank].hi =
87891708Sstevel 					    DRMACH_U64_TO_MCREGHI(madr);
87901708Sstevel 					memregs->madr[mcnum][bank].lo =
87911708Sstevel 					    DRMACH_U64_TO_MCREGLO(madr);
87921708Sstevel 				}
87931708Sstevel 			}
87941708Sstevel 		}
87951708Sstevel 	}
87961708Sstevel }
87971708Sstevel 
87981708Sstevel /*
87991708Sstevel  * Do not allow physical address range modification if either board on this
88001708Sstevel  * expander has processors in NULL LPA mode (CBASE=CBND=NULL).
88011708Sstevel  *
88021708Sstevel  * A side effect of NULL proc LPA mode in Starcat SSM is that local reads will
88031708Sstevel  * install the cache line as owned/dirty as a result of the RTSR transaction.
88041708Sstevel  * See section 5.2.3 of the Safari spec.  All processors will read the bus sync
88051708Sstevel  * list before the rename after flushing local caches.  When copy-rename
88061708Sstevel  * requires changing the physical address ranges (i.e. smaller memory target),
88071708Sstevel  * the bus sync list contains physical addresses that will not exist after the
88081708Sstevel  * rename.  If these cache lines are owned due to a RTSR, a system error can
88091708Sstevel  * occur following the rename when these cache lines are evicted and a writeback
88101708Sstevel  * is attempted.
88111708Sstevel  *
88121708Sstevel  * Incoming parameter represents either the copy-rename source or a candidate
88131708Sstevel  * target memory board.  On Starcat, only slot0 boards may have memory.
88141708Sstevel  */
88151708Sstevel int
drmach_allow_memrange_modify(drmachid_t s0id)88161708Sstevel drmach_allow_memrange_modify(drmachid_t s0id)
88171708Sstevel {
88181708Sstevel 	drmach_board_t	*s0bp, *s1bp;
88191708Sstevel 	drmachid_t	s1id;
88201708Sstevel 	int		rv;
88211708Sstevel 
88221708Sstevel 	s0bp = s0id;
88231708Sstevel 
88241708Sstevel 	ASSERT(DRMACH_IS_BOARD_ID(s0id));
88251708Sstevel 	ASSERT(DRMACH_BNUM2SLOT(s0bp->bnum) == 0);
88261708Sstevel 
88271708Sstevel 	if (s0bp->flags & DRMACH_NULL_PROC_LPA) {
88281708Sstevel 		/*
88291708Sstevel 		 * This is reason enough to fail the request, no need
88301708Sstevel 		 * to check the device list for cpus.
88311708Sstevel 		 */
88321708Sstevel 		return (0);
88331708Sstevel 	}
88341708Sstevel 
88351708Sstevel 	/*
88361708Sstevel 	 * Check for MCPU board on the same expander.
88371708Sstevel 	 *
88381708Sstevel 	 * The board flag DRMACH_NULL_PROC_LPA can be set for all board
88391708Sstevel 	 * types, as it is derived at from the POST gdcd board flag
88401708Sstevel 	 * L1SSFLG_THIS_L1_NULL_PROC_LPA, which can be set (and should be
88411708Sstevel 	 * ignored) for boards with no processors.  Since NULL proc LPA
88421708Sstevel 	 * applies only to processors, we walk the devices array to detect
88431708Sstevel 	 * MCPUs.
88441708Sstevel 	 */
88451708Sstevel 	rv = drmach_array_get(drmach_boards, s0bp->bnum + 1, &s1id);
88461708Sstevel 	s1bp = s1id;
88471708Sstevel 	if (rv == 0 && s1bp != NULL) {
88481708Sstevel 
88491708Sstevel 		ASSERT(DRMACH_IS_BOARD_ID(s1id));
88501708Sstevel 		ASSERT(DRMACH_BNUM2SLOT(s1bp->bnum) == 1);
88511708Sstevel 		ASSERT(DRMACH_BNUM2EXP(s0bp->bnum) ==
88521708Sstevel 		    DRMACH_BNUM2EXP(s1bp->bnum));
88531708Sstevel 
88541708Sstevel 		if ((s1bp->flags & DRMACH_NULL_PROC_LPA) &&
88551708Sstevel 		    s1bp->devices != NULL) {
88561708Sstevel 			int		d_idx;
88571708Sstevel 			drmachid_t	d_id;
88581708Sstevel 
88591708Sstevel 			rv = drmach_array_first(s1bp->devices, &d_idx, &d_id);
88601708Sstevel 			while (rv == 0) {
88611708Sstevel 				if (DRMACH_IS_CPU_ID(d_id)) {
88621708Sstevel 					/*
88631708Sstevel 					 * Fail MCPU in NULL LPA mode.
88641708Sstevel 					 */
88651708Sstevel 					return (0);
88661708Sstevel 				}
88671708Sstevel 
88681708Sstevel 				rv = drmach_array_next(s1bp->devices, &d_idx,
88691708Sstevel 				    &d_id);
88701708Sstevel 			}
88711708Sstevel 		}
88721708Sstevel 	}
88731708Sstevel 
88741708Sstevel 	return (1);
88751708Sstevel }
8876